File Explorer

/var/lang/lib/node_modules/npm/node_modules/lru-cache/dist/esm/node

This explorer reads the filesystem of the server it runs on, so /workspace/user isn't present here. Browsing and the terminal still work against this server's own disk from /.

index.js60.9 KB · 1722 lines
/** * @module LRUCache */import { metrics, tracing } from './diagnostics-channel.js';import { defaultPerf } from './perf.js';const hasSubscribers = () => metrics.hasSubscribers || tracing.hasSubscribers;const warned = new Set();/* c8 ignore start */const PROCESS = (typeof process === 'object' && !!process ?    process    : {});/* c8 ignore stop */const emitWarning = (msg, type, code, fn) => {    if (typeof PROCESS.emitWarning === 'function') {        PROCESS.emitWarning(msg, type, code, fn);    }    else {        //oxlint-disable-next-line no-console        console.error(`[${code}] ${type}: ${msg}`);    }};const shouldWarn = (code) => !warned.has(code);const TYPE = Symbol('type');const isPosInt = (n) => !!n && n === Math.floor(n) && n > 0 && isFinite(n);// 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./* c8 ignore start */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 {    /* c8 ignore start - not sure why this is showing up uncovered?? */    heap;    /* c8 ignore stop */    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;    /** {@link LRUCache.OptionsBase.backgroundFetchSize} */    backgroundFetchSize;    // 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, backgroundFetchSize = 1, perf, } = options;        this.backgroundFetchSize = backgroundFetchSize;        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 = Array.from({ length: max }).fill(undefined);        this.#valList = Array.from({ length: 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 ?            Array.from({                length: this.#max,            })            : undefined;        this.#autopurgeTimers = purgeTimers;        this.#setItemTTL = (index, ttl, start = this.#perf.now()) => {            starts[index] = ttl !== 0 ? start : 0;            ttls[index] = ttl;            setPurgetTimer(index, ttl);        };        this.#updateItemAge = index => {            starts[index] = ttls[index] !== 0 ? this.#perf.now() : 0;            setPurgetTimer(index, ttls[index]);        };        // 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. Don't need to do this if we're not doing        // autopurge.        const setPurgetTimer = !this.ttlAutopurge ?            () => { }            : (index, ttl) => {                if (purgeTimers?.[index]) {                    clearTimeout(purgeTimers[index]);                    purgeTimers[index] = undefined;                }                if (ttl && 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.#statusTTL = (status, index) => {            if (ttls[index]) {                const ttl = ttls[index];                const start = starts[index];                /* c8 ignore start */                if (!ttl || !start) {                    return;                }                /* c8 ignore stop */                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) => {            if (!isPosInt(size)) {                // provisionally accept background fetches.                // actual value size will be checked when they return.                if (this.#isBackgroundFetch(v)) {                    // NB: this cannot occur if v.__staleWhileFetching is set,                    // because in that case, it would take on the size of the                    // existing entry that it temporarily replaces.                    return this.backgroundFetchSize;                }                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; this.#isValidIndex(i);) {                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; this.#isValidIndex(i);) {                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 stop */        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 = {}) {        const { status = metrics.hasSubscribers ? {} : undefined } = setOptions;        setOptions.status = status;        if (status) {            status.op = 'set';            status.key = k;            if (v !== undefined)                status.value = v;            status.cache = this;        }        const result = this.#set(k, v, setOptions);        if (status && metrics.hasSubscribers) {            metrics.publish(status);        }        return result;    }    #set(k, v, setOptions, bf) {        const { ttl = this.ttl, start, noDisposeOnSet = this.noDisposeOnSet, sizeCalculation = this.sizeCalculation, status, } = setOptions;        const isBF = this.#isBackgroundFetch(v);        if (v === undefined) {            if (status)                status.set = 'deleted';            this.delete(k);            return this;        }        let { noUpdateTTL = this.noUpdateTTL } = setOptions;        if (status && !isBF)            status.value = v;        const size = this.#requireSize(k, v, setOptions.size || 0, sizeCalculation, status);        // if the item doesn't fit, don't do anything        // NB: maxEntrySize set to maxSize by default        if (this.maxEntrySize && size > this.maxEntrySize) {            // have to delete, in case something is there already.            this.#delete(k, 'set');            if (status) {                status.set = 'miss';                status.maxEntrySizeExceeded = true;            }            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 && !isBF) {                this.#onInsert?.(v, k, 'add');            }        }        else {            // update            // might be updating a background fetch!            this.#moveToTail(index);            const oldVal = this.#valList[index];            if (v !== oldVal) {                if (!noDisposeOnSet) {                    if (this.#isBackgroundFetch(oldVal)) {                        if (oldVal !== bf) {                            // setting over a background fetch, not merely resolving it.                            oldVal.__abortController.abort(new Error('replaced'));                        }                        const { __staleWhileFetching: s } = oldVal;                        if (s !== undefined && s !== v) {                            if (this.#hasDispose) {                                this.#dispose?.(s, k, 'set');                            }                            if (this.#hasDisposeAfter) {                                this.#disposed?.push([s, k, 'set']);                            }                        }                    }                    else {                        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 (!isBF) {                    const oldValue = oldVal && this.#isBackgroundFetch(oldVal) ?                        oldVal.__staleWhileFetching                        : oldVal;                    const setType = oldValue === undefined ? 'add'                        : v !== oldValue ? 'replace'                            : 'update';                    if (status) {                        status.set = setType;                        if (oldValue !== undefined)                            status.oldValue = oldValue;                    }                    if (this.#hasOnInsert) {                        this.onInsert?.(v, k, setType);                    }                }            }            else if (!isBF) {                if (status) {                    status.set = 'update';                }                if (this.#hasOnInsert) {                    this.onInsert?.(v, k, 'update');                }            }        }        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];        const isBF = this.#isBackgroundFetch(v);        if (isBF) {            v.__abortController.abort(new Error('evicted'));        }        const oldValue = isBF ? v.__staleWhileFetching : v;        if ((this.#hasDispose || this.#hasDisposeAfter) &&            oldValue !== undefined) {            if (this.#hasDispose) {                this.#dispose?.(oldValue, k, 'evict');            }            if (this.#hasDisposeAfter) {                this.#disposed?.push([oldValue, 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 { status = metrics.hasSubscribers ? {} : undefined } = hasOptions;        hasOptions.status = status;        if (status) {            status.op = 'has';            status.key = k;            status.cache = this;        }        const result = this.#has(k, hasOptions);        if (metrics.hasSubscribers)            metrics.publish(status);        return result;    }    #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 { status = hasSubscribers() ? {} : undefined } = peekOptions;        if (status) {            status.op = 'peek';            status.key = k;            status.cache = this;        }        peekOptions.status = status;        const result = this.#peek(k, peekOptions);        if (metrics.hasSubscribers) {            metrics.publish(status);        }        return result;    }    #peek(k, peekOptions) {        const { status, allowStale = this.allowStale } = peekOptions;        const index = this.#keyMap.get(k);        if (index === undefined || (!allowStale && this.#isStale(index))) {            if (status)                status.peek = index === undefined ? 'miss' : 'stale';            return undefined;        }        const v = this.#valList[index];        const val = this.#isBackgroundFetch(v) ? v.__staleWhileFetching : v;        if (status) {            if (val !== undefined) {                status.peek = 'hit';                status.value = val;            }            else {                status.peek = 'miss';            }        }        return val;    }    #backgroundFetch(k, index, options, context) {        const v = index === undefined ? undefined : this.#valList[index];        if (this.#isBackgroundFetch(v)) {            return v;        }        const ac = new AbortController();        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 || (vl === undefined && ignoreAbort && updateCache)) {                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, bf);                }            }            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);            // 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 (fmp && fmp instanceof Promise) {                fmp.then(v => res(v === undefined ? undefined : v), rej);            }            else if (fmp !== undefined) {                res(fmp);            }        };        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 {            // do not call #set, because we do not want to adjust its place            // in the lru queue, as it has not yet been "used". Also, we don't            // need to worry about evicting for size, because a background fetch            // over a stale value is treated as the same size as its stale value.            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 AbortController);    }    fetch(k, fetchOptions = {}) {        const ths = tracing.hasSubscribers;        const { status = hasSubscribers() ? {} : undefined } = fetchOptions;        fetchOptions.status = status;        if (status && fetchOptions.context) {            status.context = fetchOptions.context;        }        const p = this.#fetch(k, fetchOptions);        if (status && ths) {            status.trace = true;            tracing.tracePromise(() => p, status).catch(() => { });        }        return p;    }    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 (status) {            status.op = 'fetch';            status.key = k;            if (forceRefresh)                status.forceRefresh = true;            status.cache = this;        }        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);        }    }    forceFetch(k, fetchOptions = {}) {        const ths = tracing.hasSubscribers;        const { status = hasSubscribers() ? {} : undefined } = fetchOptions;        fetchOptions.status = status;        if (status && fetchOptions.context) {            status.context = fetchOptions.context;        }        const p = this.#forceFetch(k, fetchOptions);        if (status && ths) {            status.trace = true;            tracing.tracePromise(() => p, status).catch(() => { });        }        return 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 { status = metrics.hasSubscribers ? {} : undefined } = memoOptions;        memoOptions.status = status;        if (status) {            status.op = 'memo';            status.key = k;            if (memoOptions.context) {                status.context = memoOptions.context;            }            status.cache = this;        }        const result = this.#memo(k, memoOptions);        if (status)            status.value = result;        if (metrics.hasSubscribers)            metrics.publish(status);        return result;    }    #memo(k, memoOptions = {}) {        const memoMethod = this.#memoMethod;        if (!memoMethod) {            throw new Error('no memoMethod provided to constructor');        }        const { context, status, forceRefresh, ...options } = memoOptions;        if (status && forceRefresh)            status.forceRefresh = true;        const v = this.#get(k, options);        const refresh = forceRefresh || v === undefined;        if (status) {            status.memo = refresh ? 'miss' : 'hit';            if (!refresh)                status.value = v;        }        if (!refresh)            return v;        const vv = memoMethod(k, v, {            options,            context,        });        if (status)            status.value = vv;        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 { status = metrics.hasSubscribers ? {} : undefined } = getOptions;        getOptions.status = status;        if (status) {            status.op = 'get';            status.key = k;            status.cache = this;        }        const result = this.#get(k, getOptions);        if (status) {            if (result !== undefined)                status.value = result;            if (metrics.hasSubscribers)                metrics.publish(status);        }        return result;    }    #get(k, getOptions = {}) {        const { allowStale = this.allowStale, updateAgeOnGet = this.updateAgeOnGet, noDeleteOnStaleGet = this.noDeleteOnStaleGet, status, } = getOptions;        const index = this.#keyMap.get(k);        if (index === undefined) {            if (status)                status.get = 'miss';            return undefined;        }        const value = this.#valList[index];        const fetching = this.#isBackgroundFetch(value);        if (status)            this.#statusTTL(status, index);        if (this.#isStale(index)) {            // delete only if not an in-flight background fetch            if (!fetching) {                if (!noDeleteOnStaleGet) {                    this.#delete(k, 'expire');                }                if (status)                    status.get = 'stale';                if (allowStale) {                    if (status)                        status.returnedStale = true;                    return value;                }                return undefined;            }            if (status)                status.get = 'stale-fetching';            if (allowStale && value.__staleWhileFetching !== undefined) {                if (status)                    status.returnedStale = true;                return value.__staleWhileFetching;            }            return undefined;        }        // not stale        if (status)            status.get = fetching ? 'fetching' : '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.        this.#moveToTail(index);        if (updateAgeOnGet) {            this.#updateItemAge(index);        }        return fetching ? value.__staleWhileFetching : value;    }    #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) {        if (metrics.hasSubscribers) {            metrics.publish({                op: 'delete',                delete: reason,                key: k,                cache: this,            });        }        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();        void 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