mastercache
Version:
Multi-tier cache module for Node.js. Redis, Upstash, CloudfareKV, File, in-memory and others drivers
1 lines • 64.9 kB
Source Map (JSON)
{"version":3,"sources":["../../../src/cache/cache.ts","../../../src/events/cache/cache-hit.ts","../../../src/events/cache/cache-miss.ts","../../../src/events/cache/cache-cleared.ts","../../../src/events/cache/cache-deleted.ts","../../../src/events/cache/cache-written.ts","../../../src/events/bus/bus-message-received.ts","../../../src/events/bus/bus-message-published.ts","../../../src/events/index.ts","../../../../../node_modules/.pnpm/async-mutex@0.5.0/node_modules/async-mutex/index.mjs","../../../src/cache/locks.ts","../../../../../node_modules/.pnpm/p-timeout@6.1.3/node_modules/p-timeout/index.js","../../../src/libs/exception.ts","../../../src/errors.ts","../../../src/cache/factory-runner.ts","../../../src/cache/get-set-handler.ts","../../../src/cache/stack/cache-stack-writer.ts"],"sourcesContent":["import { events } from '../events/index';\nimport { GetSetHandler } from './get-set-handler';\nimport { CacheBusMessageType } from '../types/main';\nimport type { CacheStack } from './stack/cache-stack';\nimport type { CacheProvider } from '../types/provider';\nimport { CacheStackWriter } from './stack/cache-stack-writer';\nimport type {\n GetOrSetOptions,\n Factory,\n GetOptions,\n DeleteOptions,\n SetOptions,\n HasOptions,\n ClearOptions,\n GetSetFactory,\n GetOrSetPojoOptions,\n GetPojoOptions,\n SetPojoOptions,\n HasPojoOptions,\n DeletePojoOptions,\n DeleteManyPojoOptions,\n GetOrSetForeverPojoOptions,\n GetOrSetForeverOptions,\n} from '../types/main';\n\nexport class Cache implements CacheProvider {\n /**\n * The name of the cache\n */\n name: string;\n\n #getSetHandler: GetSetHandler;\n #cacheWriter: CacheStackWriter;\n #stack: CacheStack;\n\n constructor(name: string, stack: CacheStack) {\n this.name = name;\n\n this.#stack = stack;\n this.#cacheWriter = new CacheStackWriter(this.#stack);\n this.#getSetHandler = new GetSetHandler(this.#stack, this.#cacheWriter);\n }\n\n #resolveDefaultValue(defaultValue?: Factory) {\n return typeof defaultValue === 'function' ? defaultValue() : defaultValue ?? undefined;\n }\n\n /**\n * Returns a new instance of the driver namespaced\n */\n namespace(namespace: string) {\n return new Cache(this.name, this.#stack.namespace(namespace));\n }\n\n get<T = any>(options: GetPojoOptions<T>): Promise<T>\n get<T = any>(key: string): Promise<T | null | undefined>\n get<T = any>(key: string, defaultValue: Factory<T>, options?: GetOptions): Promise<T>\n async get<T = any>(\n keyOrOptions: string | GetPojoOptions<T>,\n defaultValue?: Factory<T>,\n rawOptions?: GetOptions,\n ): Promise<T | undefined | null> {\n let key: string;\n let providedOptions: GetOptions;\n let defaultValueFn: Factory<T>;\n\n if (typeof keyOrOptions === 'string') {\n key = keyOrOptions;\n providedOptions = rawOptions ?? {};\n defaultValueFn = this.#resolveDefaultValue(defaultValue);\n } else {\n key = keyOrOptions.key;\n providedOptions = keyOrOptions;\n defaultValueFn = this.#resolveDefaultValue(keyOrOptions.defaultValue);\n }\n\n const options = this.#stack.defaultOptions.cloneWith(providedOptions);\n const localItem = this.#stack.l1?.get(key, options);\n\n if (localItem !== undefined && !localItem.isLogicallyExpired()) {\n this.#stack.emit(new events.CacheHit(key, localItem.getValue(), this.name));\n return localItem.getValue();\n }\n\n const remoteItem = await this.#stack.l2?.get(key, options);\n\n if (remoteItem !== undefined && !remoteItem.isLogicallyExpired()) {\n this.#stack.l1?.set(key, remoteItem.serialize(), options);\n this.#stack.emit(new events.CacheHit(key, remoteItem.getValue(), this.name));\n return remoteItem.getValue();\n }\n\n if (!options.isGracePeriodEnabled) {\n this.#stack.emit(new events.CacheMiss(key, this.name));\n return this.#resolveDefaultValue(defaultValueFn);\n }\n\n if (remoteItem) {\n this.#stack.l1?.set(key, remoteItem.serialize(), options);\n this.#stack.emit(new events.CacheHit(key, remoteItem.serialize(), this.name, true));\n return remoteItem.getValue();\n }\n\n if (localItem) {\n this.#stack.emit(new events.CacheHit(key, localItem.serialize(), this.name, true));\n return localItem.getValue();\n }\n\n this.#stack.emit(new events.CacheMiss(key, this.name));\n return this.#resolveDefaultValue(defaultValueFn);\n }\n\n /**\n * Set a value in the cache\n * Returns true if the value was set, false otherwise\n */\n async set(keyOrOptions: string | SetPojoOptions, value?: any, rawOptions?: SetOptions) {\n if (typeof keyOrOptions === 'string') {\n const options = this.#stack.defaultOptions.cloneWith(rawOptions);\n return this.#cacheWriter.set(keyOrOptions, value, options);\n }\n\n const options = this.#stack.defaultOptions.cloneWith(keyOrOptions);\n return this.#cacheWriter.set(keyOrOptions.key, keyOrOptions.value, options);\n }\n\n /**\n * Set a value in the cache forever\n * Returns true if the value was set, false otherwise\n */\n async setForever<T>(keyOrOptions: string | SetPojoOptions, value?: T, rawOptions?: SetOptions) {\n return this.set(keyOrOptions, value, { ttl: null, ...rawOptions });\n }\n\n /**\n * Retrieve an item from the cache if it exists, otherwise store the value\n * provided by the factory and return it\n */\n async getOrSet<T>(\n keyOrOptions: string | GetOrSetPojoOptions<T>,\n factory?: GetSetFactory<T>,\n options?: GetOrSetOptions,\n ): Promise<T> {\n if (typeof keyOrOptions === 'string') {\n const cacheOptions = this.#stack.defaultOptions.cloneWith(options);\n return this.#getSetHandler.handle(keyOrOptions, factory, cacheOptions);\n }\n\n const cacheOptions = this.#stack.defaultOptions.cloneWith(keyOrOptions);\n return this.#getSetHandler.handle(keyOrOptions.key, keyOrOptions.factory, cacheOptions);\n }\n\n /**\n * Retrieve an item from the cache if it exists, otherwise store the value\n * provided by the factory forever and return it\n */\n async getOrSetForever<T>(\n keyOrOptions: string | GetOrSetForeverPojoOptions<T>,\n factory?: GetSetFactory<T>,\n options?: GetOrSetForeverOptions,\n ): Promise<T> {\n if (typeof keyOrOptions === 'string') {\n const cacheOptions = this.#stack.defaultOptions.cloneWith({ ttl: null, ...options });\n return this.#getSetHandler.handle(keyOrOptions, factory, cacheOptions);\n }\n\n const cacheOptions = this.#stack.defaultOptions.cloneWith({ ttl: null, ...keyOrOptions });\n return this.#getSetHandler.handle(keyOrOptions.key, keyOrOptions.factory, cacheOptions);\n }\n\n /**\n * Check if a key exists in the cache\n */\n async has(keyOrOptions: string | HasPojoOptions, options?: HasOptions) {\n const key = typeof keyOrOptions === 'string' ? keyOrOptions : keyOrOptions.key;\n const providedOptions = typeof keyOrOptions === 'string' ? options : keyOrOptions;\n\n const cacheOptions = this.#stack.defaultOptions.cloneWith(providedOptions);\n\n const inRemote = await this.#stack.l2?.has(key, cacheOptions);\n const inLocal = this.#stack.l1?.has(key);\n\n return !!(inRemote || inLocal);\n }\n\n /**\n * Check if key is missing in the cache\n */\n async missing(keyOrOptions: string | HasPojoOptions, options?: HasOptions) {\n return !(await this.has(keyOrOptions, options));\n }\n\n /**\n * Get the value of a key and delete it\n * Returns the value if the key exists, undefined otherwise\n */\n async pull<T = any>(key: string): Promise<T | undefined | null> {\n const value = await this.get<T>(key);\n await this.delete(key);\n return value;\n }\n\n /**\n * Delete a key from the cache, emit cache:deleted event and\n * publish invalidation through the bus\n */\n async delete(\n keyOrOptions: string | DeletePojoOptions,\n rawOptions?: DeleteOptions,\n ): Promise<boolean> {\n const isPojo = typeof keyOrOptions !== 'string';\n const key = isPojo ? keyOrOptions.key : keyOrOptions;\n const options = this.#stack.defaultOptions.cloneWith(isPojo ? keyOrOptions : rawOptions);\n\n this.#stack.l1?.delete(key, options);\n await this.#stack.l2?.delete(key, options);\n\n this.#stack.emit(new events.CacheDeleted(key, this.name));\n\n await this.#stack.publish({ type: CacheBusMessageType.Delete, keys: [key] });\n\n return true;\n }\n\n /**\n * Delete multiple keys from local and remote cache\n * Then emit cache:deleted events for each key\n * And finally publish invalidation through the bus\n */\n async deleteMany(\n keysOrOptions: string[] | DeleteManyPojoOptions,\n rawOptions?: DeleteOptions,\n ): Promise<boolean> {\n const isPojo = !Array.isArray(keysOrOptions);\n const options = this.#stack.defaultOptions.cloneWith(isPojo ? keysOrOptions : rawOptions);\n const keys = isPojo ? keysOrOptions.keys : keysOrOptions;\n\n this.#stack.l1?.deleteMany(keys, options);\n await this.#stack.l2?.deleteMany(keys, options);\n\n keys.forEach((key) => this.#stack.emit(new events.CacheDeleted(key, this.name)));\n\n await this.#stack.publish({ type: CacheBusMessageType.Delete, keys });\n\n return true;\n }\n\n /**\n * Remove all items from the cache\n */\n async clear(options?: ClearOptions) {\n const cacheOptions = this.#stack.defaultOptions.cloneWith(options);\n\n await Promise.all([\n this.#stack.l1?.clear(),\n this.#stack.l2?.clear(cacheOptions),\n this.#stack.publish({ type: CacheBusMessageType.Clear, keys: [] }),\n ]);\n\n this.#stack.emit(new events.CacheCleared(this.name));\n }\n\n /**\n * Closes the connection to the cache\n */\n async disconnect() {\n await Promise.all([\n this.#stack.l1?.disconnect(),\n this.#stack.l2?.disconnect(),\n this.#stack.bus?.disconnect(),\n ]);\n }\n}\n","import type { CacheEvent } from '../../types/main';\n\n/**\n * Event emitted when a cache entry is hit\n */\nexport class CacheHit implements CacheEvent {\n name = 'cache:hit' as const;\n\n constructor(\n readonly key: string,\n readonly value: any,\n readonly store: string,\n readonly graced: boolean = false,\n ) {}\n\n toJSON() {\n return {\n key: this.key,\n value: this.value,\n store: this.store,\n graced: this.graced,\n };\n }\n}\n","import type { CacheEvent } from '../../types/main';\n\n/**\n * Event emitted when a cache entry is missed\n */\nexport class CacheMiss implements CacheEvent {\n name = 'cache:miss' as const;\n\n constructor(\n readonly key: string,\n readonly store: string,\n ) {}\n\n toJSON() {\n return {\n key: this.key,\n store: this.store,\n };\n }\n}\n","import type { CacheEvent } from '../../types/main';\n\n/**\n * Event emitted when a cache store is cleared\n * using `.clear()`\n */\nexport class CacheCleared implements CacheEvent {\n name = 'cache:cleared' as const;\n\n constructor(readonly store: string) {}\n\n toJSON() {\n return {\n store: this.store,\n };\n }\n}\n","import type { CacheEvent } from '../../types/main';\n\n/**\n * Event emitted when a cache entry is deleted\n * using `.delete()` or `.deleteMany()`\n */\nexport class CacheDeleted implements CacheEvent {\n name = 'cache:deleted' as const;\n\n constructor(\n readonly key: string,\n readonly store: string,\n ) {}\n\n toJSON() {\n return {\n key: this.key,\n store: this.store,\n };\n }\n}\n","import type { CacheEvent } from '../../types/main';\n\n/**\n * Event emitted when a cache entry is written\n * using `set`,`getOrSet`\n */\nexport class CacheWritten implements CacheEvent {\n name = 'cache:written' as const;\n\n constructor(\n readonly key: string,\n readonly value: any,\n readonly store: string,\n ) {}\n\n toJSON() {\n return {\n key: this.key,\n store: this.store,\n value: this.value,\n };\n }\n}\n","import type { CacheBusMessage, CacheEvent } from '../../types/main';\n\n/**\n * Event when the bus receives a message\n */\nexport class BusMessageReceived implements CacheEvent {\n name = 'bus:message:received' as const;\n\n constructor(readonly message: CacheBusMessage) {}\n\n toJSON() {\n return {\n keys: this.message.keys,\n type: this.message.type,\n };\n }\n}\n","import type { CacheBusMessage, CacheEvent } from '../../types/main';\n\n/**\n * Event when the bus publishes a message\n */\nexport class BusMessagePublished implements CacheEvent {\n name = 'bus:message:published' as const;\n\n constructor(readonly message: CacheBusMessage) {}\n\n toJSON() {\n return {\n keys: this.message.keys,\n type: this.message.type,\n };\n }\n}\n","import { CacheHit } from './cache/cache-hit';\nimport { CacheMiss } from './cache/cache-miss';\nimport { CacheCleared } from './cache/cache-cleared';\nimport { CacheDeleted } from './cache/cache-deleted';\nimport { CacheWritten } from './cache/cache-written';\nimport { BusMessageReceived } from './bus/bus-message-received';\nimport { BusMessagePublished } from './bus/bus-message-published';\n\nexport const events = {\n BusMessagePublished,\n BusMessageReceived,\n CacheHit,\n CacheMiss,\n CacheCleared,\n CacheDeleted,\n CacheWritten,\n};\n","const E_TIMEOUT = new Error('timeout while waiting for mutex to become available');\nconst E_ALREADY_LOCKED = new Error('mutex already locked');\nconst E_CANCELED = new Error('request for lock canceled');\n\nvar __awaiter$2 = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n};\nclass Semaphore {\n constructor(_value, _cancelError = E_CANCELED) {\n this._value = _value;\n this._cancelError = _cancelError;\n this._queue = [];\n this._weightedWaiters = [];\n }\n acquire(weight = 1, priority = 0) {\n if (weight <= 0)\n throw new Error(`invalid weight ${weight}: must be positive`);\n return new Promise((resolve, reject) => {\n const task = { resolve, reject, weight, priority };\n const i = findIndexFromEnd(this._queue, (other) => priority <= other.priority);\n if (i === -1 && weight <= this._value) {\n // Needs immediate dispatch, skip the queue\n this._dispatchItem(task);\n }\n else {\n this._queue.splice(i + 1, 0, task);\n }\n });\n }\n runExclusive(callback_1) {\n return __awaiter$2(this, arguments, void 0, function* (callback, weight = 1, priority = 0) {\n const [value, release] = yield this.acquire(weight, priority);\n try {\n return yield callback(value);\n }\n finally {\n release();\n }\n });\n }\n waitForUnlock(weight = 1, priority = 0) {\n if (weight <= 0)\n throw new Error(`invalid weight ${weight}: must be positive`);\n if (this._couldLockImmediately(weight, priority)) {\n return Promise.resolve();\n }\n else {\n return new Promise((resolve) => {\n if (!this._weightedWaiters[weight - 1])\n this._weightedWaiters[weight - 1] = [];\n insertSorted(this._weightedWaiters[weight - 1], { resolve, priority });\n });\n }\n }\n isLocked() {\n return this._value <= 0;\n }\n getValue() {\n return this._value;\n }\n setValue(value) {\n this._value = value;\n this._dispatchQueue();\n }\n release(weight = 1) {\n if (weight <= 0)\n throw new Error(`invalid weight ${weight}: must be positive`);\n this._value += weight;\n this._dispatchQueue();\n }\n cancel() {\n this._queue.forEach((entry) => entry.reject(this._cancelError));\n this._queue = [];\n }\n _dispatchQueue() {\n this._drainUnlockWaiters();\n while (this._queue.length > 0 && this._queue[0].weight <= this._value) {\n this._dispatchItem(this._queue.shift());\n this._drainUnlockWaiters();\n }\n }\n _dispatchItem(item) {\n const previousValue = this._value;\n this._value -= item.weight;\n item.resolve([previousValue, this._newReleaser(item.weight)]);\n }\n _newReleaser(weight) {\n let called = false;\n return () => {\n if (called)\n return;\n called = true;\n this.release(weight);\n };\n }\n _drainUnlockWaiters() {\n if (this._queue.length === 0) {\n for (let weight = this._value; weight > 0; weight--) {\n const waiters = this._weightedWaiters[weight - 1];\n if (!waiters)\n continue;\n waiters.forEach((waiter) => waiter.resolve());\n this._weightedWaiters[weight - 1] = [];\n }\n }\n else {\n const queuedPriority = this._queue[0].priority;\n for (let weight = this._value; weight > 0; weight--) {\n const waiters = this._weightedWaiters[weight - 1];\n if (!waiters)\n continue;\n const i = waiters.findIndex((waiter) => waiter.priority <= queuedPriority);\n (i === -1 ? waiters : waiters.splice(0, i))\n .forEach((waiter => waiter.resolve()));\n }\n }\n }\n _couldLockImmediately(weight, priority) {\n return (this._queue.length === 0 || this._queue[0].priority < priority) &&\n weight <= this._value;\n }\n}\nfunction insertSorted(a, v) {\n const i = findIndexFromEnd(a, (other) => v.priority <= other.priority);\n a.splice(i + 1, 0, v);\n}\nfunction findIndexFromEnd(a, predicate) {\n for (let i = a.length - 1; i >= 0; i--) {\n if (predicate(a[i])) {\n return i;\n }\n }\n return -1;\n}\n\nvar __awaiter$1 = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n};\nclass Mutex {\n constructor(cancelError) {\n this._semaphore = new Semaphore(1, cancelError);\n }\n acquire() {\n return __awaiter$1(this, arguments, void 0, function* (priority = 0) {\n const [, releaser] = yield this._semaphore.acquire(1, priority);\n return releaser;\n });\n }\n runExclusive(callback, priority = 0) {\n return this._semaphore.runExclusive(() => callback(), 1, priority);\n }\n isLocked() {\n return this._semaphore.isLocked();\n }\n waitForUnlock(priority = 0) {\n return this._semaphore.waitForUnlock(1, priority);\n }\n release() {\n if (this._semaphore.isLocked())\n this._semaphore.release();\n }\n cancel() {\n return this._semaphore.cancel();\n }\n}\n\nvar __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n};\nfunction withTimeout(sync, timeout, timeoutError = E_TIMEOUT) {\n return {\n acquire: (weightOrPriority, priority) => {\n let weight;\n if (isSemaphore(sync)) {\n weight = weightOrPriority;\n }\n else {\n weight = undefined;\n priority = weightOrPriority;\n }\n if (weight !== undefined && weight <= 0) {\n throw new Error(`invalid weight ${weight}: must be positive`);\n }\n return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {\n let isTimeout = false;\n const handle = setTimeout(() => {\n isTimeout = true;\n reject(timeoutError);\n }, timeout);\n try {\n const ticket = yield (isSemaphore(sync)\n ? sync.acquire(weight, priority)\n : sync.acquire(priority));\n if (isTimeout) {\n const release = Array.isArray(ticket) ? ticket[1] : ticket;\n release();\n }\n else {\n clearTimeout(handle);\n resolve(ticket);\n }\n }\n catch (e) {\n if (!isTimeout) {\n clearTimeout(handle);\n reject(e);\n }\n }\n }));\n },\n runExclusive(callback, weight, priority) {\n return __awaiter(this, void 0, void 0, function* () {\n let release = () => undefined;\n try {\n const ticket = yield this.acquire(weight, priority);\n if (Array.isArray(ticket)) {\n release = ticket[1];\n return yield callback(ticket[0]);\n }\n else {\n release = ticket;\n return yield callback();\n }\n }\n finally {\n release();\n }\n });\n },\n release(weight) {\n sync.release(weight);\n },\n cancel() {\n return sync.cancel();\n },\n waitForUnlock: (weightOrPriority, priority) => {\n let weight;\n if (isSemaphore(sync)) {\n weight = weightOrPriority;\n }\n else {\n weight = undefined;\n priority = weightOrPriority;\n }\n if (weight !== undefined && weight <= 0) {\n throw new Error(`invalid weight ${weight}: must be positive`);\n }\n return new Promise((resolve, reject) => {\n const handle = setTimeout(() => reject(timeoutError), timeout);\n (isSemaphore(sync)\n ? sync.waitForUnlock(weight, priority)\n : sync.waitForUnlock(priority)).then(() => {\n clearTimeout(handle);\n resolve();\n });\n });\n },\n isLocked: () => sync.isLocked(),\n getValue: () => sync.getValue(),\n setValue: (value) => sync.setValue(value),\n };\n}\nfunction isSemaphore(sync) {\n return sync.getValue !== undefined;\n}\n\n// eslint-disable-next-lisne @typescript-eslint/explicit-module-boundary-types\nfunction tryAcquire(sync, alreadyAcquiredError = E_ALREADY_LOCKED) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return withTimeout(sync, 0, alreadyAcquiredError);\n}\n\nexport { E_ALREADY_LOCKED, E_CANCELED, E_TIMEOUT, Mutex, Semaphore, tryAcquire, withTimeout };\n","import { Mutex, withTimeout, type MutexInterface } from 'async-mutex';\n\nexport class Locks {\n /**\n * A map that will hold active locks for each key\n */\n #locks = new Map<string, MutexInterface>();\n\n /**\n * For a given key, get or create a new lock\n *\n * @param key Key to get or create a lock for\n * @param timeout Time to wait to acquire the lock\n */\n getOrCreateForKey(key: string, timeout?: number) {\n let lock = this.#locks.get(key);\n if (!lock) {\n lock = new Mutex();\n this.#locks.set(key, lock);\n }\n\n return timeout ? withTimeout(lock, timeout) : lock;\n }\n\n release(key: string, releaser: MutexInterface.Releaser) {\n releaser();\n this.#locks.delete(key);\n }\n}\n","export class TimeoutError extends Error {\n\tconstructor(message) {\n\t\tsuper(message);\n\t\tthis.name = 'TimeoutError';\n\t}\n}\n\n/**\nAn error to be thrown when the request is aborted by AbortController.\nDOMException is thrown instead of this Error when DOMException is available.\n*/\nexport class AbortError extends Error {\n\tconstructor(message) {\n\t\tsuper();\n\t\tthis.name = 'AbortError';\n\t\tthis.message = message;\n\t}\n}\n\n/**\nTODO: Remove AbortError and just throw DOMException when targeting Node 18.\n*/\nconst getDOMException = errorMessage => globalThis.DOMException === undefined\n\t? new AbortError(errorMessage)\n\t: new DOMException(errorMessage);\n\n/**\nTODO: Remove below function and just 'reject(signal.reason)' when targeting Node 18.\n*/\nconst getAbortedReason = signal => {\n\tconst reason = signal.reason === undefined\n\t\t? getDOMException('This operation was aborted.')\n\t\t: signal.reason;\n\n\treturn reason instanceof Error ? reason : getDOMException(reason);\n};\n\nexport default function pTimeout(promise, options) {\n\tconst {\n\t\tmilliseconds,\n\t\tfallback,\n\t\tmessage,\n\t\tcustomTimers = {setTimeout, clearTimeout},\n\t} = options;\n\n\tlet timer;\n\n\tconst wrappedPromise = new Promise((resolve, reject) => {\n\t\tif (typeof milliseconds !== 'number' || Math.sign(milliseconds) !== 1) {\n\t\t\tthrow new TypeError(`Expected \\`milliseconds\\` to be a positive number, got \\`${milliseconds}\\``);\n\t\t}\n\n\t\tif (options.signal) {\n\t\t\tconst {signal} = options;\n\t\t\tif (signal.aborted) {\n\t\t\t\treject(getAbortedReason(signal));\n\t\t\t}\n\n\t\t\tconst abortHandler = () => {\n\t\t\t\treject(getAbortedReason(signal));\n\t\t\t};\n\n\t\t\tsignal.addEventListener('abort', abortHandler, {once: true});\n\n\t\t\tpromise.finally(() => {\n\t\t\t\tsignal.removeEventListener('abort', abortHandler);\n\t\t\t});\n\t\t}\n\n\t\tif (milliseconds === Number.POSITIVE_INFINITY) {\n\t\t\tpromise.then(resolve, reject);\n\t\t\treturn;\n\t\t}\n\n\t\t// We create the error outside of `setTimeout` to preserve the stack trace.\n\t\tconst timeoutError = new TimeoutError();\n\n\t\ttimer = customTimers.setTimeout.call(undefined, () => {\n\t\t\tif (fallback) {\n\t\t\t\ttry {\n\t\t\t\t\tresolve(fallback());\n\t\t\t\t} catch (error) {\n\t\t\t\t\treject(error);\n\t\t\t\t}\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (typeof promise.cancel === 'function') {\n\t\t\t\tpromise.cancel();\n\t\t\t}\n\n\t\t\tif (message === false) {\n\t\t\t\tresolve();\n\t\t\t} else if (message instanceof Error) {\n\t\t\t\treject(message);\n\t\t\t} else {\n\t\t\t\ttimeoutError.message = message ?? `Promise timed out after ${milliseconds} milliseconds`;\n\t\t\t\treject(timeoutError);\n\t\t\t}\n\t\t}, milliseconds);\n\n\t\t(async () => {\n\t\t\ttry {\n\t\t\t\tresolve(await promise);\n\t\t\t} catch (error) {\n\t\t\t\treject(error);\n\t\t\t}\n\t\t})();\n\t});\n\n\tconst cancelablePromise = wrappedPromise.finally(() => {\n\t\tcancelablePromise.clear();\n\t});\n\n\tcancelablePromise.clear = () => {\n\t\tcustomTimers.clearTimeout.call(undefined, timer);\n\t\ttimer = undefined;\n\t};\n\n\treturn cancelablePromise;\n}\n","/*\n * @poppinss/utils\n *\n * (c) Poppinss\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nimport { format } from 'node:util';\n\n/**\n * Extended Error object with the option to set error `status` and `code`.\n * At AdonisJs, we prefer exceptions with proper error codes to handle\n * them without relying on message pattern matching.\n *\n * ```js\n * new Exception('message', 500, 'E_RUNTIME_EXCEPTION')\n * ```\n */\nexport class Exception extends Error {\n /**\n * Static properties to defined on the exception once\n * and then re-use them\n */\n declare static help?: string;\n declare static code?: string;\n declare static status?: number;\n declare static message?: string;\n\n /**\n * Name of the class that raised the exception.\n */\n name: string;\n\n /**\n * Optional help description for the error. You can use it to define additional\n * human readable information for the error.\n */\n declare help?: string;\n\n /**\n * A machine readable error code. This will allow the error handling logic\n * to narrow down exceptions based upon the error code.\n */\n declare code?: string;\n\n /**\n * A status code for the error. Usually helpful when converting errors\n * to HTTP responses.\n */\n status: number;\n\n constructor(message?: string, options?: ErrorOptions & { code?: string; status?: number }) {\n super(message, options);\n\n const ErrorConstructor = this.constructor as typeof Exception;\n\n this.name = ErrorConstructor.name;\n this.message = message || ErrorConstructor.message || '';\n this.status = options?.status || ErrorConstructor.status || 500;\n\n const code = options?.code || ErrorConstructor.code;\n if (code !== undefined) {\n this.code = code;\n }\n\n const help = ErrorConstructor.help;\n if (help !== undefined) {\n this.help = help;\n }\n\n Error.captureStackTrace(this, ErrorConstructor);\n }\n\n get [Symbol.toStringTag]() {\n return this.constructor.name;\n }\n\n toString() {\n if (this.code) {\n return `${this.name} [${this.code}]: ${this.message}`;\n }\n return `${this.name}: ${this.message}`;\n }\n}\n\n/**\n * Helper to create anonymous error classes\n */\nexport function createError<T extends any[] = never>(\n message: string,\n code: string,\n status?: number\n): typeof Exception & T extends never\n ? { new (args?: any, options?: ErrorOptions): Exception }\n : { new (args: T, options?: ErrorOptions): Exception } {\n return class extends Exception {\n static message = message;\n static code = code;\n static status = status;\n\n constructor(args: T, options?: ErrorOptions) {\n super(format(message, ...(args || [])), options);\n this.name = 'Exception';\n }\n };\n}","import { createError } from './libs/exception';\n\nexport const E_FACTORY_SOFT_TIMEOUT = createError(\n 'Factory has timed out after waiting for soft timeout',\n 'E_FACTORY_SOFT_TIMEOUT',\n);\n\nexport const E_FACTORY_HARD_TIMEOUT = createError(\n 'Factory has timed out after waiting for hard timeout',\n 'E_FACTORY_HARD_TIMEOUT',\n);\n","import pTimeout from 'p-timeout';\nimport type { MutexInterface } from 'async-mutex';\n\nimport type { Locks } from './locks';\nimport * as exceptions from '../errors';\nimport { events } from '../events/index';\nimport type { CacheStack } from './stack/cache-stack';\nimport type { GetSetFactory } from '../types/helpers';\nimport type { CacheStackWriter } from './stack/cache-stack-writer';\nimport type { CacheEntryOptions } from './cache-entry/cache-entry-options';\n\n/**\n * Factory Runner is responsible for executing factories\n */\nexport class FactoryRunner {\n #stack: CacheStack;\n #stackWriter: CacheStackWriter;\n #locks: Locks;\n\n constructor(stack: CacheStack, stackWriter: CacheStackWriter, locks: Locks) {\n this.#stack = stack;\n this.#stackWriter = stackWriter;\n this.#locks = locks;\n }\n\n async saveBackgroundFactoryResult(\n key: string,\n factoryResult: unknown,\n options: CacheEntryOptions,\n lockReleaser: MutexInterface.Releaser,\n ) {\n await this.#stackWriter.set(key, factoryResult, options);\n this.#locks.release(key, lockReleaser);\n }\n\n async writeFactoryResult(\n key: string,\n item: unknown,\n options: CacheEntryOptions,\n lockReleaser: MutexInterface.Releaser,\n ) {\n await this.#stackWriter.set(key, item, options);\n\n this.#stack.emit(new events.CacheMiss(key, this.#stack.name));\n this.#stack.logger.trace({ key, cache: this.#stack.name, opId: options.id }, 'cache miss');\n this.#locks.release(key, lockReleaser);\n }\n\n async run(\n key: string,\n factory: GetSetFactory,\n hasFallback: boolean,\n options: CacheEntryOptions,\n lockReleaser: MutexInterface.Releaser,\n ) {\n const timeoutDuration = options.factoryTimeout(hasFallback);\n const timeoutException =\n timeoutDuration === options.timeouts?.hard\n ? exceptions.E_FACTORY_HARD_TIMEOUT\n : exceptions.E_FACTORY_SOFT_TIMEOUT;\n\n const promisifiedFactory = async () => {\n return await factory({ setTtl: (ttl) => options.setLogicalTtl(ttl) });\n };\n\n const factoryPromise = promisifiedFactory();\n\n const factoryResult = await pTimeout(factoryPromise, {\n milliseconds: timeoutDuration ?? Number.POSITIVE_INFINITY,\n fallback: async () => {\n factoryPromise\n .then((result) => this.saveBackgroundFactoryResult(key, result, options, lockReleaser))\n .catch(() => {})\n .finally(() => this.#locks.release(key, lockReleaser));\n\n throw new timeoutException();\n },\n });\n\n await this.writeFactoryResult(key, factoryResult, options, lockReleaser);\n return factoryResult;\n }\n}\n","import type { MutexInterface } from 'async-mutex';\n\nimport { Locks } from './locks';\nimport { events } from '../events/index';\nimport type { Factory } from '../types/helpers';\nimport { FactoryRunner } from './factory-runner';\nimport type { CacheEvent } from '../types/events';\nimport { E_FACTORY_SOFT_TIMEOUT } from '../errors';\nimport type { CacheStack } from './stack/cache-stack';\nimport type { CacheEntry } from './cache-entry/cache-entry';\nimport type { CacheStackWriter } from './stack/cache-stack-writer';\nimport type { CacheEntryOptions } from './cache-entry/cache-entry-options';\n\nexport class GetSetHandler {\n /**\n * A map that will hold active locks for each key\n */\n #locks = new Locks();\n #factoryRunner: FactoryRunner;\n\n constructor(\n protected stack: CacheStack,\n protected stackWriter: CacheStackWriter,\n ) {\n this.#factoryRunner = new FactoryRunner(this.stack, this.stackWriter, this.#locks);\n }\n\n get logger() {\n return this.stack.logger;\n }\n\n get emitter() {\n return this.stack.emitter;\n }\n\n /**\n * Emit a CacheEvent using the emitter\n */\n #emit(event: CacheEvent) {\n return this.stack.emitter.emit(event.name, event.toJSON());\n }\n\n /**\n * Refresh a cache item before it expires\n */\n async #earlyExpirationRefresh(key: string, factory: Factory, options: CacheEntryOptions) {\n this.logger.debug({ key, name: this.stack.name, opId: options.id }, 'try to early refresh');\n const lock = this.#locks.getOrCreateForKey(key);\n\n /**\n * If lock is already acquired, then just exit. We only want to run\n * the factory once, in background.\n */\n if (lock.isLocked()) {\n return;\n }\n\n await lock\n .runExclusive(async () => {\n this.logger.trace(\n { key, cache: this.stack.name, opId: options.id },\n 'acquired lock for refresh',\n );\n\n await this.stackWriter.set(key, await factory(), options);\n })\n .catch((error) => {\n const msg = 'factory error in early refresh';\n this.logger.error({ key, cache: this.stack.name, opId: options.id, error }, msg);\n\n throw error;\n });\n }\n\n /**\n * Returns a value from the local cache and emit a CacheHit event\n */\n #returnLocalCacheValue(\n key: string,\n item: CacheEntry,\n options: CacheEntryOptions,\n logMsg?: string,\n ) {\n const isLogicallyExpired = item.isLogicallyExpired();\n logMsg = logMsg ?? 'local cache hit';\n\n this.#emit(new events.CacheHit(key, item.getValue(), this.stack.name, isLogicallyExpired));\n this.logger.trace({ key, cache: this.stack.name, opId: options.id }, logMsg);\n\n return item.getValue();\n }\n\n /**\n * Returns a value from the remote cache and emit a CacheHit event\n */\n async #returnRemoteCacheValue(key: string, item: CacheEntry, options: CacheEntryOptions) {\n this.logger.trace({ key, cache: this.stack.name, opId: options.id }, 'remote cache hit');\n\n this.stack.l1?.set(key, item.serialize(), options);\n\n this.#emit(new events.CacheHit(key, item.getValue(), this.stack.name));\n return item.getValue();\n }\n\n /**\n * Try acquiring a lock for a key\n *\n * If we have a fallback value, grace period enabled, and a soft timeout configured\n * we will wait at most the soft timeout to acquire the lock\n */\n #acquireLock(key: string, hasFallback: boolean, options: CacheEntryOptions) {\n const lock = this.#locks.getOrCreateForKey(key, options.getApplicableLockTimeout(hasFallback));\n return lock.acquire();\n }\n\n #returnGracedValueOrThrow(\n key: string,\n item: CacheEntry | undefined,\n options: CacheEntryOptions,\n err: Error,\n ) {\n if (options.isGracePeriodEnabled && item) {\n return this.#returnLocalCacheValue(key, item, options, 'local cache hit (graced)');\n }\n\n throw err;\n }\n\n async #applyFallbackAndReturnGracedValue(\n key: string,\n item: CacheEntry,\n options: CacheEntryOptions,\n ) {\n if (options.gracePeriod.enabled && options.gracePeriod.fallbackDuration) {\n this.logger.trace(\n { key, cache: this.stack.name, opId: options.id },\n 'apply fallback duration',\n );\n\n this.stack.l1?.set(\n key,\n item.applyFallbackDuration(options.gracePeriod.fallbackDuration).serialize(),\n options,\n );\n }\n\n this.logger.trace({ key, cache: this.stack.name, opId: options.id }, 'returns stale value');\n this.#emit(new events.CacheHit(key, item.getValue(), this.stack.name, true));\n return item.getValue();\n }\n\n /**\n * Check if a cache item is not undefined and not logically expired\n */\n #isItemValid(item: CacheEntry | undefined): item is CacheEntry {\n return !!item && !item.isLogicallyExpired();\n }\n\n async handle(key: string, factory: Factory, options: CacheEntryOptions) {\n let localItem: CacheEntry | undefined;\n\n /**\n * First we check the local cache. If we have a valid item, just\n * returns it without acquiring a lock.\n */\n localItem = this.stack.l1?.get(key, options);\n if (this.#isItemValid(localItem)) {\n if (localItem?.isEarlyExpired()) this.#earlyExpirationRefresh(key, factory, options);\n return this.#returnLocalCacheValue(key, localItem, options);\n }\n\n /**\n * Since we didn't find a valid item in the local cache, we need to\n * check the remote cache, or invoke the factory.\n *\n * We acquire a lock to prevent a cache stampede.\n */\n let releaser: MutexInterface.Releaser;\n try {\n releaser = await this.#acquireLock(key, !!localItem, options);\n } catch (err) {\n return this.#returnGracedValueOrThrow(key, localItem, options, err);\n }\n\n this.logger.trace({ key, cache: this.stack.name, opId: options.id }, 'acquired lock');\n\n /**\n * We need to check the local cache again, because another process\n * could have written a value while we were waiting for the lock.\n */\n localItem = this.stack.l1?.get(key, options);\n if (this.#isItemValid(localItem)) {\n this.#locks.release(key, releaser);\n return this.#returnLocalCacheValue(key, localItem, options, 'local cache hit after lock');\n }\n\n /**\n * If local cache was empty, maybe there is something in the remote\n * cache. If we find a valid item, we save it in the local cache\n * and returns it.\n */\n const remoteItem = await this.stack.l2?.get(key, options);\n if (this.#isItemValid(remoteItem)) {\n this.#locks.release(key, releaser);\n return this.#returnRemoteCacheValue(key, remoteItem, options);\n }\n\n try {\n const hasFallback = !!localItem || !!remoteItem;\n return await this.#factoryRunner.run(key, factory, hasFallback, options, releaser);\n } catch (err) {\n /**\n * If we hitted a soft timeout and we have a graced value, returns it\n */\n const staleItem = remoteItem ?? localItem;\n if (err instanceof E_FACTORY_SOFT_TIMEOUT && staleItem) {\n return this.#returnGracedValueOrThrow(key, staleItem, options, err);\n }\n\n /**\n * Otherwise, that means we had a factory error. If we have a graced\n * value, returns it\n */\n this.logger.trace(\n { key, cache: this.stack.name, opId: options.id, error: err },\n 'factory error',\n );\n\n if (staleItem && options.isGracePeriodEnabled) {\n this.#locks.release(key, releaser);\n return this.#applyFallbackAndReturnGracedValue(key, staleItem, options);\n }\n\n this.#locks.release(key, releaser);\n throw err;\n }\n }\n}\n","import type { CacheStack } from './cache-stack';\nimport { CacheBusMessageType } from '../../types/main';\nimport { CacheWritten } from '../../events/cache/cache-written';\nimport type { CacheEntryOptions } from '../cache-entry/cache-entry-options';\n\nexport class CacheStackWriter {\n constructor(protected cacheStack: CacheStack) {}\n\n /**\n * Write a value in the cache stack\n * - Set value in local cache\n * - Set value in remote cache\n * - Publish a message to the bus\n * - Emit a CacheWritten event\n */\n async set(key: string, value: any, options: CacheEntryOptions) {\n const item = this.cacheStack.serialize({\n value,\n logicalExpiration: options.logicalTtlFromNow(),\n earlyExpiration: options.earlyExpireTtlFromNow(),\n });\n\n this.cacheStack.l1?.set(key, item, options);\n await this.cacheStack.l2?.set(key, item, options);\n await this.cacheStack.publish({ type: CacheBusMessageType.Set, keys: [key] });\n\n this.cacheStack.emit(new CacheWritten(key, value, this.cacheStack.name));\n return true;\n }\n}\n"],"mappings":"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