1118 lines
		
	
	
		
			36 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			1118 lines
		
	
	
		
			36 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| # lru-cache
 | |
| 
 | |
| A cache object that deletes the least-recently-used items.
 | |
| 
 | |
| Specify a max number of the most recently used items that you
 | |
| want to keep, and this cache will keep that many of the most
 | |
| recently accessed items.
 | |
| 
 | |
| This is not primarily a TTL cache, and does not make strong TTL
 | |
| guarantees. There is no preemptive pruning of expired items by
 | |
| default, but you _may_ set a TTL on the cache or on a single
 | |
| `set`. If you do so, it will treat expired items as missing, and
 | |
| delete them when fetched. If you are more interested in TTL
 | |
| caching than LRU caching, check out
 | |
| [@isaacs/ttlcache](http://npm.im/@isaacs/ttlcache).
 | |
| 
 | |
| As of version 7, this is one of the most performant LRU
 | |
| implementations available in JavaScript, and supports a wide
 | |
| diversity of use cases. However, note that using some of the
 | |
| features will necessarily impact performance, by causing the
 | |
| cache to have to do more work. See the "Performance" section
 | |
| below.
 | |
| 
 | |
| ## Installation
 | |
| 
 | |
| ```bash
 | |
| npm install lru-cache --save
 | |
| ```
 | |
| 
 | |
| ## Usage
 | |
| 
 | |
| ```js
 | |
| // hybrid module, either works
 | |
| import LRUCache from 'lru-cache'
 | |
| // or:
 | |
| const LRUCache = require('lru-cache')
 | |
| 
 | |
| // At least one of 'max', 'ttl', or 'maxSize' is required, to prevent
 | |
| // unsafe unbounded storage.
 | |
| //
 | |
| // In most cases, it's best to specify a max for performance, so all
 | |
| // the required memory allocation is done up-front.
 | |
| //
 | |
| // All the other options are optional, see the sections below for
 | |
| // documentation on what each one does.  Most of them can be
 | |
| // overridden for specific items in get()/set()
 | |
| const options = {
 | |
|   max: 500,
 | |
| 
 | |
|   // for use with tracking overall storage size
 | |
|   maxSize: 5000,
 | |
|   sizeCalculation: (value, key) => {
 | |
|     return 1
 | |
|   },
 | |
| 
 | |
|   // for use when you need to clean up something when objects
 | |
|   // are evicted from the cache
 | |
|   dispose: (value, key) => {
 | |
|     freeFromMemoryOrWhatever(value)
 | |
|   },
 | |
| 
 | |
|   // how long to live in ms
 | |
|   ttl: 1000 * 60 * 5,
 | |
| 
 | |
|   // return stale items before removing from cache?
 | |
|   allowStale: false,
 | |
| 
 | |
|   updateAgeOnGet: false,
 | |
|   updateAgeOnHas: false,
 | |
| 
 | |
|   // async method to use for cache.fetch(), for
 | |
|   // stale-while-revalidate type of behavior
 | |
|   fetchMethod: async (key, staleValue, { options, signal }) => {},
 | |
| }
 | |
| 
 | |
| const cache = new LRUCache(options)
 | |
| 
 | |
| cache.set('key', 'value')
 | |
| cache.get('key') // "value"
 | |
| 
 | |
| // non-string keys ARE fully supported
 | |
| // but note that it must be THE SAME object, not
 | |
| // just a JSON-equivalent object.
 | |
| var someObject = { a: 1 }
 | |
| cache.set(someObject, 'a value')
 | |
| // Object keys are not toString()-ed
 | |
| cache.set('[object Object]', 'a different value')
 | |
| assert.equal(cache.get(someObject), 'a value')
 | |
| // A similar object with same keys/values won't work,
 | |
| // because it's a different object identity
 | |
| assert.equal(cache.get({ a: 1 }), undefined)
 | |
| 
 | |
| cache.clear() // empty the cache
 | |
| ```
 | |
| 
 | |
| If you put more stuff in it, then items will fall out.
 | |
| 
 | |
| ## Options
 | |
| 
 | |
| ### `max`
 | |
| 
 | |
| The maximum number of items that remain in the cache (assuming no
 | |
| TTL pruning or explicit deletions). Note that fewer items may be
 | |
| stored if size calculation is used, and `maxSize` is exceeded.
 | |
| This must be a positive finite intger.
 | |
| 
 | |
| At least one of `max`, `maxSize`, or `TTL` is required. This
 | |
| must be a positive integer if set.
 | |
| 
 | |
| **It is strongly recommended to set a `max` to prevent unbounded
 | |
| growth of the cache.** See "Storage Bounds Safety" below.
 | |
| 
 | |
| ### `maxSize`
 | |
| 
 | |
| Set to a positive integer to track the sizes of items added to
 | |
| the cache, and automatically evict items in order to stay below
 | |
| this size. Note that this may result in fewer than `max` items
 | |
| being stored.
 | |
| 
 | |
| Attempting to add an item to the cache whose calculated size is
 | |
| greater that this amount will be a no-op. The item will not be
 | |
| cached, and no other items will be evicted.
 | |
| 
 | |
| Optional, must be a positive integer if provided.
 | |
| 
 | |
| Sets `maxEntrySize` to the same value, unless a different value
 | |
| is provided for `maxEntrySize`.
 | |
| 
 | |
| At least one of `max`, `maxSize`, or `TTL` is required. This
 | |
| must be a positive integer if set.
 | |
| 
 | |
| Even if size tracking is enabled, **it is strongly recommended to
 | |
| set a `max` to prevent unbounded growth of the cache.** See
 | |
| "Storage Bounds Safety" below.
 | |
| 
 | |
| ### `maxEntrySize`
 | |
| 
 | |
| Set to a positive integer to track the sizes of items added to
 | |
| the cache, and prevent caching any item over a given size.
 | |
| Attempting to add an item whose calculated size is greater than
 | |
| this amount will be a no-op. The item will not be cached, and no
 | |
| other items will be evicted.
 | |
| 
 | |
| Optional, must be a positive integer if provided. Defaults to
 | |
| the value of `maxSize` if provided.
 | |
| 
 | |
| ### `sizeCalculation`
 | |
| 
 | |
| Function used to calculate the size of stored items. If you're
 | |
| storing strings or buffers, then you probably want to do
 | |
| something like `n => n.length`. The item is passed as the first
 | |
| argument, and the key is passed as the second argument.
 | |
| 
 | |
| This may be overridden by passing an options object to
 | |
| `cache.set()`.
 | |
| 
 | |
| Requires `maxSize` to be set.
 | |
| 
 | |
| If the `size` (or return value of `sizeCalculation`) for a given
 | |
| entry is greater than `maxEntrySize`, then the item will not be
 | |
| added to the cache.
 | |
| 
 | |
| Deprecated alias: `length`
 | |
| 
 | |
| ### `fetchMethod`
 | |
| 
 | |
| Function that is used to make background asynchronous fetches.
 | |
| Called with `fetchMethod(key, staleValue, { signal, options,
 | |
| context })`. May return a Promise.
 | |
| 
 | |
| If `fetchMethod` is not provided, then `cache.fetch(key)` is
 | |
| equivalent to `Promise.resolve(cache.get(key))`.
 | |
| 
 | |
| The `signal` object is an `AbortSignal` if that's available in
 | |
| the global object, otherwise it's a pretty close polyfill.
 | |
| 
 | |
| If at any time, `signal.aborted` is set to `true`, or if the
 | |
| `signal.onabort` method is called, or if it emits an `'abort'`
 | |
| event which you can listen to with `addEventListener`, then that
 | |
| means that the fetch should be abandoned. This may be passed
 | |
| along to async functions aware of AbortController/AbortSignal
 | |
| behavior.
 | |
| 
 | |
| The `fetchMethod` should **only** return `undefined` or a Promise
 | |
| resolving to `undefined` if the AbortController signaled an
 | |
| `abort` event. In all other cases, it should return or resolve
 | |
| to a value suitable for adding to the cache.
 | |
| 
 | |
| The `options` object is a union of the options that may be
 | |
| provided to `set()` and `get()`. If they are modified, then that
 | |
| will result in modifying the settings to `cache.set()` when the
 | |
| value is resolved, and in the case of `noDeleteOnFetchRejection`
 | |
| and `allowStaleOnFetchRejection`, the handling of `fetchMethod`
 | |
| failures.
 | |
| 
 | |
| For example, a DNS cache may update the TTL based on the value
 | |
| returned from a remote DNS server by changing `options.ttl` in
 | |
| the `fetchMethod`.
 | |
| 
 | |
| ### `fetchContext`
 | |
| 
 | |
| Arbitrary data that can be passed to the `fetchMethod` as the
 | |
| `context` option.
 | |
| 
 | |
| Note that this will only be relevant when the `cache.fetch()`
 | |
| call needs to call `fetchMethod()`. Thus, any data which will
 | |
| meaningfully vary the fetch response needs to be present in the
 | |
| key. This is primarily intended for including `x-request-id`
 | |
| headers and the like for debugging purposes, which do not affect
 | |
| the `fetchMethod()` response.
 | |
| 
 | |
| ### `noDeleteOnFetchRejection`
 | |
| 
 | |
| If a `fetchMethod` throws an error or returns a rejected promise,
 | |
| then by default, any existing stale value will be removed from
 | |
| the cache.
 | |
| 
 | |
| If `noDeleteOnFetchRejection` is set to `true`, then this
 | |
| behavior is suppressed, and the stale value remains in the cache
 | |
| in the case of a rejected `fetchMethod`.
 | |
| 
 | |
| This is important in cases where a `fetchMethod` is _only_ called
 | |
| as a background update while the stale value is returned, when
 | |
| `allowStale` is used.
 | |
| 
 | |
| This is implicitly in effect when `allowStaleOnFetchRejection` is
 | |
| set.
 | |
| 
 | |
| This may be set in calls to `fetch()`, or defaulted on the
 | |
| constructor, or overridden by modifying the options object in the
 | |
| `fetchMethod`.
 | |
| 
 | |
| ### `allowStaleOnFetchRejection`
 | |
| 
 | |
| Set to true to return a stale value from the cache when a
 | |
| `fetchMethod` throws an error or returns a rejected Promise.
 | |
| 
 | |
| If a `fetchMethod` fails, and there is no stale value available,
 | |
| the `fetch()` will resolve to `undefined`. Ie, all `fetchMethod`
 | |
| errors are suppressed.
 | |
| 
 | |
| Implies `noDeleteOnFetchRejection`.
 | |
| 
 | |
| This may be set in calls to `fetch()`, or defaulted on the
 | |
| constructor, or overridden by modifying the options object in the
 | |
| `fetchMethod`.
 | |
| 
 | |
| ### `allowStaleOnFetchAbort`
 | |
| 
 | |
| Set to true to return a stale value from the cache when the
 | |
| `AbortSignal` passed to the `fetchMethod` dispatches an `'abort'`
 | |
| event, whether user-triggered, or due to internal cache behavior.
 | |
| 
 | |
| Unless `ignoreFetchAbort` is also set, the underlying
 | |
| `fetchMethod` will still be considered canceled, and its return
 | |
| value will be ignored and not cached.
 | |
| 
 | |
| ### `ignoreFetchAbort`
 | |
| 
 | |
| Set to true to ignore the `abort` event emitted by the
 | |
| `AbortSignal` object passed to `fetchMethod`, and still cache the
 | |
| resulting resolution value, as long as it is not `undefined`.
 | |
| 
 | |
| When used on its own, this means aborted `fetch()` calls are not
 | |
| immediately resolved or rejected when they are aborted, and
 | |
| instead take the full time to await.
 | |
| 
 | |
| When used with `allowStaleOnFetchAbort`, aborted `fetch()` calls
 | |
| will resolve immediately to their stale cached value or
 | |
| `undefined`, and will continue to process and eventually update
 | |
| the cache when they resolve, as long as the resulting value is
 | |
| not `undefined`, thus supporting a "return stale on timeout while
 | |
| refreshing" mechanism by passing `AbortSignal.timeout(n)` as the
 | |
| signal.
 | |
| 
 | |
| For example:
 | |
| 
 | |
| ```js
 | |
| const c = new LRUCache({
 | |
|   ttl: 100,
 | |
|   ignoreFetchAbort: true,
 | |
|   allowStaleOnFetchAbort: true,
 | |
|   fetchMethod: async (key, oldValue, { signal }) => {
 | |
|     // note: do NOT pass the signal to fetch()!
 | |
|     // let's say this fetch can take a long time.
 | |
|     const res = await fetch(`https://slow-backend-server/${key}`)
 | |
|     return await res.json()
 | |
|   },
 | |
