UNPKG

mastercache

Version:

Multi-tier cache module for Node.js. Redis, Upstash, CloudfareKV, File, in-memory and others drivers

309 lines (298 loc) 8.09 kB
// ../../node_modules/.pnpm/p-timeout@6.1.3/node_modules/p-timeout/index.js var TimeoutError = class extends Error { constructor(message) { super(message); this.name = "TimeoutError"; } }; var AbortError = class extends Error { constructor(message) { super(); this.name = "AbortError"; this.message = message; } }; var getDOMException = (errorMessage) => globalThis.DOMException === void 0 ? new AbortError(errorMessage) : new DOMException(errorMessage); var getAbortedReason = (signal) => { const reason = signal.reason === void 0 ? getDOMException("This operation was aborted.") : signal.reason; return reason instanceof Error ? reason : getDOMException(reason); }; function pTimeout(promise, options) { const { milliseconds, fallback, message, customTimers = { setTimeout, clearTimeout } } = options; let timer; const wrappedPromise = new Promise((resolve, reject) => { if (typeof milliseconds !== "number" || Math.sign(milliseconds) !== 1) { throw new TypeError(`Expected \`milliseconds\` to be a positive number, got \`${milliseconds}\``); } if (options.signal) { const { signal } = options; if (signal.aborted) { reject(getAbortedReason(signal)); } const abortHandler = () => { reject(getAbortedReason(signal)); }; signal.addEventListener("abort", abortHandler, { once: true }); promise.finally(() => { signal.removeEventListener("abort", abortHandler); }); } if (milliseconds === Number.POSITIVE_INFINITY) { promise.then(resolve, reject); return; } const timeoutError = new TimeoutError(); timer = customTimers.setTimeout.call(void 0, () => { if (fallback) { try { resolve(fallback()); } catch (error) { reject(error); } return; } if (typeof promise.cancel === "function") { promise.cancel(); } if (message === false) { resolve(); } else if (message instanceof Error) { reject(message); } else { timeoutError.message = message ?? `Promise timed out after ${milliseconds} milliseconds`; reject(timeoutError); } }, milliseconds); (async () => { try { resolve(await promise); } catch (error) { reject(error); } })(); }); const cancelablePromise = wrappedPromise.finally(() => { cancelablePromise.clear(); }); cancelablePromise.clear = () => { customTimers.clearTimeout.call(void 0, timer); timer = void 0; }; return cancelablePromise; } // src/libs/exception.ts import { format } from "node:util"; var Exception = class extends Error { /** * Name of the class that raised the exception. */ name; /** * A status code for the error. Usually helpful when converting errors * to HTTP responses. */ status; constructor(message, options) { super(message, options); const ErrorConstructor = this.constructor; this.name = ErrorConstructor.name; this.message = message || ErrorConstructor.message || ""; this.status = options?.status || ErrorConstructor.status || 500; const code = options?.code || ErrorConstructor.code; if (code !== void 0) { this.code = code; } const help = ErrorConstructor.help; if (help !== void 0) { this.help = help; } Error.captureStackTrace(this, ErrorConstructor); } get [Symbol.toStringTag]() { return this.constructor.name; } toString() { if (this.code) { return `${this.name} [${this.code}]: ${this.message}`; } return `${this.name}: ${this.message}`; } }; function createError(message, code, status) { return class extends Exception { static message = message; static code = code; static status = status; constructor(args, options) { super(format(message, ...args || []), options); this.name = "Exception"; } }; } // src/errors.ts var E_FACTORY_SOFT_TIMEOUT = createError( "Factory has timed out after waiting for soft timeout", "E_FACTORY_SOFT_TIMEOUT" ); var E_FACTORY_HARD_TIMEOUT = createError( "Factory has timed out after waiting for hard timeout", "E_FACTORY_HARD_TIMEOUT" ); // src/events/cache/cache-hit.ts var CacheHit = class { constructor(key, value, store, graced = false) { this.key = key; this.value = value; this.store = store; this.graced = graced; } name = "cache:hit"; toJSON() { return { key: this.key, value: this.value, store: this.store, graced: this.graced }; } }; // src/events/cache/cache-miss.ts var CacheMiss = class { constructor(key, store) { this.key = key; this.store = store; } name = "cache:miss"; toJSON() { return { key: this.key, store: this.store }; } }; // src/events/cache/cache-cleared.ts var CacheCleared = class { constructor(store) { this.store = store; } name = "cache:cleared"; toJSON() { return { store: this.store }; } }; // src/events/cache/cache-deleted.ts var CacheDeleted = class { constructor(key, store) { this.key = key; this.store = store; } name = "cache:deleted"; toJSON() { return { key: this.key, store: this.store }; } }; // src/events/cache/cache-written.ts var CacheWritten = class { constructor(key, value, store) { this.key = key; this.value = value; this.store = store; } name = "cache:written"; toJSON() { return { key: this.key, store: this.store, value: this.value }; } }; // src/events/bus/bus-message-received.ts var BusMessageReceived = class { constructor(message) { this.message = message; } name = "bus:message:received"; toJSON() { return { keys: this.message.keys, type: this.message.type }; } }; // src/events/bus/bus-message-published.ts var BusMessagePublished = class { constructor(message) { this.message = message; } name = "bus:message:published"; toJSON() { return { keys: this.message.keys, type: this.message.type }; } }; // src/events/index.ts var events = { BusMessagePublished, BusMessageReceived, CacheHit, CacheMiss, CacheCleared, CacheDeleted, CacheWritten }; // src/cache/factory-runner.ts var FactoryRunner = class { #stack; #stackWriter; #locks; constructor(stack, stackWriter, locks) { this.#stack = stack; this.#stackWriter = stackWriter; this.#locks = locks; } async saveBackgroundFactoryResult(key, factoryResult, options, lockReleaser) { await this.#stackWriter.set(key, factoryResult, options); this.#locks.release(key, lockReleaser); } async writeFactoryResult(key, item, options, lockReleaser) { await this.#stackWriter.set(key, item, options); this.#stack.emit(new events.CacheMiss(key, this.#stack.name)); this.#stack.logger.trace({ key, cache: this.#stack.name, opId: options.id }, "cache miss"); this.#locks.release(key, lockReleaser); } async run(key, factory, hasFallback, options, lockReleaser) { const timeoutDuration = options.factoryTimeout(hasFallback); const timeoutException = timeoutDuration === options.timeouts?.hard ? E_FACTORY_HARD_TIMEOUT : E_FACTORY_SOFT_TIMEOUT; const promisifiedFactory = async () => { return await factory({ setTtl: (ttl) => options.setLogicalTtl(ttl) }); }; const factoryPromise = promisifiedFactory(); const factoryResult = await pTimeout(factoryPromise, { milliseconds: timeoutDuration ?? Number.POSITIVE_INFINITY, fallback: async () => { factoryPromise.then((result) => this.saveBackgroundFactoryResult(key, result, options, lockReleaser)).catch(() => { }).finally(() => this.#locks.release(key, lockReleaser)); throw new timeoutException(); } }); await this.writeFactoryResult(key, factoryResult, options, lockReleaser); return factoryResult; } }; export { FactoryRunner }; //# sourceMappingURL=factory-runner.js.map