{"version":3,"sources":["../../../src/server/app-render/app-render-scheduling.ts"],"sourcesContent":["import { InvariantError } from '../../shared/lib/invariant-error'\nimport { unpatchedSetImmediate } from '../node-environment-extensions/fast-set-immediate.external'\n\n/*\n==========================\n| Background             |\n==========================\n\nNode.js does not guarantee that two timers scheduled back to back will run\non the same iteration of the event loop:\n\n```ts\nsetTimeout(one, 0)\nsetTimeout(two, 0)\n```\n\nInternally, each timer is assigned a `_idleStart` property that holds\nan internal libuv timestamp in millisecond resolution.\nThis will be used to determine if the timer is already \"expired\" and should be executed.\nHowever, even in sync code, it's possible for two timers to get different `_idleStart` values.\nThis can cause one of the timers to be executed, and the other to be delayed until the next timer phase.\n\nThe delaying happens [here](https://github.com/nodejs/node/blob/c208ffc66bb9418ff026c4e3fa82e5b4387bd147/lib/internal/timers.js#L556-L564).\nand can be debugged by running node with `NODE_DEBUG=timer`.\n\nThe easiest way to observe it is to run this program in a loop until it exits with status 1:\n\n```\n// test.js\n\nlet immediateRan = false\nconst t1 = setTimeout(() => {\n  console.log('timeout 1')\n  setImmediate(() => {\n    console.log('immediate 1')\n    immediateRan = true\n  })\n})\n\nconst t2 = setTimeout(() => {\n  console.log('timeout 2')\n  if (immediateRan) {\n    console.log('immediate ran before the second timeout!')\n    console.log(\n      `t1._idleStart: ${t1._idleStart}, t2_idleStart: ${t2._idleStart}`\n    );\n    process.exit(1)\n  }\n})\n```\n\n```bash\n#!/usr/bin/env bash\n\ni=1;\nwhile true; do\n  output=\"$(NODE_DEBUG=timer node test.js 2>&1)\";\n  if [ \"$?\" -eq 1 ]; then\n    echo \"failed after $i iterations\";\n    echo \"$output\";\n    break;\n  fi;\n  i=$((i+1));\ndone\n```\n\nIf `t2` is deferred to the next iteration of the event loop,\nthen the immediate scheduled from inside `t1` will run first.\nWhen this occurs, `_idleStart` is reliably different between `t1` and `t2`.\n\n==========================\n| Solution               |\n==========================\n\nWe can guarantee that multiple timers (with the same delay, usually `0`)\nrun together without any delays by making sure that their `_idleStart`s are the same,\nbecause that's what's used to determine if a timer should be deferred or not.\nLuckily, this property is currently exposed to userland and mutable,\nso we can patch it.\n\nAnother related trick we could potentially apply is making\na timer immediately be considered expired by doing  `timer._idleStart -= 2`.\n(the value must be more than `1`, the delay that actually gets set for `setTimeout(cb, 0)`).\nThis makes node view this timer as \"a 1ms timer scheduled 2ms ago\",\nmeaning that it should definitely run in the next timer phase.\nHowever, I'm not confident we know all the side effects of doing this,\nso for now, simply ensuring coordination is enough.\n*/\n\nlet shouldAttemptPatching = true\n\nfunction warnAboutTimers() {\n  console.warn(\n    \"Next.js cannot guarantee that Cache Components will run as expected due to the current runtime's implementation of `setTimeout()`.\\nPlease report a github issue here: https://github.com/vercel/next.js/issues/new/\"\n  )\n}\n\n/**\n * Allows scheduling multiple timers (equivalent to `setTimeout(cb, delayMs)`)\n * that are guaranteed to run in the same iteration of the event loop.\n *\n * @param delayMs - the delay to pass to `setTimeout`. (default: 0)\n *\n * */\nexport function createAtomicTimerGroup(delayMs = 0) {\n  if (process.env.NEXT_RUNTIME === 'edge') {\n    throw new InvariantError(\n      'createAtomicTimerGroup cannot be called in the edge runtime'\n    )\n  } else {\n    let isFirstCallback = true\n    let firstTimerIdleStart: number | null = null\n    let didFirstTimerRun = false\n\n    // As a sanity check, we schedule an immediate from the first timeout\n    // to check if the execution was interrupted (i.e. if it ran between the timeouts).\n    // Note that we're deliberately bypassing the \"fast setImmediate\" patch here --\n    // otherwise, this check would always fail, because the immediate\n    // would always run before the second timeout.\n    let didImmediateRun = false\n    function runFirstCallback(callback: () => void) {\n      didFirstTimerRun = true\n      if (shouldAttemptPatching) {\n        unpatchedSetImmediate(() => {\n          didImmediateRun = true\n        })\n      }\n      return callback()\n    }\n\n    function runSubsequentCallback(callback: () => void) {\n      if (shouldAttemptPatching) {\n        if (didImmediateRun) {\n          // If the immediate managed to run between the timers, then we're not\n          // able to provide the guarantees that we're supposed to\n          shouldAttemptPatching = false\n          warnAboutTimers()\n        }\n      }\n      return callback()\n    }\n\n    return function scheduleTimeout(callback: () => void) {\n      if (didFirstTimerRun) {\n        throw new InvariantError(\n          'Cannot schedule more timers into a group that already executed'\n        )\n      }\n\n      const timer = setTimeout(\n        isFirstCallback ? runFirstCallback : runSubsequentCallback,\n        delayMs,\n        callback\n      )\n      isFirstCallback = false\n\n      if (!shouldAttemptPatching) {\n        // We already tried patching some timers, and it didn't work.\n        // No point trying again.\n        return timer\n      }\n\n      // NodeJS timers have a `_idleStart` property, but it doesn't exist e.g. in Bun.\n      // If it's not present, we'll warn and try to continue.\n      try {\n        if ('_idleStart' in timer && typeof timer._idleStart === 'number') {\n          // If this is the first timer that was scheduled, save its `_idleStart`.\n          // We'll copy it onto subsequent timers to guarantee that they'll all be\n          // considered expired in the same iteration of the event loop\n          // and thus will all be executed in the same timer phase.\n          if (firstTimerIdleStart === null) {\n            firstTimerIdleStart = timer._idleStart\n          } else {\n            timer._idleStart = firstTimerIdleStart\n          }\n        } else {\n          shouldAttemptPatching = false\n          warnAboutTimers()\n        }\n      } catch (err) {\n        // This should never fail in current Node, but it might start failing in the future.\n        // We might be okay even without tweaking the timers, so warn and try to continue.\n        console.error(\n          new InvariantError(\n            'An unexpected error occurred while adjusting `_idleStart` on an atomic timer',\n            { cause: err }\n          )\n        )\n        shouldAttemptPatching = false\n        warnAboutTimers()\n      }\n\n      return timer\n    }\n  }\n}\n"],"names":["createAtomicTimerGroup","shouldAttemptPatching","warnAboutTimers","console","warn","delayMs","process","env","NEXT_RUNTIME","InvariantError","isFirstCallback","firstTimerIdleStart","didFirstTimerRun","didImmediateRun","runFirstCallback","callback","unpatchedSetImmediate","runSubsequentCallback","scheduleTimeout","timer","setTimeout","_idleStart","err","error","cause"],"mappings":";;;;+BAwGgBA;;;eAAAA;;;gCAxGe;0CACO;AAEtC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoFA,GAEA,IAAIC,wBAAwB;AAE5B,SAASC;IACPC,QAAQC,IAAI,CACV;AAEJ;AASO,SAASJ,uBAAuBK,UAAU,CAAC;IAChD,IAAIC,QAAQC,GAAG,CAACC,YAAY,KAAK,QAAQ;QACvC,MAAM,qBAEL,CAFK,IAAIC,8BAAc,CACtB,gEADI,qBAAA;mBAAA;wBAAA;0BAAA;QAEN;IACF,OAAO;QACL,IAAIC,kBAAkB;QACtB,IAAIC,sBAAqC;QACzC,IAAIC,mBAAmB;QAEvB,qEAAqE;QACrE,mFAAmF;QACnF,+EAA+E;QAC/E,iEAAiE;QACjE,8CAA8C;QAC9C,IAAIC,kBAAkB;QACtB,SAASC,iBAAiBC,QAAoB;YAC5CH,mBAAmB;YACnB,IAAIX,uBAAuB;gBACzBe,IAAAA,+CAAqB,EAAC;oBACpBH,kBAAkB;gBACpB;YACF;YACA,OAAOE;QACT;QAEA,SAASE,sBAAsBF,QAAoB;YACjD,IAAId,uBAAuB;gBACzB,IAAIY,iBAAiB;oBACnB,qEAAqE;oBACrE,wDAAwD;oBACxDZ,wBAAwB;oBACxBC;gBACF;YACF;YACA,OAAOa;QACT;QAEA,OAAO,SAASG,gBAAgBH,QAAoB;YAClD,IAAIH,kBAAkB;gBACpB,MAAM,qBAEL,CAFK,IAAIH,8BAAc,CACtB,mEADI,qBAAA;2BAAA;gCAAA;kCAAA;gBAEN;YACF;YAEA,MAAMU,QAAQC,WACZV,kBAAkBI,mBAAmBG,uBACrCZ,SACAU;YAEFL,kBAAkB;YAElB,IAAI,CAACT,uBAAuB;gBAC1B,6DAA6D;gBAC7D,yBAAyB;gBACzB,OAAOkB;YACT;YAEA,gFAAgF;YAChF,uDAAuD;YACvD,IAAI;gBACF,IAAI,gBAAgBA,SAAS,OAAOA,MAAME,UAAU,KAAK,UAAU;oBACjE,wEAAwE;oBACxE,wEAAwE;oBACxE,6DAA6D;oBAC7D,yDAAyD;oBACzD,IAAIV,wBAAwB,MAAM;wBAChCA,sBAAsBQ,MAAME,UAAU;oBACxC,OAAO;wBACLF,MAAME,UAAU,GAAGV;oBACrB;gBACF,OAAO;oBACLV,wBAAwB;oBACxBC;gBACF;YACF,EAAE,OAAOoB,KAAK;gBACZ,oFAAoF;gBACpF,kFAAkF;gBAClFnB,QAAQoB,KAAK,CACX,qBAGC,CAHD,IAAId,8BAAc,CAChB,gFACA;oBAAEe,OAAOF;gBAAI,IAFf,qBAAA;2BAAA;gCAAA;kCAAA;gBAGA;gBAEFrB,wBAAwB;gBACxBC;YACF;YAEA,OAAOiB;QACT;IACF;AACF","ignoreList":[0]}