| })
 | |
| 
 | |
| // this will return the stale value after 100ms, while still
 | |
| // updating in the background for next time.
 | |
| const val = await c.fetch('key', { signal: AbortSignal.timeout(100) })
 | |
| ```
 | |
| 
 | |
| **Note**: regardless of this setting, an `abort` event _is still
 | |
| emitted on the `AbortSignal` object_, so may result in invalid
 | |
| results when passed to other underlying APIs that use
 | |
| AbortSignals.
 | |
| 
 | |
| This may be overridden on the `fetch()` call or in the
 | |
| `fetchMethod` itself.
 | |
| 
 | |
| ### `dispose`
 | |
| 
 | |
| Function that is called on items when they are dropped from the
 | |
| cache, as `this.dispose(value, key, reason)`.
 | |
| 
 | |
| This can be handy if you want to close file descriptors or do
 | |
| other cleanup tasks when items are no longer stored in the cache.
 | |
| 
 | |
| **NOTE**: It is called _before_ the item has been fully removed
 | |
| from the cache, so if you want to put it right back in, you need
 | |
| to wait until the next tick. If you try to add it back in during
 | |
| the `dispose()` function call, it will break things in subtle and
 | |
| weird ways.
 | |
| 
 | |
| Unlike several other options, this may _not_ be overridden by
 | |
| passing an option to `set()`, for performance reasons. If
 | |
| disposal functions may vary between cache entries, then the
 | |
| entire list must be scanned on every cache swap, even if no
 | |
| disposal function is in use.
 | |
| 
 | |
| The `reason` will be one of the following strings, corresponding
 | |
| to the reason for the item's deletion:
 | |
| 
 | |
| - `evict` Item was evicted to make space for a new addition
 | |
| - `set` Item was overwritten by a new value
 | |
| - `delete` Item was removed by explicit `cache.delete(key)` or by
 | |
|   calling `cache.clear()`, which deletes everything.
 | |
| 
 | |
| The `dispose()` method is _not_ called for canceled calls to
 | |
| `fetchMethod()`. If you wish to handle evictions, overwrites,
 | |
| and deletes of in-flight asynchronous fetches, you must use the
 | |
| `AbortSignal` provided.
 | |
| 
 | |
| Optional, must be a function.
 | |
| 
 | |
| ### `disposeAfter`
 | |
| 
 | |
| The same as `dispose`, but called _after_ the entry is completely
 | |
| removed and the cache is once again in a clean state.
 | |
| 
 | |
| It is safe to add an item right back into the cache at this
 | |
| point. However, note that it is _very_ easy to inadvertently
 | |
| create infinite recursion in this way.
 | |
| 
 | |
| The `disposeAfter()` method is _not_ called for canceled calls to
 | |
| `fetchMethod()`. If you wish to handle evictions, overwrites,
 | |
| and deletes of in-flight asynchronous fetches, you must use the
 | |
| `AbortSignal` provided.
 | |
| 
 | |
| ### `noDisposeOnSet`
 | |
| 
 | |
| Set to `true` to suppress calling the `dispose()` function if the
 | |
| entry key is still accessible within the cache.
 | |
| 
 | |
| This may be overridden by passing an options object to
 | |
| `cache.set()`.
 | |
| 
 | |
| Boolean, default `false`. Only relevant if `dispose` or
 | |
| `disposeAfter` options are set.
 | |
| 
 | |
| ### `ttl`
 | |
| 
 | |
| Max time to live for items before they are considered stale.
 | |
| Note that stale items are NOT preemptively removed by default,
 | |
| and MAY live in the cache, contributing to its LRU max, long
 | |
| after they have expired.
 | |
| 
 | |
| Also, as this cache is optimized for LRU/MRU operations, some of
 | |
| the staleness/TTL checks will reduce performance.
 | |
| 
 | |
| This is not primarily a TTL cache, and does not make strong TTL
 | |
| guarantees. There is no pre-emptive pruning of expired items,
 | |
| but you _may_ set a TTL on the cache, and it will treat expired
 | |
| items as missing when they are fetched, and delete them.
 | |
| 
 | |
| Optional, but must be a positive integer in ms if specified.
 | |
| 
 | |
| This may be overridden by passing an options object to
 | |
| `cache.set()`.
 | |
| 
 | |
| At least one of `max`, `maxSize`, or `TTL` is required. This
 | |
| must be a positive integer if set.
 | |
| 
 | |
| Even if ttl tracking is enabled, **it is strongly recommended to
 | |
| set a `max` to prevent unbounded growth of the cache.** See
 | |
| "Storage Bounds Safety" below.
 | |
| 
 | |
| If ttl tracking is enabled, and `max` and `maxSize` are not set,
 | |
| and `ttlAutopurge` is not set, then a warning will be emitted
 | |
| cautioning about the potential for unbounded memory consumption.
 | |
| 
 | |
| Deprecated alias: `maxAge`
 | |
| 
 | |
| ### `noUpdateTTL`
 | |
| 
 | |
| Boolean flag to tell the cache to not update the TTL when setting
 | |
| a new value for an existing key (ie, when updating a value rather
 | |
| than inserting a new value). Note that the TTL value is _always_
 | |
| set (if provided) when adding a new entry into the cache.
 | |
| 
 | |
| This may be passed as an option to `cache.set()`.
 | |
| 
 | |
| Boolean, default false.
 | |
| 
 | |
| ### `ttlResolution`
 | |
| 
 | |
| Minimum amount of time in ms in which to check for staleness.
 | |
| Defaults to `1`, which means that the current time is checked at
 | |
| most once per millisecond.
 | |
| 
 | |
| Set to `0` to check the current time every time staleness is
 | |
| tested.
 | |
| 
 | |
| Note that setting this to a higher value _will_ improve
 | |
| performance somewhat while using ttl tracking, albeit at the
 | |
| expense of keeping stale items around a bit longer than intended.
 | |
| 
 | |
| ### `ttlAutopurge`
 | |
| 
 | |
| Preemptively remove stale items from the cache.
 | |
| 
 | |
| Note that this may _significantly_ degrade performance,
 | |
| especially if the cache is storing a large number of items. It
 | |
| is almost always best to just leave the stale items in the cache,
 | |
| and let them fall out as new items are added.
 | |
| 
 | |
| Note that this means that `allowStale` is a bit pointless, as
 | |
| stale items will be deleted almost as soon as they expire.
 | |
| 
 | |
| Use with caution!
 | |
| 
 | |
| Boolean, default `false`
 | |
| 
 | |
| ### `allowStale`
 | |
| 
 | |
| By default, if you set `ttl`, it'll only delete stale items from
 | |
| the cache when you `get(key)`. That is, it's not preemptively
 | |
| pruning items.
 | |
| 
 | |
| If you set `allowStale:true`, it'll return the stale value as
 | |
| well as deleting it. If you don't set this, then it'll return
 | |
| `undefined` when you try to get a stale entry.
 | |
| 
 | |
| Note that when a stale entry is fetched, _even if it is returned
 | |
| due to `allowStale` being set_, it is removed from the cache
 | |
| immediately. You can immediately put it back in the cache if you
 | |
| wish, thus resetting the TTL.
 | |
| 
 | |
| This may be overridden by passing an options object to
 | |
| `cache.get()`. The `cache.has()` method will always return
 | |
| `false` for stale items.
 | |
| 
 | |
| Boolean, default false, only relevant if `ttl` is set.
 | |
| 
 | |
| Deprecated alias: `stale`
 | |
| 
 | |
| ### `noDeleteOnStaleGet`
 | |
| 
 | |
| When using time-expiring entries with `ttl`, by default stale
 | |
| items will be removed from the cache when the key is accessed
 | |
| with `cache.get()`.
 | |
| 
 | |
| Setting `noDeleteOnStaleGet` to `true` will cause stale items to
 | |
| remain in the cache, until they are explicitly deleted with
 | |
| `cache.delete(key)`, or retrieved with `noDeleteOnStaleGet` set
 | |
| to `false`.
 | |
| 
 | |
| This may be overridden by passing an options object to
 | |
| `cache.get()`.
 | |
| 
 | |
| Boolean, default false, only relevant if `ttl` is set.
 | |
| 
 | |
| ### `updateAgeOnGet`
 | |
| 
 | |
| When using time-expiring entries with `ttl`, setting this to
 | |
| `true` will make each item's age reset to 0 whenever it is
 | |
| retrieved from cache with `get()`, causing it to not expire. (It
 | |
| can still fall out of cache based on recency of use, of course.)
 | |
| 
 | |
| This may be overridden by passing an options object to
 | |
| `cache.get()`.
 | |
| 
 | |
| Boolean, default false, only relevant if `ttl` is set.
 | |
| 
 | |
| ### `updateAgeOnHas`
 | |
| 
 | |
| When using time-expiring entries with `ttl`, setting this to
 | |
| `true` will make each item's age reset to 0 whenever its presence
 | |
| in the cache is checked with `has()`, causing it to not expire.
 | |
| (It can still fall out of cache based on recency of use, of
 | |
| course.)
 | |
| 
 | |
| This may be overridden by passing an options object to
 | |
| `cache.has()`.
 | |
| 
 | |
| Boolean, default false, only relevant if `ttl` is set.
 | |
| 
 | |
| ## API
 | |
| 
 | |
| ### `new LRUCache(options)`
 | |
| 
 | |
| Create a new LRUCache. All options are documented above, and are
 | |
| on the cache as public members.
 | |
| 
 | |
| ### `cache.max`, `cache.maxSize`, `cache.allowStale`,
 | |
| 
 | |
| `cache.noDisposeOnSet`, `cache.sizeCalculation`, `cache.dispose`,
 | |
| `cache.maxSize`, `cache.ttl`, `cache.updateAgeOnGet`,
 | |
| `cache.updateAgeOnHas`
 | |
| 
 | |
| All option names are exposed as public members on the cache
 | |
| object.
 | |
| 
 | |
| These are intended for read access only. Changing them during
 | |
| program operation can cause undefined behavior.
 | |
| 
 | |
| ### `cache.size`
 | |
| 
 | |
| The total number of items held in the cache at the current
 | |
| moment.
 | |
| 
 | |
| ### `cache.calculatedSize`
 | |
| 
 | |
| The total size of items in cache when using size tracking.
 | |
| 
 | |
| ### `set(key, value, [{ size, sizeCalculation, ttl, noDisposeOnSet, start, status }])`
 | |
| 
 | |
| Add a value to the cache.
 | |
| 
 | |
| Optional options object may contain `ttl` and `sizeCalculation`
 | |
| as described above, which default to the settings on the cache
 | |
| object.
 | |
| 
 | |
| If `start` is provided, then that will set the effective start
 | |
| time for the TTL calculation. Note that this must be a previous
 | |
| value of `performance.now()` if supported, or a previous value of
 | |
| `Date.now()` if not.
 | |
| 
 | |
| Options object may also include `size`, which will prevent
 | |
| calling the `sizeCalculation` function and just use the specified
 | |
| number if it is a positive integer, and `noDisposeOnSet` which
 | |
| will prevent calling a `dispose` function in the case of
 | |
| overwrites.
 | |
| 
 | |
| If the `size` (or return value of `sizeCalculation`) for a given
 | |
| entry is greater than `maxEntrySize`, then the item will not be
 | |
| added to the cache.
 | |
| 
 | |
| Will update the recency of the entry.
 | |
| 
 | |
| Returns the cache object.
 | |
| 
 | |
| For the usage of the `status` option, see **Status Tracking**
 | |
| below.
 | |
| 
 | |
| ### `get(key, { updateAgeOnGet, allowStale, status } = {}) => value`
 | |
| 
 | |
| Return a value from the cache.
 | |
| 
 | |
| Will update the recency of the cache entry found.
 | |
| 
 | |
| If the key is not found, `get()` will return `undefined`. This
 | |
| can be confusing when setting values specifically to `undefined`,
 | |
| as in `cache.set(key, undefined)`. Use `cache.has()` to
 | |
| determine whether a key is present in the cache at all.
 | |
| 
 | |
| For the usage of the `status` option, see **Status Tracking**
 | |
| below.
 | |
| 
 | |
| ### `async fetch(key, options = {}) => Promise`
 | |
| 
 | |
| The following options are supported:
 | |
| 
 | |
| - `updateAgeOnGet`
 | |
| - `allowStale`
 | |
| - `size`
 | |
| - `sizeCalculation`
 | |
| - `ttl`
 | |
| - `noDisposeOnSet`
 | |
| - `forceRefresh`
 | |
| - `status` - See **Status Tracking** below.
 | |
| - `signal` - AbortSignal can be used to cancel the `fetch()`.
 | |
|   Note that the `signal` option provided to the `fetchMethod` is
 | |
|   a different object, because it must also respond to internal
 | |
|   cache state changes, but aborting this signal will abort the
 | |
|   one passed to `fetchMethod` as well.
 | |
| - `fetchContext` - sets the `context` option passed to the
 | |
|   underlying `fetchMethod`.
 | |
| 
 | |
| If the value is in the cache and not stale, then the returned
 | |
| Promise resolves to the value.
 | |
| 
 | |
| If not in the cache, or beyond its TTL staleness, then
 | |
| `fetchMethod(key, staleValue, { options, signal, context })` is
 | |
| called, and the value returned will be added to the cache once
 | |
| resolved.
 | |
| 
 | |
| If called with `allowStale`, and an asynchronous fetch is
 | |
| currently in progress to reload a stale value, then the former
 | |
| stale value will be returned.
 | |
| 
 | |
| If called with `forceRefresh`, then the cached item will be
 | |
| re-fetched, even if it is not stale. However, if `allowStale` is
 | |
| set, then the old value will still be returned. This is useful
 | |
| in cases where you want to force a reload of a cached value. If
 | |
| a background fetch is already in progress, then `forceRefresh`
 | |
| has no effect.
 | |
| 
 | |
| Multiple fetches for the same `key` will only call `fetchMethod`
 | |
| a single time, and all will be resolved when the value is
 | |
| resolved, even if different options are used.
 | |
| 
 | |
| If `fetchMethod` is not specified, then this is effectively an
 | |
| alias for `Promise.resolve(cache.get(key))`.
 | |
| 
 | |
| When the fetch method resolves to a value, if the fetch has not
 | |
| been aborted due to deletion, eviction, or being overwritten,
 | |
| then it is added to the cache using the options provided.
 | |
| 
 | |
| If the key is evicted or deleted before the `fetchMethod`
 | |
| resolves, then the AbortSignal passed to the `fetchMethod` will
 | |
| receive an `abort` event, and the promise returned by `fetch()`
 | |
| will reject with the reason for the abort.
 | |
| 
 | |
| If a `signal` is passed to the `fetch()` call, then aborting the
 | |
| signal will abort the fetch and cause the `fetch()` promise to
 | |
| reject with the reason provided.
 | |
| 
 | |
| ### `peek(key, { allowStale } = {}) => value`
 | |
| 
 | |
| Like `get()` but doesn't update recency or delete stale items.
 | |
| 
 | |
| Returns `undefined` if the item is stale, unless `allowStale` is
 | |
| set either on the cache or in the options object.
 | |
| 
 | |
| ### `has(key, { updateAgeOnHas, status } = {}) => Boolean`
 | |
| 
 | |
| Check if a key is in the cache, without updating the recency of
 | |
| use. Age is updated if `updateAgeOnHas` is set to `true` in
 | |
| either the options or the constructor.
 | |
| 
 | |
| Will return `false` if the item is stale, even though it is
 | |
| technically in the cache.  The difference can be determined (if
 | |
| it matters) by using a `status` argument, and inspecting the
 | |
| `has` field.
 | |
| 
 | |
| For the usage of the `status` option, see **Status Tracking**
 | |
| below.
 | |
| 
 | |
| ### `delete(key)`
 | |
| 
 | |
| Deletes a key out of the cache.
 | |
| 
 | |
| Returns `true` if the key was deleted, `false` otherwise.
 | |
| 
 | |
| ### `clear()`
 | |
| 
 | |
| Clear the cache entirely, throwing away all values.
 | |
| 
 | |
| Deprecated alias: `reset()`
 | |
| 
 | |
| ### `keys()`
 | |
| 
 | |
| Return a generator yielding the keys in the cache, in order from
 | |
| most recently used to least recently used.
 | |
| 
 | |
| ### `rkeys()`
 | |
| 
 | |
| Return a generator yielding the keys in the cache, in order from
 | |
| least recently used to most recently used.
 | |
| 
 | |
| ### `values()`
 | |
| 
 | |
| Return a generator yielding the values in the cache, in order
 | |
| from most recently used to least recently used.
 | |
| 
 | |
| ### `rvalues()`
 | |
| 
 | |
| Return a generator yielding the values in the cache, in order
 | |
| from least recently used to most recently used.
 | |
| 
 | |
| ### `entries()`
 | |
| 
 | |
| Return a generator yielding `[key, value]` pairs, in order from
 | |
| most recently used to least recently used.
 | |
| 
 | |
| ### `rentries()`
 | |
| 
 | |
| Return a generator yielding `[key, value]` pairs, in order from
 | |
| least recently used to most recently used.
 | |
| 
 | |
| ### `find(fn, [getOptions])`
 | |
| 
 | |
| Find a value for which the supplied `fn` method returns a truthy
 | |
| value, similar to `Array.find()`.
 | |
| 
 | |
| `fn` is called as `fn(value, key, cache)`.
 | |
| 
 | |
| The optional `getOptions` are applied to the resulting `get()` of
 | |
| the item found.
 | |
| 
 | |
| ### `dump()`
 | |
| 
 | |
| Return an array of `[key, entry]` objects which can be passed to
 | |
| `cache.load()`
 | |
| 
 | |
| The `start` fields are calculated relative to a portable
 | |
| `Date.now()` timestamp, even if `performance.now()` is available.
 | |
| 
 | |
| Stale entries are always included in the `dump`, even if
 | |
| `allowStale` is false.
 | |
| 
 | |
| Note: this returns an actual array, not a generator, so it can be
 | |
| more easily passed around.
 | |
| 
 | |
| ### `load(entries)`
 | |
| 
 | |
| Reset the cache and load in the items in `entries` in the order
 | |
| listed. Note that the shape of the resulting cache may be
 | |
| different if the same options are not used in both caches.
 | |
| 
 | |
| The `start` fields are assumed to be calculated relative to a
 | |
| portable `Date.now()` timestamp, even if `performance.now()` is
 | |
| available.
 | |
| 
 | |
| ### `purgeStale()`
 | |
| 
 | |
| Delete any stale entries. Returns `true` if anything was
 | |
| removed, `false` otherwise.
 | |
| 
 | |
| Deprecated alias: `prune`
 | |
| 
 | |
| ### `getRemainingTTL(key)`
 | |
| 
 | |
| Return the number of ms left in the item's TTL. If item is not
 | |
| in cache, returns `0`. Returns `Infinity` if item is in cache
 | |
| without a defined TTL.
 | |
| 
 | |
| ### `forEach(fn, [thisp])`
 | |
| 
 | |
| Call the `fn` function with each set of `fn(value, key, cache)`
 | |
| in the LRU cache, from most recent to least recently used.
 | |
| 
 | |
| Does not affect recency of use.
 | |
| 
 | |
| If `thisp` is provided, function will be called in the
 | |
| `this`-context of the provided object.
 | |
| 
 | |
| ### `rforEach(fn, [thisp])`
 | |
| 
 | |
| Same as `cache.forEach(fn, thisp)`, but in order from least
 | |
| recently used to most recently used.
 | |
| 
 | |
| ### `pop()`
 | |
| 
 | |
| Evict the least recently used item, returning its value.
 | |
| 
 | |
| Returns `undefined` if cache is empty.
 | |
| 
 | |
| ### Internal Methods and Properties
 | |
| 
 | |
| In order to optimize performance as much as possible, "private"
 | |
| members and methods are exposed on the object as normal
 | |
| properties, rather than being accessed via Symbols, private
 | |
| members, or closure variables.
 | |
| 
 | |
| **Do not use or rely on these.** They will change or be removed
 | |
| without notice. They will cause undefined behavior if used
 | |
| inappropriately. There is no need or reason to ever call them
 | |
| directly.
 | |
| 
 | |
| This documentation is here so that it is especially clear that
 | |
| this not "undocumented" because someone forgot; it _is_
 | |
| documented, and the documentation is telling you not to do it.
 | |
| 
 | |
| **Do not report bugs that stem from using these properties.**
 | |
| They will be ignored.
 | |
| 
 | |
| - `initializeTTLTracking()` Set up the cache for tracking TTLs
 | |
| - `updateItemAge(index)` Called when an item age is updated, by
 | |
|   internal ID
 | |
| - `setItemTTL(index)` Called when an item ttl is updated, by
 | |
|   internal ID
 | |
| - `isStale(index)` Called to check an item's staleness, by
 | |
|   internal ID
 | |
| - `initializeSizeTracking()` Set up the cache for tracking item
 | |
|   size. Called automatically when a size is specified.
 | |
| - `removeItemSize(index)` Updates the internal size calculation
 | |
|   when an item is removed or modified, by internal ID
 | |
| - `addItemSize(index)` Updates the internal size calculation when
 | |
|   an item is added or modified, by internal ID
 | |
| - `indexes()` An iterator over the non-stale internal IDs, from
 | |
|   most recently to least recently used.
 | |
| - `rindexes()` An iterator over the non-stale internal IDs, from
 | |
|   least recently to most recently used.
 | |
| - `newIndex()` Create a new internal ID, either reusing a deleted
 | |
|   ID, evicting the least recently used ID, or walking to the end
 | |
|   of the allotted space.
 | |
| - `evict()` Evict the least recently used internal ID, returning
 | |
|   its ID. Does not do any bounds checking.
 | |
| - `connect(p, n)` Connect the `p` and `n` internal IDs in the
 | |
|   linked list.
 | |
| - `moveToTail(index)` Move the specified internal ID to the most
 | |
|   recently used position.
 | |
| - `keyMap` Map of keys to internal IDs
 | |
| - `keyList` List of keys by internal ID
 | |
| - `valList` List of values by internal ID
 | |
| - `sizes` List of calculated sizes by internal ID
 | |
| - `ttls` List of TTL values by internal ID
 | |
| - `starts` List of start time values by internal ID
 | |
| - `next` Array of "next" pointers by internal ID
 | |
| - `prev` Array of "previous" pointers by internal ID
 | |
| - `head` Internal ID of least recently used item
 | |
| - `tail` Internal ID of most recently used item
 | |
| - `free` Stack of deleted internal IDs
 | |
| 
 | |
| ## Status Tracking
 | |
| 
 | |
| Occasionally, it may be useful to track the internal behavior of
 | |
| the cache, particularly for logging, debugging, or for behavior
 | |
| within the `fetchMethod`.  To do this, you can pass a `status`
 | |
| object to the `get()`, `set()`, `has()`, and `fetch()` methods.
 | |
| 
 | |
| The `status` option should be a plain JavaScript object.
 | |
| 
 | |
| The following fields will be set appropriately:
 | |
| 
 | |
| ```ts
 | |
