@xylabs/forget
Version:
Base functionality used throughout XY Labs TypeScript/JavaScript libraries
155 lines (150 loc) • 5.44 kB
JavaScript
// 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