@nxtedition/cache 1.0.4 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -14
- package/lib/index.d.ts +2 -3
- package/lib/index.js +33 -37
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -43,14 +43,14 @@ if (result.async) {
|
|
|
43
43
|
|
|
44
44
|
## API
|
|
45
45
|
|
|
46
|
-
### `new AsyncCache(location, valueSelector
|
|
46
|
+
### `new AsyncCache(location, valueSelector, keySelector, opts?)`
|
|
47
47
|
|
|
48
|
-
| Parameter | Type | Description
|
|
49
|
-
| --------------- | ------------------------------ |
|
|
50
|
-
| `location` | `string` | SQLite database path, or `':memory:'`
|
|
51
|
-
| `valueSelector` | `(...args) => V \| Promise<V>` |
|
|
52
|
-
| `keySelector` | `(...args) => string` |
|
|
53
|
-
| `opts` | `AsyncCacheOptions<V>` | Optional configuration
|
|
48
|
+
| Parameter | Type | Description |
|
|
49
|
+
| --------------- | ------------------------------ | --------------------------------------------- |
|
|
50
|
+
| `location` | `string` | SQLite database path, or `':memory:'` |
|
|
51
|
+
| `valueSelector` | `(...args) => V \| Promise<V>` | Function to fetch a value on cache miss |
|
|
52
|
+
| `keySelector` | `(...args) => string` | Function to derive a cache key from arguments |
|
|
53
|
+
| `opts` | `AsyncCacheOptions<V>` | Optional configuration |
|
|
54
54
|
|
|
55
55
|
#### Options
|
|
56
56
|
|
|
@@ -73,19 +73,15 @@ type CacheResult<V> =
|
|
|
73
73
|
| { value: Promise<V> | null; async: true } // cache miss
|
|
74
74
|
```
|
|
75
75
|
|
|
76
|
-
When `async: true`, `value` is a Promise that resolves once the `valueSelector` completes.
|
|
76
|
+
When `async: true`, `value` is a Promise that resolves once the `valueSelector` completes.
|
|
77
77
|
|
|
78
78
|
#### `cache.peek(...args): CacheResult<V>`
|
|
79
79
|
|
|
80
80
|
Same as `get()` but does **not** trigger a refresh on cache miss. Returns `{ value: null, async: true }` for missing entries.
|
|
81
81
|
|
|
82
|
-
#### `cache.refresh(...args): Promise<V
|
|
82
|
+
#### `cache.refresh(...args): Promise<V>`
|
|
83
83
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
#### `cache.set(key, value): void`
|
|
87
|
-
|
|
88
|
-
Manually set a value in the cache.
|
|
84
|
+
Triggers a fetch via `valueSelector` regardless of cache state. If a fetch for the same key is already in-flight (from a prior `get()` or `refresh()`), the existing promise is returned instead of starting a new one.
|
|
89
85
|
|
|
90
86
|
#### `cache.delete(key): void`
|
|
91
87
|
|
package/lib/index.d.ts
CHANGED
|
@@ -23,13 +23,12 @@ export type CacheResult<V> = {
|
|
|
23
23
|
};
|
|
24
24
|
export declare class AsyncCache<V = unknown, A extends unknown[] = unknown[]> {
|
|
25
25
|
#private;
|
|
26
|
-
constructor(location: string, valueSelector
|
|
26
|
+
constructor(location: string, valueSelector: (...args: A) => V | Promise<V>, keySelector: (...args: A) => string, opts?: AsyncCacheOptions<V>);
|
|
27
27
|
close(): void;
|
|
28
28
|
get(...args: A): CacheResult<V>;
|
|
29
29
|
peek(...args: A): CacheResult<V>;
|
|
30
|
-
refresh(...args: A): Promise<V
|
|
30
|
+
refresh(...args: A): Promise<V>;
|
|
31
31
|
delete(key: string): void;
|
|
32
|
-
set(key: string, value: V): void;
|
|
33
32
|
purgeStale(): void;
|
|
34
33
|
}
|
|
35
34
|
export {};
|
package/lib/index.js
CHANGED
|
@@ -64,7 +64,7 @@ const MAX_DURATION = 365000000e3
|
|
|
64
64
|
|
|
65
65
|
export class AsyncCache {
|
|
66
66
|
#lru
|
|
67
|
-
#valueSelector
|
|
67
|
+
#valueSelector
|
|
68
68
|
#keySelector
|
|
69
69
|
#dedupe = new Map ()
|
|
70
70
|
|
|
@@ -80,27 +80,23 @@ export class AsyncCache {
|
|
|
80
80
|
|
|
81
81
|
constructor(
|
|
82
82
|
location ,
|
|
83
|
-
valueSelector
|
|
84
|
-
keySelector
|
|
83
|
+
valueSelector ,
|
|
84
|
+
keySelector ,
|
|
85
85
|
opts ,
|
|
86
86
|
) {
|
|
87
|
-
if (typeof location
|
|
88
|
-
|
|
89
|
-
} else {
|
|
90
|
-
throw new TypeError('location must be undefined or a string')
|
|
87
|
+
if (typeof location !== 'string') {
|
|
88
|
+
throw new TypeError('location must be a string')
|
|
91
89
|
}
|
|
92
90
|
|
|
93
|
-
if (typeof valueSelector
|
|
94
|
-
this.#valueSelector = valueSelector
|
|
95
|
-
} else {
|
|
91
|
+
if (typeof valueSelector !== 'function') {
|
|
96
92
|
throw new TypeError('valueSelector must be a function')
|
|
97
93
|
}
|
|
94
|
+
this.#valueSelector = valueSelector
|
|
98
95
|
|
|
99
|
-
if (typeof keySelector
|
|
100
|
-
this.#keySelector = keySelector ?? ((...args ) => JSON.stringify(args))
|
|
101
|
-
} else {
|
|
96
|
+
if (typeof keySelector !== 'function') {
|
|
102
97
|
throw new TypeError('keySelector must be a function')
|
|
103
98
|
}
|
|
99
|
+
this.#keySelector = keySelector
|
|
104
100
|
|
|
105
101
|
if (typeof opts?.ttl === 'number' || opts?.ttl === undefined) {
|
|
106
102
|
const ttl = opts?.ttl ?? Number.MAX_SAFE_INTEGER
|
|
@@ -196,7 +192,7 @@ export class AsyncCache {
|
|
|
196
192
|
return this.#load(args, false)
|
|
197
193
|
}
|
|
198
194
|
|
|
199
|
-
refresh(...args )
|
|
195
|
+
refresh(...args ) {
|
|
200
196
|
return this.#refresh(args)
|
|
201
197
|
}
|
|
202
198
|
|
|
@@ -204,10 +200,6 @@ export class AsyncCache {
|
|
|
204
200
|
this.#delete(key)
|
|
205
201
|
}
|
|
206
202
|
|
|
207
|
-
set(key , value ) {
|
|
208
|
-
this.#set(key, value)
|
|
209
|
-
}
|
|
210
|
-
|
|
211
203
|
purgeStale() {
|
|
212
204
|
try {
|
|
213
205
|
this.#lru?.purgeStale()
|
|
@@ -276,32 +268,34 @@ export class AsyncCache {
|
|
|
276
268
|
: { value: promise, async: true }
|
|
277
269
|
}
|
|
278
270
|
|
|
279
|
-
#refresh(args , key = this.#keySelector(...args))
|
|
271
|
+
#refresh(args , key = this.#keySelector(...args)) {
|
|
280
272
|
if (typeof key !== 'string' || key.length === 0) {
|
|
281
273
|
throw new TypeError('keySelector must return a non-empty string')
|
|
282
274
|
}
|
|
283
275
|
|
|
284
276
|
// TODO (fix): cross process/thread dedupe...
|
|
285
|
-
|
|
286
|
-
if (
|
|
287
|
-
|
|
288
|
-
promise = Promise.resolve(this.#valueSelector(...args)).then(
|
|
289
|
-
(value) => {
|
|
290
|
-
if (this.#dedupe.get(key) === promise) {
|
|
291
|
-
this.#dedupe.delete(key)
|
|
292
|
-
this.#set(key, value)
|
|
293
|
-
}
|
|
294
|
-
return value
|
|
295
|
-
},
|
|
296
|
-
(err) => {
|
|
297
|
-
this.#dedupe.delete(key)
|
|
298
|
-
throw err
|
|
299
|
-
},
|
|
300
|
-
)
|
|
301
|
-
promise.catch(noop)
|
|
302
|
-
this.#dedupe.set(key, promise)
|
|
277
|
+
const existing = this.#dedupe.get(key)
|
|
278
|
+
if (existing !== undefined) {
|
|
279
|
+
return existing
|
|
303
280
|
}
|
|
304
281
|
|
|
282
|
+
// eslint-disable-next-line: no-unsafe-argument
|
|
283
|
+
const promise = Promise.resolve(this.#valueSelector(...args)).then(
|
|
284
|
+
(value) => {
|
|
285
|
+
if (this.#dedupe.get(key) === promise) {
|
|
286
|
+
this.#dedupe.delete(key)
|
|
287
|
+
this.#set(key, value)
|
|
288
|
+
}
|
|
289
|
+
return value
|
|
290
|
+
},
|
|
291
|
+
(err) => {
|
|
292
|
+
this.#dedupe.delete(key)
|
|
293
|
+
throw err
|
|
294
|
+
},
|
|
295
|
+
)
|
|
296
|
+
promise.catch(noop)
|
|
297
|
+
this.#dedupe.set(key, promise)
|
|
298
|
+
|
|
305
299
|
return promise
|
|
306
300
|
}
|
|
307
301
|
|
|
@@ -310,6 +304,8 @@ export class AsyncCache {
|
|
|
310
304
|
throw new TypeError('key must be a non-empty string')
|
|
311
305
|
}
|
|
312
306
|
|
|
307
|
+
this.#dedupe.delete(key)
|
|
308
|
+
|
|
313
309
|
const ttlValue = Math.min(MAX_DURATION, this.#ttl(value, key) ?? Infinity)
|
|
314
310
|
if (!Number.isFinite(ttlValue) || ttlValue < 0) {
|
|
315
311
|
throw new TypeError('ttl must be nully or a positive integer')
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nxtedition/cache",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
@@ -31,5 +31,5 @@
|
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"lru-cache": "^11.2.6"
|
|
33
33
|
},
|
|
34
|
-
"gitHead": "
|
|
34
|
+
"gitHead": "284d1bf697548d0bda69df9a240268da019b626f"
|
|
35
35
|
}
|