| interface Status<V> {
 | |
|   /**
 | |
|    * The status of a set() operation.
 | |
|    *
 | |
|    * - add: the item was not found in the cache, and was added
 | |
|    * - update: the item was in the cache, with the same value provided
 | |
|    * - replace: the item was in the cache, and replaced
 | |
|    * - miss: the item was not added to the cache for some reason
 | |
|    */
 | |
|   set?: 'add' | 'update' | 'replace' | 'miss'
 | |
| 
 | |
|   /**
 | |
|    * the ttl stored for the item, or undefined if ttls are not used.
 | |
|    */
 | |
|   ttl?: LRUMilliseconds
 | |
| 
 | |
|   /**
 | |
|    * the start time for the item, or undefined if ttls are not used.
 | |
|    */
 | |
|   start?: LRUMilliseconds
 | |
| 
 | |
|   /**
 | |
|    * The timestamp used for TTL calculation
 | |
|    */
 | |
|   now?: LRUMilliseconds
 | |
| 
 | |
|   /**
 | |
|    * the remaining ttl for the item, or undefined if ttls are not used.
 | |
|    */
 | |
|   remainingTTL?: LRUMilliseconds
 | |
| 
 | |
|   /**
 | |
|    * The calculated size for the item, if sizes are used.
 | |
|    */
 | |
|   size?: LRUSize
 | |
| 
 | |
|   /**
 | |
|    * A flag indicating that the item was not stored, due to exceeding the
 | |
|    * {@link maxEntrySize}
 | |
|    */
 | |
|   maxEntrySizeExceeded?: true
 | |
| 
 | |
|   /**
 | |
|    * The old value, specified in the case of `set:'update'` or
 | |
|    * `set:'replace'`
 | |
|    */
 | |
|   oldValue?: V
 | |
| 
 | |
|   /**
 | |
|    * The results of a {@link has} operation
 | |
|    *
 | |
|    * - hit: the item was found in the cache
 | |
|    * - stale: the item was found in the cache, but is stale
 | |
|    * - miss: the item was not found in the cache
 | |
|    */
 | |
|   has?: 'hit' | 'stale' | 'miss'
 | |
| 
 | |
|   /**
 | |
|    * The status of a {@link fetch} operation.
 | |
|    * Note that this can change as the underlying fetch() moves through
 | |
|    * various states.
 | |
|    *
 | |
|    * - inflight: there is another fetch() for this key which is in process
 | |
|    * - get: there is no fetchMethod, so {@link get} was called.
 | |
|    * - miss: the item is not in cache, and will be fetched.
 | |
|    * - hit: the item is in the cache, and was resolved immediately.
 | |
|    * - stale: the item is in the cache, but stale.
 | |
|    * - refresh: the item is in the cache, and not stale, but
 | |
|    *   {@link forceRefresh} was specified.
 | |
|    */
 | |
|   fetch?: 'get' | 'inflight' | 'miss' | 'hit' | 'stale' | 'refresh'
 | |
| 
 | |
|   /**
 | |
|    * The {@link fetchMethod} was called
 | |
|    */
 | |
|   fetchDispatched?: true
 | |
| 
 | |
|   /**
 | |
|    * The cached value was updated after a successful call to fetchMethod
 | |
|    */
 | |
|   fetchUpdated?: true
 | |
| 
 | |
|   /**
 | |
|    * The reason for a fetch() rejection.  Either the error raised by the
 | |
|    * {@link fetchMethod}, or the reason for an AbortSignal.
 | |
|    */
 | |
|   fetchError?: Error
 | |
| 
 | |
|   /**
 | |
|    * The fetch received an abort signal
 | |
|    */
 | |
|   fetchAborted?: true
 | |
| 
 | |
|   /**
 | |
|    * The abort signal received was ignored, and the fetch was allowed to
 | |
|    * continue.
 | |
|    */
 | |
|   fetchAbortIgnored?: true
 | |
| 
 | |
|   /**
 | |
|    * The fetchMethod promise resolved successfully
 | |
|    */
 | |
|   fetchResolved?: true
 | |
| 
 | |
|   /**
 | |
|    * The results of the fetchMethod promise were stored in the cache
 | |
|    */
 | |
|   fetchUpdated?: true
 | |
| 
 | |
|   /**
 | |
|    * The fetchMethod promise was rejected
 | |
|    */
 | |
|   fetchRejected?: true
 | |
| 
 | |
|   /**
 | |
|    * The status of a {@link get} operation.
 | |
|    *
 | |
|    * - fetching: The item is currently being fetched.  If a previous value is
 | |
|    *   present and allowed, that will be returned.
 | |
|    * - stale: The item is in the cache, and is stale.
 | |
|    * - hit: the item is in the cache
 | |
|    * - miss: the item is not in the cache
 | |
|    */
 | |
|   get?: 'stale' | 'hit' | 'miss'
 | |
| 
 | |
|   /**
 | |
|    * A fetch or get operation returned a stale value.
 | |
|    */
 | |
|   returnedStale?: true
 | |
| }
 | |
