@xylabs/forget 6.1.0 → 6.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,13 +4,16 @@
4
4
  export interface ForgetConfig<T = any> {
5
5
  /** Optional name for identifying the forgotten promise in logs. */
6
6
  name?: string;
7
- /** Called when the promise is cancelled due to timeout. */
7
+ /** Called when the configured timeout elapses before the promise settles. The promise keeps running. */
8
8
  onCancel?: () => void;
9
9
  /** Called when the promise completes, with a tuple of [result, error]. */
10
10
  onComplete?: (result: [T | undefined, Error | undefined]) => void;
11
11
  /** Called when an exception occurs outside the promise itself. */
12
12
  onException?: (error: Error) => void;
13
- /** Timeout in milliseconds after which the promise is considered timed out. */
13
+ /**
14
+ * Timeout in milliseconds after which `onCancel` and `timeoutHandler` run.
15
+ * This does not abort the promise; `activeForgets` stays elevated until it settles.
16
+ */
14
17
  timeout?: number;
15
18
  }
16
19
  /** Default forget configuration with a 30-second timeout. */
@@ -1 +1 @@
1
- {"version":3,"file":"ForgetConfig.d.ts","sourceRoot":"","sources":["../../src/ForgetConfig.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,YAAY,CAAC,CAAC,GAAG,GAAG;IACnC,mEAAmE;IACnE,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB,0EAA0E;IAC1E,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,SAAS,EAAE,KAAK,GAAG,SAAS,CAAC,KAAK,IAAI,CAAA;IACjE,kEAAkE;IAClE,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAA;IACpC,+EAA+E;IAC/E,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,6DAA6D;AAC7D,eAAO,MAAM,mBAAmB,EAAE,YAAY,CAAC,OAAO,CAAuB,CAAA"}
1
+ {"version":3,"file":"ForgetConfig.d.ts","sourceRoot":"","sources":["../../src/ForgetConfig.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,YAAY,CAAC,CAAC,GAAG,GAAG;IACnC,mEAAmE;IACnE,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,wGAAwG;IACxG,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB,0EAA0E;IAC1E,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,SAAS,EAAE,KAAK,GAAG,SAAS,CAAC,KAAK,IAAI,CAAA;IACjE,kEAAkE;IAClE,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAA;IACpC;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,6DAA6D;AAC7D,eAAO,MAAM,mBAAmB,EAAE,YAAY,CAAC,OAAO,CAAuB,CAAA"}
@@ -5,21 +5,30 @@ import { type ForgetConfig } from './ForgetConfig.ts';
5
5
  type PromisableFunction<T> = () => Promisable<T>;
6
6
  /**
7
7
  * Manages fire-and-forget promises with tracking, timeouts, and error handling.
8
+ *
9
+ * Timeouts are observability hooks only: they invoke `onCancel` and `timeoutHandler` but do not
10
+ * abort or await-cleanup the underlying promise. `activeForgets` therefore counts promises that
11
+ * are still running (unsettled), not promises still inside their configured timeout window.
8
12
  */
9
13
  export declare class ForgetPromise {
10
- /** Number of currently active (unresolved) forgotten promises. */
14
+ /**
15
+ * Number of forgotten promises still running (not yet settled).
16
+ * Decremented only when the underlying promise fulfills or rejects — not when a timeout fires.
17
+ */
11
18
  static activeForgets: number;
12
19
  /** Number of forgotten promises that threw exceptions. */
13
20
  static exceptedForgets: number;
14
21
  /** Logger instance used for error and warning output. */
15
22
  static logger: Logger;
16
- /** Whether any forgotten promises are still active. */
23
+ /** Whether any forgotten promises are still running, including any that have exceeded their timeout. */
17
24
  static get active(): boolean;
18
25
  /**
19
- * Waits until all forgotten promises have completed.
26
+ * Waits until all forgotten promises have settled.
20
27
  * @param interval - Polling interval in milliseconds.
21
28
  * @param timeout - Optional maximum wait time in milliseconds.
22
- * @returns The number of remaining active forgets (0 if all completed).
29
+ * @returns `0` when every forgotten promise has settled; otherwise the number still running.
30
+ * A non-zero return after `timeout` elapses can include promises whose timeout callback already
31
+ * ran but whose work is still in flight.
23
32
  */
24
33
  static awaitInactive(interval?: number, timeout?: number): Promise<number>;
25
34
  /** Handles exceptions from forgotten promises by logging error details. */
@@ -30,7 +39,10 @@ export declare class ForgetPromise {
30
39
  * @param config Configuration of forget settings
31
40
  */
32
41
  static forget<T>(promise: Promise<T> | PromiseEx<T> | PromisableFunction<T> | T, config?: ForgetConfig<T>): void;
33
- /** Handles timeout events for forgotten promises by logging timeout details. */
42
+ /**
43
+ * Handles timeout events for forgotten promises by logging timeout details.
44
+ * Does not terminate the underlying promise; it may continue running after this is called.
45
+ */
34
46
  static timeoutHandler(time: number, { name }: ForgetConfig, externalStackTrace?: string): void;
35
47
  }
36
48
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"ForgetPromise.d.ts","sourceRoot":"","sources":["../../src/ForgetPromise.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAC5C,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAG5D,OAAO,EAAuB,KAAK,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAE1E,kDAAkD;AAClD,KAAK,kBAAkB,CAAC,CAAC,IAAI,MAAM,UAAU,CAAC,CAAC,CAAC,CAAA;AAEhD;;GAEG;AAEH,qBAAa,aAAa;IACxB,kEAAkE;IAClE,MAAM,CAAC,aAAa,SAAI;IACxB,0DAA0D;IAC1D,MAAM,CAAC,eAAe,SAAI;IAC1B,yDAAyD;IACzD,MAAM,CAAC,MAAM,EAAE,MAAM,CAAU;IAE/B,uDAAuD;IACvD,MAAM,KAAK,MAAM,YAEhB;IAED;;;;;OAKG;WACU,aAAa,CAAC,QAAQ,SAAM,EAAE,OAAO,CAAC,EAAE,MAAM;IAc3D,2EAA2E;IAC3E,MAAM,CAAC,gBAAgB,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,YAAY,EAAE,kBAAkB,CAAC,EAAE,MAAM;IAOzF;;;;OAIG;IACH,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,GAAG,kBAAkB,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC;IAgEzG,gFAAgF;IAChF,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,IAAgB,EAAE,EAAE,YAAY,EAAE,kBAAkB,CAAC,EAAE,MAAM;CAMpG"}
1
+ {"version":3,"file":"ForgetPromise.d.ts","sourceRoot":"","sources":["../../src/ForgetPromise.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAC5C,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAG5D,OAAO,EAAuB,KAAK,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAE1E,kDAAkD;AAClD,KAAK,kBAAkB,CAAC,CAAC,IAAI,MAAM,UAAU,CAAC,CAAC,CAAC,CAAA;AAEhD;;;;;;GAMG;AAEH,qBAAa,aAAa;IACxB;;;OAGG;IACH,MAAM,CAAC,aAAa,SAAI;IACxB,0DAA0D;IAC1D,MAAM,CAAC,eAAe,SAAI;IAC1B,yDAAyD;IACzD,MAAM,CAAC,MAAM,EAAE,MAAM,CAAU;IAE/B,wGAAwG;IACxG,MAAM,KAAK,MAAM,YAEhB;IAED;;;;;;;OAOG;WACU,aAAa,CAAC,QAAQ,SAAM,EAAE,OAAO,CAAC,EAAE,MAAM;IAc3D,2EAA2E;IAC3E,MAAM,CAAC,gBAAgB,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,YAAY,EAAE,kBAAkB,CAAC,EAAE,MAAM;IAOzF;;;;OAIG;IACH,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,GAAG,kBAAkB,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC;IAiEzG;;;OAGG;IACH,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,IAAgB,EAAE,EAAE,YAAY,EAAE,kBAAkB,CAAC,EAAE,MAAM;CAMpG"}
@@ -3,7 +3,8 @@ import type { ForgetConfig } from './ForgetConfig.ts';
3
3
  /**
4
4
  * Explicitly launches an async function or promise without awaiting it (fire-and-forget).
5
5
  * @param promise - The promise or promisable value to forget.
6
- * @param config - Optional configuration for timeout, error handling, and completion callbacks.
6
+ * @param config - Optional configuration for timeout notification, error handling, and completion callbacks.
7
+ * Timeouts notify via `onCancel` but do not stop the underlying work; see `ForgetPromise.activeForgets`.
7
8
  */
8
9
  export declare const forget: <T>(promise: Promisable<T>, config?: ForgetConfig<T>) => void;
9
10
  //# sourceMappingURL=forget.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"forget.d.ts","sourceRoot":"","sources":["../../src/forget.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAEjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAGrD;;;;GAIG;AACH,eAAO,MAAM,MAAM,GAAI,CAAC,EAAE,SAAS,UAAU,CAAC,CAAC,CAAC,EAAE,SAAS,YAAY,CAAC,CAAC,CAAC,SAEzE,CAAA"}
1
+ {"version":3,"file":"forget.d.ts","sourceRoot":"","sources":["../../src/forget.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAEjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAGrD;;;;;GAKG;AACH,eAAO,MAAM,MAAM,GAAI,CAAC,EAAE,SAAS,UAAU,CAAC,CAAC,CAAC,EAAE,SAAS,YAAY,CAAC,CAAC,CAAC,SAEzE,CAAA"}
@@ -12,21 +12,26 @@ var defaultForgetNodeConfig = {
12
12
  import { delay } from "@xylabs/delay";
13
13
  import { isNumber, isPromise } from "@xylabs/typeof";
14
14
  var ForgetPromise = class {
15
- /** Number of currently active (unresolved) forgotten promises. */
15
+ /**
16
+ * Number of forgotten promises still running (not yet settled).
17
+ * Decremented only when the underlying promise fulfills or rejects — not when a timeout fires.
18
+ */
16
19
  static activeForgets = 0;
17
20
  /** Number of forgotten promises that threw exceptions. */
18
21
  static exceptedForgets = 0;
19
22
  /** Logger instance used for error and warning output. */
20
23
  static logger = console;
21
- /** Whether any forgotten promises are still active. */
24
+ /** Whether any forgotten promises are still running, including any that have exceeded their timeout. */
22
25
  static get active() {
23
26
  return this.activeForgets > 0;
24
27
  }
25
28
  /**
26
- * Waits until all forgotten promises have completed.
29
+ * Waits until all forgotten promises have settled.
27
30
  * @param interval - Polling interval in milliseconds.
28
31
  * @param timeout - Optional maximum wait time in milliseconds.
29
- * @returns The number of remaining active forgets (0 if all completed).
32
+ * @returns `0` when every forgotten promise has settled; otherwise the number still running.
33
+ * A non-zero return after `timeout` elapses can include promises whose timeout callback already
34
+ * ran but whose work is still in flight.
30
35
  */
31
36
  static async awaitInactive(interval = 100, timeout) {
32
37
  let timeoutRemaining = timeout;
@@ -105,9 +110,12 @@ var ForgetPromise = class {
105
110
  return;
106
111
  }
107
112
  }
108
- /** Handles timeout events for forgotten promises by logging timeout details. */
113
+ /**
114
+ * Handles timeout events for forgotten promises by logging timeout details.
115
+ * Does not terminate the underlying promise; it may continue running after this is called.
116
+ */
109
117
  static timeoutHandler(time, { name = "unknown" }, externalStackTrace) {
110
- this.logger.error(`forget promise timeout out after ${time}ms [Cancelling] [${name}]`);
118
+ this.logger.error(`forget promise timed out after ${time}ms [still running] [${name}]`);
111
119
  if (externalStackTrace !== void 0) {
112
120
  this.logger.warn(`External Stack trace [${name}]:`, externalStackTrace);
113
121
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/ForgetConfig.ts", "../../src/ForgetNodeConfig.ts", "../../src/ForgetPromise.ts", "../../src/ForgetPromiseNode.ts", "../../src/forgetNode.ts"],
4
- "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: unknown) => {\n const caughtError = error instanceof Error ? error : new Error(String(error))\n this.activeForgets--\n completed = true\n this.logger.error(`forgotten promise excepted [${config?.name ?? 'unknown'}]: ${caughtError.message}`, caughtError)\n resolvedConfig?.onComplete?.([undefined, caughtError])\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"],
5
- "mappings": "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",
4
+ "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 configured timeout elapses before the promise settles. The promise keeps running. */\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 /**\n * Timeout in milliseconds after which `onCancel` and `timeoutHandler` run.\n * This does not abort the promise; `activeForgets` stays elevated until it settles.\n */\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 * Timeouts are observability hooks only: they invoke `onCancel` and `timeoutHandler` but do not\n * abort or await-cleanup the underlying promise. `activeForgets` therefore counts promises that\n * are still running (unsettled), not promises still inside their configured timeout window.\n */\n// eslint-disable-next-line unicorn/no-static-only-class\nexport class ForgetPromise {\n /**\n * Number of forgotten promises still running (not yet settled).\n * Decremented only when the underlying promise fulfills or rejects \u2014 not when a timeout fires.\n */\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 running, including any that have exceeded their timeout. */\n static get active() {\n return this.activeForgets > 0\n }\n\n /**\n * Waits until all forgotten promises have settled.\n * @param interval - Polling interval in milliseconds.\n * @param timeout - Optional maximum wait time in milliseconds.\n * @returns `0` when every forgotten promise has settled; otherwise the number still running.\n * A non-zero return after `timeout` elapses can include promises whose timeout callback already\n * ran but whose work is still in flight.\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: unknown) => {\n const caughtError = error instanceof Error ? error : new Error(String(error))\n this.activeForgets--\n completed = true\n this.logger.error(`forgotten promise excepted [${config?.name ?? 'unknown'}]: ${caughtError.message}`, caughtError)\n resolvedConfig?.onComplete?.([undefined, caughtError])\n })\n }\n\n const promises = [promiseWrapper()]\n\n // Timeout races for notification only; it does not stop the underlying promise or\n // decrement activeForgets \u2014 callers use activeForgets to see how much work is still running.\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 /**\n * Handles timeout events for forgotten promises by logging timeout details.\n * Does not terminate the underlying promise; it may continue running after this is called.\n */\n static timeoutHandler(time: number, { name = 'unknown' }: ForgetConfig, externalStackTrace?: string) {\n this.logger.error(`forget promise timed out after ${time}ms [still running] [${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"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -4,13 +4,16 @@
4
4
  export interface ForgetConfig<T = any> {
5
5
  /** Optional name for identifying the forgotten promise in logs. */
6
6
  name?: string;
7
- /** Called when the promise is cancelled due to timeout. */
7
+ /** Called when the configured timeout elapses before the promise settles. The promise keeps running. */
8
8
  onCancel?: () => void;
9
9
  /** Called when the promise completes, with a tuple of [result, error]. */
10
10
  onComplete?: (result: [T | undefined, Error | undefined]) => void;
11
11
  /** Called when an exception occurs outside the promise itself. */
12
12
  onException?: (error: Error) => void;
13
- /** Timeout in milliseconds after which the promise is considered timed out. */
13
+ /**
14
+ * Timeout in milliseconds after which `onCancel` and `timeoutHandler` run.
15
+ * This does not abort the promise; `activeForgets` stays elevated until it settles.
16
+ */
14
17
  timeout?: number;
15
18
  }
16
19
  /** Default forget configuration with a 30-second timeout. */
@@ -1 +1 @@
1
- {"version":3,"file":"ForgetConfig.d.ts","sourceRoot":"","sources":["../../src/ForgetConfig.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,YAAY,CAAC,CAAC,GAAG,GAAG;IACnC,mEAAmE;IACnE,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB,0EAA0E;IAC1E,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,SAAS,EAAE,KAAK,GAAG,SAAS,CAAC,KAAK,IAAI,CAAA;IACjE,kEAAkE;IAClE,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAA;IACpC,+EAA+E;IAC/E,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,6DAA6D;AAC7D,eAAO,MAAM,mBAAmB,EAAE,YAAY,CAAC,OAAO,CAAuB,CAAA"}
1
+ {"version":3,"file":"ForgetConfig.d.ts","sourceRoot":"","sources":["../../src/ForgetConfig.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,YAAY,CAAC,CAAC,GAAG,GAAG;IACnC,mEAAmE;IACnE,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,wGAAwG;IACxG,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB,0EAA0E;IAC1E,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,SAAS,EAAE,KAAK,GAAG,SAAS,CAAC,KAAK,IAAI,CAAA;IACjE,kEAAkE;IAClE,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAA;IACpC;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,6DAA6D;AAC7D,eAAO,MAAM,mBAAmB,EAAE,YAAY,CAAC,OAAO,CAAuB,CAAA"}
@@ -5,21 +5,30 @@ import { type ForgetConfig } from './ForgetConfig.ts';
5
5
  type PromisableFunction<T> = () => Promisable<T>;
6
6
  /**
7
7
  * Manages fire-and-forget promises with tracking, timeouts, and error handling.
8
+ *
9
+ * Timeouts are observability hooks only: they invoke `onCancel` and `timeoutHandler` but do not
10
+ * abort or await-cleanup the underlying promise. `activeForgets` therefore counts promises that
11
+ * are still running (unsettled), not promises still inside their configured timeout window.
8
12
  */
9
13
  export declare class ForgetPromise {
10
- /** Number of currently active (unresolved) forgotten promises. */
14
+ /**
15
+ * Number of forgotten promises still running (not yet settled).
16
+ * Decremented only when the underlying promise fulfills or rejects — not when a timeout fires.
17
+ */
11
18
  static activeForgets: number;
12
19
  /** Number of forgotten promises that threw exceptions. */
13
20
  static exceptedForgets: number;
14
21
  /** Logger instance used for error and warning output. */
15
22
  static logger: Logger;
16
- /** Whether any forgotten promises are still active. */
23
+ /** Whether any forgotten promises are still running, including any that have exceeded their timeout. */
17
24
  static get active(): boolean;
18
25
  /**
19
- * Waits until all forgotten promises have completed.
26
+ * Waits until all forgotten promises have settled.
20
27
  * @param interval - Polling interval in milliseconds.
21
28
  * @param timeout - Optional maximum wait time in milliseconds.
22
- * @returns The number of remaining active forgets (0 if all completed).
29
+ * @returns `0` when every forgotten promise has settled; otherwise the number still running.
30
+ * A non-zero return after `timeout` elapses can include promises whose timeout callback already
31
+ * ran but whose work is still in flight.
23
32
  */
24
33
  static awaitInactive(interval?: number, timeout?: number): Promise<number>;
25
34
  /** Handles exceptions from forgotten promises by logging error details. */
@@ -30,7 +39,10 @@ export declare class ForgetPromise {
30
39
  * @param config Configuration of forget settings
31
40
  */
32
41
  static forget<T>(promise: Promise<T> | PromiseEx<T> | PromisableFunction<T> | T, config?: ForgetConfig<T>): void;
33
- /** Handles timeout events for forgotten promises by logging timeout details. */
42
+ /**
43
+ * Handles timeout events for forgotten promises by logging timeout details.
44
+ * Does not terminate the underlying promise; it may continue running after this is called.
45
+ */
34
46
  static timeoutHandler(time: number, { name }: ForgetConfig, externalStackTrace?: string): void;
35
47
  }
36
48
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"ForgetPromise.d.ts","sourceRoot":"","sources":["../../src/ForgetPromise.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAC5C,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAG5D,OAAO,EAAuB,KAAK,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAE1E,kDAAkD;AAClD,KAAK,kBAAkB,CAAC,CAAC,IAAI,MAAM,UAAU,CAAC,CAAC,CAAC,CAAA;AAEhD;;GAEG;AAEH,qBAAa,aAAa;IACxB,kEAAkE;IAClE,MAAM,CAAC,aAAa,SAAI;IACxB,0DAA0D;IAC1D,MAAM,CAAC,eAAe,SAAI;IAC1B,yDAAyD;IACzD,MAAM,CAAC,MAAM,EAAE,MAAM,CAAU;IAE/B,uDAAuD;IACvD,MAAM,KAAK,MAAM,YAEhB;IAED;;;;;OAKG;WACU,aAAa,CAAC,QAAQ,SAAM,EAAE,OAAO,CAAC,EAAE,MAAM;IAc3D,2EAA2E;IAC3E,MAAM,CAAC,gBAAgB,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,YAAY,EAAE,kBAAkB,CAAC,EAAE,MAAM;IAOzF;;;;OAIG;IACH,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,GAAG,kBAAkB,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC;IAgEzG,gFAAgF;IAChF,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,IAAgB,EAAE,EAAE,YAAY,EAAE,kBAAkB,CAAC,EAAE,MAAM;CAMpG"}
1
+ {"version":3,"file":"ForgetPromise.d.ts","sourceRoot":"","sources":["../../src/ForgetPromise.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAC5C,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAG5D,OAAO,EAAuB,KAAK,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAE1E,kDAAkD;AAClD,KAAK,kBAAkB,CAAC,CAAC,IAAI,MAAM,UAAU,CAAC,CAAC,CAAC,CAAA;AAEhD;;;;;;GAMG;AAEH,qBAAa,aAAa;IACxB;;;OAGG;IACH,MAAM,CAAC,aAAa,SAAI;IACxB,0DAA0D;IAC1D,MAAM,CAAC,eAAe,SAAI;IAC1B,yDAAyD;IACzD,MAAM,CAAC,MAAM,EAAE,MAAM,CAAU;IAE/B,wGAAwG;IACxG,MAAM,KAAK,MAAM,YAEhB;IAED;;;;;;;OAOG;WACU,aAAa,CAAC,QAAQ,SAAM,EAAE,OAAO,CAAC,EAAE,MAAM;IAc3D,2EAA2E;IAC3E,MAAM,CAAC,gBAAgB,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,YAAY,EAAE,kBAAkB,CAAC,EAAE,MAAM;IAOzF;;;;OAIG;IACH,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,GAAG,kBAAkB,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC;IAiEzG;;;OAGG;IACH,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,IAAgB,EAAE,EAAE,YAAY,EAAE,kBAAkB,CAAC,EAAE,MAAM;CAMpG"}
@@ -3,7 +3,8 @@ import type { ForgetConfig } from './ForgetConfig.ts';
3
3
  /**
4
4
  * Explicitly launches an async function or promise without awaiting it (fire-and-forget).
5
5
  * @param promise - The promise or promisable value to forget.
6
- * @param config - Optional configuration for timeout, error handling, and completion callbacks.
6
+ * @param config - Optional configuration for timeout notification, error handling, and completion callbacks.
7
+ * Timeouts notify via `onCancel` but do not stop the underlying work; see `ForgetPromise.activeForgets`.
7
8
  */
8
9
  export declare const forget: <T>(promise: Promisable<T>, config?: ForgetConfig<T>) => void;
9
10
  //# sourceMappingURL=forget.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"forget.d.ts","sourceRoot":"","sources":["../../src/forget.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAEjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAGrD;;;;GAIG;AACH,eAAO,MAAM,MAAM,GAAI,CAAC,EAAE,SAAS,UAAU,CAAC,CAAC,CAAC,EAAE,SAAS,YAAY,CAAC,CAAC,CAAC,SAEzE,CAAA"}
1
+ {"version":3,"file":"forget.d.ts","sourceRoot":"","sources":["../../src/forget.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAEjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAGrD;;;;;GAKG;AACH,eAAO,MAAM,MAAM,GAAI,CAAC,EAAE,SAAS,UAAU,CAAC,CAAC,CAAC,EAAE,SAAS,YAAY,CAAC,CAAC,CAAC,SAEzE,CAAA"}
@@ -12,21 +12,26 @@ var defaultForgetNodeConfig = {
12
12
  import { delay } from "@xylabs/delay";
13
13
  import { isNumber, isPromise } from "@xylabs/typeof";
14
14
  var ForgetPromise = class {
15
- /** Number of currently active (unresolved) forgotten promises. */
15
+ /**
16
+ * Number of forgotten promises still running (not yet settled).
17
+ * Decremented only when the underlying promise fulfills or rejects — not when a timeout fires.
18
+ */
16
19
  static activeForgets = 0;
17
20
  /** Number of forgotten promises that threw exceptions. */
18
21
  static exceptedForgets = 0;
19
22
  /** Logger instance used for error and warning output. */
20
23
  static logger = console;
21
- /** Whether any forgotten promises are still active. */
24
+ /** Whether any forgotten promises are still running, including any that have exceeded their timeout. */
22
25
  static get active() {
23
26
  return this.activeForgets > 0;
24
27
  }
25
28
  /**
26
- * Waits until all forgotten promises have completed.
29
+ * Waits until all forgotten promises have settled.
27
30
  * @param interval - Polling interval in milliseconds.
28
31
  * @param timeout - Optional maximum wait time in milliseconds.
29
- * @returns The number of remaining active forgets (0 if all completed).
32
+ * @returns `0` when every forgotten promise has settled; otherwise the number still running.
33
+ * A non-zero return after `timeout` elapses can include promises whose timeout callback already
34
+ * ran but whose work is still in flight.
30
35
  */
31
36
  static async awaitInactive(interval = 100, timeout) {
32
37
  let timeoutRemaining = timeout;
@@ -105,9 +110,12 @@ var ForgetPromise = class {
105
110
  return;
106
111
  }
107
112
  }
108
- /** Handles timeout events for forgotten promises by logging timeout details. */
113
+ /**
114
+ * Handles timeout events for forgotten promises by logging timeout details.
115
+ * Does not terminate the underlying promise; it may continue running after this is called.
116
+ */
109
117
  static timeoutHandler(time, { name = "unknown" }, externalStackTrace) {
110
- this.logger.error(`forget promise timeout out after ${time}ms [Cancelling] [${name}]`);
118
+ this.logger.error(`forget promise timed out after ${time}ms [still running] [${name}]`);
111
119
  if (externalStackTrace !== void 0) {
112
120
  this.logger.warn(`External Stack trace [${name}]:`, externalStackTrace);
113
121
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/ForgetConfig.ts", "../../src/ForgetNodeConfig.ts", "../../src/ForgetPromise.ts", "../../src/ForgetPromiseNode.ts", "../../src/forgetNode.ts"],
4
- "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: unknown) => {\n const caughtError = error instanceof Error ? error : new Error(String(error))\n this.activeForgets--\n completed = true\n this.logger.error(`forgotten promise excepted [${config?.name ?? 'unknown'}]: ${caughtError.message}`, caughtError)\n resolvedConfig?.onComplete?.([undefined, caughtError])\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"],
5
- "mappings": "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",
4
+ "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 configured timeout elapses before the promise settles. The promise keeps running. */\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 /**\n * Timeout in milliseconds after which `onCancel` and `timeoutHandler` run.\n * This does not abort the promise; `activeForgets` stays elevated until it settles.\n */\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 * Timeouts are observability hooks only: they invoke `onCancel` and `timeoutHandler` but do not\n * abort or await-cleanup the underlying promise. `activeForgets` therefore counts promises that\n * are still running (unsettled), not promises still inside their configured timeout window.\n */\n// eslint-disable-next-line unicorn/no-static-only-class\nexport class ForgetPromise {\n /**\n * Number of forgotten promises still running (not yet settled).\n * Decremented only when the underlying promise fulfills or rejects \u2014 not when a timeout fires.\n */\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 running, including any that have exceeded their timeout. */\n static get active() {\n return this.activeForgets > 0\n }\n\n /**\n * Waits until all forgotten promises have settled.\n * @param interval - Polling interval in milliseconds.\n * @param timeout - Optional maximum wait time in milliseconds.\n * @returns `0` when every forgotten promise has settled; otherwise the number still running.\n * A non-zero return after `timeout` elapses can include promises whose timeout callback already\n * ran but whose work is still in flight.\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: unknown) => {\n const caughtError = error instanceof Error ? error : new Error(String(error))\n this.activeForgets--\n completed = true\n this.logger.error(`forgotten promise excepted [${config?.name ?? 'unknown'}]: ${caughtError.message}`, caughtError)\n resolvedConfig?.onComplete?.([undefined, caughtError])\n })\n }\n\n const promises = [promiseWrapper()]\n\n // Timeout races for notification only; it does not stop the underlying promise or\n // decrement activeForgets \u2014 callers use activeForgets to see how much work is still running.\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 /**\n * Handles timeout events for forgotten promises by logging timeout details.\n * Does not terminate the underlying promise; it may continue running after this is called.\n */\n static timeoutHandler(time: number, { name = 'unknown' }: ForgetConfig, externalStackTrace?: string) {\n this.logger.error(`forget promise timed out after ${time}ms [still running] [${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"],
5
+ "mappings": ";AAqBO,IAAM,sBAA6C,EAAE,SAAS,IAAO;;;ACPrE,IAAM,0BAAqD;AAAA,EAChE,GAAG;AAAA,EACH,oBAAoB;AAAA,EACpB,sBAAsB;AACxB;;;AClBA,SAAS,aAAa;AAGtB,SAAS,UAAU,iBAAiB;AAe7B,IAAM,gBAAN,MAAoB;AAAA;AAAA;AAAA;AAAA;AAAA,EAKzB,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;AAAA;AAAA,EAUA,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,UAAmB;AACzB,kBAAM,cAAc,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AAC5E,iBAAK;AACL,wBAAY;AACZ,iBAAK,OAAO,MAAM,+BAA+B,QAAQ,QAAQ,SAAS,MAAM,YAAY,OAAO,IAAI,WAAW;AAClH,4BAAgB,aAAa,CAAC,QAAW,WAAW,CAAC;AAAA,UACvD,CAAC;AAAA,QACL;AAEA,cAAM,WAAW,CAAC,eAAe,CAAC;AAIlC,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;AAAA;AAAA;AAAA,EAMA,OAAO,eAAe,MAAc,EAAE,OAAO,UAAU,GAAiB,oBAA6B;AACnG,SAAK,OAAO,MAAM,kCAAkC,IAAI,uBAAuB,IAAI,GAAG;AACtF,QAAI,uBAAuB,QAAW;AACpC,WAAK,OAAO,KAAK,yBAAyB,IAAI,MAAM,kBAAkB;AAAA,IACxE;AAAA,EACF;AACF;;;ACtIO,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;",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xylabs/forget",
3
- "version": "6.1.0",
3
+ "version": "6.1.3",
4
4
  "description": "Base functionality used throughout XY Labs TypeScript/JavaScript libraries",
5
5
  "keywords": [
6
6
  "forget",
@@ -53,10 +53,10 @@
53
53
  "README.md"
54
54
  ],
55
55
  "dependencies": {
56
- "@xylabs/delay": "~6.1.0",
57
- "@xylabs/logger": "~6.1.0",
58
- "@xylabs/typeof": "~6.1.0",
59
- "@xylabs/promise": "~6.1.0"
56
+ "@xylabs/delay": "~6.1.3",
57
+ "@xylabs/promise": "~6.1.3",
58
+ "@xylabs/logger": "~6.1.3",
59
+ "@xylabs/typeof": "~6.1.3"
60
60
  },
61
61
  "devDependencies": {
62
62
  "@types/node": "^25.9.2",