diff options
| author | Adam Mathes <adam@adammathes.com> | 2026-02-14 14:46:37 -0800 |
|---|---|---|
| committer | Adam Mathes <adam@adammathes.com> | 2026-02-14 14:46:37 -0800 |
| commit | afa87af01c79a9baa539f2992d32154d2a4739bd (patch) | |
| tree | 92c7416db734270a2fee1d72ee9cc119379ff8e1 /vanilla/node_modules/lru-cache/dist/esm/index.js | |
| parent | 3b927e84d200402281f68181cd4253bc77e5528d (diff) | |
| download | neko-afa87af01c79a9baa539f2992d32154d2a4739bd.tar.gz neko-afa87af01c79a9baa539f2992d32154d2a4739bd.tar.bz2 neko-afa87af01c79a9baa539f2992d32154d2a4739bd.zip | |
task: delete vanilla js prototype\n\n- Removed vanilla/ directory and web/dist/vanilla directory\n- Updated Makefile, Dockerfile, and CI workflow to remove vanilla references\n- Cleaned up web/web.go to remove vanilla embed and routes\n- Verified build and tests pass\n\nCloses NK-2tcnmq
Diffstat (limited to 'vanilla/node_modules/lru-cache/dist/esm/index.js')
| -rw-r--r-- | vanilla/node_modules/lru-cache/dist/esm/index.js | 1585 |
1 files changed, 0 insertions, 1585 deletions
diff --git a/vanilla/node_modules/lru-cache/dist/esm/index.js b/vanilla/node_modules/lru-cache/dist/esm/index.js deleted file mode 100644 index fa0b58c..0000000 --- a/vanilla/node_modules/lru-cache/dist/esm/index.js +++ /dev/null @@ -1,1585 +0,0 @@ -/** - * @module LRUCache - */ -const defaultPerf = (typeof performance === 'object' && - performance && - typeof performance.now === 'function') ? - performance - : Date; -const warned = new Set(); -/* c8 ignore start */ -const PROCESS = (typeof process === 'object' && !!process ? - process - : {}); -/* c8 ignore start */ -const emitWarning = (msg, type, code, fn) => { - typeof PROCESS.emitWarning === 'function' ? - PROCESS.emitWarning(msg, type, code, fn) - : console.error(`[${code}] ${type}: ${msg}`); -}; -let AC = globalThis.AbortController; -let AS = globalThis.AbortSignal; -/* c8 ignore start */ -if (typeof AC === 'undefined') { - //@ts-ignore - AS = class AbortSignal { - onabort; - _onabort = []; - reason; - aborted = false; - addEventListener(_, fn) { - this._onabort.push(fn); - } - }; - //@ts-ignore - AC = class AbortController { - constructor() { - warnACPolyfill(); - } - signal = new AS(); - abort(reason) { - if (this.signal.aborted) - return; - //@ts-ignore - this.signal.reason = reason; - //@ts-ignore - this.signal.aborted = true; - //@ts-ignore - for (const fn of this.signal._onabort) { - fn(reason); - } - this.signal.onabort?.(reason); - } - }; - let printACPolyfillWarning = PROCESS.env?.LRU_CACHE_IGNORE_AC_WARNING !== '1'; - const warnACPolyfill = () => { - if (!printACPolyfillWarning) - return; - printACPolyfillWarning = false; - emitWarning('AbortController is not defined. If using lru-cache in ' + - 'node 14, load an AbortController polyfill from the ' + - '`node-abort-controller` package. A minimal polyfill is ' + - 'provided for use by LRUCache.fetch(), but it should not be ' + - 'relied upon in other contexts (eg, passing it to other APIs that ' + - 'use AbortController/AbortSignal might have undesirable effects). ' + - 'You may disable this with LRU_CACHE_IGNORE_AC_WARNING=1 in the env.', 'NO_ABORT_CONTROLLER', 'ENOTSUP', warnACPolyfill); - }; -} -/* c8 ignore stop */ -const shouldWarn = (code) => !warned.has(code); -const TYPE = Symbol('type'); -const isPosInt = (n) => n && n === Math.floor(n) && n > 0 && isFinite(n); -/* c8 ignore start */ -// This is a little bit ridiculous, tbh. -// The maximum array length is 2^32-1 or thereabouts on most JS impls. -// And well before that point, you're caching the entire world, I mean, -// that's ~32GB of just integers for the next/prev links, plus whatever -// else to hold that many keys and values. Just filling the memory with -// zeroes at init time is brutal when you get that big. -// But why not be complete? -// Maybe in the future, these limits will have expanded. -const getUintArray = (max) => !isPosInt(max) ? null - : max <= Math.pow(2, 8) ? Uint8Array - : max <= Math.pow(2, 16) ? Uint16Array - : max <= Math.pow(2, 32) ? Uint32Array - : max <= Number.MAX_SAFE_INTEGER ? ZeroArray - : null; -/* c8 ignore stop */ -class ZeroArray extends Array { - constructor(size) { - super(size); - this.fill(0); - } -} -class Stack { - heap; - length; - // private constructor - static #constructing = false; - static create(max) { - const HeapCls = getUintArray(max); - if (!HeapCls) - return []; - Stack.#constructing = true; - const s = new Stack(max, HeapCls); - Stack.#constructing = false; - return s; - } - constructor(max, HeapCls) { - /* c8 ignore start */ - if (!Stack.#constructing) { - throw new TypeError('instantiate Stack using Stack.create(n)'); - } - /* c8 ignore stop */ - this.heap = new HeapCls(max); - this.length = 0; - } - push(n) { - this.heap[this.length++] = n; - } - pop() { - return this.heap[--this.length]; - } -} -/** - * Default export, the thing you're using this module to get. - * - * The `K` and `V` types define the key and value types, respectively. The - * optional `FC` type defines the type of the `context` object passed to - * `cache.fetch()` and `cache.memo()`. - * - * Keys and values **must not** be `null` or `undefined`. - * - * All properties from the options object (with the exception of `max`, - * `maxSize`, `fetchMethod`, `memoMethod`, `dispose` and `disposeAfter`) are - * added as normal public members. (The listed options are read-only getters.) - * - * Changing any of these will alter the defaults for subsequent method calls. - */ -export class LRUCache { - // options that cannot be changed without disaster - #max; - #maxSize; - #dispose; - #onInsert; - #disposeAfter; - #fetchMethod; - #memoMethod; - #perf; - /** - * {@link LRUCache.OptionsBase.perf} - */ - get perf() { - return this.#perf; - } - /** - * {@link LRUCache.OptionsBase.ttl} - */ - ttl; - /** - * {@link LRUCache.OptionsBase.ttlResolution} - */ - ttlResolution; - /** - * {@link LRUCache.OptionsBase.ttlAutopurge} - */ - ttlAutopurge; - /** - * {@link LRUCache.OptionsBase.updateAgeOnGet} - */ - updateAgeOnGet; - /** - * {@link LRUCache.OptionsBase.updateAgeOnHas} - */ - updateAgeOnHas; - /** - * {@link LRUCache.OptionsBase.allowStale} - */ - allowStale; - /** - * {@link LRUCache.OptionsBase.noDisposeOnSet} - */ - noDisposeOnSet; - /** - * {@link LRUCache.OptionsBase.noUpdateTTL} - */ - noUpdateTTL; - /** - * {@link LRUCache.OptionsBase.maxEntrySize} - */ - maxEntrySize; - /** - * {@link LRUCache.OptionsBase.sizeCalculation} - */ - sizeCalculation; - /** - * {@link LRUCache.OptionsBase.noDeleteOnFetchRejection} - */ - noDeleteOnFetchRejection; - /** - * {@link LRUCache.OptionsBase.noDeleteOnStaleGet} - */ - noDeleteOnStaleGet; - /** - * {@link LRUCache.OptionsBase.allowStaleOnFetchAbort} - */ - allowStaleOnFetchAbort; - /** - * {@link LRUCache.OptionsBase.allowStaleOnFetchRejection} - */ - allowStaleOnFetchRejection; - /** - * {@link LRUCache.OptionsBase.ignoreFetchAbort} - */ - ignoreFetchAbort; - // computed properties - #size; - #calculatedSize; - #keyMap; - #keyList; - #valList; - #next; - #prev; - #head; - #tail; - #free; - #disposed; - #sizes; - #starts; - #ttls; - #autopurgeTimers; - #hasDispose; - #hasFetchMethod; - #hasDisposeAfter; - #hasOnInsert; - /** - * Do not call this method unless you need to inspect the - * inner workings of the cache. If anything returned by this - * object is modified in any way, strange breakage may occur. - * - * These fields are private for a reason! - * - * @internal - */ - static unsafeExposeInternals(c) { - return { - // properties - starts: c.#starts, - ttls: c.#ttls, - autopurgeTimers: c.#autopurgeTimers, - sizes: c.#sizes, - keyMap: c.#keyMap, - keyList: c.#keyList, - valList: c.#valList, - next: c.#next, - prev: c.#prev, - get head() { - return c.#head; - }, - get tail() { - return c.#tail; - }, - free: c.#free, - // methods - isBackgroundFetch: (p) => c.#isBackgroundFetch(p), - backgroundFetch: (k, index, options, context) => c.#backgroundFetch(k, index, options, context), - moveToTail: (index) => c.#moveToTail(index), - indexes: (options) => c.#indexes(options), - rindexes: (options) => c.#rindexes(options), - isStale: (index) => c.#isStale(index), - }; - } - // Protected read-only members - /** - * {@link LRUCache.OptionsBase.max} (read-only) - */ - get max() { - return this.#max; - } - /** - * {@link LRUCache.OptionsBase.maxSize} (read-only) - */ - get maxSize() { - return this.#maxSize; - } - /** - * The total computed size of items in the cache (read-only) - */ - get calculatedSize() { - return this.#calculatedSize; - } - /** - * The number of items stored in the cache (read-only) - */ - get size() { - return this.#size; - } - /** - * {@link LRUCache.OptionsBase.fetchMethod} (read-only) - */ - get fetchMethod() { - return this.#fetchMethod; - } - get memoMethod() { - return this.#memoMethod; - } - /** - * {@link LRUCache.OptionsBase.dispose} (read-only) - */ - get dispose() { - return this.#dispose; - } - /** - * {@link LRUCache.OptionsBase.onInsert} (read-only) - */ - get onInsert() { - return this.#onInsert; - } - /** - * {@link LRUCache.OptionsBase.disposeAfter} (read-only) - */ - get disposeAfter() { - return this.#disposeAfter; - } - constructor(options) { - const { max = 0, ttl, ttlResolution = 1, ttlAutopurge, updateAgeOnGet, updateAgeOnHas, allowStale, dispose, onInsert, disposeAfter, noDisposeOnSet, noUpdateTTL, maxSize = 0, maxEntrySize = 0, sizeCalculation, fetchMethod, memoMethod, noDeleteOnFetchRejection, noDeleteOnStaleGet, allowStaleOnFetchRejection, allowStaleOnFetchAbort, ignoreFetchAbort, perf, } = options; - if (perf !== undefined) { - if (typeof perf?.now !== 'function') { - throw new TypeError('perf option must have a now() method if specified'); - } - } - this.#perf = perf ?? defaultPerf; - if (max !== 0 && !isPosInt(max)) { - throw new TypeError('max option must be a nonnegative integer'); - } - const UintArray = max ? getUintArray(max) : Array; - if (!UintArray) { - throw new Error('invalid max value: ' + max); - } - this.#max = max; - this.#maxSize = maxSize; - this.maxEntrySize = maxEntrySize || this.#maxSize; - this.sizeCalculation = sizeCalculation; - if (this.sizeCalculation) { - if (!this.#maxSize && !this.maxEntrySize) { - throw new TypeError('cannot set sizeCalculation without setting maxSize or maxEntrySize'); - } - if (typeof this.sizeCalculation !== 'function') { - throw new TypeError('sizeCalculation set to non-function'); - } - } - if (memoMethod !== undefined && typeof memoMethod !== 'function') { - throw new TypeError('memoMethod must be a function if defined'); - } - this.#memoMethod = memoMethod; - if (fetchMethod !== undefined && typeof fetchMethod !== 'function') { - throw new TypeError('fetchMethod must be a function if specified'); - } - this.#fetchMethod = fetchMethod; - this.#hasFetchMethod = !!fetchMethod; - this.#keyMap = new Map(); - this.#keyList = new Array(max).fill(undefined); - this.#valList = new Array(max).fill(undefined); - this.#next = new UintArray(max); - this.#prev = new UintArray(max); - this.#head = 0; - this.#tail = 0; - this.#free = Stack.create(max); - this.#size = 0; - this.#calculatedSize = 0; - if (typeof dispose === 'function') { - this.#dispose = dispose; - } - if (typeof onInsert === 'function') { - this.#onInsert = onInsert; - } - if (typeof disposeAfter === 'function') { - this.#disposeAfter = disposeAfter; - this.#disposed = []; - } - else { - this.#disposeAfter = undefined; - this.#disposed = undefined; - } - this.#hasDispose = !!this.#dispose; - this.#hasOnInsert = !!this.#onInsert; - this.#hasDisposeAfter = !!this.#disposeAfter; - this.noDisposeOnSet = !!noDisposeOnSet; - this.noUpdateTTL = !!noUpdateTTL; - this.noDeleteOnFetchRejection = !!noDeleteOnFetchRejection; - this.allowStaleOnFetchRejection = !!allowStaleOnFetchRejection; - this.allowStaleOnFetchAbort = !!allowStaleOnFetchAbort; - this.ignoreFetchAbort = !!ignoreFetchAbort; - // NB: maxEntrySize is set to maxSize if it's set - if (this.maxEntrySize !== 0) { - if (this.#maxSize !== 0) { - if (!isPosInt(this.#maxSize)) { - throw new TypeError('maxSize must be a positive integer if specified'); - } - } - if (!isPosInt(this.maxEntrySize)) { - throw new TypeError('maxEntrySize must be a positive integer if specified'); - } - this.#initializeSizeTracking(); - } - this.allowStale = !!allowStale; - this.noDeleteOnStaleGet = !!noDeleteOnStaleGet; - this.updateAgeOnGet = !!updateAgeOnGet; - this.updateAgeOnHas = !!updateAgeOnHas; - this.ttlResolution = - isPosInt(ttlResolution) || ttlResolution === 0 ? ttlResolution : 1; - this.ttlAutopurge = !!ttlAutopurge; - this.ttl = ttl || 0; - if (this.ttl) { - if (!isPosInt(this.ttl)) { - throw new TypeError('ttl must be a positive integer if specified'); - } - this.#initializeTTLTracking(); - } - // do not allow completely unbounded caches - if (this.#max === 0 && this.ttl === 0 && this.#maxSize === 0) { - throw new TypeError('At least one of max, maxSize, or ttl is required'); - } - if (!this.ttlAutopurge && !this.#max && !this.#maxSize) { - const code = 'LRU_CACHE_UNBOUNDED'; - if (shouldWarn(code)) { - warned.add(code); - const msg = 'TTL caching without ttlAutopurge, max, or maxSize can ' + - 'result in unbounded memory consumption.'; - emitWarning(msg, 'UnboundedCacheWarning', code, LRUCache); - } - } - } - /** - * Return the number of ms left in the item's TTL. If item is not in cache, - * returns `0`. Returns `Infinity` if item is in cache without a defined TTL. - */ - getRemainingTTL(key) { - return this.#keyMap.has(key) ? Infinity : 0; - } - #initializeTTLTracking() { - const ttls = new ZeroArray(this.#max); - const starts = new ZeroArray(this.#max); - this.#ttls = ttls; - this.#starts = starts; - const purgeTimers = this.ttlAutopurge ? - new Array(this.#max) - : undefined; - this.#autopurgeTimers = purgeTimers; - this.#setItemTTL = (index, ttl, start = this.#perf.now()) => { - starts[index] = ttl !== 0 ? start : 0; - ttls[index] = ttl; - // clear out the purge timer if we're setting TTL to 0, and - // previously had a ttl purge timer running, so it doesn't - // fire unnecessarily. - if (purgeTimers?.[index]) { - clearTimeout(purgeTimers[index]); - purgeTimers[index] = undefined; - } - if (ttl !== 0 && purgeTimers) { - const t = setTimeout(() => { - if (this.#isStale(index)) { - this.#delete(this.#keyList[index], 'expire'); - } - }, ttl + 1); - // unref() not supported on all platforms - /* c8 ignore start */ - if (t.unref) { - t.unref(); - } - /* c8 ignore stop */ - purgeTimers[index] = t; - } - }; - this.#updateItemAge = index => { - starts[index] = ttls[index] !== 0 ? this.#perf.now() : 0; - }; - this.#statusTTL = (status, index) => { - if (ttls[index]) { - const ttl = ttls[index]; - const start = starts[index]; - /* c8 ignore next */ - if (!ttl || !start) - return; - status.ttl = ttl; - status.start = start; - status.now = cachedNow || getNow(); - const age = status.now - start; - status.remainingTTL = ttl - age; - } - }; - // debounce calls to perf.now() to 1s so we're not hitting - // that costly call repeatedly. - let cachedNow = 0; - const getNow = () => { - const n = this.#perf.now(); - if (this.ttlResolution > 0) { - cachedNow = n; - const t = setTimeout(() => (cachedNow = 0), this.ttlResolution); - // not available on all platforms - /* c8 ignore start */ - if (t.unref) { - t.unref(); - } - /* c8 ignore stop */ - } - return n; - }; - this.getRemainingTTL = key => { - const index = this.#keyMap.get(key); - if (index === undefined) { - return 0; - } - const ttl = ttls[index]; - const start = starts[index]; - if (!ttl || !start) { - return Infinity; - } - const age = (cachedNow || getNow()) - start; - return ttl - age; - }; - this.#isStale = index => { - const s = starts[index]; - const t = ttls[index]; - return !!t && !!s && (cachedNow || getNow()) - s > t; - }; - } - // conditionally set private methods related to TTL - #updateItemAge = () => { }; - #statusTTL = () => { }; - #setItemTTL = () => { }; - /* c8 ignore stop */ - #isStale = () => false; - #initializeSizeTracking() { - const sizes = new ZeroArray(this.#max); - this.#calculatedSize = 0; - this.#sizes = sizes; - this.#removeItemSize = index => { - this.#calculatedSize -= sizes[index]; - sizes[index] = 0; - }; - this.#requireSize = (k, v, size, sizeCalculation) => { - // provisionally accept background fetches. - // actual value size will be checked when they return. - if (this.#isBackgroundFetch(v)) { - return 0; - } - if (!isPosInt(size)) { - if (sizeCalculation) { - if (typeof sizeCalculation !== 'function') { - throw new TypeError('sizeCalculation must be a function'); - } - size = sizeCalculation(v, k); - if (!isPosInt(size)) { - throw new TypeError('sizeCalculation return invalid (expect positive integer)'); - } - } - else { - throw new TypeError('invalid size value (must be positive integer). ' + - 'When maxSize or maxEntrySize is used, sizeCalculation ' + - 'or size must be set.'); - } - } - return size; - }; - this.#addItemSize = (index, size, status) => { - sizes[index] = size; - if (this.#maxSize) { - const maxSize = this.#maxSize - sizes[index]; - while (this.#calculatedSize > maxSize) { - this.#evict(true); - } - } - this.#calculatedSize += sizes[index]; - if (status) { - status.entrySize = size; - status.totalCalculatedSize = this.#calculatedSize; - } - }; - } - #removeItemSize = _i => { }; - #addItemSize = (_i, _s, _st) => { }; - #requireSize = (_k, _v, size, sizeCalculation) => { - if (size || sizeCalculation) { - throw new TypeError('cannot set size without setting maxSize or maxEntrySize on cache'); - } - return 0; - }; - *#indexes({ allowStale = this.allowStale } = {}) { - if (this.#size) { - for (let i = this.#tail; true;) { - if (!this.#isValidIndex(i)) { - break; - } - if (allowStale || !this.#isStale(i)) { - yield i; - } - if (i === this.#head) { - break; - } - else { - i = this.#prev[i]; - } - } - } - } - *#rindexes({ allowStale = this.allowStale } = {}) { - if (this.#size) { - for (let i = this.#head; true;) { - if (!this.#isValidIndex(i)) { - break; - } - if (allowStale || !this.#isStale(i)) { - yield i; - } - if (i === this.#tail) { - break; - } - else { - i = this.#next[i]; - } - } - } - } - #isValidIndex(index) { - return (index !== undefined && - this.#keyMap.get(this.#keyList[index]) === index); - } - /** - * Return a generator yielding `[key, value]` pairs, - * in order from most recently used to least recently used. - */ - *entries() { - for (const i of this.#indexes()) { - if (this.#valList[i] !== undefined && - this.#keyList[i] !== undefined && - !this.#isBackgroundFetch(this.#valList[i])) { - yield [this.#keyList[i], this.#valList[i]]; - } - } - } - /** - * Inverse order version of {@link LRUCache.entries} - * - * Return a generator yielding `[key, value]` pairs, - * in order from least recently used to most recently used. - */ - *rentries() { - for (const i of this.#rindexes()) { - if (this.#valList[i] !== undefined && - this.#keyList[i] !== undefined && - !this.#isBackgroundFetch(this.#valList[i])) { - yield [this.#keyList[i], this.#valList[i]]; - } - } - } - /** - * Return a generator yielding the keys in the cache, - * in order from most recently used to least recently used. - */ - *keys() { - for (const i of this.#indexes()) { - const k = this.#keyList[i]; - if (k !== undefined && !this.#isBackgroundFetch(this.#valList[i])) { - yield k; - } - } - } - /** - * Inverse order version of {@link LRUCache.keys} - * - * Return a generator yielding the keys in the cache, - * in order from least recently used to most recently used. - */ - *rkeys() { - for (const i of this.#rindexes()) { - const k = this.#keyList[i]; - if (k !== undefined && !this.#isBackgroundFetch(this.#valList[i])) { - yield k; - } - } - } - /** - * Return a generator yielding the values in the cache, - * in order from most recently used to least recently used. - */ - *values() { - for (const i of this.#indexes()) { - const v = this.#valList[i]; - if (v !== undefined && !this.#isBackgroundFetch(this.#valList[i])) { - yield this.#valList[i]; - } - } - } - /** - * Inverse order version of {@link LRUCache.values} - * - * Return a generator yielding the values in the cache, - * in order from least recently used to most recently used. - */ - *rvalues() { - for (const i of this.#rindexes()) { - const v = this.#valList[i]; - if (v !== undefined && !this.#isBackgroundFetch(this.#valList[i])) { - yield this.#valList[i]; - } - } - } - /** - * Iterating over the cache itself yields the same results as - * {@link LRUCache.entries} - */ - [Symbol.iterator]() { - return this.entries(); - } - /** - * A String value that is used in the creation of the default string - * description of an object. Called by the built-in method - * `Object.prototype.toString`. - */ - [Symbol.toStringTag] = 'LRUCache'; - /** - * Find a value for which the supplied fn method returns a truthy value, - * similar to `Array.find()`. fn is called as `fn(value, key, cache)`. - */ - find(fn, getOptions = {}) { - for (const i of this.#indexes()) { - const v = this.#valList[i]; - const value = this.#isBackgroundFetch(v) ? v.__staleWhileFetching : v; - if (value === undefined) - continue; - if (fn(value, this.#keyList[i], this)) { - return this.get(this.#keyList[i], getOptions); - } - } - } - /** - * Call the supplied function on each item in the cache, in order from most - * recently used to least recently used. - * - * `fn` is called as `fn(value, key, cache)`. - * - * If `thisp` is provided, function will be called in the `this`-context of - * the provided object, or the cache if no `thisp` object is provided. - * - * Does not update age or recenty of use, or iterate over stale values. - */ - forEach(fn, thisp = this) { - for (const i of this.#indexes()) { - const v = this.#valList[i]; - const value = this.#isBackgroundFetch(v) ? v.__staleWhileFetching : v; - if (value === undefined) - continue; - fn.call(thisp, value, this.#keyList[i], this); - } - } - /** - * The same as {@link LRUCache.forEach} but items are iterated over in - * reverse order. (ie, less recently used items are iterated over first.) - */ - rforEach(fn, thisp = this) { - for (const i of this.#rindexes()) { - const v = this.#valList[i]; - const value = this.#isBackgroundFetch(v) ? v.__staleWhileFetching : v; - if (value === undefined) - continue; - fn.call(thisp, value, this.#keyList[i], this); - } - } - /** - * Delete any stale entries. Returns true if anything was removed, - * false otherwise. - */ - purgeStale() { - let deleted = false; - for (const i of this.#rindexes({ allowStale: true })) { - if (this.#isStale(i)) { - this.#delete(this.#keyList[i], 'expire'); - deleted = true; - } - } - return deleted; - } - /** - * Get the extended info about a given entry, to get its value, size, and - * TTL info simultaneously. Returns `undefined` if the key is not present. - * - * Unlike {@link LRUCache#dump}, which is designed to be portable and survive - * serialization, the `start` value is always the current timestamp, and the - * `ttl` is a calculated remaining time to live (negative if expired). - * - * Always returns stale values, if their info is found in the cache, so be - * sure to check for expirations (ie, a negative {@link LRUCache.Entry#ttl}) - * if relevant. - */ - info(key) { - const i = this.#keyMap.get(key); - if (i === undefined) - return undefined; - const v = this.#valList[i]; - /* c8 ignore start - this isn't tested for the info function, - * but it's the same logic as found in other places. */ - const value = this.#isBackgroundFetch(v) ? v.__staleWhileFetching : v; - if (value === undefined) - return undefined; - /* c8 ignore end */ - const entry = { value }; - if (this.#ttls && this.#starts) { - const ttl = this.#ttls[i]; - const start = this.#starts[i]; - if (ttl && start) { - const remain = ttl - (this.#perf.now() - start); - entry.ttl = remain; - entry.start = Date.now(); - } - } - if (this.#sizes) { - entry.size = this.#sizes[i]; - } - return entry; - } - /** - * Return an array of [key, {@link LRUCache.Entry}] tuples which can be - * passed to {@link LRUCache#load}. - * - * The `start` fields are calculated relative to a portable `Date.now()` - * timestamp, even if `performance.now()` is available. - * - * Stale entries are always included in the `dump`, even if - * {@link LRUCache.OptionsBase.allowStale} is false. - * - * Note: this returns an actual array, not a generator, so it can be more - * easily passed around. - */ - dump() { - const arr = []; - for (const i of this.#indexes({ allowStale: true })) { - const key = this.#keyList[i]; - const v = this.#valList[i]; - const value = this.#isBackgroundFetch(v) ? v.__staleWhileFetching : v; - if (value === undefined || key === undefined) - continue; - const entry = { value }; - if (this.#ttls && this.#starts) { - entry.ttl = this.#ttls[i]; - // always dump the start relative to a portable timestamp - // it's ok for this to be a bit slow, it's a rare operation. - const age = this.#perf.now() - this.#starts[i]; - entry.start = Math.floor(Date.now() - age); - } - if (this.#sizes) { - entry.size = this.#sizes[i]; - } - arr.unshift([key, entry]); - } - return arr; - } - /** - * Reset the cache and load in the items in entries in the order listed. - * - * The shape of the resulting cache may be different if the same options are - * not used in both caches. - * - * The `start` fields are assumed to be calculated relative to a portable - * `Date.now()` timestamp, even if `performance.now()` is available. - */ - load(arr) { - this.clear(); - for (const [key, entry] of arr) { - if (entry.start) { - // entry.start is a portable timestamp, but we may be using - // node's performance.now(), so calculate the offset, so that - // we get the intended remaining TTL, no matter how long it's - // been on ice. - // - // it's ok for this to be a bit slow, it's a rare operation. - const age = Date.now() - entry.start; - entry.start = this.#perf.now() - age; - } - this.set(key, entry.value, entry); - } - } - /** - * Add a value to the cache. - * - * Note: if `undefined` is specified as a value, this is an alias for - * {@link LRUCache#delete} - * - * Fields on the {@link LRUCache.SetOptions} options param will override - * their corresponding values in the constructor options for the scope - * of this single `set()` operation. - * - * If `start` is provided, then that will set the effective start - * time for the TTL calculation. Note that this must be a previous - * value of `performance.now()` if supported, or a previous value of - * `Date.now()` if not. - * - * Options object may also include `size`, which will prevent - * calling the `sizeCalculation` function and just use the specified - * number if it is a positive integer, and `noDisposeOnSet` which - * will prevent calling a `dispose` function in the case of - * overwrites. - * - * If the `size` (or return value of `sizeCalculation`) for a given - * entry is greater than `maxEntrySize`, then the item will not be - * added to the cache. - * - * Will update the recency of the entry. - * - * If the value is `undefined`, then this is an alias for - * `cache.delete(key)`. `undefined` is never stored in the cache. - */ - set(k, v, setOptions = {}) { - if (v === undefined) { - this.delete(k); - return this; - } - const { ttl = this.ttl, start, noDisposeOnSet = this.noDisposeOnSet, sizeCalculation = this.sizeCalculation, status, } = setOptions; - let { noUpdateTTL = this.noUpdateTTL } = setOptions; - const size = this.#requireSize(k, v, setOptions.size || 0, sizeCalculation); - // if the item doesn't fit, don't do anything - // NB: maxEntrySize set to maxSize by default - if (this.maxEntrySize && size > this.maxEntrySize) { - if (status) { - status.set = 'miss'; - status.maxEntrySizeExceeded = true; - } - // have to delete, in case something is there already. - this.#delete(k, 'set'); - return this; - } - let index = this.#size === 0 ? undefined : this.#keyMap.get(k); - if (index === undefined) { - // addition - index = (this.#size === 0 ? this.#tail - : this.#free.length !== 0 ? this.#free.pop() - : this.#size === this.#max ? this.#evict(false) - : this.#size); - this.#keyList[index] = k; - this.#valList[index] = v; - this.#keyMap.set(k, index); - this.#next[this.#tail] = index; - this.#prev[index] = this.#tail; - this.#tail = index; - this.#size++; - this.#addItemSize(index, size, status); - if (status) - status.set = 'add'; - noUpdateTTL = false; - if (this.#hasOnInsert) { - this.#onInsert?.(v, k, 'add'); - } - } - else { - // update - this.#moveToTail(index); - const oldVal = this.#valList[index]; - if (v !== oldVal) { - if (this.#hasFetchMethod && this.#isBackgroundFetch(oldVal)) { - oldVal.__abortController.abort(new Error('replaced')); - const { __staleWhileFetching: s } = oldVal; - if (s !== undefined && !noDisposeOnSet) { - if (this.#hasDispose) { - this.#dispose?.(s, k, 'set'); - } - if (this.#hasDisposeAfter) { - this.#disposed?.push([s, k, 'set']); - } - } - } - else if (!noDisposeOnSet) { - if (this.#hasDispose) { - this.#dispose?.(oldVal, k, 'set'); - } - if (this.#hasDisposeAfter) { - this.#disposed?.push([oldVal, k, 'set']); - } - } - this.#removeItemSize(index); - this.#addItemSize(index, size, status); - this.#valList[index] = v; - if (status) { - status.set = 'replace'; - const oldValue = oldVal && this.#isBackgroundFetch(oldVal) ? - oldVal.__staleWhileFetching - : oldVal; - if (oldValue !== undefined) - status.oldValue = oldValue; - } - } - else if (status) { - status.set = 'update'; - } - if (this.#hasOnInsert) { - this.onInsert?.(v, k, v === oldVal ? 'update' : 'replace'); - } - } - if (ttl !== 0 && !this.#ttls) { - this.#initializeTTLTracking(); - } - if (this.#ttls) { - if (!noUpdateTTL) { - this.#setItemTTL(index, ttl, start); - } - if (status) - this.#statusTTL(status, index); - } - if (!noDisposeOnSet && this.#hasDisposeAfter && this.#disposed) { - const dt = this.#disposed; - let task; - while ((task = dt?.shift())) { - this.#disposeAfter?.(...task); - } - } - return this; - } - /** - * Evict the least recently used item, returning its value or - * `undefined` if cache is empty. - */ - pop() { - try { - while (this.#size) { - const val = this.#valList[this.#head]; - this.#evict(true); - if (this.#isBackgroundFetch(val)) { - if (val.__staleWhileFetching) { - return val.__staleWhileFetching; - } - } - else if (val !== undefined) { - return val; - } - } - } - finally { - if (this.#hasDisposeAfter && this.#disposed) { - const dt = this.#disposed; - let task; - while ((task = dt?.shift())) { - this.#disposeAfter?.(...task); - } - } - } - } - #evict(free) { - const head = this.#head; - const k = this.#keyList[head]; - const v = this.#valList[head]; - if (this.#hasFetchMethod && this.#isBackgroundFetch(v)) { - v.__abortController.abort(new Error('evicted')); - } - else if (this.#hasDispose || this.#hasDisposeAfter) { - if (this.#hasDispose) { - this.#dispose?.(v, k, 'evict'); - } - if (this.#hasDisposeAfter) { - this.#disposed?.push([v, k, 'evict']); - } - } - this.#removeItemSize(head); - if (this.#autopurgeTimers?.[head]) { - clearTimeout(this.#autopurgeTimers[head]); - this.#autopurgeTimers[head] = undefined; - } - // if we aren't about to use the index, then null these out - if (free) { - this.#keyList[head] = undefined; - this.#valList[head] = undefined; - this.#free.push(head); - } - if (this.#size === 1) { - this.#head = this.#tail = 0; - this.#free.length = 0; - } - else { - this.#head = this.#next[head]; - } - this.#keyMap.delete(k); - this.#size--; - return head; - } - /** - * Check if a key is in the cache, without updating the recency of use. - * Will return false if the item is stale, even though it is technically - * in the cache. - * - * Check if a key is in the cache, without updating the recency of - * use. Age is updated if {@link LRUCache.OptionsBase.updateAgeOnHas} is set - * to `true` in either the options or the constructor. - * - * Will return `false` if the item is stale, even though it is technically in - * the cache. The difference can be determined (if it matters) by using a - * `status` argument, and inspecting the `has` field. - * - * Will not update item age unless - * {@link LRUCache.OptionsBase.updateAgeOnHas} is set. - */ - has(k, hasOptions = {}) { - const { updateAgeOnHas = this.updateAgeOnHas, status } = hasOptions; - const index = this.#keyMap.get(k); - if (index !== undefined) { - const v = this.#valList[index]; - if (this.#isBackgroundFetch(v) && - v.__staleWhileFetching === undefined) { - return false; - } - if (!this.#isStale(index)) { - if (updateAgeOnHas) { - this.#updateItemAge(index); - } - if (status) { - status.has = 'hit'; - this.#statusTTL(status, index); - } - return true; - } - else if (status) { - status.has = 'stale'; - this.#statusTTL(status, index); - } - } - else if (status) { - status.has = 'miss'; - } - return false; - } - /** - * Like {@link LRUCache#get} but doesn't update recency or delete stale - * items. - * - * Returns `undefined` if the item is stale, unless - * {@link LRUCache.OptionsBase.allowStale} is set. - */ - peek(k, peekOptions = {}) { - const { allowStale = this.allowStale } = peekOptions; - const index = this.#keyMap.get(k); - if (index === undefined || (!allowStale && this.#isStale(index))) { - return; - } - const v = this.#valList[index]; - // either stale and allowed, or forcing a refresh of non-stale value - return this.#isBackgroundFetch(v) ? v.__staleWhileFetching : v; - } - #backgroundFetch(k, index, options, context) { - const v = index === undefined ? undefined : this.#valList[index]; - if (this.#isBackgroundFetch(v)) { - return v; - } - const ac = new AC(); - const { signal } = options; - // when/if our AC signals, then stop listening to theirs. - signal?.addEventListener('abort', () => ac.abort(signal.reason), { - signal: ac.signal, - }); - const fetchOpts = { - signal: ac.signal, - options, - context, - }; - const cb = (v, updateCache = false) => { - const { aborted } = ac.signal; - const ignoreAbort = options.ignoreFetchAbort && v !== undefined; - const proceed = options.ignoreFetchAbort || - !!(options.allowStaleOnFetchAbort && v !== undefined); - if (options.status) { - if (aborted && !updateCache) { - options.status.fetchAborted = true; - options.status.fetchError = ac.signal.reason; - if (ignoreAbort) - options.status.fetchAbortIgnored = true; - } - else { - options.status.fetchResolved = true; - } - } - if (aborted && !ignoreAbort && !updateCache) { - return fetchFail(ac.signal.reason, proceed); - } - // either we didn't abort, and are still here, or we did, and ignored - const bf = p; - // if nothing else has been written there but we're set to update the - // cache and ignore the abort, or if it's still pending on this specific - // background request, then write it to the cache. - const vl = this.#valList[index]; - if (vl === p || (ignoreAbort && updateCache && vl === undefined)) { - if (v === undefined) { - if (bf.__staleWhileFetching !== undefined) { - this.#valList[index] = bf.__staleWhileFetching; - } - else { - this.#delete(k, 'fetch'); - } - } - else { - if (options.status) - options.status.fetchUpdated = true; - this.set(k, v, fetchOpts.options); - } - } - return v; - }; - const eb = (er) => { - if (options.status) { - options.status.fetchRejected = true; - options.status.fetchError = er; - } - // do not pass go, do not collect $200 - return fetchFail(er, false); - }; - const fetchFail = (er, proceed) => { - const { aborted } = ac.signal; - const allowStaleAborted = aborted && options.allowStaleOnFetchAbort; - const allowStale = allowStaleAborted || options.allowStaleOnFetchRejection; - const noDelete = allowStale || options.noDeleteOnFetchRejection; - const bf = p; - if (this.#valList[index] === p) { - // if we allow stale on fetch rejections, then we need to ensure that - // the stale value is not removed from the cache when the fetch fails. - const del = !noDelete || - !proceed && bf.__staleWhileFetching === undefined; - if (del) { - this.#delete(k, 'fetch'); - } - else if (!allowStaleAborted) { - // still replace the *promise* with the stale value, - // since we are done with the promise at this point. - // leave it untouched if we're still waiting for an - // aborted background fetch that hasn't yet returned. - this.#valList[index] = bf.__staleWhileFetching; - } - } - if (allowStale) { - if (options.status && bf.__staleWhileFetching !== undefined) { - options.status.returnedStale = true; - } - return bf.__staleWhileFetching; - } - else if (bf.__returned === bf) { - throw er; - } - }; - const pcall = (res, rej) => { - const fmp = this.#fetchMethod?.(k, v, fetchOpts); - if (fmp && fmp instanceof Promise) { - fmp.then(v => res(v === undefined ? undefined : v), rej); - } - // ignored, we go until we finish, regardless. - // defer check until we are actually aborting, - // so fetchMethod can override. - ac.signal.addEventListener('abort', () => { - if (!options.ignoreFetchAbort || options.allowStaleOnFetchAbort) { - res(undefined); - // when it eventually resolves, update the cache. - if (options.allowStaleOnFetchAbort) { - res = v => cb(v, true); - } - } - }); - }; - if (options.status) - options.status.fetchDispatched = true; - const p = new Promise(pcall).then(cb, eb); - const bf = Object.assign(p, { - __abortController: ac, - __staleWhileFetching: v, - __returned: undefined, - }); - if (index === undefined) { - // internal, don't expose status. - this.set(k, bf, { ...fetchOpts.options, status: undefined }); - index = this.#keyMap.get(k); - } - else { - this.#valList[index] = bf; - } - return bf; - } - #isBackgroundFetch(p) { - if (!this.#hasFetchMethod) - return false; - const b = p; - return (!!b && - b instanceof Promise && - b.hasOwnProperty('__staleWhileFetching') && - b.__abortController instanceof AC); - } - async fetch(k, fetchOptions = {}) { - const { - // get options - allowStale = this.allowStale, updateAgeOnGet = this.updateAgeOnGet, noDeleteOnStaleGet = this.noDeleteOnStaleGet, - // set options - ttl = this.ttl, noDisposeOnSet = this.noDisposeOnSet, size = 0, sizeCalculation = this.sizeCalculation, noUpdateTTL = this.noUpdateTTL, - // fetch exclusive options - noDeleteOnFetchRejection = this.noDeleteOnFetchRejection, allowStaleOnFetchRejection = this.allowStaleOnFetchRejection, ignoreFetchAbort = this.ignoreFetchAbort, allowStaleOnFetchAbort = this.allowStaleOnFetchAbort, context, forceRefresh = false, status, signal, } = fetchOptions; - if (!this.#hasFetchMethod) { - if (status) - status.fetch = 'get'; - return this.get(k, { - allowStale, - updateAgeOnGet, - noDeleteOnStaleGet, - status, - }); - } - const options = { - allowStale, - updateAgeOnGet, - noDeleteOnStaleGet, - ttl, - noDisposeOnSet, - size, - sizeCalculation, - noUpdateTTL, - noDeleteOnFetchRejection, - allowStaleOnFetchRejection, - allowStaleOnFetchAbort, - ignoreFetchAbort, - status, - signal, - }; - let index = this.#keyMap.get(k); - if (index === undefined) { - if (status) - status.fetch = 'miss'; - const p = this.#backgroundFetch(k, index, options, context); - return (p.__returned = p); - } - else { - // in cache, maybe already fetching - const v = this.#valList[index]; - if (this.#isBackgroundFetch(v)) { - const stale = allowStale && v.__staleWhileFetching !== undefined; - if (status) { - status.fetch = 'inflight'; - if (stale) - status.returnedStale = true; - } - return stale ? v.__staleWhileFetching : (v.__returned = v); - } - // if we force a refresh, that means do NOT serve the cached value, - // unless we are already in the process of refreshing the cache. - const isStale = this.#isStale(index); - if (!forceRefresh && !isStale) { - if (status) - status.fetch = 'hit'; - this.#moveToTail(index); - if (updateAgeOnGet) { - this.#updateItemAge(index); - } - if (status) - this.#statusTTL(status, index); - return v; - } - // ok, it is stale or a forced refresh, and not already fetching. - // refresh the cache. - const p = this.#backgroundFetch(k, index, options, context); - const hasStale = p.__staleWhileFetching !== undefined; - const staleVal = hasStale && allowStale; - if (status) { - status.fetch = isStale ? 'stale' : 'refresh'; - if (staleVal && isStale) - status.returnedStale = true; - } - return staleVal ? p.__staleWhileFetching : (p.__returned = p); - } - } - async forceFetch(k, fetchOptions = {}) { - const v = await this.fetch(k, fetchOptions); - if (v === undefined) - throw new Error('fetch() returned undefined'); - return v; - } - memo(k, memoOptions = {}) { - const memoMethod = this.#memoMethod; - if (!memoMethod) { - throw new Error('no memoMethod provided to constructor'); - } - const { context, forceRefresh, ...options } = memoOptions; - const v = this.get(k, options); - if (!forceRefresh && v !== undefined) - return v; - const vv = memoMethod(k, v, { - options, - context, - }); - this.set(k, vv, options); - return vv; - } - /** - * Return a value from the cache. Will update the recency of the cache - * entry found. - * - * If the key is not found, get() will return `undefined`. - */ - get(k, getOptions = {}) { - const { allowStale = this.allowStale, updateAgeOnGet = this.updateAgeOnGet, noDeleteOnStaleGet = this.noDeleteOnStaleGet, status, } = getOptions; - const index = this.#keyMap.get(k); - if (index !== undefined) { - const value = this.#valList[index]; - const fetching = this.#isBackgroundFetch(value); - if (status) - this.#statusTTL(status, index); - if (this.#isStale(index)) { - if (status) - status.get = 'stale'; - // delete only if not an in-flight background fetch - if (!fetching) { - if (!noDeleteOnStaleGet) { - this.#delete(k, 'expire'); - } - if (status && allowStale) - status.returnedStale = true; - return allowStale ? value : undefined; - } - else { - if (status && - allowStale && - value.__staleWhileFetching !== undefined) { - status.returnedStale = true; - } - return allowStale ? value.__staleWhileFetching : undefined; - } - } - else { - if (status) - status.get = 'hit'; - // if we're currently fetching it, we don't actually have it yet - // it's not stale, which means this isn't a staleWhileRefetching. - // If it's not stale, and fetching, AND has a __staleWhileFetching - // value, then that means the user fetched with {forceRefresh:true}, - // so it's safe to return that value. - if (fetching) { - return value.__staleWhileFetching; - } - this.#moveToTail(index); - if (updateAgeOnGet) { - this.#updateItemAge(index); - } - return value; - } - } - else if (status) { - status.get = 'miss'; - } - } - #connect(p, n) { - this.#prev[n] = p; - this.#next[p] = n; - } - #moveToTail(index) { - // if tail already, nothing to do - // if head, move head to next[index] - // else - // move next[prev[index]] to next[index] (head has no prev) - // move prev[next[index]] to prev[index] - // prev[index] = tail - // next[tail] = index - // tail = index - if (index !== this.#tail) { - if (index === this.#head) { - this.#head = this.#next[index]; - } - else { - this.#connect(this.#prev[index], this.#next[index]); - } - this.#connect(this.#tail, index); - this.#tail = index; - } - } - /** - * Deletes a key out of the cache. - * - * Returns true if the key was deleted, false otherwise. - */ - delete(k) { - return this.#delete(k, 'delete'); - } - #delete(k, reason) { - let deleted = false; - if (this.#size !== 0) { - const index = this.#keyMap.get(k); - if (index !== undefined) { - if (this.#autopurgeTimers?.[index]) { - clearTimeout(this.#autopurgeTimers?.[index]); - this.#autopurgeTimers[index] = undefined; - } - deleted = true; - if (this.#size === 1) { - this.#clear(reason); - } - else { - this.#removeItemSize(index); - const v = this.#valList[index]; - if (this.#isBackgroundFetch(v)) { - v.__abortController.abort(new Error('deleted')); - } - else if (this.#hasDispose || this.#hasDisposeAfter) { - if (this.#hasDispose) { - this.#dispose?.(v, k, reason); - } - if (this.#hasDisposeAfter) { - this.#disposed?.push([v, k, reason]); - } - } - this.#keyMap.delete(k); - this.#keyList[index] = undefined; - this.#valList[index] = undefined; - if (index === this.#tail) { - this.#tail = this.#prev[index]; - } - else if (index === this.#head) { - this.#head = this.#next[index]; - } - else { - const pi = this.#prev[index]; - this.#next[pi] = this.#next[index]; - const ni = this.#next[index]; - this.#prev[ni] = this.#prev[index]; - } - this.#size--; - this.#free.push(index); - } - } - } - if (this.#hasDisposeAfter && this.#disposed?.length) { - const dt = this.#disposed; - let task; - while ((task = dt?.shift())) { - this.#disposeAfter?.(...task); - } - } - return deleted; - } - /** - * Clear the cache entirely, throwing away all values. - */ - clear() { - return this.#clear('delete'); - } - #clear(reason) { - for (const index of this.#rindexes({ allowStale: true })) { - const v = this.#valList[index]; - if (this.#isBackgroundFetch(v)) { - v.__abortController.abort(new Error('deleted')); - } - else { - const k = this.#keyList[index]; - if (this.#hasDispose) { - this.#dispose?.(v, k, reason); - } - if (this.#hasDisposeAfter) { - this.#disposed?.push([v, k, reason]); - } - } - } - this.#keyMap.clear(); - this.#valList.fill(undefined); - this.#keyList.fill(undefined); - if (this.#ttls && this.#starts) { - this.#ttls.fill(0); - this.#starts.fill(0); - for (const t of this.#autopurgeTimers ?? []) { - if (t !== undefined) - clearTimeout(t); - } - this.#autopurgeTimers?.fill(undefined); - } - if (this.#sizes) { - this.#sizes.fill(0); - } - this.#head = 0; - this.#tail = 0; - this.#free.length = 0; - this.#calculatedSize = 0; - this.#size = 0; - if (this.#hasDisposeAfter && this.#disposed) { - const dt = this.#disposed; - let task; - while ((task = dt?.shift())) { - this.#disposeAfter?.(...task); - } - } - } -} -//# sourceMappingURL=index.js.map
\ No newline at end of file |