| ```
 | |
| 
 | |
| ## Storage Bounds Safety
 | |
| 
 | |
| This implementation aims to be as flexible as possible, within
 | |
| the limits of safe memory consumption and optimal performance.
 | |
| 
 | |
| At initial object creation, storage is allocated for `max` items.
 | |
| If `max` is set to zero, then some performance is lost, and item
 | |
| count is unbounded. Either `maxSize` or `ttl` _must_ be set if
 | |
| `max` is not specified.
 | |
| 
 | |
| If `maxSize` is set, then this creates a safe limit on the
 | |
| maximum storage consumed, but without the performance benefits of
 | |
| pre-allocation. When `maxSize` is set, every item _must_ provide
 | |
| a size, either via the `sizeCalculation` method provided to the
 | |
| constructor, or via a `size` or `sizeCalculation` option provided
 | |
| to `cache.set()`. The size of every item _must_ be a positive
 | |
| integer.
 | |
| 
 | |
| If neither `max` nor `maxSize` are set, then `ttl` tracking must
 | |
| be enabled. Note that, even when tracking item `ttl`, items are
 | |
| _not_ preemptively deleted when they become stale, unless
 | |
| `ttlAutopurge` is enabled. Instead, they are only purged the
 | |
| next time the key is requested. Thus, if `ttlAutopurge`, `max`,
 | |
| and `maxSize` are all not set, then the cache will potentially
 | |
| grow unbounded.
 | |
| 
 | |
| In this case, a warning is printed to standard error. Future
 | |
| versions may require the use of `ttlAutopurge` if `max` and
 | |
| `maxSize` are not specified.
 | |
| 
 | |
| If you truly wish to use a cache that is bound _only_ by TTL
 | |
| expiration, consider using a `Map` object, and calling
 | |
| `setTimeout` to delete entries when they expire. It will perform
 | |
| much better than an LRU cache.
 | |
| 
 | |
| Here is an implementation you may use, under the same
 | |
| [license](./LICENSE) as this package:
 | |
| 
 | |
| ```js
 | |
