UNPKG

@xylabs/forget

Version:

Base functionality used throughout XY Labs TypeScript/JavaScript libraries

155 lines (150 loc) 5.44 kB
// src/ForgetConfig.ts var defaultForgetConfig = { timeout: 3e4 }; // src/ForgetNodeConfig.ts var defaultForgetNodeConfig = { ...defaultForgetConfig, terminateOnTimeout: false, terminateOnException: false }; // src/ForgetPromise.ts import { delay } from "@xylabs/delay"; import { isNumber, isPromise } from "@xylabs/typeof"; var ForgetPromise = class { /** Number of currently active (unresolved) forgotten promises. */ static activeForgets = 0; /** Number of forgotten promises that threw exceptions. */ static exceptedForgets = 0; /** Logger instance used for error and warning output. */ static logger = console; /** Whether any forgotten promises are still active. */ static get active() { return this.activeForgets > 0; } /** * Waits until all forgotten promises have completed. * @param interval - Polling interval in milliseconds. * @param timeout - Optional maximum wait time in milliseconds. * @returns The number of remaining active forgets (0 if all completed). */ static async awaitInactive(interval = 100, timeout) { let timeoutRemaining = timeout; while (this.active) { await delay(interval); if (timeoutRemaining !== void 0) { timeoutRemaining -= interval; if (timeoutRemaining <= 0) { return this.activeForgets; } } } return 0; } /** Handles exceptions from forgotten promises by logging error details. */ static exceptionHandler(error, { name }, externalStackTrace) { this.logger.error(`forget promise handler excepted [${name}]: ${error.message}`, error); if (externalStackTrace !== void 0) { this.logger.warn(`External Stack trace [${name}]:`, externalStackTrace); } } /** * Used to explicitly launch an async function (or Promise) with awaiting it * @param promise The promise to forget * @param config Configuration of forget settings */ static forget(promise, config) { const externalStackTrace = new Error("Stack").stack; const resolvedConfig = { ...defaultForgetConfig, ...globalThis.xy?.forget?.config, ...config }; const resolvedPromise = typeof promise === "function" ? promise() : promise; if (isPromise(resolvedPromise)) { try { let completed = false; this.activeForgets++; const promiseWrapper = async () => { await resolvedPromise.then((result) => { this.activeForgets--; completed = true; resolvedConfig?.onComplete?.([result, void 0]); }).catch((error) => { this.activeForgets--; completed = true; this.logger.error(`forgotten promise excepted [${config?.name ?? "unknown"}]: ${error.message}`, error); resolvedConfig?.onComplete?.([void 0, error]); }); }; const promises = [promiseWrapper()]; const timeout = resolvedConfig.timeout ?? defaultForgetConfig.timeout; if (isNumber(timeout)) { const timeoutFunc = async () => { await delay(timeout); if (!completed) { resolvedConfig.onCancel?.(); this.timeoutHandler(timeout, resolvedConfig, externalStackTrace); } }; promises.push(timeoutFunc()); } const all = Promise.race(promises); all.then(() => { return; }).catch(() => { return; }); } catch (ex) { this.exceptedForgets += 1; resolvedConfig?.onException?.(ex); this.exceptionHandler(ex, resolvedConfig, externalStackTrace); } } else { return; } } /** Handles timeout events for forgotten promises by logging timeout details. */ static timeoutHandler(time, { name = "unknown" }, externalStackTrace) { this.logger.error(`forget promise timeout out after ${time}ms [Cancelling] [${name}]`); if (externalStackTrace !== void 0) { this.logger.warn(`External Stack trace [${name}]:`, externalStackTrace); } } }; // src/ForgetPromiseNode.ts var ForgetPromiseNode = class extends ForgetPromise { /** Handles exceptions, optionally terminating the process based on config. */ static exceptionHandler(error, config, externalStackTrace) { super.exceptionHandler(error, config, externalStackTrace); if (config?.terminateOnException === true) { this.logger.error(`Attempting to terminate process [${config?.name ?? "unknown"}]...`); process.exit(1); } } /** Forgets a promise using Node.js-specific configuration with process termination support. */ static forget(promise, config) { const resolvedConfig = { ...defaultForgetNodeConfig, ...globalThis.xy?.forget?.config, ...config }; super.forget(promise, resolvedConfig); } /** Handles timeouts, optionally terminating the process based on config. */ static timeoutHandler(time, config, externalStackTrace) { super.timeoutHandler(time, config, externalStackTrace); if (config?.terminateOnTimeout === true) { this.logger.error(`Attempting to terminate process [${config?.name ?? "unknown"}]...`); process.exit(2); } } }; // src/forgetNode.ts var forgetNode = (promise, config) => { ForgetPromiseNode.forget(promise, config); }; export { ForgetPromiseNode as ForgetPromise, defaultForgetNodeConfig, forgetNode as forget }; //# sourceMappingURL=index.mjs.map