@xylabs/forget
Version:
Base functionality used throughout XY Labs TypeScript/JavaScript libraries
8 lines (7 loc) • 11.9 kB
Source Map (JSON)
{
"version": 3,
"sources": ["../../src/ForgetConfig.ts", "../../src/ForgetNodeConfig.ts", "../../src/ForgetPromise.ts", "../../src/ForgetPromiseNode.ts", "../../src/forgetNode.ts"],
"sourcesContent": ["/**\n * Configuration options for fire-and-forget promises.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport interface ForgetConfig<T = any> {\n /** Optional name for identifying the forgotten promise in logs. */\n name?: string\n /** Called when the promise is cancelled due to timeout. */\n onCancel?: () => void\n /** Called when the promise completes, with a tuple of [result, error]. */\n onComplete?: (result: [T | undefined, Error | undefined]) => void\n /** Called when an exception occurs outside the promise itself. */\n onException?: (error: Error) => void\n /** Timeout in milliseconds after which the promise is considered timed out. */\n timeout?: number\n}\n\n/** Default forget configuration with a 30-second timeout. */\nexport const defaultForgetConfig: ForgetConfig<unknown> = { timeout: 30_000 }\n", "import { defaultForgetConfig, type ForgetConfig } from './ForgetConfig.ts'\n\n/**\n * Node.js-specific forget configuration that extends ForgetConfig with process termination options.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport interface ForgetNodeConfig<T = any> extends ForgetConfig<T> {\n /** Terminate the process on an exception that happens outside of the promise being forgotten. */\n terminateOnException?: boolean\n /** Terminate the process if the promise times out. */\n terminateOnTimeout?: boolean\n}\n\n/** Default Node.js forget configuration with termination disabled. */\nexport const defaultForgetNodeConfig: ForgetNodeConfig<unknown> = {\n ...defaultForgetConfig,\n terminateOnTimeout: false,\n terminateOnException: false,\n}\n", "import { delay } from '@xylabs/delay'\nimport type { Logger } from '@xylabs/logger'\nimport type { Promisable, PromiseEx } from '@xylabs/promise'\nimport { isNumber, isPromise } from '@xylabs/typeof'\n\nimport { defaultForgetConfig, type ForgetConfig } from './ForgetConfig.ts'\n\n/** A function that returns a promisable value. */\ntype PromisableFunction<T> = () => Promisable<T>\n\n/**\n * Manages fire-and-forget promises with tracking, timeouts, and error handling.\n */\n// eslint-disable-next-line unicorn/no-static-only-class\nexport class ForgetPromise {\n /** Number of currently active (unresolved) forgotten promises. */\n static activeForgets = 0\n /** Number of forgotten promises that threw exceptions. */\n static exceptedForgets = 0\n /** Logger instance used for error and warning output. */\n static logger: Logger = console\n\n /** Whether any forgotten promises are still active. */\n static get active() {\n return this.activeForgets > 0\n }\n\n /**\n * Waits until all forgotten promises have completed.\n * @param interval - Polling interval in milliseconds.\n * @param timeout - Optional maximum wait time in milliseconds.\n * @returns The number of remaining active forgets (0 if all completed).\n */\n static async awaitInactive(interval = 100, timeout?: number) {\n let timeoutRemaining = timeout\n while (this.active) {\n await delay(interval)\n if (timeoutRemaining !== undefined) {\n timeoutRemaining -= interval\n if (timeoutRemaining <= 0) {\n return this.activeForgets\n }\n }\n }\n return 0\n }\n\n /** Handles exceptions from forgotten promises by logging error details. */\n static exceptionHandler(error: Error, { name }: ForgetConfig, externalStackTrace?: string) {\n this.logger.error(`forget promise handler excepted [${name}]: ${error.message}`, error)\n if (externalStackTrace !== undefined) {\n this.logger.warn(`External Stack trace [${name}]:`, externalStackTrace)\n }\n }\n\n /**\n * Used to explicitly launch an async function (or Promise) with awaiting it\n * @param promise The promise to forget\n * @param config Configuration of forget settings\n */\n static forget<T>(promise: Promise<T> | PromiseEx<T> | PromisableFunction<T> | T, config?: ForgetConfig<T>) {\n const externalStackTrace = (new Error('Stack')).stack\n\n // default | global | provided priorities for config (not deep merge)\n const resolvedConfig = {\n ...defaultForgetConfig, ...globalThis.xy?.forget?.config, ...config,\n }\n const resolvedPromise = typeof promise === 'function' ? (promise as PromisableFunction<T>)() : promise\n if (isPromise(resolvedPromise)) {\n try {\n let completed = false\n this.activeForgets++\n\n const promiseWrapper = async () => {\n await resolvedPromise\n .then((result: T) => {\n this.activeForgets--\n completed = true\n resolvedConfig?.onComplete?.([result, undefined])\n })\n .catch((error) => {\n this.activeForgets--\n completed = true\n this.logger.error(`forgotten promise excepted [${config?.name ?? 'unknown'}]: ${error.message}`, error)\n resolvedConfig?.onComplete?.([undefined, error])\n })\n }\n\n const promises = [promiseWrapper()]\n\n // if there is a timeout, add it to the race\n const timeout = resolvedConfig.timeout ?? defaultForgetConfig.timeout\n if (isNumber(timeout)) {\n const timeoutFunc = async () => {\n await delay(timeout)\n if (!completed) {\n resolvedConfig.onCancel?.()\n this.timeoutHandler(timeout, resolvedConfig, externalStackTrace)\n }\n }\n promises.push(timeoutFunc())\n }\n\n const all = Promise.race(promises)\n\n all\n .then(() => {\n return\n })\n .catch(() => {\n return\n })\n } catch (ex) {\n this.exceptedForgets += 1\n resolvedConfig?.onException?.(ex as Error)\n this.exceptionHandler(ex as Error, resolvedConfig, externalStackTrace)\n }\n } else {\n // we do nothing here since if it was a non-promise, it already got invoked.\n return\n }\n }\n\n /** Handles timeout events for forgotten promises by logging timeout details. */\n static timeoutHandler(time: number, { name = 'unknown' }: ForgetConfig, externalStackTrace?: string) {\n this.logger.error(`forget promise timeout out after ${time}ms [Cancelling] [${name}]`)\n if (externalStackTrace !== undefined) {\n this.logger.warn(`External Stack trace [${name}]:`, externalStackTrace)\n }\n }\n}\n", "/// <reference types=\"node\" />\n\nimport type { Promisable } from '@xylabs/promise'\n\nimport { defaultForgetNodeConfig, type ForgetNodeConfig } from './ForgetNodeConfig.ts'\nimport { ForgetPromise } from './ForgetPromise.ts'\n\n/**\n * Node.js extension of ForgetPromise that can terminate the process on exceptions or timeouts.\n */\nexport class ForgetPromiseNode extends ForgetPromise {\n /** Handles exceptions, optionally terminating the process based on config. */\n static override exceptionHandler(error: Error, config: ForgetNodeConfig, externalStackTrace?: string) {\n // default | global | provided priorities for config (not deep merge)\n super.exceptionHandler(error, config, externalStackTrace)\n if (config?.terminateOnException === true) {\n this.logger.error(`Attempting to terminate process [${config?.name ?? 'unknown'}]...`)\n // eslint-disable-next-line unicorn/no-process-exit\n process.exit(1)\n }\n }\n\n /** Forgets a promise using Node.js-specific configuration with process termination support. */\n static override forget<T>(promise: Promisable<T>, config?: ForgetNodeConfig<T>) {\n const resolvedConfig = {\n ...defaultForgetNodeConfig, ...globalThis.xy?.forget?.config, ...config,\n }\n super.forget(promise, resolvedConfig)\n }\n\n /** Handles timeouts, optionally terminating the process based on config. */\n static override timeoutHandler(time: number, config: ForgetNodeConfig, externalStackTrace?: string) {\n super.timeoutHandler(time, config, externalStackTrace)\n if (config?.terminateOnTimeout === true) {\n this.logger.error(`Attempting to terminate process [${config?.name ?? 'unknown'}]...`)\n // eslint-disable-next-line unicorn/no-process-exit\n process.exit(2)\n }\n }\n}\n", "import { type Promisable } from '@xylabs/promise'\n\nimport type { ForgetNodeConfig } from './ForgetNodeConfig.ts'\nimport { ForgetPromiseNode } from './ForgetPromiseNode.ts'\n\n/**\n * Node.js variant of forget that can optionally terminate the process on exceptions or timeouts.\n * @param promise - The promise or promisable value to forget.\n * @param config - Optional Node.js-specific configuration including process termination options.\n */\nexport const forgetNode = <T>(promise: Promisable<T>, config?: ForgetNodeConfig<T>) => {\n ForgetPromiseNode.forget<T>(promise, config)\n}\n"],
"mappings": ";AAkBO,IAAM,sBAA6C,EAAE,SAAS,IAAO;;;ACJrE,IAAM,0BAAqD;AAAA,EAChE,GAAG;AAAA,EACH,oBAAoB;AAAA,EACpB,sBAAsB;AACxB;;;AClBA,SAAS,aAAa;AAGtB,SAAS,UAAU,iBAAiB;AAW7B,IAAM,gBAAN,MAAoB;AAAA;AAAA,EAEzB,OAAO,gBAAgB;AAAA;AAAA,EAEvB,OAAO,kBAAkB;AAAA;AAAA,EAEzB,OAAO,SAAiB;AAAA;AAAA,EAGxB,WAAW,SAAS;AAClB,WAAO,KAAK,gBAAgB;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,aAAa,cAAc,WAAW,KAAK,SAAkB;AAC3D,QAAI,mBAAmB;AACvB,WAAO,KAAK,QAAQ;AAClB,YAAM,MAAM,QAAQ;AACpB,UAAI,qBAAqB,QAAW;AAClC,4BAAoB;AACpB,YAAI,oBAAoB,GAAG;AACzB,iBAAO,KAAK;AAAA,QACd;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,OAAO,iBAAiB,OAAc,EAAE,KAAK,GAAiB,oBAA6B;AACzF,SAAK,OAAO,MAAM,oCAAoC,IAAI,MAAM,MAAM,OAAO,IAAI,KAAK;AACtF,QAAI,uBAAuB,QAAW;AACpC,WAAK,OAAO,KAAK,yBAAyB,IAAI,MAAM,kBAAkB;AAAA,IACxE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,OAAO,OAAU,SAAgE,QAA0B;AACzG,UAAM,qBAAsB,IAAI,MAAM,OAAO,EAAG;AAGhD,UAAM,iBAAiB;AAAA,MACrB,GAAG;AAAA,MAAqB,GAAG,WAAW,IAAI,QAAQ;AAAA,MAAQ,GAAG;AAAA,IAC/D;AACA,UAAM,kBAAkB,OAAO,YAAY,aAAc,QAAkC,IAAI;AAC/F,QAAI,UAAU,eAAe,GAAG;AAC9B,UAAI;AACF,YAAI,YAAY;AAChB,aAAK;AAEL,cAAM,iBAAiB,YAAY;AACjC,gBAAM,gBACH,KAAK,CAAC,WAAc;AACnB,iBAAK;AACL,wBAAY;AACZ,4BAAgB,aAAa,CAAC,QAAQ,MAAS,CAAC;AAAA,UAClD,CAAC,EACA,MAAM,CAAC,UAAU;AAChB,iBAAK;AACL,wBAAY;AACZ,iBAAK,OAAO,MAAM,+BAA+B,QAAQ,QAAQ,SAAS,MAAM,MAAM,OAAO,IAAI,KAAK;AACtG,4BAAgB,aAAa,CAAC,QAAW,KAAK,CAAC;AAAA,UACjD,CAAC;AAAA,QACL;AAEA,cAAM,WAAW,CAAC,eAAe,CAAC;AAGlC,cAAM,UAAU,eAAe,WAAW,oBAAoB;AAC9D,YAAI,SAAS,OAAO,GAAG;AACrB,gBAAM,cAAc,YAAY;AAC9B,kBAAM,MAAM,OAAO;AACnB,gBAAI,CAAC,WAAW;AACd,6BAAe,WAAW;AAC1B,mBAAK,eAAe,SAAS,gBAAgB,kBAAkB;AAAA,YACjE;AAAA,UACF;AACA,mBAAS,KAAK,YAAY,CAAC;AAAA,QAC7B;AAEA,cAAM,MAAM,QAAQ,KAAK,QAAQ;AAEjC,YACG,KAAK,MAAM;AACV;AAAA,QACF,CAAC,EACA,MAAM,MAAM;AACX;AAAA,QACF,CAAC;AAAA,MACL,SAAS,IAAI;AACX,aAAK,mBAAmB;AACxB,wBAAgB,cAAc,EAAW;AACzC,aAAK,iBAAiB,IAAa,gBAAgB,kBAAkB;AAAA,MACvE;AAAA,IACF,OAAO;AAEL;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAGA,OAAO,eAAe,MAAc,EAAE,OAAO,UAAU,GAAiB,oBAA6B;AACnG,SAAK,OAAO,MAAM,oCAAoC,IAAI,oBAAoB,IAAI,GAAG;AACrF,QAAI,uBAAuB,QAAW;AACpC,WAAK,OAAO,KAAK,yBAAyB,IAAI,MAAM,kBAAkB;AAAA,IACxE;AAAA,EACF;AACF;;;ACxHO,IAAM,oBAAN,cAAgC,cAAc;AAAA;AAAA,EAEnD,OAAgB,iBAAiB,OAAc,QAA0B,oBAA6B;AAEpG,UAAM,iBAAiB,OAAO,QAAQ,kBAAkB;AACxD,QAAI,QAAQ,yBAAyB,MAAM;AACzC,WAAK,OAAO,MAAM,oCAAoC,QAAQ,QAAQ,SAAS,MAAM;AAErF,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF;AAAA;AAAA,EAGA,OAAgB,OAAU,SAAwB,QAA8B;AAC9E,UAAM,iBAAiB;AAAA,MACrB,GAAG;AAAA,MAAyB,GAAG,WAAW,IAAI,QAAQ;AAAA,MAAQ,GAAG;AAAA,IACnE;AACA,UAAM,OAAO,SAAS,cAAc;AAAA,EACtC;AAAA;AAAA,EAGA,OAAgB,eAAe,MAAc,QAA0B,oBAA6B;AAClG,UAAM,eAAe,MAAM,QAAQ,kBAAkB;AACrD,QAAI,QAAQ,uBAAuB,MAAM;AACvC,WAAK,OAAO,MAAM,oCAAoC,QAAQ,QAAQ,SAAS,MAAM;AAErF,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF;AACF;;;AC7BO,IAAM,aAAa,CAAI,SAAwB,WAAiC;AACrF,oBAAkB,OAAU,SAAS,MAAM;AAC7C;",
"names": []
}