| // a storage-unbounded ttl cache that is not an lru-cache
 | |
| const cache = {
 | |
|   data: new Map(),
 | |
|   timers: new Map(),
 | |
|   set: (k, v, ttl) => {
 | |
|     if (cache.timers.has(k)) {
 | |
|       clearTimeout(cache.timers.get(k))
 | |
|     }
 | |
|     cache.timers.set(
 | |
|       k,
 | |
|       setTimeout(() => cache.delete(k), ttl)
 | |
|     )
 | |
|     cache.data.set(k, v)
 | |
|   },
 | |
|   get: k => cache.data.get(k),
 | |
|   has: k => cache.data.has(k),
 | |
|   delete: k => {
 | |
|     if (cache.timers.has(k)) {
 | |
|       clearTimeout(cache.timers.get(k))
 | |
|     }
 | |
|     cache.timers.delete(k)
 | |
|     return cache.data.delete(k)
 | |
|   },
 | |
|   clear: () => {
 | |
|     cache.data.clear()
 | |
|     for (const v of cache.timers.values()) {
 | |
|       clearTimeout(v)
 | |
|     }
 | |
|     cache.timers.clear()
 | |
|   },
 | |
| }
 | |
| ```
 | |
| 
 | |
| If that isn't to your liking, check out
 | |
| [@isaacs/ttlcache](http://npm.im/@isaacs/ttlcache).
 | |
| 
 | |
| ## Performance
 | |
| 
 | |
| 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
 | |
| particular, the performance of set/get/delete operations on
 | |
| objects will vary _wildly_ depending on the type of key used. V8
 | |
| is highly optimized for objects with keys that are short strings,
 | |
| especially integer numeric strings. Thus any benchmark which
 | |
| tests _solely_ using numbers as keys will tend to find that an
 | |
| object-based approach performs the best.
 | |
| 
 | |
| Note that coercing _anything_ to strings to use as object keys is
 | |
| unsafe, unless you can be 100% certain that no other type of
 | |
| value will be used. For example:
 | |
| 
 | |
| ```js
 | |
