lru-cache
Advanced tools
| {"version":3,"file":"diagnostics-channel-browser.d.mts","sourceRoot":"","sources":["../../../src/diagnostics-channel-browser.mts"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,OAAO,EACZ,KAAK,cAAc,EACpB,MAAM,0BAA0B,CAAA;AACjC,YAAY,EAAE,cAAc,EAAE,OAAO,EAAE,CAAA;AAGvC,eAAO,MAAM,OAAO,EAAY,OAAO,CAAC,OAAO,CAAC,CAAA;AAChD,eAAO,MAAM,OAAO,EAAY,cAAc,CAAC,OAAO,CAAC,CAAA"} |
| {"version":3,"file":"diagnostics-channel-browser.mjs","sourceRoot":"","sources":["../../../src/diagnostics-channel-browser.mts"],"names":[],"mappings":"AAQA,MAAM,KAAK,GAAG,EAAE,cAAc,EAAE,KAAK,EAAE,CAAA;AACvC,MAAM,CAAC,MAAM,OAAO,GAAG,KAAyB,CAAA;AAChD,MAAM,CAAC,MAAM,OAAO,GAAG,KAAgC,CAAA","sourcesContent":["// this is used in ESM environments that follow the 'browser' import\n// condition, to avoid even trying to load node:diagnostics_channel\nimport {\n type Channel,\n type TracingChannel,\n} from 'node:diagnostics_channel'\nexport type { TracingChannel, Channel }\n\nconst dummy = { hasSubscribers: false }\nexport const metrics = dummy as Channel<unknown>\nexport const tracing = dummy as TracingChannel<unknown>\n"]} |
| import { type Channel, type TracingChannel } from 'node:diagnostics_channel'; | ||
| export type { TracingChannel, Channel }; | ||
| export declare const metrics: Channel<unknown>; | ||
| export declare const tracing: TracingChannel<unknown>; | ||
| //# sourceMappingURL=diagnostics-channel-browser.d.mts.map |
| const dummy = { hasSubscribers: false }; | ||
| export const metrics = dummy; | ||
| export const tracing = dummy; | ||
| //# sourceMappingURL=diagnostics-channel-browser.mjs.map |
| /** | ||
| * @module LRUCache | ||
| */ | ||
| export type Perf = { | ||
| now: () => number; | ||
| }; | ||
| declare const TYPE: unique symbol; | ||
| export type PosInt = number & { | ||
| [TYPE]: 'Positive Integer'; | ||
| }; | ||
| export type Index = number & { | ||
| [TYPE]: 'LRUCache Index'; | ||
| }; | ||
| export type UintArray = Uint8Array | Uint16Array | Uint32Array; | ||
| export type NumberArray = UintArray | number[]; | ||
| declare class ZeroArray extends Array<number> { | ||
| constructor(size: number); | ||
| } | ||
| export type { ZeroArray }; | ||
| export type { Stack }; | ||
| export type StackLike = Stack | Index[]; | ||
| declare class Stack { | ||
| #private; | ||
| heap: NumberArray; | ||
| length: number; | ||
| static create(max: number): StackLike; | ||
| constructor(max: number, HeapCls: { | ||
| new (n: number): NumberArray; | ||
| }); | ||
| push(n: Index): void; | ||
| pop(): Index; | ||
| } | ||
| /** | ||
| * Promise representing an in-progress {@link LRUCache#fetch} call | ||
| */ | ||
| export type BackgroundFetch<V> = Promise<V | undefined> & { | ||
| __returned: BackgroundFetch<V> | undefined; | ||
| __abortController: AbortController; | ||
| __staleWhileFetching: V | undefined; | ||
| }; | ||
| export type DisposeTask<K, V> = [ | ||
| value: V, | ||
| key: K, | ||
| reason: LRUCache.DisposeReason | ||
| ]; | ||
| export declare namespace LRUCache { | ||
| /** | ||
| * An integer greater than 0, reflecting the calculated size of items | ||
| */ | ||
| type Size = number; | ||
| /** | ||
| * Integer greater than 0, representing some number of milliseconds, or the | ||
| * time at which a TTL started counting from. | ||
| */ | ||
| type Milliseconds = number; | ||
| /** | ||
| * An integer greater than 0, reflecting a number of items | ||
| */ | ||
| type Count = number; | ||
| /** | ||
| * The reason why an item was removed from the cache, passed | ||
| * to the {@link Disposer} methods. | ||
| * | ||
| * - `evict`: The item was evicted because it is the least recently used, | ||
| * and the cache is full. | ||
| * - `set`: A new value was set, overwriting the old value being disposed. | ||
| * - `delete`: The item was explicitly deleted, either by calling | ||
| * {@link LRUCache#delete}, {@link LRUCache#clear}, or | ||
| * {@link LRUCache#set} with an undefined value. | ||
| * - `expire`: The item was removed due to exceeding its TTL. | ||
| * - `fetch`: A {@link OptionsBase#fetchMethod} operation returned | ||
| * `undefined` or was aborted, causing the item to be deleted. | ||
| */ | ||
| type DisposeReason = 'evict' | 'set' | 'delete' | 'expire' | 'fetch'; | ||
| /** | ||
| * A method called upon item removal, passed as the | ||
| * {@link OptionsBase.dispose} and/or | ||
| * {@link OptionsBase.disposeAfter} options. | ||
| */ | ||
| type Disposer<K, V> = (value: V, key: K, reason: DisposeReason) => void; | ||
| /** | ||
| * The reason why an item was added to the cache, passed | ||
| * to the {@link Inserter} methods. | ||
| * | ||
| * - `add`: the item was not found in the cache, and was added | ||
| * - `update`: the item was in the cache, with the same value provided | ||
| * - `replace`: the item was in the cache, and replaced | ||
| */ | ||
| type InsertReason = 'add' | 'update' | 'replace'; | ||
| /** | ||
| * A method called upon item insertion, passed as the | ||
| * {@link OptionsBase.insert} | ||
| */ | ||
| type Inserter<K, V> = (value: V, key: K, reason: InsertReason) => void; | ||
| /** | ||
| * A function that returns the effective calculated size | ||
| * of an entry in the cache. | ||
| */ | ||
| type SizeCalculator<K, V> = (value: V, key: K) => Size; | ||
| /** | ||
| * Options provided to the | ||
| * {@link OptionsBase.fetchMethod} function. | ||
| */ | ||
| interface FetcherOptions<K, V, FC = unknown> { | ||
| signal: AbortSignal; | ||
| options: FetcherFetchOptions<K, V, FC>; | ||
| /** | ||
| * Object provided in the {@link FetchOptions.context} option to | ||
| * {@link LRUCache#fetch} | ||
| */ | ||
| context: FC; | ||
| } | ||
| /** | ||
| * Occasionally, it may be useful to track the internal behavior of the | ||
| * cache, particularly for logging, debugging, or for behavior within the | ||
| * `fetchMethod`. To do this, you can pass a `status` object to the | ||
| * {@link LRUCache#fetch}, {@link LRUCache#get}, {@link LRUCache#set}, | ||
| * {@link LRUCache#memo}, and {@link LRUCache#has} methods. | ||
| * | ||
| * The `status` option should be a plain JavaScript object. The following | ||
| * fields will be set on it appropriately, depending on the situation. | ||
| * | ||
| * These objects are also the context objects passed to listeners on the | ||
| * `lru-cache:metrics` diagnostic channel, and the `lru-cache` tracing | ||
| * channels, in platforms that support them. | ||
| */ | ||
| interface Status<K, V> { | ||
| /** | ||
| * The operation being performed | ||
| */ | ||
| op?: 'get' | 'set' | 'memo' | 'fetch' | 'delete' | 'has' | 'peek'; | ||
| /** | ||
| * The status of a set() operation. | ||
| * | ||
| * - add: the item was not found in the cache, and was added | ||
| * - update: the item was in the cache, with the same value provided | ||
| * - replace: the item was in the cache, and replaced | ||
| * - miss: the item was not added to the cache for some reason | ||
| */ | ||
| set?: 'add' | 'update' | 'replace' | 'miss' | 'deleted'; | ||
| /** | ||
| * The status of a delete() operation. | ||
| */ | ||
| delete?: LRUCache.DisposeReason; | ||
| /** | ||
| * The result of a peek() operation | ||
| * | ||
| * - hit: the item was found and returned | ||
| * - stale: the item is in the cache, but past its ttl and not returned | ||
| * - miss: item not in the cache | ||
| */ | ||
| peek?: 'hit' | 'miss' | 'stale'; | ||
| /** | ||
| * The status of a memo() operation. | ||
| * | ||
| * - 'hit': the item was found in the cache and returned | ||
| * - 'miss': the `memoMethod` function was called | ||
| */ | ||
| memo?: 'hit' | 'miss'; | ||
| /** | ||
| * The `context` option provided to a memo or fetch operation | ||
| * | ||
| * In practice, of course, this will be the same type as the `FC` | ||
| * fetch context param used to instantiate the LRUCache, but the | ||
| * convolutions of threading that through would get quite complicated, | ||
| * and preclude forcing/forbidding the passing of a `context` param | ||
| * where it is/isn't expected, which is more valuable for error | ||
| * prevention. | ||
| */ | ||
| context?: unknown; | ||
| /** | ||
| * the ttl stored for the item, or undefined if ttls are not used. | ||
| */ | ||
| ttl?: Milliseconds; | ||
| /** | ||
| * the start time for the item, or undefined if ttls are not used. | ||
| */ | ||
| start?: Milliseconds; | ||
| /** | ||
| * The timestamp used for TTL calculation | ||
| */ | ||
| now?: Milliseconds; | ||
| /** | ||
| * the remaining ttl for the item, or undefined if ttls are not used. | ||
| */ | ||
| remainingTTL?: Milliseconds; | ||
| /** | ||
| * The calculated size for the item, if sizes are used. | ||
| */ | ||
| entrySize?: Size; | ||
| /** | ||
| * The total calculated size of the cache, if sizes are used. | ||
| */ | ||
| totalCalculatedSize?: Size; | ||
| /** | ||
| * A flag indicating that the item was not stored, due to exceeding the | ||
| * {@link OptionsBase.maxEntrySize} | ||
| */ | ||
| maxEntrySizeExceeded?: true; | ||
| /** | ||
| * The key that was set or retrieved | ||
| */ | ||
| key?: K; | ||
| /** | ||
| * The value that was set | ||
| */ | ||
| value?: V; | ||
| /** | ||
| * The old value, specified in the case of `set:'replace'` | ||
| */ | ||
| oldValue?: V; | ||
| /** | ||
| * The results of a {@link LRUCache#has} operation | ||
| * | ||
| * - hit: the item was found in the cache | ||
| * - stale: the item was found in the cache, but is stale | ||
| * - miss: the item was not found in the cache | ||
| */ | ||
| has?: 'hit' | 'stale' | 'miss'; | ||
| /** | ||
| * The status of a {@link LRUCache#fetch} operation. | ||
| * Note that this can change as the underlying fetch() moves through | ||
| * various states. | ||
| * | ||
| * - inflight: there is another fetch() for this key which is in process | ||
| * - get: there is no {@link OptionsBase.fetchMethod}, so | ||
| * {@link LRUCache#get} was called. | ||
| * - miss: the item is not in cache, and will be fetched. | ||
| * - hit: the item is in the cache, and was resolved immediately. | ||
| * - stale: the item is in the cache, but stale. | ||
| * - refresh: the item is in the cache, and not stale, but | ||
| * {@link FetchOptions.forceRefresh} was specified. | ||
| */ | ||
| fetch?: 'get' | 'inflight' | 'miss' | 'hit' | 'stale' | 'refresh'; | ||
| /** | ||
| * `forceRefresh` option was used for either a fetch or memo operation | ||
| */ | ||
| forceRefresh?: boolean; | ||
| /** | ||
| * The {@link OptionsBase.fetchMethod} was called | ||
| */ | ||
| fetchDispatched?: true; | ||
| /** | ||
| * The cached value was updated after a successful call to | ||
| * {@link OptionsBase.fetchMethod} | ||
| */ | ||
| fetchUpdated?: true; | ||
| /** | ||
| * The reason for a fetch() rejection. Either the error raised by the | ||
| * {@link OptionsBase.fetchMethod}, or the reason for an | ||
| * AbortSignal. | ||
| */ | ||
| fetchError?: Error; | ||
| /** | ||
| * The fetch received an abort signal | ||
| */ | ||
| fetchAborted?: true; | ||
| /** | ||
| * The abort signal received was ignored, and the fetch was allowed to | ||
| * continue in the background. | ||
| */ | ||
| fetchAbortIgnored?: true; | ||
| /** | ||
| * The fetchMethod promise resolved successfully | ||
| */ | ||
| fetchResolved?: true; | ||
| /** | ||
| * The fetchMethod promise was rejected | ||
| */ | ||
| fetchRejected?: true; | ||
| /** | ||
| * The status of a {@link LRUCache#get} operation. | ||
| * | ||
| * - fetching: The item is currently being fetched. If a previous value | ||
| * is present and allowed, that will be returned. | ||
| * - stale: The item is in the cache, and is stale. If it was returned, | ||
| * then the `returnedStale` flag will be set. | ||
| * - stale-fetching: The value is being fetched in the background, but is | ||
| * currently stale. If the stale value was returned, then the | ||
| * `returnedStale` flag will be set. | ||
| * - hit: the item is in the cache | ||
| * - miss: the item is not in the cache | ||
| */ | ||
| get?: 'stale' | 'hit' | 'miss' | 'fetching' | 'stale-fetching'; | ||
| /** | ||
| * A fetch or get operation returned a stale value. | ||
| */ | ||
| returnedStale?: true; | ||
| /** | ||
| * A tracingChannel trace was started for this operation | ||
| */ | ||
| trace?: boolean; | ||
| } | ||
| /** | ||
| * options which override the options set in the LRUCache constructor | ||
| * when calling {@link LRUCache#fetch}. | ||
| * | ||
| * This is the union of {@link GetOptions} and {@link SetOptions}, plus | ||
| * {@link OptionsBase.noDeleteOnFetchRejection}, | ||
| * {@link OptionsBase.allowStaleOnFetchRejection}, | ||
| * {@link FetchOptions.forceRefresh}, and | ||
| * {@link FetcherOptions.context} | ||
| * | ||
| * Any of these may be modified in the {@link OptionsBase.fetchMethod} | ||
| * function, but the {@link GetOptions} fields will of course have no | ||
| * effect, as the {@link LRUCache#get} call already happened by the time | ||
| * the fetchMethod is called. | ||
| */ | ||
| interface FetcherFetchOptions<K, V, FC = unknown> extends Pick<OptionsBase<K, V, FC>, 'allowStale' | 'updateAgeOnGet' | 'noDeleteOnStaleGet' | 'sizeCalculation' | 'ttl' | 'noDisposeOnSet' | 'noUpdateTTL' | 'noDeleteOnFetchRejection' | 'allowStaleOnFetchRejection' | 'ignoreFetchAbort' | 'allowStaleOnFetchAbort'> { | ||
| status?: Status<K, V>; | ||
| size?: Size; | ||
| } | ||
| /** | ||
| * Options that may be passed to the {@link LRUCache#fetch} method. | ||
| */ | ||
| interface FetchOptions<K, V, FC> extends FetcherFetchOptions<K, V, FC> { | ||
| /** | ||
| * Set to true to force a re-load of the existing data, even if it | ||
| * is not yet stale. | ||
| */ | ||
| forceRefresh?: boolean; | ||
| /** | ||
| * Context provided to the {@link OptionsBase.fetchMethod} as | ||
| * the {@link FetcherOptions.context} param. | ||
| * | ||
| * If the FC type is specified as unknown (the default), | ||
| * undefined or void, then this is optional. Otherwise, it will | ||
| * be required. | ||
| */ | ||
| context?: FC; | ||
| signal?: AbortSignal; | ||
| status?: Status<K, V>; | ||
| } | ||
| /** | ||
| * Options provided to {@link LRUCache#fetch} when the FC type is something | ||
| * other than `unknown`, `undefined`, or `void` | ||
| */ | ||
| interface FetchOptionsWithContext<K, V, FC> extends FetchOptions<K, V, FC> { | ||
| context: FC; | ||
| } | ||
| /** | ||
| * Options provided to {@link LRUCache#fetch} when the FC type is | ||
| * `undefined` or `void` | ||
| */ | ||
| interface FetchOptionsNoContext<K, V> extends FetchOptions<K, V, undefined> { | ||
| context?: undefined; | ||
| } | ||
| interface MemoOptions<K, V, FC = unknown> extends Pick<OptionsBase<K, V, FC>, 'allowStale' | 'updateAgeOnGet' | 'noDeleteOnStaleGet' | 'sizeCalculation' | 'ttl' | 'noDisposeOnSet' | 'noUpdateTTL' | 'noDeleteOnFetchRejection' | 'allowStaleOnFetchRejection' | 'ignoreFetchAbort' | 'allowStaleOnFetchAbort'> { | ||
| /** | ||
| * Set to true to force a re-load of the existing data, even if it | ||
| * is not yet stale. | ||
| */ | ||
| forceRefresh?: boolean; | ||
| /** | ||
| * Context provided to the {@link OptionsBase.memoMethod} as | ||
| * the {@link MemoizerOptions.context} param. | ||
| * | ||
| * If the FC type is specified as unknown (the default), | ||
| * undefined or void, then this is optional. Otherwise, it will | ||
| * be required. | ||
| */ | ||
| context?: FC; | ||
| status?: Status<K, V>; | ||
| } | ||
| /** | ||
| * Options provided to {@link LRUCache#memo} when the FC type is something | ||
| * other than `unknown`, `undefined`, or `void` | ||
| */ | ||
| interface MemoOptionsWithContext<K, V, FC> extends MemoOptions<K, V, FC> { | ||
| context: FC; | ||
| } | ||
| /** | ||
| * Options provided to {@link LRUCache#memo} when the FC type is | ||
| * `undefined` or `void` | ||
| */ | ||
| interface MemoOptionsNoContext<K, V> extends MemoOptions<K, V, undefined> { | ||
| context?: undefined; | ||
| } | ||
| /** | ||
| * Options provided to the | ||
| * {@link OptionsBase.memoMethod} function. | ||
| */ | ||
| interface MemoizerOptions<K, V, FC = unknown> { | ||
| options: MemoizerMemoOptions<K, V, FC>; | ||
| /** | ||
| * Object provided in the {@link MemoOptions.context} option to | ||
| * {@link LRUCache#memo} | ||
| */ | ||
| context: FC; | ||
| } | ||
| /** | ||
| * options which override the options set in the LRUCache constructor | ||
| * when calling {@link LRUCache#memo}. | ||
| * | ||
| * This is the union of {@link GetOptions} and {@link SetOptions}, plus | ||
| * {@link MemoOptions.forceRefresh}, and | ||
| * {@link MemoOptions.context} | ||
| * | ||
| * Any of these may be modified in the {@link OptionsBase.memoMethod} | ||
| * function, but the {@link GetOptions} fields will of course have no | ||
| * effect, as the {@link LRUCache#get} call already happened by the time | ||
| * the memoMethod is called. | ||
| */ | ||
| interface MemoizerMemoOptions<K, V, FC = unknown> extends Pick<OptionsBase<K, V, FC>, 'allowStale' | 'updateAgeOnGet' | 'noDeleteOnStaleGet' | 'sizeCalculation' | 'ttl' | 'noDisposeOnSet' | 'noUpdateTTL'> { | ||
| status?: Status<K, V>; | ||
| size?: Size; | ||
| start?: Milliseconds; | ||
| } | ||
| /** | ||
| * Options that may be passed to the {@link LRUCache#has} method. | ||
| */ | ||
| interface HasOptions<K, V, FC> extends Pick<OptionsBase<K, V, FC>, 'updateAgeOnHas'> { | ||
| status?: Status<K, V>; | ||
| } | ||
| /** | ||
| * Options that may be passed to the {@link LRUCache#get} method. | ||
| */ | ||
| interface GetOptions<K, V, FC> extends Pick<OptionsBase<K, V, FC>, 'allowStale' | 'updateAgeOnGet' | 'noDeleteOnStaleGet'> { | ||
| status?: Status<K, V>; | ||
| } | ||
| /** | ||
| * Options that may be passed to the {@link LRUCache#peek} method. | ||
| */ | ||
| interface PeekOptions<K, V, FC> extends Pick<OptionsBase<K, V, FC>, 'allowStale'> { | ||
| status?: Status<K, V>; | ||
| } | ||
| /** | ||
| * Options that may be passed to the {@link LRUCache#set} method. | ||
| */ | ||
| interface SetOptions<K, V, FC> extends Pick<OptionsBase<K, V, FC>, 'sizeCalculation' | 'ttl' | 'noDisposeOnSet' | 'noUpdateTTL'> { | ||
| /** | ||
| * If size tracking is enabled, then setting an explicit size | ||
| * in the {@link LRUCache#set} call will prevent calling the | ||
| * {@link OptionsBase.sizeCalculation} function. | ||
| */ | ||
| size?: Size; | ||
| /** | ||
| * If TTL tracking is enabled, then setting an explicit start | ||
| * time in the {@link LRUCache#set} call will override the | ||
| * default time from `performance.now()` or `Date.now()`. | ||
| * | ||
| * Note that it must be a valid value for whichever time-tracking | ||
| * method is in use. | ||
| */ | ||
| start?: Milliseconds; | ||
| status?: Status<K, V>; | ||
| } | ||
| /** | ||
| * The type signature for the {@link OptionsBase.fetchMethod} option. | ||
| */ | ||
| type Fetcher<K, V, FC = unknown> = (key: K, staleValue: V | undefined, options: FetcherOptions<K, V, FC>) => Promise<V | undefined | void> | V | undefined | void; | ||
| /** | ||
| * the type signature for the {@link OptionsBase.memoMethod} option. | ||
| */ | ||
| type Memoizer<K, V, FC = unknown> = (key: K, staleValue: V | undefined, options: MemoizerOptions<K, V, FC>) => V; | ||
| /** | ||
| * Options which may be passed to the {@link LRUCache} constructor. | ||
| * | ||
| * Most of these may be overridden in the various options that use | ||
| * them. | ||
| * | ||
| * Despite all being technically optional, the constructor requires that | ||
| * a cache is at minimum limited by one or more of {@link OptionsBase.max}, | ||
| * {@link OptionsBase.ttl}, or {@link OptionsBase.maxSize}. | ||
| * | ||
| * If {@link OptionsBase.ttl} is used alone, then it is strongly advised | ||
| * (and in fact required by the type definitions here) that the cache | ||
| * also set {@link OptionsBase.ttlAutopurge}, to prevent potentially | ||
| * unbounded storage. | ||
| * | ||
| * All options are also available on the {@link LRUCache} instance, making | ||
| * it safe to pass an LRUCache instance as the options argumemnt to | ||
| * make another empty cache of the same type. | ||
| * | ||
| * Some options are marked as read-only, because changing them after | ||
| * instantiation is not safe. Changing any of the other options will of | ||
| * course only have an effect on subsequent method calls. | ||
| */ | ||
| interface OptionsBase<K, V, FC> { | ||
| /** | ||
| * The maximum number of items to store in the cache before evicting | ||
| * old entries. This is read-only on the {@link LRUCache} instance, | ||
| * and may not be overridden. | ||
| * | ||
| * If set, then storage space will be pre-allocated at construction | ||
| * time, and the cache will perform significantly faster. | ||
| * | ||
| * Note that significantly fewer items may be stored, if | ||
| * {@link OptionsBase.maxSize} and/or {@link OptionsBase.ttl} are also | ||
| * set. | ||
| * | ||
| * **It is strongly recommended to set a `max` to prevent unbounded growth | ||
| * of the cache.** | ||
| */ | ||
| max?: Count; | ||
| /** | ||
| * Max time in milliseconds for items to live in cache before they are | ||
| * considered stale. Note that stale items are NOT preemptively removed by | ||
| * default, and MAY live in the cache, contributing to its LRU max, long | ||
| * after they have expired, unless {@link OptionsBase.ttlAutopurge} is | ||
| * set. | ||
| * | ||
| * If set to `0` (the default value), then that means "do not track | ||
| * TTL", not "expire immediately". | ||
| * | ||
| * Also, as this cache is optimized for LRU/MRU operations, some of | ||
| * the staleness/TTL checks will reduce performance, as they will incur | ||
| * overhead by deleting items. | ||
| * | ||
| * This is not primarily a TTL cache, and does not make strong TTL | ||
| * guarantees. There is no pre-emptive pruning of expired items, but you | ||
| * _may_ set a TTL on the cache, and it will treat expired items as missing | ||
| * when they are fetched, and delete them. | ||
| * | ||
| * Optional, but must be a non-negative integer in ms if specified. | ||
| * | ||
| * This may be overridden by passing an options object to `cache.set()`. | ||
| * | ||
| * At least one of `max`, `maxSize`, or `TTL` is required. This must be a | ||
| * positive integer if set. | ||
| * | ||
| * Even if ttl tracking is enabled, **it is strongly recommended to set a | ||
| * `max` to prevent unbounded growth of the cache.** | ||
| * | ||
| * If ttl tracking is enabled, and `max` and `maxSize` are not set, | ||
| * and `ttlAutopurge` is not set, then a warning will be emitted | ||
| * cautioning about the potential for unbounded memory consumption. | ||
| * (The TypeScript definitions will also discourage this.) | ||
| */ | ||
| ttl?: Milliseconds; | ||
| /** | ||
| * Minimum amount of time in ms in which to check for staleness. | ||
| * Defaults to 1, which means that the current time is checked | ||
| * at most once per millisecond. | ||
| * | ||
| * Set to 0 to check the current time every time staleness is tested. | ||
| * (This reduces performance, and is theoretically unnecessary.) | ||
| * | ||
| * Setting this to a higher value will improve performance somewhat | ||
| * while using ttl tracking, albeit at the expense of keeping stale | ||
| * items around a bit longer than their TTLs would indicate. | ||
| * | ||
| * @default 1 | ||
| */ | ||
| ttlResolution?: Milliseconds; | ||
| /** | ||
| * Preemptively remove stale items from the cache. | ||
| * | ||
| * Note that this may *significantly* degrade performance, especially if | ||
| * the cache is storing a large number of items. It is almost always best | ||
| * to just leave the stale items in the cache, and let them fall out as new | ||
| * items are added. | ||
| * | ||
| * Note that this means that {@link OptionsBase.allowStale} is a bit | ||
| * pointless, as stale items will be deleted almost as soon as they | ||
| * expire. | ||
| * | ||
| * Use with caution! | ||
| */ | ||
| ttlAutopurge?: boolean; | ||
| /** | ||
| * When using time-expiring entries with `ttl`, setting this to `true` will | ||
| * make each item's age reset to 0 whenever it is retrieved from cache with | ||
| * {@link LRUCache#get}, causing it to not expire. (It can still fall out | ||
| * of cache based on recency of use, of course.) | ||
| * | ||
| * Has no effect if {@link OptionsBase.ttl} is not set. | ||
| * | ||
| * This may be overridden by passing an options object to `cache.get()`. | ||
| */ | ||
| updateAgeOnGet?: boolean; | ||
| /** | ||
| * When using time-expiring entries with `ttl`, setting this to `true` will | ||
| * make each item's age reset to 0 whenever its presence in the cache is | ||
| * checked with {@link LRUCache#has}, causing it to not expire. (It can | ||
| * still fall out of cache based on recency of use, of course.) | ||
| * | ||
| * Has no effect if {@link OptionsBase.ttl} is not set. | ||
| */ | ||
| updateAgeOnHas?: boolean; | ||
| /** | ||
| * Allow {@link LRUCache#get} and {@link LRUCache#fetch} calls to return | ||
| * stale data, if available. | ||
| * | ||
| * By default, if you set `ttl`, stale items will only be deleted from the | ||
| * cache when you `get(key)`. That is, it's not preemptively pruning items, | ||
| * unless {@link OptionsBase.ttlAutopurge} is set. | ||
| * | ||
| * If you set `allowStale:true`, it'll return the stale value *as well as* | ||
| * deleting it. If you don't set this, then it'll return `undefined` when | ||
| * you try to get a stale entry. | ||
| * | ||
| * Note that when a stale entry is fetched, _even if it is returned due to | ||
| * `allowStale` being set_, it is removed from the cache immediately. You | ||
| * can suppress this behavior by setting | ||
| * {@link OptionsBase.noDeleteOnStaleGet}, either in the constructor, or in | ||
| * the options provided to {@link LRUCache#get}. | ||
| * | ||
| * This may be overridden by passing an options object to `cache.get()`. | ||
| * The `cache.has()` method will always return `false` for stale items. | ||
| * | ||
| * Only relevant if a ttl is set. | ||
| */ | ||
| allowStale?: boolean; | ||
| /** | ||
| * Function that is called on items when they are dropped from the | ||
| * cache, as `dispose(value, key, reason)`. | ||
| * | ||
| * This can be handy if you want to close file descriptors or do | ||
| * other cleanup tasks when items are no longer stored in the cache. | ||
| * | ||
| * **NOTE**: It is called _before_ the item has been fully removed | ||
| * from the cache, so if you want to put it right back in, you need | ||
| * to wait until the next tick. If you try to add it back in during | ||
| * the `dispose()` function call, it will break things in subtle and | ||
| * weird ways. | ||
| * | ||
| * Unlike several other options, this may _not_ be overridden by | ||
| * passing an option to `set()`, for performance reasons. | ||
| * | ||
| * The `reason` will be one of the following strings, corresponding | ||
| * to the reason for the item's deletion: | ||
| * | ||
| * - `evict` Item was evicted to make space for a new addition | ||
| * - `set` Item was overwritten by a new value | ||
| * - `expire` Item expired its TTL | ||
| * - `fetch` Item was deleted due to a failed or aborted fetch, or a | ||
| * fetchMethod returning `undefined. | ||
| * - `delete` Item was removed by explicit `cache.delete(key)`, | ||
| * `cache.clear()`, or `cache.set(key, undefined)`. | ||
| */ | ||
| dispose?: Disposer<K, V>; | ||
| /** | ||
| * Function that is called when new items are inserted into the cache, | ||
| * as `onInsert(value, key, reason)`. | ||
| * | ||
| * This can be useful if you need to perform actions when an item is | ||
| * added, such as logging or tracking insertions. | ||
| * | ||
| * Unlike some other options, this may _not_ be overridden by passing | ||
| * an option to `set()`, for performance and consistency reasons. | ||
| */ | ||
| onInsert?: Inserter<K, V>; | ||
| /** | ||
| * The same as {@link OptionsBase.dispose}, but called *after* the entry | ||
| * is completely removed and the cache is once again in a clean state. | ||
| * | ||
| * It is safe to add an item right back into the cache at this point. | ||
| * However, note that it is *very* easy to inadvertently create infinite | ||
| * recursion this way. | ||
| */ | ||
| disposeAfter?: Disposer<K, V>; | ||
| /** | ||
| * Set to true to suppress calling the | ||
| * {@link OptionsBase.dispose} function if the entry key is | ||
| * still accessible within the cache. | ||
| * | ||
| * This may be overridden by passing an options object to | ||
| * {@link LRUCache#set}. | ||
| * | ||
| * Only relevant if `dispose` or `disposeAfter` are set. | ||
| */ | ||
| noDisposeOnSet?: boolean; | ||
| /** | ||
| * Boolean flag to tell the cache to not update the TTL when setting a new | ||
| * value for an existing key (ie, when updating a value rather than | ||
| * inserting a new value). Note that the TTL value is _always_ set (if | ||
| * provided) when adding a new entry into the cache. | ||
| * | ||
| * Has no effect if a {@link OptionsBase.ttl} is not set. | ||
| * | ||
| * May be passed as an option to {@link LRUCache#set}. | ||
| */ | ||
| noUpdateTTL?: boolean; | ||
| /** | ||
| * Set to a positive integer to track the sizes of items added to the | ||
| * cache, and automatically evict items in order to stay below this size. | ||
| * Note that this may result in fewer than `max` items being stored. | ||
| * | ||
| * Attempting to add an item to the cache whose calculated size is greater | ||
| * that this amount will be a no-op. The item will not be cached, and no | ||
| * other items will be evicted. | ||
| * | ||
| * Optional, must be a positive integer if provided. | ||
| * | ||
| * Sets `maxEntrySize` to the same value, unless a different value is | ||
| * provided for `maxEntrySize`. | ||
| * | ||
| * At least one of `max`, `maxSize`, or `TTL` is required. This must be a | ||
| * positive integer if set. | ||
| * | ||
| * Even if size tracking is enabled, **it is strongly recommended to set a | ||
| * `max` to prevent unbounded growth of the cache.** | ||
| * | ||
| * Note also that size tracking can negatively impact performance, | ||
| * though for most cases, only minimally. | ||
| */ | ||
| maxSize?: Size; | ||
| /** | ||
| * The maximum allowed size for any single item in the cache. | ||
| * | ||
| * If a larger item is passed to {@link LRUCache#set} or returned by a | ||
| * {@link OptionsBase.fetchMethod} or {@link OptionsBase.memoMethod}, then | ||
| * it will not be stored in the cache. | ||
| * | ||
| * Attempting to add an item whose calculated size is greater than | ||
| * this amount will not cache the item or evict any old items, but | ||
| * WILL delete an existing value if one is already present. | ||
| * | ||
| * Optional, must be a positive integer if provided. Defaults to | ||
| * the value of `maxSize` if provided. | ||
| */ | ||
| maxEntrySize?: Size; | ||
| /** | ||
| * A function that returns a number indicating the item's size. | ||
| * | ||
| * Requires {@link OptionsBase.maxSize} to be set. | ||
| * | ||
| * If not provided, and {@link OptionsBase.maxSize} or | ||
| * {@link OptionsBase.maxEntrySize} are set, then all | ||
| * {@link LRUCache#set} calls **must** provide an explicit | ||
| * {@link SetOptions.size} or sizeCalculation param. | ||
| */ | ||
| sizeCalculation?: SizeCalculator<K, V>; | ||
| /** | ||
| * Method that provides the implementation for {@link LRUCache#fetch} | ||
| * | ||
| * ```ts | ||
| * fetchMethod(key, staleValue, { signal, options, context }) | ||
| * ``` | ||
| * | ||
| * If `fetchMethod` is not provided, then `cache.fetch(key)` is equivalent | ||
| * to `Promise.resolve(cache.get(key))`. | ||
| * | ||
| * If at any time, `signal.aborted` is set to `true`, or if the | ||
| * `signal.onabort` method is called, or if it emits an `'abort'` event | ||
| * which you can listen to with `addEventListener`, then that means that | ||
| * the fetch should be abandoned. This may be passed along to async | ||
| * functions aware of AbortController/AbortSignal behavior. | ||
| * | ||
| * The `fetchMethod` should **only** return `undefined` or a Promise | ||
| * resolving to `undefined` if the AbortController signaled an `abort` | ||
| * event. In all other cases, it should return or resolve to a value | ||
| * suitable for adding to the cache. | ||
| * | ||
| * The `options` object is a union of the options that may be provided to | ||
| * `set()` and `get()`. If they are modified, then that will result in | ||
| * modifying the settings to `cache.set()` when the value is resolved, and | ||
| * in the case of | ||
| * {@link OptionsBase.noDeleteOnFetchRejection} and | ||
| * {@link OptionsBase.allowStaleOnFetchRejection}, the handling of | ||
| * `fetchMethod` failures. | ||
| * | ||
| * For example, a DNS cache may update the TTL based on the value returned | ||
| * from a remote DNS server by changing `options.ttl` in the `fetchMethod`. | ||
| */ | ||
| fetchMethod?: Fetcher<K, V, FC>; | ||
| /** | ||
| * Method that provides the implementation for {@link LRUCache#memo} | ||
| */ | ||
| memoMethod?: Memoizer<K, V, FC>; | ||
| /** | ||
| * Set to true to suppress the deletion of stale data when a | ||
| * {@link OptionsBase.fetchMethod} returns a rejected promise. | ||
| */ | ||
| noDeleteOnFetchRejection?: boolean; | ||
| /** | ||
| * Do not delete stale items when they are retrieved with | ||
| * {@link LRUCache#get}. | ||
| * | ||
| * Note that the `get` return value will still be `undefined` | ||
| * unless {@link OptionsBase.allowStale} is true. | ||
| * | ||
| * When using time-expiring entries with `ttl`, by default stale | ||
| * items will be removed from the cache when the key is accessed | ||
| * with `cache.get()`. | ||
| * | ||
| * Setting this option will cause stale items to remain in the cache, until | ||
| * they are explicitly deleted with `cache.delete(key)`, or retrieved with | ||
| * `noDeleteOnStaleGet` set to `false`. | ||
| * | ||
| * This may be overridden by passing an options object to `cache.get()`. | ||
| * | ||
| * Only relevant if a ttl is used. | ||
| */ | ||
| noDeleteOnStaleGet?: boolean; | ||
| /** | ||
| * Set to true to allow returning stale data when a | ||
| * {@link OptionsBase.fetchMethod} throws an error or returns a rejected | ||
| * promise. | ||
| * | ||
| * This differs from using {@link OptionsBase.allowStale} in that stale | ||
| * data will ONLY be returned in the case that the {@link LRUCache#fetch} | ||
| * fails, not any other times. | ||
| * | ||
| * If a `fetchMethod` fails, and there is no stale value available, the | ||
| * `fetch()` will resolve to `undefined`. Ie, all `fetchMethod` errors are | ||
| * suppressed. | ||
| * | ||
| * Implies `noDeleteOnFetchRejection`. | ||
| * | ||
| * This may be set in calls to `fetch()`, or defaulted on the constructor, | ||
| * or overridden by modifying the options object in the `fetchMethod`. | ||
| */ | ||
| allowStaleOnFetchRejection?: boolean; | ||
| /** | ||
| * Set to true to return a stale value from the cache when the | ||
| * `AbortSignal` passed to the {@link OptionsBase.fetchMethod} dispatches | ||
| * an `'abort'` event, whether user-triggered, or due to internal cache | ||
| * behavior. | ||
| * | ||
| * Unless {@link OptionsBase.ignoreFetchAbort} is also set, the underlying | ||
| * {@link OptionsBase.fetchMethod} will still be considered canceled, and | ||
| * any value it returns will be ignored and not cached. | ||
| * | ||
| * Caveat: since fetches are aborted when a new value is explicitly | ||
| * set in the cache, this can lead to fetch returning a stale value, | ||
| * since that was the fallback value _at the moment the `fetch()` was | ||
| * initiated_, even though the new updated value is now present in | ||
| * the cache. | ||
| * | ||
| * For example: | ||
| * | ||
| * ```ts | ||
| * const cache = new LRUCache<string, any>({ | ||
| * ttl: 100, | ||
| * fetchMethod: async (url, oldValue, { signal }) => { | ||
| * const res = await fetch(url, { signal }) | ||
| * return await res.json() | ||
| * } | ||
| * }) | ||
| * cache.set('https://example.com/', { some: 'data' }) | ||
| * // 100ms go by... | ||
| * const result = cache.fetch('https://example.com/') | ||
| * cache.set('https://example.com/', { other: 'thing' }) | ||
| * console.log(await result) // { some: 'data' } | ||
| * console.log(cache.get('https://example.com/')) // { other: 'thing' } | ||
| * ``` | ||
| */ | ||
| allowStaleOnFetchAbort?: boolean; | ||
| /** | ||
| * Set to true to ignore the `abort` event emitted by the `AbortSignal` | ||
| * object passed to {@link OptionsBase.fetchMethod}, and still cache the | ||
| * resulting resolution value, as long as it is not `undefined`. | ||
| * | ||
| * When used on its own, this means aborted {@link LRUCache#fetch} calls | ||
| * are not immediately resolved or rejected when they are aborted, and | ||
| * instead take the full time to await. | ||
| * | ||
| * When used with {@link OptionsBase.allowStaleOnFetchAbort}, aborted | ||
| * {@link LRUCache#fetch} calls will resolve immediately to their stale | ||
| * cached value or `undefined`, and will continue to process and eventually | ||
| * update the cache when they resolve, as long as the resulting value is | ||
| * not `undefined`, thus supporting a "return stale on timeout while | ||
| * refreshing" mechanism by passing `AbortSignal.timeout(n)` as the signal. | ||
| * | ||
| * For example: | ||
| * | ||
| * ```ts | ||
| * const c = new LRUCache({ | ||
| * ttl: 100, | ||
| * ignoreFetchAbort: true, | ||
| * allowStaleOnFetchAbort: true, | ||
| * fetchMethod: async (key, oldValue, { signal }) => { | ||
| * // note: do NOT pass the signal to fetch()! | ||
| * // let's say this fetch can take a long time. | ||
| * const res = await fetch(`https://slow-backend-server/${key}`) | ||
| * return await res.json() | ||
| * }, | ||
| * }) | ||
| * | ||
| * // this will return the stale value after 100ms, while still | ||
| * // updating in the background for next time. | ||
| * const val = await c.fetch('key', { signal: AbortSignal.timeout(100) }) | ||
| * ``` | ||
| * | ||
| * **Note**: regardless of this setting, an `abort` event _is still | ||
| * emitted on the `AbortSignal` object_, so may result in invalid results | ||
| * when passed to other underlying APIs that use AbortSignals. | ||
| * | ||
| * This may be overridden in the {@link OptionsBase.fetchMethod} or the | ||
| * call to {@link LRUCache#fetch}. | ||
| */ | ||
| ignoreFetchAbort?: boolean; | ||
| /** | ||
| * In some cases, you may want to swap out the performance/Date object | ||
| * used for TTL tracking. This should almost certainly NOT be done in | ||
| * production environments! | ||
| * | ||
| * This value defaults to `global.performance` if it has a `now()` method, | ||
| * or the `global.Date` object otherwise. | ||
| */ | ||
| perf?: Perf; | ||
| } | ||
| interface OptionsMaxLimit<K, V, FC> extends OptionsBase<K, V, FC> { | ||
| max: Count; | ||
| } | ||
| interface OptionsTTLLimit<K, V, FC> extends OptionsBase<K, V, FC> { | ||
| ttl: Milliseconds; | ||
| ttlAutopurge: boolean; | ||
| } | ||
| interface OptionsSizeLimit<K, V, FC> extends OptionsBase<K, V, FC> { | ||
| maxSize: Size; | ||
| } | ||
| /** | ||
| * The valid safe options for the {@link LRUCache} constructor | ||
| */ | ||
| type Options<K, V, FC> = OptionsMaxLimit<K, V, FC> | OptionsSizeLimit<K, V, FC> | OptionsTTLLimit<K, V, FC>; | ||
| /** | ||
| * Entry objects used by {@link LRUCache#load} and {@link LRUCache#dump}, | ||
| * and returned by {@link LRUCache#info}. | ||
| */ | ||
| interface Entry<V> { | ||
| value: V; | ||
| ttl?: Milliseconds; | ||
| size?: Size; | ||
| start?: Milliseconds; | ||
| } | ||
| } | ||
| /** | ||
| * 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 declare class LRUCache<K extends {}, V extends {}, FC = unknown> { | ||
| #private; | ||
| /** | ||
| * {@link LRUCache.OptionsBase.perf} | ||
| */ | ||
| get perf(): Perf; | ||
| /** | ||
| * {@link LRUCache.OptionsBase.ttl} | ||
| */ | ||
| ttl: LRUCache.Milliseconds; | ||
| /** | ||
| * {@link LRUCache.OptionsBase.ttlResolution} | ||
| */ | ||
| ttlResolution: LRUCache.Milliseconds; | ||
| /** | ||
| * {@link LRUCache.OptionsBase.ttlAutopurge} | ||
| */ | ||
| ttlAutopurge: boolean; | ||
| /** | ||
| * {@link LRUCache.OptionsBase.updateAgeOnGet} | ||
| */ | ||
| updateAgeOnGet: boolean; | ||
| /** | ||
| * {@link LRUCache.OptionsBase.updateAgeOnHas} | ||
| */ | ||
| updateAgeOnHas: boolean; | ||
| /** | ||
| * {@link LRUCache.OptionsBase.allowStale} | ||
| */ | ||
| allowStale: boolean; | ||
| /** | ||
| * {@link LRUCache.OptionsBase.noDisposeOnSet} | ||
| */ | ||
| noDisposeOnSet: boolean; | ||
| /** | ||
| * {@link LRUCache.OptionsBase.noUpdateTTL} | ||
| */ | ||
| noUpdateTTL: boolean; | ||
| /** | ||
| * {@link LRUCache.OptionsBase.maxEntrySize} | ||
| */ | ||
| maxEntrySize: LRUCache.Size; | ||
| /** | ||
| * {@link LRUCache.OptionsBase.sizeCalculation} | ||
| */ | ||
| sizeCalculation?: LRUCache.SizeCalculator<K, V>; | ||
| /** | ||
| * {@link LRUCache.OptionsBase.noDeleteOnFetchRejection} | ||
| */ | ||
| noDeleteOnFetchRejection: boolean; | ||
| /** | ||
| * {@link LRUCache.OptionsBase.noDeleteOnStaleGet} | ||
| */ | ||
| noDeleteOnStaleGet: boolean; | ||
| /** | ||
| * {@link LRUCache.OptionsBase.allowStaleOnFetchAbort} | ||
| */ | ||
| allowStaleOnFetchAbort: boolean; | ||
| /** | ||
| * {@link LRUCache.OptionsBase.allowStaleOnFetchRejection} | ||
| */ | ||
| allowStaleOnFetchRejection: boolean; | ||
| /** | ||
| * {@link LRUCache.OptionsBase.ignoreFetchAbort} | ||
| */ | ||
| ignoreFetchAbort: boolean; | ||
| /** | ||
| * 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<K extends {}, V extends {}, FC extends unknown = unknown>(c: LRUCache<K, V, FC>): { | ||
| starts: ZeroArray | undefined; | ||
| ttls: ZeroArray | undefined; | ||
| autopurgeTimers: (NodeJS.Timeout | undefined)[] | undefined; | ||
| sizes: ZeroArray | undefined; | ||
| keyMap: Map<K, number>; | ||
| keyList: (K | undefined)[]; | ||
| valList: (V | BackgroundFetch<V> | undefined)[]; | ||
| next: NumberArray; | ||
| prev: NumberArray; | ||
| readonly head: Index; | ||
| readonly tail: Index; | ||
| free: StackLike; | ||
| isBackgroundFetch: (p: unknown) => p is BackgroundFetch<V>; | ||
| backgroundFetch: (k: K, index: number | undefined, options: LRUCache.FetchOptions<K, V, FC>, context: unknown) => BackgroundFetch<V>; | ||
| moveToTail: (index: number) => void; | ||
| indexes: (options?: { | ||
| allowStale: boolean; | ||
| }) => Generator<Index, void, unknown>; | ||
| rindexes: (options?: { | ||
| allowStale: boolean; | ||
| }) => Generator<Index, void, unknown>; | ||
| isStale: (index: number | undefined) => boolean; | ||
| }; | ||
| /** | ||
| * {@link LRUCache.OptionsBase.max} (read-only) | ||
| */ | ||
| get max(): LRUCache.Count; | ||
| /** | ||
| * {@link LRUCache.OptionsBase.maxSize} (read-only) | ||
| */ | ||
| get maxSize(): LRUCache.Count; | ||
| /** | ||
| * The total computed size of items in the cache (read-only) | ||
| */ | ||
| get calculatedSize(): LRUCache.Size; | ||
| /** | ||
| * The number of items stored in the cache (read-only) | ||
| */ | ||
| get size(): LRUCache.Count; | ||
| /** | ||
| * {@link LRUCache.OptionsBase.fetchMethod} (read-only) | ||
| */ | ||
| get fetchMethod(): LRUCache.Fetcher<K, V, FC> | undefined; | ||
| get memoMethod(): LRUCache.Memoizer<K, V, FC> | undefined; | ||
| /** | ||
| * {@link LRUCache.OptionsBase.dispose} (read-only) | ||
| */ | ||
| get dispose(): LRUCache.Disposer<K, V> | undefined; | ||
| /** | ||
| * {@link LRUCache.OptionsBase.onInsert} (read-only) | ||
| */ | ||
| get onInsert(): LRUCache.Inserter<K, V> | undefined; | ||
| /** | ||
| * {@link LRUCache.OptionsBase.disposeAfter} (read-only) | ||
| */ | ||
| get disposeAfter(): LRUCache.Disposer<K, V> | undefined; | ||
| constructor(options: LRUCache.Options<K, V, FC> | LRUCache<K, V, FC>); | ||
| /** | ||
| * 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: K): number; | ||
| /** | ||
| * Return a generator yielding `[key, value]` pairs, | ||
| * in order from most recently used to least recently used. | ||
| */ | ||
| entries(): Generator<[K, V], void, unknown>; | ||
| /** | ||
| * 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(): Generator<(K | V)[], void, unknown>; | ||
| /** | ||
| * Return a generator yielding the keys in the cache, | ||
| * in order from most recently used to least recently used. | ||
| */ | ||
| keys(): Generator<K, void, unknown>; | ||
| /** | ||
| * 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(): Generator<K, void, unknown>; | ||
| /** | ||
| * Return a generator yielding the values in the cache, | ||
| * in order from most recently used to least recently used. | ||
| */ | ||
| values(): Generator<V, void, unknown>; | ||
| /** | ||
| * 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(): Generator<V | undefined, void, unknown>; | ||
| /** | ||
| * Iterating over the cache itself yields the same results as | ||
| * {@link LRUCache.entries} | ||
| */ | ||
| [Symbol.iterator](): Generator<[K, V], void, unknown>; | ||
| /** | ||
| * 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]: string; | ||
| /** | ||
| * 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: (v: V, k: K, self: LRUCache<K, V, FC>) => boolean, getOptions?: LRUCache.GetOptions<K, V, FC>): V | undefined; | ||
| /** | ||
| * 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: (v: V, k: K, self: LRUCache<K, V, FC>) => unknown, thisp?: unknown): void; | ||
| /** | ||
| * 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: (v: V, k: K, self: LRUCache<K, V, FC>) => unknown, thisp?: unknown): void; | ||
| /** | ||
| * Delete any stale entries. Returns true if anything was removed, | ||
| * false otherwise. | ||
| */ | ||
| purgeStale(): boolean; | ||
| /** | ||
| * 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: K): LRUCache.Entry<V> | undefined; | ||
| /** | ||
| * 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(): [K, LRUCache.Entry<V>][]; | ||
| /** | ||
| * 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: [K, LRUCache.Entry<V>][]): void; | ||
| /** | ||
| * 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: K, v: V | undefined, setOptions?: LRUCache.SetOptions<K, V, FC>): this; | ||
| /** | ||
| * Evict the least recently used item, returning its value or | ||
| * `undefined` if cache is empty. | ||
| */ | ||
| pop(): V | undefined; | ||
| /** | ||
| * 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: K, hasOptions?: LRUCache.HasOptions<K, V, FC>): boolean; | ||
| /** | ||
| * 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: K, peekOptions?: LRUCache.PeekOptions<K, V, FC>): V | undefined; | ||
| /** | ||
| * Make an asynchronous cached fetch using the | ||
| * {@link LRUCache.OptionsBase.fetchMethod} function. | ||
| * | ||
| * If the value is in the cache and not stale, then the returned | ||
| * Promise resolves to the value. | ||
| * | ||
| * If not in the cache, or beyond its TTL staleness, then | ||
| * `fetchMethod(key, staleValue, { options, signal, context })` is | ||
| * called, and the value returned will be added to the cache once | ||
| * resolved. | ||
| * | ||
| * If called with `allowStale`, and an asynchronous fetch is | ||
| * currently in progress to reload a stale value, then the former | ||
| * stale value will be returned. | ||
| * | ||
| * If called with `forceRefresh`, then the cached item will be | ||
| * re-fetched, even if it is not stale. However, if `allowStale` is also | ||
| * set, then the old value will still be returned. This is useful | ||
| * in cases where you want to force a reload of a cached value. If | ||
| * a background fetch is already in progress, then `forceRefresh` | ||
| * has no effect. | ||
| * | ||
| * If multiple fetches for the same key are issued, then they will all be | ||
| * coalesced into a single call to fetchMethod. | ||
| * | ||
| * Note that this means that handling options such as | ||
| * {@link LRUCache.OptionsBase.allowStaleOnFetchAbort}, | ||
| * {@link LRUCache.FetchOptions.signal}, | ||
| * and {@link LRUCache.OptionsBase.allowStaleOnFetchRejection} will be | ||
| * determined by the FIRST fetch() call for a given key. | ||
| * | ||
| * This is a known (fixable) shortcoming which will be addresed on when | ||
| * someone complains about it, as the fix would involve added complexity and | ||
| * may not be worth the costs for this edge case. | ||
| * | ||
| * If {@link LRUCache.OptionsBase.fetchMethod} is not specified, then this is | ||
| * effectively an alias for `Promise.resolve(cache.get(key))`. | ||
| * | ||
| * When the fetch method resolves to a value, if the fetch has not | ||
| * been aborted due to deletion, eviction, or being overwritten, | ||
| * then it is added to the cache using the options provided. | ||
| * | ||
| * If the key is evicted or deleted before the `fetchMethod` | ||
| * resolves, then the AbortSignal passed to the `fetchMethod` will | ||
| * receive an `abort` event, and the promise returned by `fetch()` | ||
| * will reject with the reason for the abort. | ||
| * | ||
| * If a `signal` is passed to the `fetch()` call, then aborting the | ||
| * signal will abort the fetch and cause the `fetch()` promise to | ||
| * reject with the reason provided. | ||
| * | ||
| * **Setting `context`** | ||
| * | ||
| * If an `FC` type is set to a type other than `unknown`, `void`, or | ||
| * `undefined` in the {@link LRUCache} constructor, then all | ||
| * calls to `cache.fetch()` _must_ provide a `context` option. If | ||
| * set to `undefined` or `void`, then calls to fetch _must not_ | ||
| * provide a `context` option. | ||
| * | ||
| * The `context` param allows you to provide arbitrary data that | ||
| * might be relevant in the course of fetching the data. It is only | ||
| * relevant for the course of a single `fetch()` operation, and | ||
| * discarded afterwards. | ||
| * | ||
| * **Note: `fetch()` calls are inflight-unique** | ||
| * | ||
| * If you call `fetch()` multiple times with the same key value, | ||
| * then every call after the first will resolve on the same | ||
| * promise<sup>1</sup>, | ||
| * _even if they have different settings that would otherwise change | ||
| * the behavior of the fetch_, such as `noDeleteOnFetchRejection` | ||
| * or `ignoreFetchAbort`. | ||
| * | ||
| * In most cases, this is not a problem (in fact, only fetching | ||
| * something once is what you probably want, if you're caching in | ||
| * the first place). If you are changing the fetch() options | ||
| * dramatically between runs, there's a good chance that you might | ||
| * be trying to fit divergent semantics into a single object, and | ||
| * would be better off with multiple cache instances. | ||
| * | ||
| * **1**: Ie, they're not the "same Promise", but they resolve at | ||
| * the same time, because they're both waiting on the same | ||
| * underlying fetchMethod response. | ||
| */ | ||
| fetch(k: K, fetchOptions: unknown extends FC ? LRUCache.FetchOptions<K, V, FC> : FC extends undefined | void ? LRUCache.FetchOptionsNoContext<K, V> : LRUCache.FetchOptionsWithContext<K, V, FC>): Promise<undefined | V>; | ||
| fetch(k: unknown extends FC ? K : FC extends undefined | void ? K : never, fetchOptions?: unknown extends FC ? LRUCache.FetchOptions<K, V, FC> : FC extends undefined | void ? LRUCache.FetchOptionsNoContext<K, V> : never): Promise<undefined | V>; | ||
| /** | ||
| * In some cases, `cache.fetch()` may resolve to `undefined`, either because | ||
| * a {@link LRUCache.OptionsBase#fetchMethod} was not provided (turning | ||
| * `cache.fetch(k)` into just an async wrapper around `cache.get(k)`) or | ||
| * because `ignoreFetchAbort` was specified (either to the constructor or | ||
| * in the {@link LRUCache.FetchOptions}). Also, the | ||
| * {@link LRUCache.OptionsBase.fetchMethod} may return `undefined` or `void`, making | ||
| * the test even more complicated. | ||
| * | ||
| * Because inferring the cases where `undefined` might be returned are so | ||
| * cumbersome, but testing for `undefined` can also be annoying, this method | ||
| * can be used, which will reject if `this.fetch()` resolves to undefined. | ||
| */ | ||
| forceFetch(k: K, fetchOptions: unknown extends FC ? LRUCache.FetchOptions<K, V, FC> : FC extends undefined | void ? LRUCache.FetchOptionsNoContext<K, V> : LRUCache.FetchOptionsWithContext<K, V, FC>): Promise<V>; | ||
| forceFetch(k: unknown extends FC ? K : FC extends undefined | void ? K : never, fetchOptions?: unknown extends FC ? LRUCache.FetchOptions<K, V, FC> : FC extends undefined | void ? LRUCache.FetchOptionsNoContext<K, V> : never): Promise<V>; | ||
| /** | ||
| * If the key is found in the cache, then this is equivalent to | ||
| * {@link LRUCache#get}. If not, in the cache, then calculate the value using | ||
| * the {@link LRUCache.OptionsBase.memoMethod}, and add it to the cache. | ||
| * | ||
| * If an `FC` type is set to a type other than `unknown`, `void`, or | ||
| * `undefined` in the LRUCache constructor, then all calls to `cache.memo()` | ||
| * _must_ provide a `context` option. If set to `undefined` or `void`, then | ||
| * calls to memo _must not_ provide a `context` option. | ||
| * | ||
| * The `context` param allows you to provide arbitrary data that might be | ||
| * relevant in the course of fetching the data. It is only relevant for the | ||
| * course of a single `memo()` operation, and discarded afterwards. | ||
| */ | ||
| memo(k: K, memoOptions: unknown extends FC ? LRUCache.MemoOptions<K, V, FC> : FC extends undefined | void ? LRUCache.MemoOptionsNoContext<K, V> : LRUCache.MemoOptionsWithContext<K, V, FC>): V; | ||
| memo(k: unknown extends FC ? K : FC extends undefined | void ? K : never, memoOptions?: unknown extends FC ? LRUCache.MemoOptions<K, V, FC> : FC extends undefined | void ? LRUCache.MemoOptionsNoContext<K, V> : never): V; | ||
| /** | ||
| * 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: K, getOptions?: LRUCache.GetOptions<K, V, FC>): V | undefined; | ||
| /** | ||
| * Deletes a key out of the cache. | ||
| * | ||
| * Returns true if the key was deleted, false otherwise. | ||
| */ | ||
| delete(k: K): boolean; | ||
| /** | ||
| * Clear the cache entirely, throwing away all values. | ||
| */ | ||
| clear(): void; | ||
| } | ||
| //# sourceMappingURL=index.d.ts.map |
| {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"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"} |
| /** | ||
| * @module LRUCache | ||
| */ | ||
| import { metrics, tracing } from './diagnostics-channel.js'; | ||
| const hasSubscribers = () => metrics.hasSubscribers || tracing.hasSubscribers; | ||
| 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 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 { | ||
| 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 = 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) => { | ||
| // 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; 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; | ||
| } | ||
| const result = this.#set(k, v, setOptions); | ||
| if (status && metrics.hasSubscribers) { | ||
| metrics.publish(status); | ||
| } | ||
| return result; | ||
| } | ||
| #set(k, v, setOptions = {}) { | ||
| const { ttl = this.ttl, start, noDisposeOnSet = this.noDisposeOnSet, sizeCalculation = this.sizeCalculation, status, } = setOptions; | ||
| if (v === undefined) { | ||
| if (status) | ||
| status.set = 'deleted'; | ||
| this.delete(k); | ||
| return this; | ||
| } | ||
| let { noUpdateTTL = this.noUpdateTTL } = setOptions; | ||
| if (status && !this.#isBackgroundFetch(v)) | ||
| 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) { | ||
| 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 { status = metrics.hasSubscribers ? {} : undefined } = hasOptions; | ||
| hasOptions.status = status; | ||
| if (status) { | ||
| status.op = 'has'; | ||
| status.key = k; | ||
| } | ||
| 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; | ||
| } | ||
| 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); | ||
| } | ||
| } | ||
| 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 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 && hasSubscribers()) { | ||
| if (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; | ||
| } | ||
| 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 && hasSubscribers()) { | ||
| if (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; | ||
| } | ||
| } | ||
| 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; | ||
| } | ||
| 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, | ||
| }); | ||
| } | ||
| 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 |
Sorry, the diff of this file is too big to display
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"diagnostics-channel-esm.d.mts","sourceRoot":"","sources":["../../src/diagnostics-channel-esm.mts"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,OAAO,EACZ,KAAK,cAAc,EACpB,MAAM,0BAA0B,CAAA;AACjC,YAAY,EAAE,cAAc,EAAE,OAAO,EAAE,CAAA;AAavC,eAAO,IAAI,OAAO,EAAY,OAAO,CAAC,OAAO,CAAC,CAAA;AAC9C,eAAO,IAAI,OAAO,EAAY,cAAc,CAAC,OAAO,CAAC,CAAA"} | ||
| {"version":3,"file":"diagnostics-channel-esm.d.mts","sourceRoot":"","sources":["../../src/diagnostics-channel-esm.mts"],"names":[],"mappings":"AAGA,OAAO,EACL,KAAK,OAAO,EACZ,KAAK,cAAc,EACpB,MAAM,0BAA0B,CAAA;AACjC,YAAY,EAAE,cAAc,EAAE,OAAO,EAAE,CAAA;AAavC,eAAO,IAAI,OAAO,EAAY,OAAO,CAAC,OAAO,CAAC,CAAA;AAC9C,eAAO,IAAI,OAAO,EAAY,cAAc,CAAC,OAAO,CAAC,CAAA"} |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"diagnostics-channel-esm.mjs","sourceRoot":"","sources":["../../src/diagnostics-channel-esm.mts"],"names":[],"mappings":"AAQA;;;;;GAKG;AAEH,uEAAuE;AACvE,4EAA4E;AAC5E,oBAAoB;AACpB,MAAM,KAAK,GAAG,EAAE,cAAc,EAAE,KAAK,EAAE,CAAA;AACvC,MAAM,CAAC,IAAI,OAAO,GAAG,KAAyB,CAAA;AAC9C,MAAM,CAAC,IAAI,OAAO,GAAG,KAAgC,CAAA;AACrD,MAAM,CAAC,0BAA0B,CAAC;KAC/B,IAAI,CAAC,EAAE,CAAC,EAAE;IACT,OAAO,GAAG,EAAE,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAA;IACzC,OAAO,GAAG,EAAE,CAAC,cAAc,CAAC,WAAW,CAAC,CAAA;AAC1C,CAAC,CAAC;KACD,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA","sourcesContent":["// this is used in ESM environments where top level await is allowed,\n// but node:diagnostics_channel might not be present, such as browsers.\nimport {\n type Channel,\n type TracingChannel,\n} from 'node:diagnostics_channel'\nexport type { TracingChannel, Channel }\n\n/**\n * no-op polyfills for non-node environments. tries to load the actual\n * diagnostics_channel module on platforms that support it, but fails\n * gracefully if not found. This means that the first tick of metrics\n * and tracing will be missed, but that probably doesn't matter much.\n */\n\n// conditionally import from diagnostic_channel, fall back to dummyfill\n// all we actually have to mock is the hasSubscribers, since we alwasy check\n/* v8 ignore next */\nconst dummy = { hasSubscribers: false }\nexport let metrics = dummy as Channel<unknown>\nexport let tracing = dummy as TracingChannel<unknown>\nimport('node:diagnostics_channel')\n .then(dc => {\n metrics = dc.channel('lru-cache:metrics')\n tracing = dc.tracingChannel('lru-cache')\n })\n .catch(() => {})\n"]} | ||
| {"version":3,"file":"diagnostics-channel-esm.mjs","sourceRoot":"","sources":["../../src/diagnostics-channel-esm.mts"],"names":[],"mappings":"AASA;;;;;GAKG;AAEH,uEAAuE;AACvE,4EAA4E;AAC5E,oBAAoB;AACpB,MAAM,KAAK,GAAG,EAAE,cAAc,EAAE,KAAK,EAAE,CAAA;AACvC,MAAM,CAAC,IAAI,OAAO,GAAG,KAAyB,CAAA;AAC9C,MAAM,CAAC,IAAI,OAAO,GAAG,KAAgC,CAAA;AACrD,MAAM,CAAC,0BAA0B,CAAC;KAC/B,IAAI,CAAC,EAAE,CAAC,EAAE;IACT,OAAO,GAAG,EAAE,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAA;IACzC,OAAO,GAAG,EAAE,CAAC,cAAc,CAAC,WAAW,CAAC,CAAA;AAC1C,CAAC,CAAC;KACD,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA","sourcesContent":["// this is used in ESM environments that do not follow the 'node' import\n// condition. So, `node:diagnostics_channel` MAY be present, but might not.\n// Eg: browsers, webpack, react-native, etc.\nimport {\n type Channel,\n type TracingChannel,\n} from 'node:diagnostics_channel'\nexport type { TracingChannel, Channel }\n\n/**\n * no-op polyfills for non-node environments. tries to load the actual\n * diagnostics_channel module on platforms that support it, but fails\n * gracefully if not found. This means that the first tick of metrics\n * and tracing will be missed, but that probably doesn't matter much.\n */\n\n// conditionally import from diagnostic_channel, fall back to dummyfill\n// all we actually have to mock is the hasSubscribers, since we always check\n/* v8 ignore next */\nconst dummy = { hasSubscribers: false }\nexport let metrics = dummy as Channel<unknown>\nexport let tracing = dummy as TracingChannel<unknown>\nimport('node:diagnostics_channel')\n .then(dc => {\n metrics = dc.channel('lru-cache:metrics')\n tracing = dc.tracingChannel('lru-cache')\n })\n .catch(() => {})\n"]} |
@@ -8,3 +8,3 @@ /** | ||
| // conditionally import from diagnostic_channel, fall back to dummyfill | ||
| // all we actually have to mock is the hasSubscribers, since we alwasy check | ||
| // all we actually have to mock is the hasSubscribers, since we always check | ||
| /* v8 ignore next */ | ||
@@ -11,0 +11,0 @@ const dummy = { hasSubscribers: false }; |
+7
-2
| { | ||
| "name": "lru-cache", | ||
| "description": "A cache object that deletes the least-recently-used items.", | ||
| "version": "11.3.3", | ||
| "version": "11.3.4", | ||
| "author": "Isaac Z. Schlueter <i@izs.me>", | ||
@@ -37,3 +37,4 @@ "keywords": [ | ||
| "esmDialects": [ | ||
| "node" | ||
| "node", | ||
| "browser" | ||
| ], | ||
@@ -89,2 +90,6 @@ "exports": { | ||
| }, | ||
| "browser": { | ||
| "types": "./dist/esm/browser/index.d.ts", | ||
| "default": "./dist/esm/browser/index.js" | ||
| }, | ||
| "types": "./dist/esm/index.d.ts", | ||
@@ -91,0 +96,0 @@ "default": "./dist/esm/index.js" |
Sorry, the diff of this file is too big to display
Debug access
Supply chain riskUses debug, reflection and dynamic code execution features.
1632092
21.54%43
22.86%12557
32.47%5
25%66
22.22%