Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: isaacs/node-lru-cache
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v7.11.0
Choose a base ref
...
head repository: isaacs/node-lru-cache
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v7.12.0
Choose a head ref
  • 2 commits
  • 6 files changed
  • 1 contributor

Commits on Jun 29, 2022

  1. add fetchContext option

    isaacs committed Jun 29, 2022

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    0bb5d01 View commit details
  2. 7.12.0

    isaacs committed Jun 29, 2022

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    7ef678e View commit details
Showing with 79 additions and 16 deletions.
  1. +14 −2 README.md
  2. +22 −8 index.d.ts
  3. +13 −3 index.js
  4. +2 −2 package-lock.json
  5. +1 −1 package.json
  6. +27 −0 test/fetch.ts
16 changes: 14 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -141,8 +141,8 @@ Deprecated alias: `length`
### `fetchMethod`

Function that is used to make background asynchronous fetches.
Called with `fetchMethod(key, staleValue, { signal, options })`.
May return a Promise.
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))`.
@@ -164,6 +164,18 @@ value is resolved. 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,
30 changes: 22 additions & 8 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -496,6 +496,15 @@ declare namespace LRUCache {
* @since 7.10.0
*/
noDeleteOnFetchRejection?: boolean

/**
* Set to any value in the constructor or fetch() options to
* pass arbitrary data to the fetch() method in the options.context
* field.
*
* @since 7.12.0
*/
fetchContext?: any
}

type Options<K, V> = SharedOptions<K, V> &
@@ -545,13 +554,7 @@ declare namespace LRUCache {
allowStale?: boolean
}

/**
* options which override the options set in the LRUCache constructor
* when making `cache.fetch()` calls.
* This is the union of GetOptions and SetOptions, plus the
* `noDeleteOnFetchRejection` boolean.
*/
interface FetchOptions<K, V> {
interface FetcherFetchOptions<K, V> {
allowStale?: boolean
updateAgeOnGet?: boolean
noDeleteOnStaleGet?: boolean
@@ -563,9 +566,20 @@ declare namespace LRUCache {
noDeleteOnFetchRejection?: boolean
}

/**
* options which override the options set in the LRUCache constructor
* when making `cache.fetch()` calls.
* This is the union of GetOptions and SetOptions, plus the
* `noDeleteOnFetchRejection` and `fetchContext` fields.
*/
interface FetchOptions<K, V> extends FetcherFetchOptions<K, V> {
fetchContext?: any
}

interface FetcherOptions<K, V> {
signal: AbortSignal
options: FetchOptions<K, V>
options: FetcherFetchOptions<K, V>
context: any
}

interface Entry<V> {
16 changes: 13 additions & 3 deletions index.js
Original file line number Diff line number Diff line change
@@ -159,6 +159,7 @@ class LRUCache {
maxSize = 0,
sizeCalculation,
fetchMethod,
fetchContext,
noDeleteOnFetchRejection,
noDeleteOnStaleGet,
} = options
@@ -198,6 +199,13 @@ class LRUCache {
)
}

this.fetchContext = fetchContext
if (!this.fetchMethod && fetchContext !== undefined) {
throw new TypeError(
'cannot set fetchContext without fetchMethod'
)
}

this.keyMap = new Map()
this.keyList = new Array(max).fill(null)
this.valList = new Array(max).fill(null)
@@ -676,7 +684,7 @@ class LRUCache {
}
}

backgroundFetch(k, index, options) {
backgroundFetch(k, index, options, context) {
const v = index === undefined ? undefined : this.valList[index]
if (this.isBackgroundFetch(v)) {
return v
@@ -685,6 +693,7 @@ class LRUCache {
const fetchOpts = {
signal: ac.signal,
options,
context,
}
const cb = v => {
if (!ac.signal.aborted) {
@@ -753,6 +762,7 @@ class LRUCache {
noUpdateTTL = this.noUpdateTTL,
// fetch exclusive options
noDeleteOnFetchRejection = this.noDeleteOnFetchRejection,
fetchContext = this.fetchContext,
} = {}
) {
if (!this.fetchMethod) {
@@ -773,7 +783,7 @@ class LRUCache {

let index = this.keyMap.get(k)
if (index === undefined) {
const p = this.backgroundFetch(k, index, options)
const p = this.backgroundFetch(k, index, options, fetchContext)
return (p.__returned = p)
} else {
// in cache, maybe already fetching
@@ -794,7 +804,7 @@ class LRUCache {

// ok, it is stale, and not already fetching
// refresh the cache.
const p = this.backgroundFetch(k, index, options)
const p = this.backgroundFetch(k, index, options, fetchContext)
return allowStale && p.__staleWhileFetching !== undefined
? p.__staleWhileFetching
: (p.__returned = p)
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "lru-cache",
"description": "A cache object that deletes the least-recently-used items.",
"version": "7.11.0",
"version": "7.12.0",
"author": "Isaac Z. Schlueter <i@izs.me>",
"keywords": [
"mru",
27 changes: 27 additions & 0 deletions test/fetch.ts
Original file line number Diff line number Diff line change
@@ -135,6 +135,10 @@ t.test('fetchMethod must be a function', async t => {
t.throws(() => new LRU({ fetchMethod: true, max: 2 }))
})

t.test('no fetchContext without fetchMethod', async t => {
t.throws(() => new LRU({ fetchContext: true, max: 2 }))
})

t.test('fetch without fetch method', async t => {
const c = new LRU({ max: 3 })
c.set(0, 0)
@@ -468,3 +472,26 @@ t.test(
t.equal(e.valList[1], null, 'not in cache')
}
)

t.test('fetchContext', async t => {
const cache = new LRU<string, [string, any]>({
max: 10,
ttl: 10,
allowStale: true,
noDeleteOnFetchRejection: true,
fetchContext: 'default context',
fetchMethod: async (k, _, { context, options }) => {
//@ts-expect-error
t.equal(options.fetchContext, undefined)
t.equal(context, expectContext)
return [k, context]
},
})

let expectContext = 'default context'
t.strictSame(await cache.fetch('x'), ['x', 'default context'])
expectContext = 'overridden'
t.strictSame(await cache.fetch('y', { fetchContext: 'overridden' }), ['y', 'overridden'])
// if still in cache, doesn't call fetchMethod again
t.strictSame(await cache.fetch('x', { fetchContext: 'ignored' }), ['x', 'default context'])
})