inngest
Version:
Official SDK for Inngest.com. Inngest is the reliability layer for modern applications. Inngest combines durable execution, events, and queues into a zero-infra platform with built-in observability.
1 lines • 11.2 kB
Source Map (JSON)
{"version":3,"file":"promises.cjs","names":["resolve: DeferredPromiseReturn<T>[\"resolve\"]","reject: DeferredPromiseReturn<T>[\"reject\"]","settledPromises: Promise<T>[]","rotateQueue: (value: void) => void","timeout: ReturnType<typeof setTimeout> | undefined","ret: TimeoutPromise"],"sources":["../../src/helpers/promises.ts"],"sourcesContent":["import type { MaybePromise } from \"./types.ts\";\n\n/**\n * Some environments don't allow access to the global queueMicrotask(). While we\n * had assumed this was only true for those powered by earlier versions of Node\n * (<14) that we don't officially support, Vercel's Edge Functions also obscure\n * the function in dev, even though the platform it's based on (Cloudflare\n * Workers) appropriately exposes it. Even worse, production Vercel Edge\n * Functions can see the function, but it immediately blows up the function when\n * used.\n *\n * Therefore, we can fall back to a reasonable alternative of\n * `Promise.resolve().then(fn)` instead. This _may_ be slightly slower in modern\n * environments, but at least we can still work in these environments.\n */\nconst shimQueueMicrotask = (callback: () => void): void => {\n void Promise.resolve().then(callback);\n};\n\n/**\n * A helper function to create a `Promise` that will never settle.\n *\n * It purposefully creates no references to `resolve` or `reject` so that the\n * returned `Promise` will remain unsettled until it falls out of scope and is\n * garbage collected.\n *\n * This should be used within transient closures to fake asynchronous action, so\n * long as it's guaranteed that they will fall out of scope.\n */\nexport const createFrozenPromise = (): Promise<unknown> => {\n return new Promise(() => undefined);\n};\n\n/**\n * Returns a Promise that resolves after the current event loop's microtasks\n * have finished, but before the next event loop tick.\n */\nexport const resolveAfterPending = (count = 100): Promise<void> => {\n /**\n * This uses a brute force implementation that will continue to enqueue\n * microtasks 10 times before resolving. This is to ensure that the microtask\n * queue is drained, even if the microtask queue is being manipulated by other\n * code.\n *\n * While this still doesn't guarantee that the microtask queue is drained,\n * it's our best bet for giving other non-controlled promises a chance to\n * resolve before we continue without resorting to falling in to the next\n * tick.\n */\n return new Promise((resolve) => {\n let i = 0;\n\n const iterate = () => {\n shimQueueMicrotask(() => {\n if (i++ > count) {\n return resolve();\n }\n\n iterate();\n });\n };\n\n iterate();\n });\n};\n\ntype DeferredPromiseReturn<T> = {\n promise: Promise<T>;\n resolve: (value: T) => DeferredPromiseReturn<T>;\n // biome-ignore lint/suspicious/noExplicitAny: intentional\n reject: (reason: any) => DeferredPromiseReturn<T>;\n};\n\n/**\n * Creates and returns Promise that can be resolved or rejected with the\n * returned `resolve` and `reject` functions.\n *\n * Resolving or rejecting the function will return a new set of Promise control\n * functions. These can be ignored if the original Promise is all that's needed.\n */\nexport const createDeferredPromise = <T>(): DeferredPromiseReturn<T> => {\n let resolve: DeferredPromiseReturn<T>[\"resolve\"];\n let reject: DeferredPromiseReturn<T>[\"reject\"];\n\n const promise = new Promise<T>((_resolve, _reject) => {\n resolve = (value: T) => {\n _resolve(value);\n return createDeferredPromise<T>();\n };\n\n reject = (reason) => {\n _reject(reason);\n return createDeferredPromise<T>();\n };\n });\n\n return { promise, resolve: resolve!, reject: reject! };\n};\n\n/**\n * Creates and returns a deferred Promise that can be resolved or rejected with\n * the returned `resolve` and `reject` functions.\n *\n * For each Promise resolved or rejected this way, this will also keep a stack\n * of all unhandled Promises, resolved or rejected.\n *\n * Once a Promise is read, it is removed from the stack.\n */\nexport const createDeferredPromiseWithStack = <T>(): {\n deferred: DeferredPromiseReturn<T>;\n results: AsyncGenerator<Awaited<T>, void, void>;\n} => {\n const settledPromises: Promise<T>[] = [];\n // biome-ignore lint/suspicious/noConfusingVoidType: intentional\n let rotateQueue: (value: void) => void = () => {};\n\n const results = (async function* () {\n while (true) {\n const next = settledPromises.shift();\n\n if (next) {\n yield next;\n } else {\n await new Promise<void>((resolve) => {\n rotateQueue = resolve;\n });\n }\n }\n })();\n\n const shimDeferredPromise = (deferred: DeferredPromiseReturn<T>) => {\n const originalResolve = deferred.resolve;\n const originalReject = deferred.reject;\n\n deferred.resolve = (value: T) => {\n settledPromises.push(deferred.promise);\n rotateQueue();\n return shimDeferredPromise(originalResolve(value));\n };\n\n deferred.reject = (reason) => {\n settledPromises.push(deferred.promise);\n rotateQueue();\n return shimDeferredPromise(originalReject(reason));\n };\n\n return deferred;\n };\n\n const deferred = shimDeferredPromise(createDeferredPromise<T>());\n\n return { deferred, results };\n};\n\ninterface TimeoutPromise extends Promise<void> {\n /**\n * Starts the timeout. If the timer is already started, this does nothing.\n *\n * @returns The promise that will resolve when the timeout expires.\n */\n start: () => TimeoutPromise;\n\n /**\n * Clears the timeout.\n */\n clear: () => void;\n\n /**\n * Clears the timeout and starts it again.\n *\n * @returns The promise that will resolve when the timeout expires.\n */\n reset: () => TimeoutPromise;\n}\n\n/**\n * Creates a Promise that will resolve after the given duration, along with\n * methods to start, clear, and reset the timeout.\n */\nexport const createTimeoutPromise = (duration: number): TimeoutPromise => {\n const { promise, resolve } = createDeferredPromise<void>();\n\n let timeout: ReturnType<typeof setTimeout> | undefined;\n // biome-ignore lint/style/useConst: intentional\n let ret: TimeoutPromise;\n\n const start = () => {\n if (timeout) return ret;\n\n timeout = setTimeout(() => {\n resolve();\n }, duration);\n\n return ret;\n };\n\n const clear = () => {\n clearTimeout(timeout);\n timeout = undefined;\n };\n\n const reset = () => {\n clear();\n return start();\n };\n\n ret = Object.assign(promise, { start, clear, reset });\n\n return ret;\n};\n\n/**\n * Take any function and safely promisify such that both synchronous and\n * asynchronous errors are caught and returned as a rejected Promise.\n *\n * The passed `fn` can be undefined to support functions that may conditionally\n * be defined.\n */\n// biome-ignore lint/suspicious/noExplicitAny: intentional\nexport const runAsPromise = <T extends (() => any) | undefined>(\n fn: T,\n // biome-ignore lint/suspicious/noExplicitAny: intentional\n): Promise<T extends () => any ? Awaited<ReturnType<T>> : T> => {\n return Promise.resolve().then(fn);\n};\n\n/**\n * Returns a Promise that resolve after the current event loop tick.\n */\nexport const resolveNextTick = (): Promise<void> => {\n return new Promise((resolve) => setTimeout(resolve));\n};\n\nexport const retryWithBackoff = async <T>(\n fn: () => MaybePromise<T>,\n opts?: {\n maxAttempts?: number;\n baseDelay?: number;\n },\n): Promise<T> => {\n const maxAttempts = opts?.maxAttempts || 5;\n const baseDelay = opts?.baseDelay ?? 100;\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n return await fn();\n } catch (err) {\n if (attempt >= maxAttempts) {\n throw err;\n }\n\n const jitter = Math.random() * baseDelay;\n const delay = baseDelay * Math.pow(2, attempt - 1) + jitter;\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n }\n\n throw new Error(\"Max retries reached; this should be unreachable.\");\n};\n\nexport type GoInterval = {\n a: number;\n b: number;\n};\n\n/**\n * Given a function, returns a Promise that resolves with the result of the\n * function and a Go-compatible `interval.Interval` timing object.\n */\n// biome-ignore lint/suspicious/noExplicitAny: match any fn\nexport const goIntervalTiming = async <T extends (...args: any[]) => any>(\n fn: T,\n): Promise<{\n resultPromise: Promise<Awaited<ReturnType<T>>>;\n interval: { a: number; b: number };\n}> => {\n // Ideally this would use process.hrtime, but that's not available in all\n // runtimes, so we must revert to less accurate timing and `Date`.\n const start = Date.now();\n const resultPromise = runAsPromise(fn) as Promise<Awaited<ReturnType<T>>>;\n\n // Let the function run to completion.\n try {\n await resultPromise;\n } catch {\n // no-op\n }\n\n const end = Date.now();\n\n const interval = {\n a: start * 1_000_000,\n b: (end - start) * 1_000_000,\n };\n\n return { resultPromise, interval };\n};\n"],"mappings":";;;;;;;;;;;;;;;AAeA,MAAM,sBAAsB,aAA+B;AACzD,CAAK,QAAQ,SAAS,CAAC,KAAK,SAAS;;;;;;AAqBvC,MAAa,uBAAuB,QAAQ,QAAuB;;;;;;;;;;;;AAYjE,QAAO,IAAI,SAAS,YAAY;EAC9B,IAAI,IAAI;EAER,MAAM,gBAAgB;AACpB,4BAAyB;AACvB,QAAI,MAAM,MACR,QAAO,SAAS;AAGlB,aAAS;KACT;;AAGJ,WAAS;GACT;;;;;;;;;AAiBJ,MAAa,8BAA2D;CACtE,IAAIA;CACJ,IAAIC;AAcJ,QAAO;EAAE,SAZO,IAAI,SAAY,UAAU,YAAY;AACpD,cAAW,UAAa;AACtB,aAAS,MAAM;AACf,WAAO,uBAA0B;;AAGnC,aAAU,WAAW;AACnB,YAAQ,OAAO;AACf,WAAO,uBAA0B;;IAEnC;EAEyB;EAAkB;EAAS;;;;;;;;;;;AAYxD,MAAa,uCAGR;CACH,MAAMC,kBAAgC,EAAE;CAExC,IAAIC,oBAA2C;CAE/C,MAAM,WAAW,mBAAmB;AAClC,SAAO,MAAM;GACX,MAAM,OAAO,gBAAgB,OAAO;AAEpC,OAAI,KACF,OAAM;OAEN,OAAM,IAAI,SAAe,YAAY;AACnC,kBAAc;KACd;;KAGJ;CAEJ,MAAM,uBAAuB,aAAuC;EAClE,MAAM,kBAAkB,SAAS;EACjC,MAAM,iBAAiB,SAAS;AAEhC,WAAS,WAAW,UAAa;AAC/B,mBAAgB,KAAK,SAAS,QAAQ;AACtC,gBAAa;AACb,UAAO,oBAAoB,gBAAgB,MAAM,CAAC;;AAGpD,WAAS,UAAU,WAAW;AAC5B,mBAAgB,KAAK,SAAS,QAAQ;AACtC,gBAAa;AACb,UAAO,oBAAoB,eAAe,OAAO,CAAC;;AAGpD,SAAO;;AAKT,QAAO;EAAE,UAFQ,oBAAoB,uBAA0B,CAAC;EAE7C;EAAS;;;;;;AA4B9B,MAAa,wBAAwB,aAAqC;CACxE,MAAM,EAAE,SAAS,YAAY,uBAA6B;CAE1D,IAAIC;CAEJ,IAAIC;CAEJ,MAAM,cAAc;AAClB,MAAI,QAAS,QAAO;AAEpB,YAAU,iBAAiB;AACzB,YAAS;KACR,SAAS;AAEZ,SAAO;;CAGT,MAAM,cAAc;AAClB,eAAa,QAAQ;AACrB,YAAU;;CAGZ,MAAM,cAAc;AAClB,SAAO;AACP,SAAO,OAAO;;AAGhB,OAAM,OAAO,OAAO,SAAS;EAAE;EAAO;EAAO;EAAO,CAAC;AAErD,QAAO;;;;;;;;;AAWT,MAAa,gBACX,OAE8D;AAC9D,QAAO,QAAQ,SAAS,CAAC,KAAK,GAAG;;;;;AAMnC,MAAa,wBAAuC;AAClD,QAAO,IAAI,SAAS,YAAY,WAAW,QAAQ,CAAC;;AAGtD,MAAa,mBAAmB,OAC9B,IACA,SAIe;CACf,MAAM,cAAc,MAAM,eAAe;CACzC,MAAM,YAAY,MAAM,aAAa;AAErC,MAAK,IAAI,UAAU,GAAG,WAAW,aAAa,UAC5C,KAAI;AACF,SAAO,MAAM,IAAI;UACV,KAAK;AACZ,MAAI,WAAW,YACb,OAAM;EAGR,MAAM,SAAS,KAAK,QAAQ,GAAG;EAC/B,MAAM,QAAQ,YAAY,KAAK,IAAI,GAAG,UAAU,EAAE,GAAG;AACrD,QAAM,IAAI,SAAS,YAAY,WAAW,SAAS,MAAM,CAAC;;AAI9D,OAAM,IAAI,MAAM,mDAAmD;;;;;;AAarE,MAAa,mBAAmB,OAC9B,OAII;CAGJ,MAAM,QAAQ,KAAK,KAAK;CACxB,MAAM,gBAAgB,aAAa,GAAG;AAGtC,KAAI;AACF,QAAM;SACA;CAIR,MAAM,MAAM,KAAK,KAAK;AAOtB,QAAO;EAAE;EAAe,UALP;GACf,GAAG,QAAQ;GACX,IAAI,MAAM,SAAS;GACpB;EAEiC"}