Skip to content

Commit 0bb5d01

Browse files
committed
add fetchContext option
1 parent 118a078 commit 0bb5d01

File tree

4 files changed

+76
-13
lines changed

4 files changed

+76
-13
lines changed

README.md

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,8 +141,8 @@ Deprecated alias: `length`
141141
### `fetchMethod`
142142

143143
Function that is used to make background asynchronous fetches.
144-
Called with `fetchMethod(key, staleValue, { signal, options })`.
145-
May return a Promise.
144+
Called with `fetchMethod(key, staleValue, { signal, options,
145+
context })`. May return a Promise.
146146

147147
If `fetchMethod` is not provided, then `cache.fetch(key)` is
148148
equivalent to `Promise.resolve(cache.get(key))`.
@@ -164,6 +164,18 @@ value is resolved. For example, a DNS cache may update the TTL
164164
based on the value returned from a remote DNS server by changing
165165
`options.ttl` in the `fetchMethod`.
166166

167+
### `fetchContext`
168+
169+
Arbitrary data that can be passed to the `fetchMethod` as the
170+
`context` option.
171+
172+
Note that this will only be relevant when the `cache.fetch()`
173+
call needs to call `fetchMethod()`. Thus, any data which will
174+
meaningfully vary the fetch response needs to be present in the
175+
key. This is primarily intended for including `x-request-id`
176+
headers and the like for debugging purposes, which do not affect
177+
the `fetchMethod()` response.
178+
167179
### `noDeleteOnFetchRejection`
168180

169181
If a `fetchMethod` throws an error or returns a rejected promise,

index.d.ts

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,15 @@ declare namespace LRUCache {
496496
* @since 7.10.0
497497
*/
498498
noDeleteOnFetchRejection?: boolean
499+
500+
/**
501+
* Set to any value in the constructor or fetch() options to
502+
* pass arbitrary data to the fetch() method in the options.context
503+
* field.
504+
*
505+
* @since 7.12.0
506+
*/
507+
fetchContext?: any
499508
}
500509

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

548-
/**
549-
* options which override the options set in the LRUCache constructor
550-
* when making `cache.fetch()` calls.
551-
* This is the union of GetOptions and SetOptions, plus the
552-
* `noDeleteOnFetchRejection` boolean.
553-
*/
554-
interface FetchOptions<K, V> {
557+
interface FetcherFetchOptions<K, V> {
555558
allowStale?: boolean
556559
updateAgeOnGet?: boolean
557560
noDeleteOnStaleGet?: boolean
@@ -563,9 +566,20 @@ declare namespace LRUCache {
563566
noDeleteOnFetchRejection?: boolean
564567
}
565568

569+
/**
570+
* options which override the options set in the LRUCache constructor
571+
* when making `cache.fetch()` calls.
572+
* This is the union of GetOptions and SetOptions, plus the
573+
* `noDeleteOnFetchRejection` and `fetchContext` fields.
574+
*/
575+
interface FetchOptions<K, V> extends FetcherFetchOptions<K, V> {
576+
fetchContext?: any
577+
}
578+
566579
interface FetcherOptions<K, V> {
567580
signal: AbortSignal
568-
options: FetchOptions<K, V>
581+
options: FetcherFetchOptions<K, V>
582+
context: any
569583
}
570584

571585
interface Entry<V> {

index.js

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ class LRUCache {
159159
maxSize = 0,
160160
sizeCalculation,
161161
fetchMethod,
162+
fetchContext,
162163
noDeleteOnFetchRejection,
163164
noDeleteOnStaleGet,
164165
} = options
@@ -198,6 +199,13 @@ class LRUCache {
198199
)
199200
}
200201

202+
this.fetchContext = fetchContext
203+
if (!this.fetchMethod && fetchContext !== undefined) {
204+
throw new TypeError(
205+
'cannot set fetchContext without fetchMethod'
206+
)
207+
}
208+
201209
this.keyMap = new Map()
202210
this.keyList = new Array(max).fill(null)
203211
this.valList = new Array(max).fill(null)
@@ -676,7 +684,7 @@ class LRUCache {
676684
}
677685
}
678686

679-
backgroundFetch(k, index, options) {
687+
backgroundFetch(k, index, options, context) {
680688
const v = index === undefined ? undefined : this.valList[index]
681689
if (this.isBackgroundFetch(v)) {
682690
return v
@@ -685,6 +693,7 @@ class LRUCache {
685693
const fetchOpts = {
686694
signal: ac.signal,
687695
options,
696+
context,
688697
}
689698
const cb = v => {
690699
if (!ac.signal.aborted) {
@@ -753,6 +762,7 @@ class LRUCache {
753762
noUpdateTTL = this.noUpdateTTL,
754763
// fetch exclusive options
755764
noDeleteOnFetchRejection = this.noDeleteOnFetchRejection,
765+
fetchContext = this.fetchContext,
756766
} = {}
757767
) {
758768
if (!this.fetchMethod) {
@@ -773,7 +783,7 @@ class LRUCache {
773783

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

795805
// ok, it is stale, and not already fetching
796806
// refresh the cache.
797-
const p = this.backgroundFetch(k, index, options)
807+
const p = this.backgroundFetch(k, index, options, fetchContext)
798808
return allowStale && p.__staleWhileFetching !== undefined
799809
? p.__staleWhileFetching
800810
: (p.__returned = p)

test/fetch.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,10 @@ t.test('fetchMethod must be a function', async t => {
135135
t.throws(() => new LRU({ fetchMethod: true, max: 2 }))
136136
})
137137

138+
t.test('no fetchContext without fetchMethod', async t => {
139+
t.throws(() => new LRU({ fetchContext: true, max: 2 }))
140+
})
141+
138142
t.test('fetch without fetch method', async t => {
139143
const c = new LRU({ max: 3 })
140144
c.set(0, 0)
@@ -468,3 +472,26 @@ t.test(
468472
t.equal(e.valList[1], null, 'not in cache')
469473
}
470474
)
475+
476+
t.test('fetchContext', async t => {
477+
const cache = new LRU<string, [string, any]>({
478+
max: 10,
479+
ttl: 10,
480+
allowStale: true,
481+
noDeleteOnFetchRejection: true,
482+
fetchContext: 'default context',
483+
fetchMethod: async (k, _, { context, options }) => {
484+
//@ts-expect-error
485+
t.equal(options.fetchContext, undefined)
486+
t.equal(context, expectContext)
487+
return [k, context]
488+
},
489+
})
490+
491+
let expectContext = 'default context'
492+
t.strictSame(await cache.fetch('x'), ['x', 'default context'])
493+
expectContext = 'overridden'
494+
t.strictSame(await cache.fetch('y', { fetchContext: 'overridden' }), ['y', 'overridden'])
495+
// if still in cache, doesn't call fetchMethod again
496+
t.strictSame(await cache.fetch('x', { fetchContext: 'ignored' }), ['x', 'default context'])
497+
})

0 commit comments

Comments
 (0)