feat(planning): grille hebdomadaire complète avec API et filtres
- Connexion API via proxy Angular (résolution CORS, base path /api) - Import CSS ng-zorro global pour les modales et composants - Filtres Camion/Show câblés sur l'affichage de la grille - Camions affichés via TrucksService (linkés au show du même créneau) - Panneau de détails : spectacles + camions du jour sélectionné - Modale de création de spectacle stylisée avec fond et centrage - Positionnement précis des events à la minute dans leur créneau - Auto-scroll vers l'heure courante au chargement - Ligne "maintenant" sur la colonne du jour actuel - Régénération des services OpenAPI (nouveaux noms de types) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+2
-2
@@ -110,7 +110,7 @@ hosts.gitlab = {
|
||||
blobpath: 'tree',
|
||||
editpath: '-/edit',
|
||||
tarballtemplate: ({ domain, user, project, committish }) =>
|
||||
`https://${domain}/api/v4/projects/${maybeEncode(user + '/' + project)}/repository/archive.tar.gz?sha=${maybeEncode(committish || 'HEAD')}`,
|
||||
`https://${domain}/${user}/${project}/repository/archive.tar.gz?ref=${maybeEncode(committish || 'HEAD')}`,
|
||||
extract: (url) => {
|
||||
const path = url.pathname.slice(1)
|
||||
if (path.includes('/-/') || path.includes('/archive.tar.gz')) {
|
||||
@@ -198,7 +198,7 @@ hosts.sourcehut = {
|
||||
filetemplate: ({ domain, user, project, committish, path }) =>
|
||||
`https://${domain}/${user}/${project}/blob/${maybeEncode(committish) || 'HEAD'}/${path}`,
|
||||
httpstemplate: ({ domain, user, project, committish }) =>
|
||||
`https://${domain}/${user}/${project}${maybeJoin('#', committish)}`,
|
||||
`https://${domain}/${user}/${project}.git${maybeJoin('#', committish)}`,
|
||||
tarballtemplate: ({ domain, user, project, committish }) =>
|
||||
`https://${domain}/${user}/${project}/archive/${maybeEncode(committish) || 'HEAD'}.tar.gz`,
|
||||
bugstemplate: () => null,
|
||||
|
||||
+15
-146
@@ -79,7 +79,11 @@ const options = {
|
||||
|
||||
// async method to use for cache.fetch(), for
|
||||
// stale-while-revalidate type of behavior
|
||||
fetchMethod: async (key, staleValue, { options, signal, context }) => {},
|
||||
fetchMethod: async (
|
||||
key,
|
||||
staleValue,
|
||||
{ options, signal, context },
|
||||
) => {},
|
||||
}
|
||||
|
||||
const cache = new LRUCache(options)
|
||||
@@ -215,95 +219,9 @@ const myGet = (key, value) => {
|
||||
}
|
||||
```
|
||||
|
||||
## Tracing and Observability
|
||||
|
||||
Most methods can accept a `status` option, which is an
|
||||
[`LRUCache.Status`](https://isaacs.github.io/node-lru-cache/interfaces/LRUCache.LRUCache.Status.html)
|
||||
object that will be decorated along the operation with
|
||||
indications about what was done and why.
|
||||
|
||||
Additionally, this library is instrumented using the
|
||||
[`node:diagnostics_channel`](https://nodejs.org/api/diagnostics_channel.html)
|
||||
module on Node and other platforms that support it. In order to
|
||||
get diagnostics metrics, listen on the
|
||||
`channel('lru-cache:metrics')`. To get Tracing Channel traces,
|
||||
subscribe to the `tracingChannel('lru-cache')`. The
|
||||
[`LRUCache.Status`](https://isaacs.github.io/node-lru-cache/interfaces/LRUCache.LRUCache.Status.html)
|
||||
objects will be provided as the message context to those channel
|
||||
listeners.
|
||||
|
||||
For example, you could do the following to get comprehensive
|
||||
information about every LRUCache instance in your application:
|
||||
|
||||
```ts
|
||||
import { tracingChannel, subscribe } from 'node:diagnostics_channel'
|
||||
|
||||
subscribe('lru-cache:metrics', (message, name) => {
|
||||
// name will always be 'lru-cache:metrics'
|
||||
// message will be the LRUCache.Status object for whatever
|
||||
// synchronous operation was performed.
|
||||
console.error('LRUCache Metrics', message)
|
||||
})
|
||||
|
||||
tracingChannel('lru-cache').subscribe({
|
||||
start: status => {
|
||||
// a traced operation is starting
|
||||
},
|
||||
asyncStart: status => {
|
||||
// an async traced operation is starting
|
||||
},
|
||||
asyncEnd: status => {
|
||||
// an async traced operation is ending
|
||||
}
|
||||
error: status => {
|
||||
// a traced operation failed
|
||||
},
|
||||
end: status => {
|
||||
// a traced operation is complete
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
The async `cache.fetch()` and `cache.forceFetch` methods are
|
||||
covered by `tracingChannels`. All the other operations are
|
||||
covered by the `lru-cache:metrics` channel, because they are
|
||||
strictly synchronous, and thus don't have an asynchronous
|
||||
lifecycle to track.
|
||||
|
||||
Note that using `status` objects or using
|
||||
`node:diagnostics_channel` listeners _will_ impose a modest
|
||||
performance penalty. Creating data objects is not ever free; do
|
||||
not believe anyone who tells you otherwise. But it is as small as
|
||||
possible.
|
||||
|
||||
### Platform Compatibility Caveat
|
||||
|
||||
Not all platforms support the `node:diagnostics_channel` module.
|
||||
Currently, this is only available in Node, Bun, and Deno, and
|
||||
some edge computing platforms that provide a Node compatibility
|
||||
layer.
|
||||
|
||||
To work around this, if you are loading in a non-Node
|
||||
environment, the package.json exports will direct your module
|
||||
loader to pull in a version that starts out with a dummy
|
||||
implementation, then does a conditional dynamic `import` of the
|
||||
`node:diagnostics_channel` module, and then swaps out those
|
||||
dummy objects with the real thing if it succeeds. This means that
|
||||
cache metrics and tracing channels started in the first load-time
|
||||
tick of your application will _not_ be covered, except in
|
||||
environments that load using the `require` import
|
||||
condition, or both the `node` and `esm` import conditions
|
||||
together.
|
||||
|
||||
Top-level await _could_ be used to remove this caveat, but that
|
||||
feature is dead on arrival, unfortunately. See
|
||||
[#397](https://github.com/isaacs/node-lru-cache/issues/397) and
|
||||
[#398](https://github.com/isaacs/node-lru-cache/issues/398) for
|
||||
more details.
|
||||
|
||||
## Performance
|
||||
|
||||
As of April 2026, version 11 of this library is one of the most
|
||||
As of January 2022, version 7 of this library is one of the most
|
||||
performant LRU cache implementations in JavaScript.
|
||||
|
||||
Benchmarks can be extremely difficult to get right. In
|
||||
@@ -358,9 +276,9 @@ If performance matters to you:
|
||||
LRUCache](https://yomguithereal.github.io/mnemonist/lru-cache)
|
||||
which uses an Object as its data store.
|
||||
|
||||
2. Failing that, if you can use short non-numeric strings (ie,
|
||||
less than 256 characters) as your keys, and you do not need
|
||||
any of the other features of this library, use [mnemonist's
|
||||
2. Failing that, if at all possible, use short non-numeric
|
||||
strings (ie, less than 256 characters) as your keys, and use
|
||||
[mnemonist's
|
||||
LRUCache](https://yomguithereal.github.io/mnemonist/lru-cache).
|
||||
|
||||
3. If the types of your keys will be anything else, especially
|
||||
@@ -372,63 +290,14 @@ If performance matters to you:
|
||||
(like asynchronous fetching, a variety of TTL staleness
|
||||
options, and so on), then [mnemonist's
|
||||
LRUMap](https://yomguithereal.github.io/mnemonist/lru-map) is
|
||||
also a very good option, and just slightly faster than this
|
||||
module (since it does considerably less).
|
||||
a very good option, and just slightly faster than this module
|
||||
(since it does considerably less).
|
||||
|
||||
4. Do not use a `dispose` function, size tracking, or especially
|
||||
ttl behavior or observability features, unless absolutely
|
||||
needed. These features are convenient, and necessary in some
|
||||
use cases, and every attempt has been made to make the
|
||||
performance impact minimal, but it isn't nothing.
|
||||
|
||||
## Testing
|
||||
|
||||
When writing tests that involve TTL-related functionality, note
|
||||
that this module creates an internal reference to the global
|
||||
`performance` or `Date` objects at import time. If you import it
|
||||
statically at the top level, those references cannot be mocked or
|
||||
overridden in your test environment.
|
||||
|
||||
To avoid this, dynamically import the package within your tests
|
||||
so that the references are captured after your mocks are applied.
|
||||
For example:
|
||||
|
||||
```ts
|
||||
// ❌ Not recommended
|
||||
import { LRUCache } from 'lru-cache'
|
||||
// mocking timers, e.g. jest.useFakeTimers()
|
||||
|
||||
// ✅ Recommended for TTL tests
|
||||
// mocking timers, e.g. jest.useFakeTimers()
|
||||
const { LRUCache } = await import('lru-cache')
|
||||
```
|
||||
|
||||
This ensures that your mocked timers or time sources are
|
||||
respected when testing TTL behavior.
|
||||
|
||||
Additionally, you can pass in a `perf` option when creating your
|
||||
LRUCache instance. This option accepts any object with a `now`
|
||||
method that returns a number.
|
||||
|
||||
For example, this would be a very bare-bones time-mocking system
|
||||
you could use in your tests, without any particular test
|
||||
framework:
|
||||
|
||||
```ts
|
||||
import { LRUCache } from 'lru-cache'
|
||||
|
||||
let myClockTime = 0
|
||||
|
||||
const cache = new LRUCache<string>({
|
||||
max: 10,
|
||||
ttl: 1000,
|
||||
perf: {
|
||||
now: () => myClockTime,
|
||||
},
|
||||
})
|
||||
|
||||
// run tests, updating myClockTime as needed
|
||||
```
|
||||
ttl behavior, unless absolutely needed. These features are
|
||||
convenient, and necessary in some use cases, and every attempt
|
||||
has been made to make the performance impact minimal, but it
|
||||
isn't nothing.
|
||||
|
||||
## Breaking Changes in Version 7
|
||||
|
||||
|
||||
+30
-108
@@ -1,8 +1,9 @@
|
||||
/**
|
||||
* @module LRUCache
|
||||
*/
|
||||
import type { Perf } from './perf.js';
|
||||
export type { Perf } from './perf.js';
|
||||
export type Perf = {
|
||||
now: () => number;
|
||||
};
|
||||
declare const TYPE: unique symbol;
|
||||
export type PosInt = number & {
|
||||
[TYPE]: 'Positive Integer';
|
||||
@@ -118,16 +119,8 @@ export declare namespace LRUCache {
|
||||
*
|
||||
* 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, FC = unknown> {
|
||||
/**
|
||||
* The operation being performed
|
||||
*/
|
||||
op?: 'get' | 'set' | 'memo' | 'fetch' | 'delete' | 'has' | 'peek';
|
||||
interface Status<V> {
|
||||
/**
|
||||
* The status of a set() operation.
|
||||
*
|
||||
@@ -136,37 +129,7 @@ export declare namespace LRUCache {
|
||||
* - 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;
|
||||
set?: 'add' | 'update' | 'replace' | 'miss';
|
||||
/**
|
||||
* the ttl stored for the item, or undefined if ttls are not used.
|
||||
*/
|
||||
@@ -197,15 +160,8 @@ export declare namespace LRUCache {
|
||||
*/
|
||||
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'`
|
||||
* The old value, specified in the case of `set:'update'` or
|
||||
* `set:'replace'`
|
||||
*/
|
||||
oldValue?: V;
|
||||
/**
|
||||
@@ -231,10 +187,6 @@ export declare namespace LRUCache {
|
||||
* {@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
|
||||
*/
|
||||
@@ -256,7 +208,7 @@ export declare namespace LRUCache {
|
||||
fetchAborted?: true;
|
||||
/**
|
||||
* The abort signal received was ignored, and the fetch was allowed to
|
||||
* continue in the background.
|
||||
* continue.
|
||||
*/
|
||||
fetchAbortIgnored?: true;
|
||||
/**
|
||||
@@ -272,27 +224,15 @@ export declare namespace LRUCache {
|
||||
*
|
||||
* - 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.
|
||||
* - stale: The item is in the cache, and is stale.
|
||||
* - hit: the item is in the cache
|
||||
* - miss: the item is not in the cache
|
||||
*/
|
||||
get?: 'stale' | 'hit' | 'miss' | 'fetching' | 'stale-fetching';
|
||||
get?: 'stale' | 'hit' | 'miss';
|
||||
/**
|
||||
* A fetch or get operation returned a stale value.
|
||||
*/
|
||||
returnedStale?: true;
|
||||
/**
|
||||
* A tracingChannel trace was started for this operation
|
||||
*/
|
||||
trace?: boolean;
|
||||
/**
|
||||
* A reference to the cache instance associated with this operation
|
||||
*/
|
||||
cache?: LRUCache<K & {}, V & {}, FC>;
|
||||
}
|
||||
/**
|
||||
* options which override the options set in the LRUCache constructor
|
||||
@@ -310,7 +250,7 @@ export declare namespace LRUCache {
|
||||
* 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, FC>;
|
||||
status?: Status<V>;
|
||||
size?: Size;
|
||||
}
|
||||
/**
|
||||
@@ -332,7 +272,7 @@ export declare namespace LRUCache {
|
||||
*/
|
||||
context?: FC;
|
||||
signal?: AbortSignal;
|
||||
status?: Status<K, V, FC>;
|
||||
status?: Status<V>;
|
||||
}
|
||||
/**
|
||||
* Options provided to {@link LRUCache#fetch} when the FC type is something
|
||||
@@ -345,7 +285,7 @@ export declare namespace LRUCache {
|
||||
* Options provided to {@link LRUCache#fetch} when the FC type is
|
||||
* `undefined` or `void`
|
||||
*/
|
||||
interface FetchOptionsNoContext<K, V, FC extends undefined | void = undefined> extends FetchOptions<K, V, FC> {
|
||||
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'> {
|
||||
@@ -363,7 +303,7 @@ export declare namespace LRUCache {
|
||||
* be required.
|
||||
*/
|
||||
context?: FC;
|
||||
status?: Status<K, V, FC>;
|
||||
status?: Status<V>;
|
||||
}
|
||||
/**
|
||||
* Options provided to {@link LRUCache#memo} when the FC type is something
|
||||
@@ -376,7 +316,7 @@ export declare namespace LRUCache {
|
||||
* Options provided to {@link LRUCache#memo} when the FC type is
|
||||
* `undefined` or `void`
|
||||
*/
|
||||
interface MemoOptionsNoContext<K, V, FC extends undefined | void = undefined> extends MemoOptions<K, V, FC> {
|
||||
interface MemoOptionsNoContext<K, V> extends MemoOptions<K, V, undefined> {
|
||||
context?: undefined;
|
||||
}
|
||||
/**
|
||||
@@ -405,7 +345,7 @@ export declare namespace LRUCache {
|
||||
* 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, FC>;
|
||||
status?: Status<V>;
|
||||
size?: Size;
|
||||
start?: Milliseconds;
|
||||
}
|
||||
@@ -413,19 +353,18 @@ export declare namespace LRUCache {
|
||||
* 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, FC>;
|
||||
status?: Status<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, FC>;
|
||||
status?: Status<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, FC>;
|
||||
}
|
||||
/**
|
||||
* Options that may be passed to the {@link LRUCache#set} method.
|
||||
@@ -446,7 +385,7 @@ export declare namespace LRUCache {
|
||||
* method is in use.
|
||||
*/
|
||||
start?: Milliseconds;
|
||||
status?: Status<K, V, FC>;
|
||||
status?: Status<V>;
|
||||
}
|
||||
/**
|
||||
* The type signature for the {@link OptionsBase.fetchMethod} option.
|
||||
@@ -699,20 +638,6 @@ export declare namespace LRUCache {
|
||||
* though for most cases, only minimally.
|
||||
*/
|
||||
maxSize?: Size;
|
||||
/**
|
||||
* The effective size for background fetch promises.
|
||||
*
|
||||
* This has no effect unless `maxSize` and `sizeCalculation` are used,
|
||||
* and a {@link LRUCache.OptionsBase.fetchMethod} is provided to
|
||||
* support {@link LRUCache#fetch}.
|
||||
*
|
||||
* If a stale value is present in the cache, then the effective size of
|
||||
* the background fetch is the size of the stale item it will eventually
|
||||
* replace. If not, then this value is used as its effective size.
|
||||
*
|
||||
* @default 1
|
||||
*/
|
||||
backgroundFetchSize?: number;
|
||||
/**
|
||||
* The maximum allowed size for any single item in the cache.
|
||||
*
|
||||
@@ -1015,8 +940,6 @@ export declare class LRUCache<K extends {}, V extends {}, FC = unknown> {
|
||||
* {@link LRUCache.OptionsBase.ignoreFetchAbort}
|
||||
*/
|
||||
ignoreFetchAbort: boolean;
|
||||
/** {@link LRUCache.OptionsBase.backgroundFetchSize} */
|
||||
backgroundFetchSize: number;
|
||||
/**
|
||||
* Do not call this method unless you need to inspect the
|
||||
* inner workings of the cache. If anything returned by this
|
||||
@@ -1029,7 +952,6 @@ export declare class LRUCache<K extends {}, V extends {}, FC = unknown> {
|
||||
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)[];
|
||||
@@ -1039,8 +961,8 @@ export declare class LRUCache<K extends {}, V extends {}, FC = unknown> {
|
||||
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>;
|
||||
isBackgroundFetch: (p: any) => p is BackgroundFetch<V>;
|
||||
backgroundFetch: (k: K, index: number | undefined, options: LRUCache.FetchOptions<K, V, FC>, context: any) => BackgroundFetch<V>;
|
||||
moveToTail: (index: number) => void;
|
||||
indexes: (options?: {
|
||||
allowStale: boolean;
|
||||
@@ -1152,12 +1074,12 @@ export declare class LRUCache<K extends {}, V extends {}, FC = unknown> {
|
||||
*
|
||||
* 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;
|
||||
forEach(fn: (v: V, k: K, self: LRUCache<K, V, FC>) => any, thisp?: any): 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;
|
||||
rforEach(fn: (v: V, k: K, self: LRUCache<K, V, FC>) => any, thisp?: any): void;
|
||||
/**
|
||||
* Delete any stale entries. Returns true if anything was removed,
|
||||
* false otherwise.
|
||||
@@ -1230,7 +1152,7 @@ export declare class LRUCache<K extends {}, V extends {}, FC = unknown> {
|
||||
* 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;
|
||||
set(k: K, v: V | BackgroundFetch<V> | undefined, setOptions?: LRUCache.SetOptions<K, V, FC>): this;
|
||||
/**
|
||||
* Evict the least recently used item, returning its value or
|
||||
* `undefined` if cache is empty.
|
||||
@@ -1346,8 +1268,8 @@ export declare class LRUCache<K extends {}, V extends {}, FC = unknown> {
|
||||
* 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, FC> : 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, FC> : never): Promise<undefined | V>;
|
||||
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
|
||||
@@ -1361,8 +1283,8 @@ export declare class LRUCache<K extends {}, V extends {}, FC = unknown> {
|
||||
* 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, FC> : 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, FC> : never): Promise<V>;
|
||||
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
|
||||
@@ -1377,8 +1299,8 @@ export declare class LRUCache<K extends {}, V extends {}, FC = unknown> {
|
||||
* 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, FC> : 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, FC> : never): V;
|
||||
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.
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+208
-366
@@ -4,27 +4,75 @@
|
||||
*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.LRUCache = void 0;
|
||||
const diagnostics_channel_js_1 = require("./diagnostics-channel.js");
|
||||
const perf_js_1 = require("./perf.js");
|
||||
const hasSubscribers = () => diagnostics_channel_js_1.metrics.hasSubscribers || diagnostics_channel_js_1.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 */
|
||||
/* c8 ignore start */
|
||||
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}`);
|
||||
}
|
||||
typeof PROCESS.emitWarning === 'function' ?
|
||||
PROCESS.emitWarning(msg, type, code, fn)
|
||||
: console.error(`[${code}] ${type}: ${msg}`);
|
||||
};
|
||||
let AC = globalThis.AbortController;
|
||||
let AS = globalThis.AbortSignal;
|
||||
/* c8 ignore start */
|
||||
if (typeof AC === 'undefined') {
|
||||
//@ts-ignore
|
||||
AS = class AbortSignal {
|
||||
onabort;
|
||||
_onabort = [];
|
||||
reason;
|
||||
aborted = false;
|
||||
addEventListener(_, fn) {
|
||||
this._onabort.push(fn);
|
||||
}
|
||||
};
|
||||
//@ts-ignore
|
||||
AC = class AbortController {
|
||||
constructor() {
|
||||
warnACPolyfill();
|
||||
}
|
||||
signal = new AS();
|
||||
abort(reason) {
|
||||
if (this.signal.aborted)
|
||||
return;
|
||||
//@ts-ignore
|
||||
this.signal.reason = reason;
|
||||
//@ts-ignore
|
||||
this.signal.aborted = true;
|
||||
//@ts-ignore
|
||||
for (const fn of this.signal._onabort) {
|
||||
fn(reason);
|
||||
}
|
||||
this.signal.onabort?.(reason);
|
||||
}
|
||||
};
|
||||
let printACPolyfillWarning = PROCESS.env?.LRU_CACHE_IGNORE_AC_WARNING !== '1';
|
||||
const warnACPolyfill = () => {
|
||||
if (!printACPolyfillWarning)
|
||||
return;
|
||||
printACPolyfillWarning = false;
|
||||
emitWarning('AbortController is not defined. If using lru-cache in ' +
|
||||
'node 14, load an AbortController polyfill from the ' +
|
||||
'`node-abort-controller` package. A minimal polyfill is ' +
|
||||
'provided for use by LRUCache.fetch(), but it should not be ' +
|
||||
'relied upon in other contexts (eg, passing it to other APIs that ' +
|
||||
'use AbortController/AbortSignal might have undesirable effects). ' +
|
||||
'You may disable this with LRU_CACHE_IGNORE_AC_WARNING=1 in the env.', 'NO_ABORT_CONTROLLER', 'ENOTSUP', warnACPolyfill);
|
||||
};
|
||||
}
|
||||
/* c8 ignore stop */
|
||||
const shouldWarn = (code) => !warned.has(code);
|
||||
const TYPE = Symbol('type');
|
||||
const isPosInt = (n) => !!n && n === Math.floor(n) && n > 0 && isFinite(n);
|
||||
const isPosInt = (n) => n && n === Math.floor(n) && n > 0 && isFinite(n);
|
||||
/* c8 ignore start */
|
||||
// This is a little bit ridiculous, tbh.
|
||||
// The maximum array length is 2^32-1 or thereabouts on most JS impls.
|
||||
// And well before that point, you're caching the entire world, I mean,
|
||||
@@ -33,7 +81,6 @@ const isPosInt = (n) => !!n && n === Math.floor(n) && n > 0 && isFinite(n);
|
||||
// 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
|
||||
@@ -48,9 +95,7 @@ class ZeroArray extends Array {
|
||||
}
|
||||
}
|
||||
class Stack {
|
||||
/* c8 ignore start - not sure why this is showing up uncovered?? */
|
||||
heap;
|
||||
/* c8 ignore stop */
|
||||
length;
|
||||
// private constructor
|
||||
static #constructing = false;
|
||||
@@ -170,8 +215,6 @@ class LRUCache {
|
||||
* {@link LRUCache.OptionsBase.ignoreFetchAbort}
|
||||
*/
|
||||
ignoreFetchAbort;
|
||||
/** {@link LRUCache.OptionsBase.backgroundFetchSize} */
|
||||
backgroundFetchSize;
|
||||
// computed properties
|
||||
#size;
|
||||
#calculatedSize;
|
||||
@@ -187,7 +230,6 @@ class LRUCache {
|
||||
#sizes;
|
||||
#starts;
|
||||
#ttls;
|
||||
#autopurgeTimers;
|
||||
#hasDispose;
|
||||
#hasFetchMethod;
|
||||
#hasDisposeAfter;
|
||||
@@ -206,7 +248,6 @@ class LRUCache {
|
||||
// properties
|
||||
starts: c.#starts,
|
||||
ttls: c.#ttls,
|
||||
autopurgeTimers: c.#autopurgeTimers,
|
||||
sizes: c.#sizes,
|
||||
keyMap: c.#keyMap,
|
||||
keyList: c.#keyList,
|
||||
@@ -282,14 +323,13 @@ class LRUCache {
|
||||
return this.#disposeAfter;
|
||||
}
|
||||
constructor(options) {
|
||||
const { max = 0, ttl, ttlResolution = 1, ttlAutopurge, updateAgeOnGet, updateAgeOnHas, allowStale, dispose, onInsert, disposeAfter, noDisposeOnSet, noUpdateTTL, maxSize = 0, maxEntrySize = 0, sizeCalculation, fetchMethod, memoMethod, noDeleteOnFetchRejection, noDeleteOnStaleGet, allowStaleOnFetchRejection, allowStaleOnFetchAbort, ignoreFetchAbort, backgroundFetchSize = 1, perf, } = options;
|
||||
this.backgroundFetchSize = backgroundFetchSize;
|
||||
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 ?? perf_js_1.defaultPerf;
|
||||
this.#perf = perf ?? defaultPerf;
|
||||
if (max !== 0 && !isPosInt(max)) {
|
||||
throw new TypeError('max option must be a nonnegative integer');
|
||||
}
|
||||
@@ -309,18 +349,20 @@ class LRUCache {
|
||||
throw new TypeError('sizeCalculation set to non-function');
|
||||
}
|
||||
}
|
||||
if (memoMethod !== undefined && typeof memoMethod !== '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') {
|
||||
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.#keyList = new Array(max).fill(undefined);
|
||||
this.#valList = new Array(max).fill(undefined);
|
||||
this.#next = new UintArray(max);
|
||||
this.#prev = new UintArray(max);
|
||||
this.#head = 0;
|
||||
@@ -368,7 +410,9 @@ class LRUCache {
|
||||
this.updateAgeOnGet = !!updateAgeOnGet;
|
||||
this.updateAgeOnHas = !!updateAgeOnHas;
|
||||
this.ttlResolution =
|
||||
isPosInt(ttlResolution) || ttlResolution === 0 ? ttlResolution : 1;
|
||||
isPosInt(ttlResolution) || ttlResolution === 0 ?
|
||||
ttlResolution
|
||||
: 1;
|
||||
this.ttlAutopurge = !!ttlAutopurge;
|
||||
this.ttl = ttl || 0;
|
||||
if (this.ttl) {
|
||||
@@ -403,56 +447,33 @@ class LRUCache {
|
||||
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);
|
||||
if (ttl !== 0 && this.ttlAutopurge) {
|
||||
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 */
|
||||
}
|
||||
};
|
||||
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) {
|
||||
/* c8 ignore next */
|
||||
if (!ttl || !start)
|
||||
return;
|
||||
}
|
||||
/* c8 ignore stop */
|
||||
status.ttl = ttl;
|
||||
status.start = start;
|
||||
status.now = cachedNow || getNow();
|
||||
@@ -511,15 +532,12 @@ class LRUCache {
|
||||
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)) {
|
||||
// provisionally accept background fetches.
|
||||
// actual value size will be checked when they return.
|
||||
if (this.#isBackgroundFetch(v)) {
|
||||
// NB: this cannot occur if v.__staleWhileFetching is set,
|
||||
// because in that case, it would take on the size of the
|
||||
// existing entry that it temporarily replaces.
|
||||
return this.backgroundFetchSize;
|
||||
}
|
||||
if (sizeCalculation) {
|
||||
if (typeof sizeCalculation !== 'function') {
|
||||
throw new TypeError('sizeCalculation must be a function');
|
||||
@@ -562,7 +580,10 @@ class LRUCache {
|
||||
};
|
||||
*#indexes({ allowStale = this.allowStale } = {}) {
|
||||
if (this.#size) {
|
||||
for (let i = this.#tail; this.#isValidIndex(i);) {
|
||||
for (let i = this.#tail; true;) {
|
||||
if (!this.#isValidIndex(i)) {
|
||||
break;
|
||||
}
|
||||
if (allowStale || !this.#isStale(i)) {
|
||||
yield i;
|
||||
}
|
||||
@@ -577,7 +598,10 @@ class LRUCache {
|
||||
}
|
||||
*#rindexes({ allowStale = this.allowStale } = {}) {
|
||||
if (this.#size) {
|
||||
for (let i = this.#head; this.#isValidIndex(i);) {
|
||||
for (let i = this.#head; true;) {
|
||||
if (!this.#isValidIndex(i)) {
|
||||
break;
|
||||
}
|
||||
if (allowStale || !this.#isStale(i)) {
|
||||
yield i;
|
||||
}
|
||||
@@ -629,7 +653,8 @@ class LRUCache {
|
||||
*keys() {
|
||||
for (const i of this.#indexes()) {
|
||||
const k = this.#keyList[i];
|
||||
if (k !== undefined && !this.#isBackgroundFetch(this.#valList[i])) {
|
||||
if (k !== undefined &&
|
||||
!this.#isBackgroundFetch(this.#valList[i])) {
|
||||
yield k;
|
||||
}
|
||||
}
|
||||
@@ -643,7 +668,8 @@ class LRUCache {
|
||||
*rkeys() {
|
||||
for (const i of this.#rindexes()) {
|
||||
const k = this.#keyList[i];
|
||||
if (k !== undefined && !this.#isBackgroundFetch(this.#valList[i])) {
|
||||
if (k !== undefined &&
|
||||
!this.#isBackgroundFetch(this.#valList[i])) {
|
||||
yield k;
|
||||
}
|
||||
}
|
||||
@@ -655,7 +681,8 @@ class LRUCache {
|
||||
*values() {
|
||||
for (const i of this.#indexes()) {
|
||||
const v = this.#valList[i];
|
||||
if (v !== undefined && !this.#isBackgroundFetch(this.#valList[i])) {
|
||||
if (v !== undefined &&
|
||||
!this.#isBackgroundFetch(this.#valList[i])) {
|
||||
yield this.#valList[i];
|
||||
}
|
||||
}
|
||||
@@ -669,7 +696,8 @@ class LRUCache {
|
||||
*rvalues() {
|
||||
for (const i of this.#rindexes()) {
|
||||
const v = this.#valList[i];
|
||||
if (v !== undefined && !this.#isBackgroundFetch(this.#valList[i])) {
|
||||
if (v !== undefined &&
|
||||
!this.#isBackgroundFetch(this.#valList[i])) {
|
||||
yield this.#valList[i];
|
||||
}
|
||||
}
|
||||
@@ -698,7 +726,7 @@ class LRUCache {
|
||||
if (value === undefined)
|
||||
continue;
|
||||
if (fn(value, this.#keyList[i], this)) {
|
||||
return this.#get(this.#keyList[i], getOptions);
|
||||
return this.get(this.#keyList[i], getOptions);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -771,7 +799,7 @@ class LRUCache {
|
||||
const value = this.#isBackgroundFetch(v) ? v.__staleWhileFetching : v;
|
||||
if (value === undefined)
|
||||
return undefined;
|
||||
/* c8 ignore stop */
|
||||
/* c8 ignore end */
|
||||
const entry = { value };
|
||||
if (this.#ttls && this.#starts) {
|
||||
const ttl = this.#ttls[i];
|
||||
@@ -845,7 +873,7 @@ class LRUCache {
|
||||
const age = Date.now() - entry.start;
|
||||
entry.start = this.#perf.now() - age;
|
||||
}
|
||||
this.#set(key, entry.value, entry);
|
||||
this.set(key, entry.value, entry);
|
||||
}
|
||||
}
|
||||
/**
|
||||
@@ -879,43 +907,22 @@ class LRUCache {
|
||||
* `cache.delete(key)`. `undefined` is never stored in the cache.
|
||||
*/
|
||||
set(k, v, setOptions = {}) {
|
||||
const { status = diagnostics_channel_js_1.metrics.hasSubscribers ? {} : undefined } = setOptions;
|
||||
setOptions.status = status;
|
||||
if (status) {
|
||||
status.op = 'set';
|
||||
status.key = k;
|
||||
if (v !== undefined)
|
||||
status.value = v;
|
||||
status.cache = this;
|
||||
}
|
||||
const result = this.#set(k, v, setOptions);
|
||||
if (status && diagnostics_channel_js_1.metrics.hasSubscribers) {
|
||||
diagnostics_channel_js_1.metrics.publish(status);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
#set(k, v, setOptions, bf) {
|
||||
const { ttl = this.ttl, start, noDisposeOnSet = this.noDisposeOnSet, sizeCalculation = this.sizeCalculation, status, } = setOptions;
|
||||
const isBF = this.#isBackgroundFetch(v);
|
||||
if (v === undefined) {
|
||||
if (status)
|
||||
status.set = 'deleted';
|
||||
this.delete(k);
|
||||
return this;
|
||||
}
|
||||
const { ttl = this.ttl, start, noDisposeOnSet = this.noDisposeOnSet, sizeCalculation = this.sizeCalculation, status, } = setOptions;
|
||||
let { noUpdateTTL = this.noUpdateTTL } = setOptions;
|
||||
if (status && !isBF)
|
||||
status.value = v;
|
||||
const size = this.#requireSize(k, v, setOptions.size || 0, sizeCalculation, status);
|
||||
const size = this.#requireSize(k, v, setOptions.size || 0, sizeCalculation);
|
||||
// if the item doesn't fit, don't do anything
|
||||
// NB: maxEntrySize set to maxSize by default
|
||||
if (this.maxEntrySize && size > this.maxEntrySize) {
|
||||
// have to delete, in case something is there already.
|
||||
this.#delete(k, 'set');
|
||||
if (status) {
|
||||
status.set = 'miss';
|
||||
status.maxEntrySizeExceeded = true;
|
||||
}
|
||||
// have to delete, in case something is there already.
|
||||
this.#delete(k, 'set');
|
||||
return this;
|
||||
}
|
||||
let index = this.#size === 0 ? undefined : this.#keyMap.get(k);
|
||||
@@ -936,68 +943,52 @@ class LRUCache {
|
||||
if (status)
|
||||
status.set = 'add';
|
||||
noUpdateTTL = false;
|
||||
if (this.#hasOnInsert && !isBF) {
|
||||
if (this.#hasOnInsert) {
|
||||
this.#onInsert?.(v, k, 'add');
|
||||
}
|
||||
}
|
||||
else {
|
||||
// update
|
||||
// might be updating a background fetch!
|
||||
this.#moveToTail(index);
|
||||
const oldVal = this.#valList[index];
|
||||
if (v !== oldVal) {
|
||||
if (!noDisposeOnSet) {
|
||||
if (this.#isBackgroundFetch(oldVal)) {
|
||||
if (oldVal !== bf) {
|
||||
// setting over a background fetch, not merely resolving it.
|
||||
oldVal.__abortController.abort(new Error('replaced'));
|
||||
}
|
||||
const { __staleWhileFetching: s } = oldVal;
|
||||
if (s !== undefined && s !== v) {
|
||||
if (this.#hasDispose) {
|
||||
this.#dispose?.(s, k, 'set');
|
||||
}
|
||||
if (this.#hasDisposeAfter) {
|
||||
this.#disposed?.push([s, k, 'set']);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (this.#hasFetchMethod && this.#isBackgroundFetch(oldVal)) {
|
||||
oldVal.__abortController.abort(new Error('replaced'));
|
||||
const { __staleWhileFetching: s } = oldVal;
|
||||
if (s !== undefined && !noDisposeOnSet) {
|
||||
if (this.#hasDispose) {
|
||||
this.#dispose?.(oldVal, k, 'set');
|
||||
this.#dispose?.(s, k, 'set');
|
||||
}
|
||||
if (this.#hasDisposeAfter) {
|
||||
this.#disposed?.push([oldVal, k, 'set']);
|
||||
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 (!isBF) {
|
||||
if (status) {
|
||||
status.set = 'replace';
|
||||
const oldValue = oldVal && this.#isBackgroundFetch(oldVal) ?
|
||||
oldVal.__staleWhileFetching
|
||||
: oldVal;
|
||||
const setType = oldValue === undefined ? 'add'
|
||||
: v !== oldValue ? 'replace'
|
||||
: 'update';
|
||||
if (status) {
|
||||
status.set = setType;
|
||||
if (oldValue !== undefined)
|
||||
status.oldValue = oldValue;
|
||||
}
|
||||
if (this.#hasOnInsert) {
|
||||
this.onInsert?.(v, k, setType);
|
||||
}
|
||||
if (oldValue !== undefined)
|
||||
status.oldValue = oldValue;
|
||||
}
|
||||
}
|
||||
else if (!isBF) {
|
||||
if (status) {
|
||||
status.set = 'update';
|
||||
}
|
||||
if (this.#hasOnInsert) {
|
||||
this.onInsert?.(v, k, 'update');
|
||||
}
|
||||
else if (status) {
|
||||
status.set = 'update';
|
||||
}
|
||||
if (this.#hasOnInsert) {
|
||||
this.onInsert?.(v, k, v === oldVal ? 'update' : 'replace');
|
||||
}
|
||||
}
|
||||
if (ttl !== 0 && !this.#ttls) {
|
||||
@@ -1052,25 +1043,18 @@ class LRUCache {
|
||||
const head = this.#head;
|
||||
const k = this.#keyList[head];
|
||||
const v = this.#valList[head];
|
||||
const isBF = this.#isBackgroundFetch(v);
|
||||
if (isBF) {
|
||||
if (this.#hasFetchMethod && this.#isBackgroundFetch(v)) {
|
||||
v.__abortController.abort(new Error('evicted'));
|
||||
}
|
||||
const oldValue = isBF ? v.__staleWhileFetching : v;
|
||||
if ((this.#hasDispose || this.#hasDisposeAfter) &&
|
||||
oldValue !== undefined) {
|
||||
else if (this.#hasDispose || this.#hasDisposeAfter) {
|
||||
if (this.#hasDispose) {
|
||||
this.#dispose?.(oldValue, k, 'evict');
|
||||
this.#dispose?.(v, k, 'evict');
|
||||
}
|
||||
if (this.#hasDisposeAfter) {
|
||||
this.#disposed?.push([oldValue, k, 'evict']);
|
||||
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;
|
||||
@@ -1105,19 +1089,6 @@ class LRUCache {
|
||||
* {@link LRUCache.OptionsBase.updateAgeOnHas} is set.
|
||||
*/
|
||||
has(k, hasOptions = {}) {
|
||||
const { status = diagnostics_channel_js_1.metrics.hasSubscribers ? {} : undefined } = hasOptions;
|
||||
hasOptions.status = status;
|
||||
if (status) {
|
||||
status.op = 'has';
|
||||
status.key = k;
|
||||
status.cache = this;
|
||||
}
|
||||
const result = this.#has(k, hasOptions);
|
||||
if (diagnostics_channel_js_1.metrics.hasSubscribers)
|
||||
diagnostics_channel_js_1.metrics.publish(status);
|
||||
return result;
|
||||
}
|
||||
#has(k, hasOptions = {}) {
|
||||
const { updateAgeOnHas = this.updateAgeOnHas, status } = hasOptions;
|
||||
const index = this.#keyMap.get(k);
|
||||
if (index !== undefined) {
|
||||
@@ -1154,46 +1125,22 @@ class LRUCache {
|
||||
* {@link LRUCache.OptionsBase.allowStale} is set.
|
||||
*/
|
||||
peek(k, peekOptions = {}) {
|
||||
const { status = hasSubscribers() ? {} : undefined } = peekOptions;
|
||||
if (status) {
|
||||
status.op = 'peek';
|
||||
status.key = k;
|
||||
status.cache = this;
|
||||
}
|
||||
peekOptions.status = status;
|
||||
const result = this.#peek(k, peekOptions);
|
||||
if (diagnostics_channel_js_1.metrics.hasSubscribers) {
|
||||
diagnostics_channel_js_1.metrics.publish(status);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
#peek(k, peekOptions) {
|
||||
const { status, allowStale = this.allowStale } = peekOptions;
|
||||
const { 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;
|
||||
if (index === undefined ||
|
||||
(!allowStale && this.#isStale(index))) {
|
||||
return;
|
||||
}
|
||||
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;
|
||||
// either stale and allowed, or forcing a refresh of non-stale value
|
||||
return this.#isBackgroundFetch(v) ? v.__staleWhileFetching : v;
|
||||
}
|
||||
#backgroundFetch(k, index, options, context) {
|
||||
const v = index === undefined ? undefined : this.#valList[index];
|
||||
if (this.#isBackgroundFetch(v)) {
|
||||
return v;
|
||||
}
|
||||
const ac = new AbortController();
|
||||
const ac = new AC();
|
||||
const { signal } = options;
|
||||
// when/if our AC signals, then stop listening to theirs.
|
||||
signal?.addEventListener('abort', () => ac.abort(signal.reason), {
|
||||
@@ -1207,8 +1154,6 @@ class LRUCache {
|
||||
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;
|
||||
@@ -1221,7 +1166,7 @@ class LRUCache {
|
||||
}
|
||||
}
|
||||
if (aborted && !ignoreAbort && !updateCache) {
|
||||
return fetchFail(ac.signal.reason, proceed);
|
||||
return fetchFail(ac.signal.reason);
|
||||
}
|
||||
// either we didn't abort, and are still here, or we did, and ignored
|
||||
const bf = p;
|
||||
@@ -1229,7 +1174,7 @@ class LRUCache {
|
||||
// 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 (vl === p || ignoreAbort && updateCache && vl === undefined) {
|
||||
if (v === undefined) {
|
||||
if (bf.__staleWhileFetching !== undefined) {
|
||||
this.#valList[index] = bf.__staleWhileFetching;
|
||||
@@ -1241,7 +1186,7 @@ class LRUCache {
|
||||
else {
|
||||
if (options.status)
|
||||
options.status.fetchUpdated = true;
|
||||
this.#set(k, v, fetchOpts.options, bf);
|
||||
this.set(k, v, fetchOpts.options);
|
||||
}
|
||||
}
|
||||
return v;
|
||||
@@ -1251,10 +1196,9 @@ class LRUCache {
|
||||
options.status.fetchRejected = true;
|
||||
options.status.fetchError = er;
|
||||
}
|
||||
// do not pass go, do not collect $200
|
||||
return fetchFail(er, false);
|
||||
return fetchFail(er);
|
||||
};
|
||||
const fetchFail = (er, proceed) => {
|
||||
const fetchFail = (er) => {
|
||||
const { aborted } = ac.signal;
|
||||
const allowStaleAborted = aborted && options.allowStaleOnFetchAbort;
|
||||
const allowStale = allowStaleAborted || options.allowStaleOnFetchRejection;
|
||||
@@ -1263,7 +1207,7 @@ class LRUCache {
|
||||
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);
|
||||
const del = !noDelete || bf.__staleWhileFetching === undefined;
|
||||
if (del) {
|
||||
this.#delete(k, 'fetch');
|
||||
}
|
||||
@@ -1287,11 +1231,15 @@ class LRUCache {
|
||||
};
|
||||
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) {
|
||||
if (!options.ignoreFetchAbort ||
|
||||
options.allowStaleOnFetchAbort) {
|
||||
res(undefined);
|
||||
// when it eventually resolves, update the cache.
|
||||
if (options.allowStaleOnFetchAbort) {
|
||||
@@ -1299,12 +1247,6 @@ class LRUCache {
|
||||
}
|
||||
}
|
||||
});
|
||||
if (fmp && fmp instanceof Promise) {
|
||||
fmp.then(v => res(v === undefined ? undefined : v), rej);
|
||||
}
|
||||
else if (fmp !== undefined) {
|
||||
res(fmp);
|
||||
}
|
||||
};
|
||||
if (options.status)
|
||||
options.status.fetchDispatched = true;
|
||||
@@ -1316,14 +1258,10 @@ class LRUCache {
|
||||
});
|
||||
if (index === undefined) {
|
||||
// internal, don't expose status.
|
||||
this.#set(k, bf, { ...fetchOpts.options, status: undefined });
|
||||
this.set(k, bf, { ...fetchOpts.options, status: undefined });
|
||||
index = this.#keyMap.get(k);
|
||||
}
|
||||
else {
|
||||
// do not call #set, because we do not want to adjust its place
|
||||
// in the lru queue, as it has not yet been "used". Also, we don't
|
||||
// need to worry about evicting for size, because a background fetch
|
||||
// over a stale value is treated as the same size as its stale value.
|
||||
this.#valList[index] = bf;
|
||||
}
|
||||
return bf;
|
||||
@@ -1335,23 +1273,9 @@ class LRUCache {
|
||||
return (!!b &&
|
||||
b instanceof Promise &&
|
||||
b.hasOwnProperty('__staleWhileFetching') &&
|
||||
b.__abortController instanceof AbortController);
|
||||
b.__abortController instanceof AC);
|
||||
}
|
||||
fetch(k, fetchOptions = {}) {
|
||||
const ths = diagnostics_channel_js_1.tracing.hasSubscribers;
|
||||
const { status = hasSubscribers() ? {} : undefined } = fetchOptions;
|
||||
fetchOptions.status = status;
|
||||
if (status && fetchOptions.context) {
|
||||
status.context = fetchOptions.context;
|
||||
}
|
||||
const p = this.#fetch(k, fetchOptions);
|
||||
if (status && ths) {
|
||||
status.trace = true;
|
||||
diagnostics_channel_js_1.tracing.tracePromise(() => p, status).catch(() => { });
|
||||
}
|
||||
return p;
|
||||
}
|
||||
async #fetch(k, fetchOptions = {}) {
|
||||
async fetch(k, fetchOptions = {}) {
|
||||
const {
|
||||
// get options
|
||||
allowStale = this.allowStale, updateAgeOnGet = this.updateAgeOnGet, noDeleteOnStaleGet = this.noDeleteOnStaleGet,
|
||||
@@ -1359,17 +1283,10 @@ class LRUCache {
|
||||
ttl = this.ttl, noDisposeOnSet = this.noDisposeOnSet, size = 0, sizeCalculation = this.sizeCalculation, noUpdateTTL = this.noUpdateTTL,
|
||||
// fetch exclusive options
|
||||
noDeleteOnFetchRejection = this.noDeleteOnFetchRejection, allowStaleOnFetchRejection = this.allowStaleOnFetchRejection, ignoreFetchAbort = this.ignoreFetchAbort, allowStaleOnFetchAbort = this.allowStaleOnFetchAbort, context, forceRefresh = false, status, signal, } = fetchOptions;
|
||||
if (status) {
|
||||
status.op = 'fetch';
|
||||
status.key = k;
|
||||
if (forceRefresh)
|
||||
status.forceRefresh = true;
|
||||
status.cache = this;
|
||||
}
|
||||
if (!this.#hasFetchMethod) {
|
||||
if (status)
|
||||
status.fetch = 'get';
|
||||
return this.#get(k, {
|
||||
return this.get(k, {
|
||||
allowStale,
|
||||
updateAgeOnGet,
|
||||
noDeleteOnStaleGet,
|
||||
@@ -1438,68 +1355,26 @@ class LRUCache {
|
||||
return staleVal ? p.__staleWhileFetching : (p.__returned = p);
|
||||
}
|
||||
}
|
||||
forceFetch(k, fetchOptions = {}) {
|
||||
const ths = diagnostics_channel_js_1.tracing.hasSubscribers;
|
||||
const { status = hasSubscribers() ? {} : undefined } = fetchOptions;
|
||||
fetchOptions.status = status;
|
||||
if (status && fetchOptions.context) {
|
||||
status.context = fetchOptions.context;
|
||||
}
|
||||
const p = this.#forceFetch(k, fetchOptions);
|
||||
if (status && ths) {
|
||||
status.trace = true;
|
||||
diagnostics_channel_js_1.tracing.tracePromise(() => p, status).catch(() => { });
|
||||
}
|
||||
return p;
|
||||
}
|
||||
async #forceFetch(k, fetchOptions = {}) {
|
||||
const v = await this.#fetch(k, fetchOptions);
|
||||
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 = diagnostics_channel_js_1.metrics.hasSubscribers ? {} : undefined } = memoOptions;
|
||||
memoOptions.status = status;
|
||||
if (status) {
|
||||
status.op = 'memo';
|
||||
status.key = k;
|
||||
if (memoOptions.context) {
|
||||
status.context = memoOptions.context;
|
||||
}
|
||||
status.cache = this;
|
||||
}
|
||||
const result = this.#memo(k, memoOptions);
|
||||
if (status)
|
||||
status.value = result;
|
||||
if (diagnostics_channel_js_1.metrics.hasSubscribers)
|
||||
diagnostics_channel_js_1.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)
|
||||
const { context, forceRefresh, ...options } = memoOptions;
|
||||
const v = this.get(k, options);
|
||||
if (!forceRefresh && v !== undefined)
|
||||
return v;
|
||||
const vv = memoMethod(k, v, {
|
||||
options,
|
||||
context,
|
||||
});
|
||||
if (status)
|
||||
status.value = vv;
|
||||
this.#set(k, vv, options);
|
||||
this.set(k, vv, options);
|
||||
return vv;
|
||||
}
|
||||
/**
|
||||
@@ -1509,71 +1384,55 @@ class LRUCache {
|
||||
* If the key is not found, get() will return `undefined`.
|
||||
*/
|
||||
get(k, getOptions = {}) {
|
||||
const { status = diagnostics_channel_js_1.metrics.hasSubscribers ? {} : undefined } = getOptions;
|
||||
getOptions.status = status;
|
||||
if (status) {
|
||||
status.op = 'get';
|
||||
status.key = k;
|
||||
status.cache = this;
|
||||
}
|
||||
const result = this.#get(k, getOptions);
|
||||
if (status) {
|
||||
if (result !== undefined)
|
||||
status.value = result;
|
||||
if (diagnostics_channel_js_1.metrics.hasSubscribers)
|
||||
diagnostics_channel_js_1.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 (index !== undefined) {
|
||||
const value = this.#valList[index];
|
||||
const fetching = this.#isBackgroundFetch(value);
|
||||
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');
|
||||
}
|
||||
this.#statusTTL(status, index);
|
||||
if (this.#isStale(index)) {
|
||||
if (status)
|
||||
status.get = 'stale';
|
||||
if (allowStale) {
|
||||
if (status)
|
||||
// delete only if not an in-flight background fetch
|
||||
if (!fetching) {
|
||||
if (!noDeleteOnStaleGet) {
|
||||
this.#delete(k, 'expire');
|
||||
}
|
||||
if (status && allowStale)
|
||||
status.returnedStale = true;
|
||||
return value;
|
||||
return allowStale ? value : undefined;
|
||||
}
|
||||
else {
|
||||
if (status &&
|
||||
allowStale &&
|
||||
value.__staleWhileFetching !== undefined) {
|
||||
status.returnedStale = true;
|
||||
}
|
||||
return allowStale ? value.__staleWhileFetching : undefined;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
if (status)
|
||||
status.get = 'stale-fetching';
|
||||
if (allowStale && value.__staleWhileFetching !== undefined) {
|
||||
else {
|
||||
if (status)
|
||||
status.returnedStale = true;
|
||||
return value.__staleWhileFetching;
|
||||
status.get = 'hit';
|
||||
// if we're currently fetching it, we don't actually have it yet
|
||||
// it's not stale, which means this isn't a staleWhileRefetching.
|
||||
// If it's not stale, and fetching, AND has a __staleWhileFetching
|
||||
// value, then that means the user fetched with {forceRefresh:true},
|
||||
// so it's safe to return that value.
|
||||
if (fetching) {
|
||||
return value.__staleWhileFetching;
|
||||
}
|
||||
this.#moveToTail(index);
|
||||
if (updateAgeOnGet) {
|
||||
this.#updateItemAge(index);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
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);
|
||||
else if (status) {
|
||||
status.get = 'miss';
|
||||
}
|
||||
return fetching ? value.__staleWhileFetching : value;
|
||||
}
|
||||
#connect(p, n) {
|
||||
this.#prev[n] = p;
|
||||
@@ -1608,22 +1467,10 @@ class LRUCache {
|
||||
return this.#delete(k, 'delete');
|
||||
}
|
||||
#delete(k, reason) {
|
||||
if (diagnostics_channel_js_1.metrics.hasSubscribers) {
|
||||
diagnostics_channel_js_1.metrics.publish({
|
||||
op: 'delete',
|
||||
delete: reason,
|
||||
key: k,
|
||||
cache: this,
|
||||
});
|
||||
}
|
||||
let deleted = false;
|
||||
if (this.#size !== 0) {
|
||||
const index = this.#keyMap.get(k);
|
||||
if (index !== undefined) {
|
||||
if (this.#autopurgeTimers?.[index]) {
|
||||
clearTimeout(this.#autopurgeTimers?.[index]);
|
||||
this.#autopurgeTimers[index] = undefined;
|
||||
}
|
||||
deleted = true;
|
||||
if (this.#size === 1) {
|
||||
this.#clear(reason);
|
||||
@@ -1694,16 +1541,11 @@ class LRUCache {
|
||||
}
|
||||
}
|
||||
this.#keyMap.clear();
|
||||
void this.#valList.fill(undefined);
|
||||
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);
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
Generated
Vendored
+4
-4
File diff suppressed because one or more lines are too long
+30
-108
@@ -1,8 +1,9 @@
|
||||
/**
|
||||
* @module LRUCache
|
||||
*/
|
||||
import type { Perf } from './perf.js';
|
||||
export type { Perf } from './perf.js';
|
||||
export type Perf = {
|
||||
now: () => number;
|
||||
};
|
||||
declare const TYPE: unique symbol;
|
||||
export type PosInt = number & {
|
||||
[TYPE]: 'Positive Integer';
|
||||
@@ -118,16 +119,8 @@ export declare namespace LRUCache {
|
||||
*
|
||||
* 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, FC = unknown> {
|
||||
/**
|
||||
* The operation being performed
|
||||
*/
|
||||
op?: 'get' | 'set' | 'memo' | 'fetch' | 'delete' | 'has' | 'peek';
|
||||
interface Status<V> {
|
||||
/**
|
||||
* The status of a set() operation.
|
||||
*
|
||||
@@ -136,37 +129,7 @@ export declare namespace LRUCache {
|
||||
* - 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;
|
||||
set?: 'add' | 'update' | 'replace' | 'miss';
|
||||
/**
|
||||
* the ttl stored for the item, or undefined if ttls are not used.
|
||||
*/
|
||||
@@ -197,15 +160,8 @@ export declare namespace LRUCache {
|
||||
*/
|
||||
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'`
|
||||
* The old value, specified in the case of `set:'update'` or
|
||||
* `set:'replace'`
|
||||
*/
|
||||
oldValue?: V;
|
||||
/**
|
||||
@@ -231,10 +187,6 @@ export declare namespace LRUCache {
|
||||
* {@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
|
||||
*/
|
||||
@@ -256,7 +208,7 @@ export declare namespace LRUCache {
|
||||
fetchAborted?: true;
|
||||
/**
|
||||
* The abort signal received was ignored, and the fetch was allowed to
|
||||
* continue in the background.
|
||||
* continue.
|
||||
*/
|
||||
fetchAbortIgnored?: true;
|
||||
/**
|
||||
@@ -272,27 +224,15 @@ export declare namespace LRUCache {
|
||||
*
|
||||
* - 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.
|
||||
* - stale: The item is in the cache, and is stale.
|
||||
* - hit: the item is in the cache
|
||||
* - miss: the item is not in the cache
|
||||
*/
|
||||
get?: 'stale' | 'hit' | 'miss' | 'fetching' | 'stale-fetching';
|
||||
get?: 'stale' | 'hit' | 'miss';
|
||||
/**
|
||||
* A fetch or get operation returned a stale value.
|
||||
*/
|
||||
returnedStale?: true;
|
||||
/**
|
||||
* A tracingChannel trace was started for this operation
|
||||
*/
|
||||
trace?: boolean;
|
||||
/**
|
||||
* A reference to the cache instance associated with this operation
|
||||
*/
|
||||
cache?: LRUCache<K & {}, V & {}, FC>;
|
||||
}
|
||||
/**
|
||||
* options which override the options set in the LRUCache constructor
|
||||
@@ -310,7 +250,7 @@ export declare namespace LRUCache {
|
||||
* 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, FC>;
|
||||
status?: Status<V>;
|
||||
size?: Size;
|
||||
}
|
||||
/**
|
||||
@@ -332,7 +272,7 @@ export declare namespace LRUCache {
|
||||
*/
|
||||
context?: FC;
|
||||
signal?: AbortSignal;
|
||||
status?: Status<K, V, FC>;
|
||||
status?: Status<V>;
|
||||
}
|
||||
/**
|
||||
* Options provided to {@link LRUCache#fetch} when the FC type is something
|
||||
@@ -345,7 +285,7 @@ export declare namespace LRUCache {
|
||||
* Options provided to {@link LRUCache#fetch} when the FC type is
|
||||
* `undefined` or `void`
|
||||
*/
|
||||
interface FetchOptionsNoContext<K, V, FC extends undefined | void = undefined> extends FetchOptions<K, V, FC> {
|
||||
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'> {
|
||||
@@ -363,7 +303,7 @@ export declare namespace LRUCache {
|
||||
* be required.
|
||||
*/
|
||||
context?: FC;
|
||||
status?: Status<K, V, FC>;
|
||||
status?: Status<V>;
|
||||
}
|
||||
/**
|
||||
* Options provided to {@link LRUCache#memo} when the FC type is something
|
||||
@@ -376,7 +316,7 @@ export declare namespace LRUCache {
|
||||
* Options provided to {@link LRUCache#memo} when the FC type is
|
||||
* `undefined` or `void`
|
||||
*/
|
||||
interface MemoOptionsNoContext<K, V, FC extends undefined | void = undefined> extends MemoOptions<K, V, FC> {
|
||||
interface MemoOptionsNoContext<K, V> extends MemoOptions<K, V, undefined> {
|
||||
context?: undefined;
|
||||
}
|
||||
/**
|
||||
@@ -405,7 +345,7 @@ export declare namespace LRUCache {
|
||||
* 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, FC>;
|
||||
status?: Status<V>;
|
||||
size?: Size;
|
||||
start?: Milliseconds;
|
||||
}
|
||||
@@ -413,19 +353,18 @@ export declare namespace LRUCache {
|
||||
* 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, FC>;
|
||||
status?: Status<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, FC>;
|
||||
status?: Status<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, FC>;
|
||||
}
|
||||
/**
|
||||
* Options that may be passed to the {@link LRUCache#set} method.
|
||||
@@ -446,7 +385,7 @@ export declare namespace LRUCache {
|
||||
* method is in use.
|
||||
*/
|
||||
start?: Milliseconds;
|
||||
status?: Status<K, V, FC>;
|
||||
status?: Status<V>;
|
||||
}
|
||||
/**
|
||||
* The type signature for the {@link OptionsBase.fetchMethod} option.
|
||||
@@ -699,20 +638,6 @@ export declare namespace LRUCache {
|
||||
* though for most cases, only minimally.
|
||||
*/
|
||||
maxSize?: Size;
|
||||
/**
|
||||
* The effective size for background fetch promises.
|
||||
*
|
||||
* This has no effect unless `maxSize` and `sizeCalculation` are used,
|
||||
* and a {@link LRUCache.OptionsBase.fetchMethod} is provided to
|
||||
* support {@link LRUCache#fetch}.
|
||||
*
|
||||
* If a stale value is present in the cache, then the effective size of
|
||||
* the background fetch is the size of the stale item it will eventually
|
||||
* replace. If not, then this value is used as its effective size.
|
||||
*
|
||||
* @default 1
|
||||
*/
|
||||
backgroundFetchSize?: number;
|
||||
/**
|
||||
* The maximum allowed size for any single item in the cache.
|
||||
*
|
||||
@@ -1015,8 +940,6 @@ export declare class LRUCache<K extends {}, V extends {}, FC = unknown> {
|
||||
* {@link LRUCache.OptionsBase.ignoreFetchAbort}
|
||||
*/
|
||||
ignoreFetchAbort: boolean;
|
||||
/** {@link LRUCache.OptionsBase.backgroundFetchSize} */
|
||||
backgroundFetchSize: number;
|
||||
/**
|
||||
* Do not call this method unless you need to inspect the
|
||||
* inner workings of the cache. If anything returned by this
|
||||
@@ -1029,7 +952,6 @@ export declare class LRUCache<K extends {}, V extends {}, FC = unknown> {
|
||||
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)[];
|
||||
@@ -1039,8 +961,8 @@ export declare class LRUCache<K extends {}, V extends {}, FC = unknown> {
|
||||
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>;
|
||||
isBackgroundFetch: (p: any) => p is BackgroundFetch<V>;
|
||||
backgroundFetch: (k: K, index: number | undefined, options: LRUCache.FetchOptions<K, V, FC>, context: any) => BackgroundFetch<V>;
|
||||
moveToTail: (index: number) => void;
|
||||
indexes: (options?: {
|
||||
allowStale: boolean;
|
||||
@@ -1152,12 +1074,12 @@ export declare class LRUCache<K extends {}, V extends {}, FC = unknown> {
|
||||
*
|
||||
* 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;
|
||||
forEach(fn: (v: V, k: K, self: LRUCache<K, V, FC>) => any, thisp?: any): 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;
|
||||
rforEach(fn: (v: V, k: K, self: LRUCache<K, V, FC>) => any, thisp?: any): void;
|
||||
/**
|
||||
* Delete any stale entries. Returns true if anything was removed,
|
||||
* false otherwise.
|
||||
@@ -1230,7 +1152,7 @@ export declare class LRUCache<K extends {}, V extends {}, FC = unknown> {
|
||||
* 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;
|
||||
set(k: K, v: V | BackgroundFetch<V> | undefined, setOptions?: LRUCache.SetOptions<K, V, FC>): this;
|
||||
/**
|
||||
* Evict the least recently used item, returning its value or
|
||||
* `undefined` if cache is empty.
|
||||
@@ -1346,8 +1268,8 @@ export declare class LRUCache<K extends {}, V extends {}, FC = unknown> {
|
||||
* 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, FC> : 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, FC> : never): Promise<undefined | V>;
|
||||
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
|
||||
@@ -1361,8 +1283,8 @@ export declare class LRUCache<K extends {}, V extends {}, FC = unknown> {
|
||||
* 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, FC> : 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, FC> : never): Promise<V>;
|
||||
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
|
||||
@@ -1377,8 +1299,8 @@ export declare class LRUCache<K extends {}, V extends {}, FC = unknown> {
|
||||
* 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, FC> : 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, FC> : never): V;
|
||||
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.
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+207
-365
@@ -1,27 +1,75 @@
|
||||
/**
|
||||
* @module LRUCache
|
||||
*/
|
||||
import { metrics, tracing } from './diagnostics-channel.js';
|
||||
import { defaultPerf } from './perf.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 */
|
||||
/* c8 ignore start */
|
||||
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}`);
|
||||
}
|
||||
typeof PROCESS.emitWarning === 'function' ?
|
||||
PROCESS.emitWarning(msg, type, code, fn)
|
||||
: console.error(`[${code}] ${type}: ${msg}`);
|
||||
};
|
||||
let AC = globalThis.AbortController;
|
||||
let AS = globalThis.AbortSignal;
|
||||
/* c8 ignore start */
|
||||
if (typeof AC === 'undefined') {
|
||||
//@ts-ignore
|
||||
AS = class AbortSignal {
|
||||
onabort;
|
||||
_onabort = [];
|
||||
reason;
|
||||
aborted = false;
|
||||
addEventListener(_, fn) {
|
||||
this._onabort.push(fn);
|
||||
}
|
||||
};
|
||||
//@ts-ignore
|
||||
AC = class AbortController {
|
||||
constructor() {
|
||||
warnACPolyfill();
|
||||
}
|
||||
signal = new AS();
|
||||
abort(reason) {
|
||||
if (this.signal.aborted)
|
||||
return;
|
||||
//@ts-ignore
|
||||
this.signal.reason = reason;
|
||||
//@ts-ignore
|
||||
this.signal.aborted = true;
|
||||
//@ts-ignore
|
||||
for (const fn of this.signal._onabort) {
|
||||
fn(reason);
|
||||
}
|
||||
this.signal.onabort?.(reason);
|
||||
}
|
||||
};
|
||||
let printACPolyfillWarning = PROCESS.env?.LRU_CACHE_IGNORE_AC_WARNING !== '1';
|
||||
const warnACPolyfill = () => {
|
||||
if (!printACPolyfillWarning)
|
||||
return;
|
||||
printACPolyfillWarning = false;
|
||||
emitWarning('AbortController is not defined. If using lru-cache in ' +
|
||||
'node 14, load an AbortController polyfill from the ' +
|
||||
'`node-abort-controller` package. A minimal polyfill is ' +
|
||||
'provided for use by LRUCache.fetch(), but it should not be ' +
|
||||
'relied upon in other contexts (eg, passing it to other APIs that ' +
|
||||
'use AbortController/AbortSignal might have undesirable effects). ' +
|
||||
'You may disable this with LRU_CACHE_IGNORE_AC_WARNING=1 in the env.', 'NO_ABORT_CONTROLLER', 'ENOTSUP', warnACPolyfill);
|
||||
};
|
||||
}
|
||||
/* c8 ignore stop */
|
||||
const shouldWarn = (code) => !warned.has(code);
|
||||
const TYPE = Symbol('type');
|
||||
const isPosInt = (n) => !!n && n === Math.floor(n) && n > 0 && isFinite(n);
|
||||
const isPosInt = (n) => n && n === Math.floor(n) && n > 0 && isFinite(n);
|
||||
/* c8 ignore start */
|
||||
// This is a little bit ridiculous, tbh.
|
||||
// The maximum array length is 2^32-1 or thereabouts on most JS impls.
|
||||
// And well before that point, you're caching the entire world, I mean,
|
||||
@@ -30,7 +78,6 @@ const isPosInt = (n) => !!n && n === Math.floor(n) && n > 0 && isFinite(n);
|
||||
// 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
|
||||
@@ -45,9 +92,7 @@ class ZeroArray extends Array {
|
||||
}
|
||||
}
|
||||
class Stack {
|
||||
/* c8 ignore start - not sure why this is showing up uncovered?? */
|
||||
heap;
|
||||
/* c8 ignore stop */
|
||||
length;
|
||||
// private constructor
|
||||
static #constructing = false;
|
||||
@@ -167,8 +212,6 @@ export class LRUCache {
|
||||
* {@link LRUCache.OptionsBase.ignoreFetchAbort}
|
||||
*/
|
||||
ignoreFetchAbort;
|
||||
/** {@link LRUCache.OptionsBase.backgroundFetchSize} */
|
||||
backgroundFetchSize;
|
||||
// computed properties
|
||||
#size;
|
||||
#calculatedSize;
|
||||
@@ -184,7 +227,6 @@ export class LRUCache {
|
||||
#sizes;
|
||||
#starts;
|
||||
#ttls;
|
||||
#autopurgeTimers;
|
||||
#hasDispose;
|
||||
#hasFetchMethod;
|
||||
#hasDisposeAfter;
|
||||
@@ -203,7 +245,6 @@ export class LRUCache {
|
||||
// properties
|
||||
starts: c.#starts,
|
||||
ttls: c.#ttls,
|
||||
autopurgeTimers: c.#autopurgeTimers,
|
||||
sizes: c.#sizes,
|
||||
keyMap: c.#keyMap,
|
||||
keyList: c.#keyList,
|
||||
@@ -279,8 +320,7 @@ export class LRUCache {
|
||||
return this.#disposeAfter;
|
||||
}
|
||||
constructor(options) {
|
||||
const { max = 0, ttl, ttlResolution = 1, ttlAutopurge, updateAgeOnGet, updateAgeOnHas, allowStale, dispose, onInsert, disposeAfter, noDisposeOnSet, noUpdateTTL, maxSize = 0, maxEntrySize = 0, sizeCalculation, fetchMethod, memoMethod, noDeleteOnFetchRejection, noDeleteOnStaleGet, allowStaleOnFetchRejection, allowStaleOnFetchAbort, ignoreFetchAbort, backgroundFetchSize = 1, perf, } = options;
|
||||
this.backgroundFetchSize = backgroundFetchSize;
|
||||
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');
|
||||
@@ -306,18 +346,20 @@ export class LRUCache {
|
||||
throw new TypeError('sizeCalculation set to non-function');
|
||||
}
|
||||
}
|
||||
if (memoMethod !== undefined && typeof memoMethod !== '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') {
|
||||
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.#keyList = new Array(max).fill(undefined);
|
||||
this.#valList = new Array(max).fill(undefined);
|
||||
this.#next = new UintArray(max);
|
||||
this.#prev = new UintArray(max);
|
||||
this.#head = 0;
|
||||
@@ -365,7 +407,9 @@ export class LRUCache {
|
||||
this.updateAgeOnGet = !!updateAgeOnGet;
|
||||
this.updateAgeOnHas = !!updateAgeOnHas;
|
||||
this.ttlResolution =
|
||||
isPosInt(ttlResolution) || ttlResolution === 0 ? ttlResolution : 1;
|
||||
isPosInt(ttlResolution) || ttlResolution === 0 ?
|
||||
ttlResolution
|
||||
: 1;
|
||||
this.ttlAutopurge = !!ttlAutopurge;
|
||||
this.ttl = ttl || 0;
|
||||
if (this.ttl) {
|
||||
@@ -400,56 +444,33 @@ export class LRUCache {
|
||||
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);
|
||||
if (ttl !== 0 && this.ttlAutopurge) {
|
||||
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 */
|
||||
}
|
||||
};
|
||||
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) {
|
||||
/* c8 ignore next */
|
||||
if (!ttl || !start)
|
||||
return;
|
||||
}
|
||||
/* c8 ignore stop */
|
||||
status.ttl = ttl;
|
||||
status.start = start;
|
||||
status.now = cachedNow || getNow();
|
||||
@@ -508,15 +529,12 @@ export class LRUCache {
|
||||
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)) {
|
||||
// provisionally accept background fetches.
|
||||
// actual value size will be checked when they return.
|
||||
if (this.#isBackgroundFetch(v)) {
|
||||
// NB: this cannot occur if v.__staleWhileFetching is set,
|
||||
// because in that case, it would take on the size of the
|
||||
// existing entry that it temporarily replaces.
|
||||
return this.backgroundFetchSize;
|
||||
}
|
||||
if (sizeCalculation) {
|
||||
if (typeof sizeCalculation !== 'function') {
|
||||
throw new TypeError('sizeCalculation must be a function');
|
||||
@@ -559,7 +577,10 @@ export class LRUCache {
|
||||
};
|
||||
*#indexes({ allowStale = this.allowStale } = {}) {
|
||||
if (this.#size) {
|
||||
for (let i = this.#tail; this.#isValidIndex(i);) {
|
||||
for (let i = this.#tail; true;) {
|
||||
if (!this.#isValidIndex(i)) {
|
||||
break;
|
||||
}
|
||||
if (allowStale || !this.#isStale(i)) {
|
||||
yield i;
|
||||
}
|
||||
@@ -574,7 +595,10 @@ export class LRUCache {
|
||||
}
|
||||
*#rindexes({ allowStale = this.allowStale } = {}) {
|
||||
if (this.#size) {
|
||||
for (let i = this.#head; this.#isValidIndex(i);) {
|
||||
for (let i = this.#head; true;) {
|
||||
if (!this.#isValidIndex(i)) {
|
||||
break;
|
||||
}
|
||||
if (allowStale || !this.#isStale(i)) {
|
||||
yield i;
|
||||
}
|
||||
@@ -626,7 +650,8 @@ export class LRUCache {
|
||||
*keys() {
|
||||
for (const i of this.#indexes()) {
|
||||
const k = this.#keyList[i];
|
||||
if (k !== undefined && !this.#isBackgroundFetch(this.#valList[i])) {
|
||||
if (k !== undefined &&
|
||||
!this.#isBackgroundFetch(this.#valList[i])) {
|
||||
yield k;
|
||||
}
|
||||
}
|
||||
@@ -640,7 +665,8 @@ export class LRUCache {
|
||||
*rkeys() {
|
||||
for (const i of this.#rindexes()) {
|
||||
const k = this.#keyList[i];
|
||||
if (k !== undefined && !this.#isBackgroundFetch(this.#valList[i])) {
|
||||
if (k !== undefined &&
|
||||
!this.#isBackgroundFetch(this.#valList[i])) {
|
||||
yield k;
|
||||
}
|
||||
}
|
||||
@@ -652,7 +678,8 @@ export class LRUCache {
|
||||
*values() {
|
||||
for (const i of this.#indexes()) {
|
||||
const v = this.#valList[i];
|
||||
if (v !== undefined && !this.#isBackgroundFetch(this.#valList[i])) {
|
||||
if (v !== undefined &&
|
||||
!this.#isBackgroundFetch(this.#valList[i])) {
|
||||
yield this.#valList[i];
|
||||
}
|
||||
}
|
||||
@@ -666,7 +693,8 @@ export class LRUCache {
|
||||
*rvalues() {
|
||||
for (const i of this.#rindexes()) {
|
||||
const v = this.#valList[i];
|
||||
if (v !== undefined && !this.#isBackgroundFetch(this.#valList[i])) {
|
||||
if (v !== undefined &&
|
||||
!this.#isBackgroundFetch(this.#valList[i])) {
|
||||
yield this.#valList[i];
|
||||
}
|
||||
}
|
||||
@@ -695,7 +723,7 @@ export class LRUCache {
|
||||
if (value === undefined)
|
||||
continue;
|
||||
if (fn(value, this.#keyList[i], this)) {
|
||||
return this.#get(this.#keyList[i], getOptions);
|
||||
return this.get(this.#keyList[i], getOptions);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -768,7 +796,7 @@ export class LRUCache {
|
||||
const value = this.#isBackgroundFetch(v) ? v.__staleWhileFetching : v;
|
||||
if (value === undefined)
|
||||
return undefined;
|
||||
/* c8 ignore stop */
|
||||
/* c8 ignore end */
|
||||
const entry = { value };
|
||||
if (this.#ttls && this.#starts) {
|
||||
const ttl = this.#ttls[i];
|
||||
@@ -842,7 +870,7 @@ export class LRUCache {
|
||||
const age = Date.now() - entry.start;
|
||||
entry.start = this.#perf.now() - age;
|
||||
}
|
||||
this.#set(key, entry.value, entry);
|
||||
this.set(key, entry.value, entry);
|
||||
}
|
||||
}
|
||||
/**
|
||||
@@ -876,43 +904,22 @@ export class LRUCache {
|
||||
* `cache.delete(key)`. `undefined` is never stored in the cache.
|
||||
*/
|
||||
set(k, v, setOptions = {}) {
|
||||
const { status = metrics.hasSubscribers ? {} : undefined } = setOptions;
|
||||
setOptions.status = status;
|
||||
if (status) {
|
||||
status.op = 'set';
|
||||
status.key = k;
|
||||
if (v !== undefined)
|
||||
status.value = v;
|
||||
status.cache = this;
|
||||
}
|
||||
const result = this.#set(k, v, setOptions);
|
||||
if (status && metrics.hasSubscribers) {
|
||||
metrics.publish(status);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
#set(k, v, setOptions, bf) {
|
||||
const { ttl = this.ttl, start, noDisposeOnSet = this.noDisposeOnSet, sizeCalculation = this.sizeCalculation, status, } = setOptions;
|
||||
const isBF = this.#isBackgroundFetch(v);
|
||||
if (v === undefined) {
|
||||
if (status)
|
||||
status.set = 'deleted';
|
||||
this.delete(k);
|
||||
return this;
|
||||
}
|
||||
const { ttl = this.ttl, start, noDisposeOnSet = this.noDisposeOnSet, sizeCalculation = this.sizeCalculation, status, } = setOptions;
|
||||
let { noUpdateTTL = this.noUpdateTTL } = setOptions;
|
||||
if (status && !isBF)
|
||||
status.value = v;
|
||||
const size = this.#requireSize(k, v, setOptions.size || 0, sizeCalculation, status);
|
||||
const size = this.#requireSize(k, v, setOptions.size || 0, sizeCalculation);
|
||||
// if the item doesn't fit, don't do anything
|
||||
// NB: maxEntrySize set to maxSize by default
|
||||
if (this.maxEntrySize && size > this.maxEntrySize) {
|
||||
// have to delete, in case something is there already.
|
||||
this.#delete(k, 'set');
|
||||
if (status) {
|
||||
status.set = 'miss';
|
||||
status.maxEntrySizeExceeded = true;
|
||||
}
|
||||
// have to delete, in case something is there already.
|
||||
this.#delete(k, 'set');
|
||||
return this;
|
||||
}
|
||||
let index = this.#size === 0 ? undefined : this.#keyMap.get(k);
|
||||
@@ -933,68 +940,52 @@ export class LRUCache {
|
||||
if (status)
|
||||
status.set = 'add';
|
||||
noUpdateTTL = false;
|
||||
if (this.#hasOnInsert && !isBF) {
|
||||
if (this.#hasOnInsert) {
|
||||
this.#onInsert?.(v, k, 'add');
|
||||
}
|
||||
}
|
||||
else {
|
||||
// update
|
||||
// might be updating a background fetch!
|
||||
this.#moveToTail(index);
|
||||
const oldVal = this.#valList[index];
|
||||
if (v !== oldVal) {
|
||||
if (!noDisposeOnSet) {
|
||||
if (this.#isBackgroundFetch(oldVal)) {
|
||||
if (oldVal !== bf) {
|
||||
// setting over a background fetch, not merely resolving it.
|
||||
oldVal.__abortController.abort(new Error('replaced'));
|
||||
}
|
||||
const { __staleWhileFetching: s } = oldVal;
|
||||
if (s !== undefined && s !== v) {
|
||||
if (this.#hasDispose) {
|
||||
this.#dispose?.(s, k, 'set');
|
||||
}
|
||||
if (this.#hasDisposeAfter) {
|
||||
this.#disposed?.push([s, k, 'set']);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (this.#hasFetchMethod && this.#isBackgroundFetch(oldVal)) {
|
||||
oldVal.__abortController.abort(new Error('replaced'));
|
||||
const { __staleWhileFetching: s } = oldVal;
|
||||
if (s !== undefined && !noDisposeOnSet) {
|
||||
if (this.#hasDispose) {
|
||||
this.#dispose?.(oldVal, k, 'set');
|
||||
this.#dispose?.(s, k, 'set');
|
||||
}
|
||||
if (this.#hasDisposeAfter) {
|
||||
this.#disposed?.push([oldVal, k, 'set']);
|
||||
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 (!isBF) {
|
||||
if (status) {
|
||||
status.set = 'replace';
|
||||
const oldValue = oldVal && this.#isBackgroundFetch(oldVal) ?
|
||||
oldVal.__staleWhileFetching
|
||||
: oldVal;
|
||||
const setType = oldValue === undefined ? 'add'
|
||||
: v !== oldValue ? 'replace'
|
||||
: 'update';
|
||||
if (status) {
|
||||
status.set = setType;
|
||||
if (oldValue !== undefined)
|
||||
status.oldValue = oldValue;
|
||||
}
|
||||
if (this.#hasOnInsert) {
|
||||
this.onInsert?.(v, k, setType);
|
||||
}
|
||||
if (oldValue !== undefined)
|
||||
status.oldValue = oldValue;
|
||||
}
|
||||
}
|
||||
else if (!isBF) {
|
||||
if (status) {
|
||||
status.set = 'update';
|
||||
}
|
||||
if (this.#hasOnInsert) {
|
||||
this.onInsert?.(v, k, 'update');
|
||||
}
|
||||
else if (status) {
|
||||
status.set = 'update';
|
||||
}
|
||||
if (this.#hasOnInsert) {
|
||||
this.onInsert?.(v, k, v === oldVal ? 'update' : 'replace');
|
||||
}
|
||||
}
|
||||
if (ttl !== 0 && !this.#ttls) {
|
||||
@@ -1049,25 +1040,18 @@ export class LRUCache {
|
||||
const head = this.#head;
|
||||
const k = this.#keyList[head];
|
||||
const v = this.#valList[head];
|
||||
const isBF = this.#isBackgroundFetch(v);
|
||||
if (isBF) {
|
||||
if (this.#hasFetchMethod && this.#isBackgroundFetch(v)) {
|
||||
v.__abortController.abort(new Error('evicted'));
|
||||
}
|
||||
const oldValue = isBF ? v.__staleWhileFetching : v;
|
||||
if ((this.#hasDispose || this.#hasDisposeAfter) &&
|
||||
oldValue !== undefined) {
|
||||
else if (this.#hasDispose || this.#hasDisposeAfter) {
|
||||
if (this.#hasDispose) {
|
||||
this.#dispose?.(oldValue, k, 'evict');
|
||||
this.#dispose?.(v, k, 'evict');
|
||||
}
|
||||
if (this.#hasDisposeAfter) {
|
||||
this.#disposed?.push([oldValue, k, 'evict']);
|
||||
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;
|
||||
@@ -1102,19 +1086,6 @@ export class LRUCache {
|
||||
* {@link LRUCache.OptionsBase.updateAgeOnHas} is set.
|
||||
*/
|
||||
has(k, hasOptions = {}) {
|
||||
const { status = metrics.hasSubscribers ? {} : undefined } = hasOptions;
|
||||
hasOptions.status = status;
|
||||
if (status) {
|
||||
status.op = 'has';
|
||||
status.key = k;
|
||||
status.cache = this;
|
||||
}
|
||||
const result = this.#has(k, hasOptions);
|
||||
if (metrics.hasSubscribers)
|
||||
metrics.publish(status);
|
||||
return result;
|
||||
}
|
||||
#has(k, hasOptions = {}) {
|
||||
const { updateAgeOnHas = this.updateAgeOnHas, status } = hasOptions;
|
||||
const index = this.#keyMap.get(k);
|
||||
if (index !== undefined) {
|
||||
@@ -1151,46 +1122,22 @@ export class LRUCache {
|
||||
* {@link LRUCache.OptionsBase.allowStale} is set.
|
||||
*/
|
||||
peek(k, peekOptions = {}) {
|
||||
const { status = hasSubscribers() ? {} : undefined } = peekOptions;
|
||||
if (status) {
|
||||
status.op = 'peek';
|
||||
status.key = k;
|
||||
status.cache = this;
|
||||
}
|
||||
peekOptions.status = status;
|
||||
const result = this.#peek(k, peekOptions);
|
||||
if (metrics.hasSubscribers) {
|
||||
metrics.publish(status);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
#peek(k, peekOptions) {
|
||||
const { status, allowStale = this.allowStale } = peekOptions;
|
||||
const { 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;
|
||||
if (index === undefined ||
|
||||
(!allowStale && this.#isStale(index))) {
|
||||
return;
|
||||
}
|
||||
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;
|
||||
// either stale and allowed, or forcing a refresh of non-stale value
|
||||
return this.#isBackgroundFetch(v) ? v.__staleWhileFetching : v;
|
||||
}
|
||||
#backgroundFetch(k, index, options, context) {
|
||||
const v = index === undefined ? undefined : this.#valList[index];
|
||||
if (this.#isBackgroundFetch(v)) {
|
||||
return v;
|
||||
}
|
||||
const ac = new AbortController();
|
||||
const ac = new AC();
|
||||
const { signal } = options;
|
||||
// when/if our AC signals, then stop listening to theirs.
|
||||
signal?.addEventListener('abort', () => ac.abort(signal.reason), {
|
||||
@@ -1204,8 +1151,6 @@ export class LRUCache {
|
||||
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;
|
||||
@@ -1218,7 +1163,7 @@ export class LRUCache {
|
||||
}
|
||||
}
|
||||
if (aborted && !ignoreAbort && !updateCache) {
|
||||
return fetchFail(ac.signal.reason, proceed);
|
||||
return fetchFail(ac.signal.reason);
|
||||
}
|
||||
// either we didn't abort, and are still here, or we did, and ignored
|
||||
const bf = p;
|
||||
@@ -1226,7 +1171,7 @@ export class LRUCache {
|
||||
// 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 (vl === p || ignoreAbort && updateCache && vl === undefined) {
|
||||
if (v === undefined) {
|
||||
if (bf.__staleWhileFetching !== undefined) {
|
||||
this.#valList[index] = bf.__staleWhileFetching;
|
||||
@@ -1238,7 +1183,7 @@ export class LRUCache {
|
||||
else {
|
||||
if (options.status)
|
||||
options.status.fetchUpdated = true;
|
||||
this.#set(k, v, fetchOpts.options, bf);
|
||||
this.set(k, v, fetchOpts.options);
|
||||
}
|
||||
}
|
||||
return v;
|
||||
@@ -1248,10 +1193,9 @@ export class LRUCache {
|
||||
options.status.fetchRejected = true;
|
||||
options.status.fetchError = er;
|
||||
}
|
||||
// do not pass go, do not collect $200
|
||||
return fetchFail(er, false);
|
||||
return fetchFail(er);
|
||||
};
|
||||
const fetchFail = (er, proceed) => {
|
||||
const fetchFail = (er) => {
|
||||
const { aborted } = ac.signal;
|
||||
const allowStaleAborted = aborted && options.allowStaleOnFetchAbort;
|
||||
const allowStale = allowStaleAborted || options.allowStaleOnFetchRejection;
|
||||
@@ -1260,7 +1204,7 @@ export class LRUCache {
|
||||
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);
|
||||
const del = !noDelete || bf.__staleWhileFetching === undefined;
|
||||
if (del) {
|
||||
this.#delete(k, 'fetch');
|
||||
}
|
||||
@@ -1284,11 +1228,15 @@ export class LRUCache {
|
||||
};
|
||||
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) {
|
||||
if (!options.ignoreFetchAbort ||
|
||||
options.allowStaleOnFetchAbort) {
|
||||
res(undefined);
|
||||
// when it eventually resolves, update the cache.
|
||||
if (options.allowStaleOnFetchAbort) {
|
||||
@@ -1296,12 +1244,6 @@ export class LRUCache {
|
||||
}
|
||||
}
|
||||
});
|
||||
if (fmp && fmp instanceof Promise) {
|
||||
fmp.then(v => res(v === undefined ? undefined : v), rej);
|
||||
}
|
||||
else if (fmp !== undefined) {
|
||||
res(fmp);
|
||||
}
|
||||
};
|
||||
if (options.status)
|
||||
options.status.fetchDispatched = true;
|
||||
@@ -1313,14 +1255,10 @@ export class LRUCache {
|
||||
});
|
||||
if (index === undefined) {
|
||||
// internal, don't expose status.
|
||||
this.#set(k, bf, { ...fetchOpts.options, status: undefined });
|
||||
this.set(k, bf, { ...fetchOpts.options, status: undefined });
|
||||
index = this.#keyMap.get(k);
|
||||
}
|
||||
else {
|
||||
// do not call #set, because we do not want to adjust its place
|
||||
// in the lru queue, as it has not yet been "used". Also, we don't
|
||||
// need to worry about evicting for size, because a background fetch
|
||||
// over a stale value is treated as the same size as its stale value.
|
||||
this.#valList[index] = bf;
|
||||
}
|
||||
return bf;
|
||||
@@ -1332,23 +1270,9 @@ export class LRUCache {
|
||||
return (!!b &&
|
||||
b instanceof Promise &&
|
||||
b.hasOwnProperty('__staleWhileFetching') &&
|
||||
b.__abortController instanceof AbortController);
|
||||
b.__abortController instanceof AC);
|
||||
}
|
||||
fetch(k, fetchOptions = {}) {
|
||||
const ths = tracing.hasSubscribers;
|
||||
const { status = hasSubscribers() ? {} : undefined } = fetchOptions;
|
||||
fetchOptions.status = status;
|
||||
if (status && fetchOptions.context) {
|
||||
status.context = fetchOptions.context;
|
||||
}
|
||||
const p = this.#fetch(k, fetchOptions);
|
||||
if (status && ths) {
|
||||
status.trace = true;
|
||||
tracing.tracePromise(() => p, status).catch(() => { });
|
||||
}
|
||||
return p;
|
||||
}
|
||||
async #fetch(k, fetchOptions = {}) {
|
||||
async fetch(k, fetchOptions = {}) {
|
||||
const {
|
||||
// get options
|
||||
allowStale = this.allowStale, updateAgeOnGet = this.updateAgeOnGet, noDeleteOnStaleGet = this.noDeleteOnStaleGet,
|
||||
@@ -1356,17 +1280,10 @@ export class LRUCache {
|
||||
ttl = this.ttl, noDisposeOnSet = this.noDisposeOnSet, size = 0, sizeCalculation = this.sizeCalculation, noUpdateTTL = this.noUpdateTTL,
|
||||
// fetch exclusive options
|
||||
noDeleteOnFetchRejection = this.noDeleteOnFetchRejection, allowStaleOnFetchRejection = this.allowStaleOnFetchRejection, ignoreFetchAbort = this.ignoreFetchAbort, allowStaleOnFetchAbort = this.allowStaleOnFetchAbort, context, forceRefresh = false, status, signal, } = fetchOptions;
|
||||
if (status) {
|
||||
status.op = 'fetch';
|
||||
status.key = k;
|
||||
if (forceRefresh)
|
||||
status.forceRefresh = true;
|
||||
status.cache = this;
|
||||
}
|
||||
if (!this.#hasFetchMethod) {
|
||||
if (status)
|
||||
status.fetch = 'get';
|
||||
return this.#get(k, {
|
||||
return this.get(k, {
|
||||
allowStale,
|
||||
updateAgeOnGet,
|
||||
noDeleteOnStaleGet,
|
||||
@@ -1435,68 +1352,26 @@ export class LRUCache {
|
||||
return staleVal ? p.__staleWhileFetching : (p.__returned = p);
|
||||
}
|
||||
}
|
||||
forceFetch(k, fetchOptions = {}) {
|
||||
const ths = tracing.hasSubscribers;
|
||||
const { status = hasSubscribers() ? {} : undefined } = fetchOptions;
|
||||
fetchOptions.status = status;
|
||||
if (status && fetchOptions.context) {
|
||||
status.context = fetchOptions.context;
|
||||
}
|
||||
const p = this.#forceFetch(k, fetchOptions);
|
||||
if (status && ths) {
|
||||
status.trace = true;
|
||||
tracing.tracePromise(() => p, status).catch(() => { });
|
||||
}
|
||||
return p;
|
||||
}
|
||||
async #forceFetch(k, fetchOptions = {}) {
|
||||
const v = await this.#fetch(k, fetchOptions);
|
||||
async forceFetch(k, fetchOptions = {}) {
|
||||
const v = await this.fetch(k, fetchOptions);
|
||||
if (v === undefined)
|
||||
throw new Error('fetch() returned undefined');
|
||||
return v;
|
||||
}
|
||||
memo(k, memoOptions = {}) {
|
||||
const { status = metrics.hasSubscribers ? {} : undefined } = memoOptions;
|
||||
memoOptions.status = status;
|
||||
if (status) {
|
||||
status.op = 'memo';
|
||||
status.key = k;
|
||||
if (memoOptions.context) {
|
||||
status.context = memoOptions.context;
|
||||
}
|
||||
status.cache = this;
|
||||
}
|
||||
const result = this.#memo(k, memoOptions);
|
||||
if (status)
|
||||
status.value = result;
|
||||
if (metrics.hasSubscribers)
|
||||
metrics.publish(status);
|
||||
return result;
|
||||
}
|
||||
#memo(k, memoOptions = {}) {
|
||||
const memoMethod = this.#memoMethod;
|
||||
if (!memoMethod) {
|
||||
throw new Error('no memoMethod provided to constructor');
|
||||
}
|
||||
const { context, status, forceRefresh, ...options } = memoOptions;
|
||||
if (status && forceRefresh)
|
||||
status.forceRefresh = true;
|
||||
const v = this.#get(k, options);
|
||||
const refresh = forceRefresh || v === undefined;
|
||||
if (status) {
|
||||
status.memo = refresh ? 'miss' : 'hit';
|
||||
if (!refresh)
|
||||
status.value = v;
|
||||
}
|
||||
if (!refresh)
|
||||
const { context, forceRefresh, ...options } = memoOptions;
|
||||
const v = this.get(k, options);
|
||||
if (!forceRefresh && v !== undefined)
|
||||
return v;
|
||||
const vv = memoMethod(k, v, {
|
||||
options,
|
||||
context,
|
||||
});
|
||||
if (status)
|
||||
status.value = vv;
|
||||
this.#set(k, vv, options);
|
||||
this.set(k, vv, options);
|
||||
return vv;
|
||||
}
|
||||
/**
|
||||
@@ -1506,71 +1381,55 @@ export class LRUCache {
|
||||
* If the key is not found, get() will return `undefined`.
|
||||
*/
|
||||
get(k, getOptions = {}) {
|
||||
const { status = metrics.hasSubscribers ? {} : undefined } = getOptions;
|
||||
getOptions.status = status;
|
||||
if (status) {
|
||||
status.op = 'get';
|
||||
status.key = k;
|
||||
status.cache = this;
|
||||
}
|
||||
const result = this.#get(k, getOptions);
|
||||
if (status) {
|
||||
if (result !== undefined)
|
||||
status.value = result;
|
||||
if (metrics.hasSubscribers)
|
||||
metrics.publish(status);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
#get(k, getOptions = {}) {
|
||||
const { allowStale = this.allowStale, updateAgeOnGet = this.updateAgeOnGet, noDeleteOnStaleGet = this.noDeleteOnStaleGet, status, } = getOptions;
|
||||
const index = this.#keyMap.get(k);
|
||||
if (index === undefined) {
|
||||
if (index !== undefined) {
|
||||
const value = this.#valList[index];
|
||||
const fetching = this.#isBackgroundFetch(value);
|
||||
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');
|
||||
}
|
||||
this.#statusTTL(status, index);
|
||||
if (this.#isStale(index)) {
|
||||
if (status)
|
||||
status.get = 'stale';
|
||||
if (allowStale) {
|
||||
if (status)
|
||||
// delete only if not an in-flight background fetch
|
||||
if (!fetching) {
|
||||
if (!noDeleteOnStaleGet) {
|
||||
this.#delete(k, 'expire');
|
||||
}
|
||||
if (status && allowStale)
|
||||
status.returnedStale = true;
|
||||
return value;
|
||||
return allowStale ? value : undefined;
|
||||
}
|
||||
else {
|
||||
if (status &&
|
||||
allowStale &&
|
||||
value.__staleWhileFetching !== undefined) {
|
||||
status.returnedStale = true;
|
||||
}
|
||||
return allowStale ? value.__staleWhileFetching : undefined;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
if (status)
|
||||
status.get = 'stale-fetching';
|
||||
if (allowStale && value.__staleWhileFetching !== undefined) {
|
||||
else {
|
||||
if (status)
|
||||
status.returnedStale = true;
|
||||
return value.__staleWhileFetching;
|
||||
status.get = 'hit';
|
||||
// if we're currently fetching it, we don't actually have it yet
|
||||
// it's not stale, which means this isn't a staleWhileRefetching.
|
||||
// If it's not stale, and fetching, AND has a __staleWhileFetching
|
||||
// value, then that means the user fetched with {forceRefresh:true},
|
||||
// so it's safe to return that value.
|
||||
if (fetching) {
|
||||
return value.__staleWhileFetching;
|
||||
}
|
||||
this.#moveToTail(index);
|
||||
if (updateAgeOnGet) {
|
||||
this.#updateItemAge(index);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
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);
|
||||
else if (status) {
|
||||
status.get = 'miss';
|
||||
}
|
||||
return fetching ? value.__staleWhileFetching : value;
|
||||
}
|
||||
#connect(p, n) {
|
||||
this.#prev[n] = p;
|
||||
@@ -1605,22 +1464,10 @@ export class LRUCache {
|
||||
return this.#delete(k, 'delete');
|
||||
}
|
||||
#delete(k, reason) {
|
||||
if (metrics.hasSubscribers) {
|
||||
metrics.publish({
|
||||
op: 'delete',
|
||||
delete: reason,
|
||||
key: k,
|
||||
cache: this,
|
||||
});
|
||||
}
|
||||
let deleted = false;
|
||||
if (this.#size !== 0) {
|
||||
const index = this.#keyMap.get(k);
|
||||
if (index !== undefined) {
|
||||
if (this.#autopurgeTimers?.[index]) {
|
||||
clearTimeout(this.#autopurgeTimers?.[index]);
|
||||
this.#autopurgeTimers[index] = undefined;
|
||||
}
|
||||
deleted = true;
|
||||
if (this.#size === 1) {
|
||||
this.#clear(reason);
|
||||
@@ -1691,16 +1538,11 @@ export class LRUCache {
|
||||
}
|
||||
}
|
||||
this.#keyMap.clear();
|
||||
void this.#valList.fill(undefined);
|
||||
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);
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+4
-4
File diff suppressed because one or more lines are too long
+38
-79
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "lru-cache",
|
||||
"description": "A cache object that deletes the least-recently-used items.",
|
||||
"version": "11.5.0",
|
||||
"version": "11.2.2",
|
||||
"author": "Isaac Z. Schlueter <i@izs.me>",
|
||||
"keywords": [
|
||||
"mru",
|
||||
@@ -11,7 +11,7 @@
|
||||
"sideEffects": false,
|
||||
"scripts": {
|
||||
"build": "npm run prepare",
|
||||
"prepare": "tshy && bash scripts/build.sh",
|
||||
"prepare": "tshy && bash fixup.sh",
|
||||
"pretest": "npm run prepare",
|
||||
"presnap": "npm run prepare",
|
||||
"test": "tap",
|
||||
@@ -25,130 +25,89 @@
|
||||
"prebenchmark": "npm run prepare",
|
||||
"benchmark": "make -C benchmark",
|
||||
"preprofile": "npm run prepare",
|
||||
"profile": "make -C benchmark profile",
|
||||
"lint": "oxlint --fix src test",
|
||||
"postsnap": "npm run lint",
|
||||
"postlint": "npm run format"
|
||||
"profile": "make -C benchmark profile"
|
||||
},
|
||||
"main": "./dist/commonjs/index.min.js",
|
||||
"main": "./dist/commonjs/index.js",
|
||||
"types": "./dist/commonjs/index.d.ts",
|
||||
"tshy": {
|
||||
"esmDialects": [
|
||||
"browser",
|
||||
"node"
|
||||
],
|
||||
"commonjsDialects": [
|
||||
"browser",
|
||||
"node"
|
||||
],
|
||||
"exports": {
|
||||
"./raw": "./src/index.ts",
|
||||
".": {
|
||||
".": "./src/index.ts",
|
||||
"./min": {
|
||||
"import": {
|
||||
"browser": {
|
||||
"types": "./dist/esm/browser/index.d.ts",
|
||||
"default": "./dist/esm/browser/index.min.js"
|
||||
},
|
||||
"node": {
|
||||
"types": "./dist/esm/node/index.d.ts",
|
||||
"default": "./dist/esm/node/index.min.js"
|
||||
},
|
||||
"types": "./dist/esm/index.d.ts",
|
||||
"default": "./dist/esm/index.min.js"
|
||||
},
|
||||
"require": {
|
||||
"browser": {
|
||||
"types": "./dist/commonjs/browser/index.d.ts",
|
||||
"default": "./dist/commonjs/browser/index.min.js"
|
||||
},
|
||||
"node": {
|
||||
"types": "./dist/commonjs/node/index.d.ts",
|
||||
"default": "./dist/commonjs/node/index.min.js"
|
||||
},
|
||||
"types": "./dist/commonjs/index.d.ts",
|
||||
"default": "./dist/commonjs/index.min.js"
|
||||
}
|
||||
}
|
||||
},
|
||||
"selfLink": false
|
||||
}
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+ssh://git@github.com/isaacs/node-lru-cache.git"
|
||||
"url": "git://github.com/isaacs/node-lru-cache.git"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^24.3.0",
|
||||
"benchmark": "^2.1.4",
|
||||
"esbuild": "^0.28.0",
|
||||
"esbuild": "^0.25.9",
|
||||
"marked": "^4.2.12",
|
||||
"mkdirp": "^3.0.1",
|
||||
"oxlint": "^1.65.0",
|
||||
"oxlint-tsgolint": "^0.22.1",
|
||||
"prettier": "^3.8.3",
|
||||
"tap": "^21.7.4",
|
||||
"tshy": "^4.1.2",
|
||||
"typedoc": "^0.28.19"
|
||||
"prettier": "^3.6.2",
|
||||
"tap": "^21.1.0",
|
||||
"tshy": "^3.0.2",
|
||||
"typedoc": "^0.28.12"
|
||||
},
|
||||
"license": "BlueOak-1.0.0",
|
||||
"license": "ISC",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"engines": {
|
||||
"node": "20 || >=22"
|
||||
},
|
||||
"prettier": {
|
||||
"experimentalTernaries": true,
|
||||
"semi": false,
|
||||
"printWidth": 70,
|
||||
"tabWidth": 2,
|
||||
"useTabs": false,
|
||||
"singleQuote": true,
|
||||
"jsxSingleQuote": false,
|
||||
"bracketSameLine": true,
|
||||
"arrowParens": "avoid",
|
||||
"endOfLine": "lf"
|
||||
},
|
||||
"tap": {
|
||||
"node-arg": [
|
||||
"--expose-gc"
|
||||
],
|
||||
"plugin": [
|
||||
"@tapjs/clock"
|
||||
]
|
||||
},
|
||||
"exports": {
|
||||
"./raw": {
|
||||
".": {
|
||||
"import": {
|
||||
"browser": {
|
||||
"types": "./dist/esm/browser/index.d.ts",
|
||||
"default": "./dist/esm/browser/index.js"
|
||||
},
|
||||
"node": {
|
||||
"types": "./dist/esm/node/index.d.ts",
|
||||
"default": "./dist/esm/node/index.js"
|
||||
},
|
||||
"types": "./dist/esm/index.d.ts",
|
||||
"default": "./dist/esm/index.js"
|
||||
},
|
||||
"require": {
|
||||
"browser": {
|
||||
"types": "./dist/commonjs/browser/index.d.ts",
|
||||
"default": "./dist/commonjs/browser/index.js"
|
||||
},
|
||||
"node": {
|
||||
"types": "./dist/commonjs/node/index.d.ts",
|
||||
"default": "./dist/commonjs/node/index.js"
|
||||
},
|
||||
"types": "./dist/commonjs/index.d.ts",
|
||||
"default": "./dist/commonjs/index.js"
|
||||
}
|
||||
},
|
||||
".": {
|
||||
"./min": {
|
||||
"import": {
|
||||
"browser": {
|
||||
"types": "./dist/esm/browser/index.d.ts",
|
||||
"default": "./dist/esm/browser/index.min.js"
|
||||
},
|
||||
"node": {
|
||||
"types": "./dist/esm/node/index.d.ts",
|
||||
"default": "./dist/esm/node/index.min.js"
|
||||
},
|
||||
"types": "./dist/esm/index.d.ts",
|
||||
"default": "./dist/esm/index.min.js"
|
||||
},
|
||||
"require": {
|
||||
"browser": {
|
||||
"types": "./dist/commonjs/browser/index.d.ts",
|
||||
"default": "./dist/commonjs/browser/index.min.js"
|
||||
},
|
||||
"node": {
|
||||
"types": "./dist/commonjs/node/index.d.ts",
|
||||
"default": "./dist/commonjs/node/index.min.js"
|
||||
},
|
||||
"types": "./dist/commonjs/index.d.ts",
|
||||
"default": "./dist/commonjs/index.min.js"
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "module",
|
||||
"module": "./dist/esm/index.min.js"
|
||||
"module": "./dist/esm/index.js"
|
||||
}
|
||||
|
||||
+17
-13
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "hosted-git-info",
|
||||
"version": "9.0.3",
|
||||
"version": "9.0.2",
|
||||
"description": "Provides metadata and conversions from repository urls for GitHub, Bitbucket and GitLab",
|
||||
"main": "./lib/index.js",
|
||||
"repository": {
|
||||
@@ -21,23 +21,22 @@
|
||||
"homepage": "https://github.com/npm/hosted-git-info",
|
||||
"scripts": {
|
||||
"posttest": "npm run lint",
|
||||
"snap": "node --test --test-update-snapshots './test/**/*.js'",
|
||||
"test": "node --test './test/**/*.js'",
|
||||
"snap": "tap",
|
||||
"test": "tap",
|
||||
"test:coverage": "tap --coverage-report=html",
|
||||
"lint": "npm run eslint",
|
||||
"postlint": "template-oss-check",
|
||||
"lintfix": "npm run eslint -- --fix",
|
||||
"template-oss-apply": "template-oss-apply --force",
|
||||
"eslint": "eslint \"**/*.{js,cjs,ts,mjs,jsx,tsx}\"",
|
||||
"test:node20": "node --test test",
|
||||
"test:cover": "node --test --experimental-test-coverage --test-timeout=3000 --test-coverage-lines=100 --test-coverage-functions=100 --test-coverage-branches=100 './test/**/*.js'"
|
||||
"eslint": "eslint \"**/*.{js,cjs,ts,mjs,jsx,tsx}\""
|
||||
},
|
||||
"dependencies": {
|
||||
"lru-cache": "^11.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@npmcli/eslint-config": "^6.0.0",
|
||||
"@npmcli/template-oss": "4.30.0"
|
||||
"@npmcli/eslint-config": "^5.0.0",
|
||||
"@npmcli/template-oss": "4.25.1",
|
||||
"tap": "^16.0.1"
|
||||
},
|
||||
"files": [
|
||||
"bin/",
|
||||
@@ -46,12 +45,17 @@
|
||||
"engines": {
|
||||
"node": "^20.17.0 || >=22.9.0"
|
||||
},
|
||||
"tap": {
|
||||
"color": 1,
|
||||
"coverage": true,
|
||||
"nyc-arg": [
|
||||
"--exclude",
|
||||
"tap-snapshots/**"
|
||||
]
|
||||
},
|
||||
"templateOSS": {
|
||||
"//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.",
|
||||
"version": "4.30.0",
|
||||
"publish": "true",
|
||||
"testRunner": "node:test",
|
||||
"latestCiVersion": 24,
|
||||
"updateNpm": false
|
||||
"version": "4.25.1",
|
||||
"publish": "true"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user