{"version":3,"sources":["../../src/timeoutManager.ts"],"sourcesContent":["/**\n * {@link TimeoutManager} does not support passing arguments to the callback.\n *\n * `(_: void)` is the argument type inferred by TypeScript's default typings for\n * `setTimeout(cb, number)`.\n * If we don't accept a single void argument, then\n * `new Promise(resolve => timeoutManager.setTimeout(resolve, N))` is a type error.\n */\nexport type TimeoutCallback = (_: void) => void\n\n/**\n * Wrapping `setTimeout` is awkward from a typing perspective because platform\n * typings may extend the return type of `setTimeout`. For example, NodeJS\n * typings add `NodeJS.Timeout`; but a non-default `timeoutManager` may not be\n * able to return such a type.\n */\nexport type ManagedTimerId = number | { [Symbol.toPrimitive]: () => number }\n\n/**\n * Backend for timer functions.\n */\nexport type TimeoutProvider =\n {\n readonly setTimeout: (callback: TimeoutCallback, delay: number) => TTimerId\n readonly clearTimeout: (timeoutId: TTimerId | undefined) => void\n\n readonly setInterval: (callback: TimeoutCallback, delay: number) => TTimerId\n readonly clearInterval: (intervalId: TTimerId | undefined) => void\n }\n\nexport const defaultTimeoutProvider: TimeoutProvider<\n ReturnType\n> = {\n // We need the wrapper function syntax below instead of direct references to\n // global setTimeout etc.\n //\n // BAD: `setTimeout: setTimeout`\n // GOOD: `setTimeout: (cb, delay) => setTimeout(cb, delay)`\n //\n // If we use direct references here, then anything that wants to spy on or\n // replace the global setTimeout (like tests) won't work since we'll already\n // have a hard reference to the original implementation at the time when this\n // file was imported.\n setTimeout: (callback, delay) => setTimeout(callback, delay),\n clearTimeout: (timeoutId) => clearTimeout(timeoutId),\n\n setInterval: (callback, delay) => setInterval(callback, delay),\n clearInterval: (intervalId) => clearInterval(intervalId),\n}\n\n/**\n * Allows customization of how timeouts are created.\n *\n * @tanstack/query-core makes liberal use of timeouts to implement `staleTime`\n * and `gcTime`. The default TimeoutManager provider uses the platform's global\n * `setTimeout` implementation, which is known to have scalability issues with\n * thousands of timeouts on the event loop.\n *\n * If you hit this limitation, consider providing a custom TimeoutProvider that\n * coalesces timeouts.\n */\nexport class TimeoutManager implements Omit {\n // We cannot have TimeoutManager as we must instantiate it with a concrete\n // type at app boot; and if we leave that type, then any new timer provider\n // would need to support ReturnType, which is infeasible.\n //\n // We settle for type safety for the TimeoutProvider type, and accept that\n // this class is unsafe internally to allow for extension.\n #provider: TimeoutProvider = defaultTimeoutProvider\n #providerCalled = false\n\n setTimeoutProvider(\n provider: TimeoutProvider,\n ): void {\n if (process.env.NODE_ENV !== 'production') {\n if (this.#providerCalled && provider !== this.#provider) {\n // After changing providers, `clearTimeout` will not work as expected for\n // timeouts from the previous provider.\n //\n // Since they may allocate the same timeout ID, clearTimeout may cancel an\n // arbitrary different timeout, or unexpected no-op.\n //\n // We could protect against this by mixing the timeout ID bits\n // deterministically with some per-provider bits.\n //\n // We could internally queue `setTimeout` calls to `TimeoutManager` until\n // some API call to set the initial provider.\n console.error(\n `[timeoutManager]: Switching provider after calls to previous provider might result in unexpected behavior.`,\n { previous: this.#provider, provider },\n )\n }\n }\n\n this.#provider = provider\n if (process.env.NODE_ENV !== 'production') {\n this.#providerCalled = false\n }\n }\n\n setTimeout(callback: TimeoutCallback, delay: number): ManagedTimerId {\n if (process.env.NODE_ENV !== 'production') {\n this.#providerCalled = true\n }\n return this.#provider.setTimeout(callback, delay)\n }\n\n clearTimeout(timeoutId: ManagedTimerId | undefined): void {\n this.#provider.clearTimeout(timeoutId)\n }\n\n setInterval(callback: TimeoutCallback, delay: number): ManagedTimerId {\n if (process.env.NODE_ENV !== 'production') {\n this.#providerCalled = true\n }\n return this.#provider.setInterval(callback, delay)\n }\n\n clearInterval(intervalId: ManagedTimerId | undefined): void {\n this.#provider.clearInterval(intervalId)\n }\n}\n\nexport const timeoutManager = new TimeoutManager()\n\n/**\n * In many cases code wants to delay to the next event loop tick; this is not\n * mediated by {@link timeoutManager}.\n *\n * This function is provided to make auditing the `tanstack/query-core` for\n * incorrect use of system `setTimeout` easier.\n */\nexport function systemSetTimeoutZero(callback: TimeoutCallback): void {\n setTimeout(callback, 0)\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA8BO,IAAM,yBAET;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWF,YAAY,CAAC,UAAU,UAAU,WAAW,UAAU,KAAK;AAAA,EAC3D,cAAc,CAAC,cAAc,aAAa,SAAS;AAAA,EAEnD,aAAa,CAAC,UAAU,UAAU,YAAY,UAAU,KAAK;AAAA,EAC7D,eAAe,CAAC,eAAe,cAAc,UAAU;AACzD;AAhDA;AA6DO,IAAM,iBAAN,MAA8D;AAAA,EAA9D;AAOL;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kCAAkC;AAClC,wCAAkB;AAAA;AAAA,EAElB,mBACE,UACM;AACN,QAAI,QAAQ,IAAI,aAAa,cAAc;AACzC,UAAI,mBAAK,oBAAmB,aAAa,mBAAK,YAAW;AAYvD,gBAAQ;AAAA,UACN;AAAA,UACA,EAAE,UAAU,mBAAK,YAAW,SAAS;AAAA,QACvC;AAAA,MACF;AAAA,IACF;AAEA,uBAAK,WAAY;AACjB,QAAI,QAAQ,IAAI,aAAa,cAAc;AACzC,yBAAK,iBAAkB;AAAA,IACzB;AAAA,EACF;AAAA,EAEA,WAAW,UAA2B,OAA+B;AACnE,QAAI,QAAQ,IAAI,aAAa,cAAc;AACzC,yBAAK,iBAAkB;AAAA,IACzB;AACA,WAAO,mBAAK,WAAU,WAAW,UAAU,KAAK;AAAA,EAClD;AAAA,EAEA,aAAa,WAA6C;AACxD,uBAAK,WAAU,aAAa,SAAS;AAAA,EACvC;AAAA,EAEA,YAAY,UAA2B,OAA+B;AACpE,QAAI,QAAQ,IAAI,aAAa,cAAc;AACzC,yBAAK,iBAAkB;AAAA,IACzB;AACA,WAAO,mBAAK,WAAU,YAAY,UAAU,KAAK;AAAA,EACnD;AAAA,EAEA,cAAc,YAA8C;AAC1D,uBAAK,WAAU,cAAc,UAAU;AAAA,EACzC;AACF;AArDE;AACA;AAsDK,IAAM,iBAAiB,IAAI,eAAe;AAS1C,SAAS,qBAAqB,UAAiC;AACpE,aAAW,UAAU,CAAC;AACxB;","names":[]}