| const myCache = {}
 | |
| const set = (k, v) => (myCache[k] = v)
 | |
| const get = k => myCache[k]
 | |
| 
 | |
| set({}, 'please hang onto this for me')
 | |
| set('[object Object]', 'oopsie')
 | |
| ```
 | |
| 
 | |
| Also beware of "Just So" stories regarding performance. Garbage
 | |
| collection of large (especially: deep) object graphs can be
 | |
| incredibly costly, with several "tipping points" where it
 | |
| increases exponentially. As a result, putting that off until
 | |
| later can make it much worse, and less predictable. If a library
 | |
| performs well, but only in a scenario where the object graph is
 | |
| kept shallow, then that won't help you if you are using large
 | |
| objects as keys.
 | |
| 
 | |
| In general, when attempting to use a library to improve
 | |
| performance (such as a cache like this one), it's best to choose
 | |
| an option that will perform well in the sorts of scenarios where
 | |
| you'll actually use it.
 | |
| 
 | |
| This library is optimized for repeated gets and minimizing
 | |
| eviction time, since that is the expected need of a LRU. Set
 | |
| operations are somewhat slower on average than a few other
 | |
| options, in part because of that optimization. It is assumed
 | |
| that you'll be caching some costly operation, ideally as rarely
 | |
| as possible, so optimizing set over get would be unwise.
 | |
| 
 | |
| If performance matters to you:
 | |
| 
 | |
| 1. If it's at all possible to use small integer values as keys,
 | |
|    and you can guarantee that no other types of values will be
 | |
|    used as keys, then do that, and use a cache such as
 | |
|    [lru-fast](https://npmjs.com/package/lru-fast), or
 | |
|    [mnemonist's
 | |
|    LRUCache](https://yomguithereal.github.io/mnemonist/lru-cache)
 | |
|    which uses an Object as its data store.
 | |
| 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 long strings, strings that
 | |
|    look like floats, `null`, objects, or some mix of types, or if
 | |
|    you aren't sure, then this library will work well for you.
 | |
| 4. Do not use a `dispose` function, size tracking, or especially
 | |
|    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
 | |
| 
 | |
| This library changed to a different algorithm and internal data
 | |
| structure in version 7, yielding significantly better
 | |
| performance, albeit with some subtle changes as a result.
 | |
| 
 | |
| If you were relying on the internals of LRUCache in version 6 or
 | |
| before, it probably will not work in version 7 and above.
 | |
| 
 | |
| For more info, see the [change log](CHANGELOG.md).
 |