@isaacs/ttlcache 1.4.1 → 2.0.0
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/LICENSE.md +63 -0
- package/README.md +37 -38
- package/dist/commonjs/index.d.ts +46 -0
- package/dist/commonjs/index.d.ts.map +1 -0
- package/dist/commonjs/index.js +290 -0
- package/dist/commonjs/index.js.map +1 -0
- package/dist/commonjs/package.json +3 -0
- package/dist/esm/index.d.ts +46 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +286 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/package.json +3 -0
- package/package.json +42 -24
- package/LICENSE +0 -15
- package/index.d.ts +0 -231
- package/index.js +0 -324
package/LICENSE.md
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
All packages under `src/` are licensed according to the terms in
|
|
2
|
+
their respective `LICENSE` or `LICENSE.md` files.
|
|
3
|
+
|
|
4
|
+
The remainder of this project is licensed under the Blue Oak
|
|
5
|
+
Model License, as follows:
|
|
6
|
+
|
|
7
|
+
-----
|
|
8
|
+
|
|
9
|
+
# Blue Oak Model License
|
|
10
|
+
|
|
11
|
+
Version 1.0.0
|
|
12
|
+
|
|
13
|
+
## Purpose
|
|
14
|
+
|
|
15
|
+
This license gives everyone as much permission to work with
|
|
16
|
+
this software as possible, while protecting contributors
|
|
17
|
+
from liability.
|
|
18
|
+
|
|
19
|
+
## Acceptance
|
|
20
|
+
|
|
21
|
+
In order to receive this license, you must agree to its
|
|
22
|
+
rules. The rules of this license are both obligations
|
|
23
|
+
under that agreement and conditions to your license.
|
|
24
|
+
You must not do anything with this software that triggers
|
|
25
|
+
a rule that you cannot or will not follow.
|
|
26
|
+
|
|
27
|
+
## Copyright
|
|
28
|
+
|
|
29
|
+
Each contributor licenses you to do everything with this
|
|
30
|
+
software that would otherwise infringe that contributor's
|
|
31
|
+
copyright in it.
|
|
32
|
+
|
|
33
|
+
## Notices
|
|
34
|
+
|
|
35
|
+
You must ensure that everyone who gets a copy of
|
|
36
|
+
any part of this software from you, with or without
|
|
37
|
+
changes, also gets the text of this license or a link to
|
|
38
|
+
<https://blueoakcouncil.org/license/1.0.0>.
|
|
39
|
+
|
|
40
|
+
## Excuse
|
|
41
|
+
|
|
42
|
+
If anyone notifies you in writing that you have not
|
|
43
|
+
complied with [Notices](#notices), you can keep your
|
|
44
|
+
license by taking all practical steps to comply within 30
|
|
45
|
+
days after the notice. If you do not do so, your license
|
|
46
|
+
ends immediately.
|
|
47
|
+
|
|
48
|
+
## Patent
|
|
49
|
+
|
|
50
|
+
Each contributor licenses you to do everything with this
|
|
51
|
+
software that would otherwise infringe any patent claims
|
|
52
|
+
they can license or become able to license.
|
|
53
|
+
|
|
54
|
+
## Reliability
|
|
55
|
+
|
|
56
|
+
No contributor can revoke this license.
|
|
57
|
+
|
|
58
|
+
## No Liability
|
|
59
|
+
|
|
60
|
+
***As far as the law allows, this software comes as is,
|
|
61
|
+
without any warranty or condition, and no contributor
|
|
62
|
+
will be liable to anyone for any damages related to this
|
|
63
|
+
software or this license, under any kind of legal claim.***
|
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ The time-based use-recency-unaware cousin of
|
|
|
7
7
|
|
|
8
8
|
Essentially, this is the same API as
|
|
9
9
|
[`lru-cache`](http://npm.im/lru-cache), but it does not do LRU tracking,
|
|
10
|
-
and is bound primarily by time, rather than space.
|
|
10
|
+
and is bound primarily by time, rather than space. Since entries are not
|
|
11
11
|
purged based on recency of use, it can save a lot of extra work managing
|
|
12
12
|
linked lists, mapping keys to pointers, and so on.
|
|
13
13
|
|
|
@@ -25,7 +25,7 @@ capacity.
|
|
|
25
25
|
A TTL _must_ be set for every entry, which can be defaulted in the
|
|
26
26
|
constructor.
|
|
27
27
|
|
|
28
|
-
Custom size calculation is not supported.
|
|
28
|
+
Custom size calculation is not supported. Max capacity is simply the count
|
|
29
29
|
of items in the cache.
|
|
30
30
|
|
|
31
31
|
```js
|
|
@@ -48,7 +48,7 @@ cache.has(1) // returns false
|
|
|
48
48
|
|
|
49
49
|
On Node.js, this module uses the `Timeout.unref()` method to
|
|
50
50
|
prevent its internal `setTimeout` calls from keeping the process
|
|
51
|
-
running indefinitely.
|
|
51
|
+
running indefinitely. However, on other systems such as Deno,
|
|
52
52
|
where the `setTimeout` method does not return an object with an
|
|
53
53
|
`unref()` method, the process will stay open as long as any
|
|
54
54
|
unexpired entry exists in the cache.
|
|
@@ -67,41 +67,40 @@ Default export is the `TTLCache` class.
|
|
|
67
67
|
|
|
68
68
|
Create a new `TTLCache` object.
|
|
69
69
|
|
|
70
|
-
|
|
70
|
+
- `max` The max number of items to keep in the cache. Must be
|
|
71
71
|
positive integer or `Infinity`, defaults to `Infinity` (ie,
|
|
72
72
|
limited only by TTL, not by item count).
|
|
73
|
-
|
|
74
|
-
method.
|
|
75
|
-
below about immortality hazards).
|
|
73
|
+
- `ttl` The max time in ms to store items. Overridable on the `set()`
|
|
74
|
+
method. Must be a positive integer or `Infinity` (see note
|
|
75
|
+
below about immortality hazards). If `undefined` in
|
|
76
76
|
constructor, then a TTL _must_ be provided in each `set()`
|
|
77
77
|
call.
|
|
78
|
-
|
|
79
|
-
retrieved?
|
|
80
|
-
|
|
78
|
+
- `updateAgeOnGet` Should the age of an item be updated when it is
|
|
79
|
+
retrieved? Defaults to `false`. Overridable on the `get()` method.
|
|
80
|
+
- `checkAgeOnGet` Check the TTL whenever an item is retrieved
|
|
81
81
|
with `get()`. If the item is past its ttl, but the timer has
|
|
82
82
|
not yet fired, then delete it and return undefined. By default,
|
|
83
83
|
the cache will return a value if it has one, even if it is
|
|
84
84
|
technically beyond its TTL.
|
|
85
|
-
|
|
86
|
-
TTL unchanged?
|
|
85
|
+
- `noUpdateTTL` Should setting a new value for an existing key leave the
|
|
86
|
+
TTL unchanged? Defaults to `false`. Overridable on the `set()` method.
|
|
87
87
|
(Note that TTL is _always_ updated if the item is expired, since that is
|
|
88
88
|
treated as a new `set()` and the old item is no longer relevant.)
|
|
89
|
-
|
|
90
|
-
removed from the cache.
|
|
89
|
+
- `dispose` Method called with `(value, key, reason)` when an item is
|
|
90
|
+
removed from the cache. Called once item is fully removed from cache.
|
|
91
91
|
It is safe to re-add at this point, but note that adding when `reason` is
|
|
92
92
|
`'set'` can result in infinite recursion if `noDisponseOnSet` is not
|
|
93
93
|
specified.
|
|
94
94
|
|
|
95
|
-
|
|
95
|
+
Disposal reasons:
|
|
96
|
+
- `'stale'` TTL expired.
|
|
97
|
+
- `'set'` Overwritten with a new different value.
|
|
98
|
+
- `'evict'` Removed from the cache to stay within capacity limit.
|
|
99
|
+
- `'delete'` Explicitly deleted with `cache.delete()` or
|
|
100
|
+
`cache.clear()`
|
|
96
101
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
* `'evict'` Removed from the cache to stay within capacity limit.
|
|
100
|
-
* `'delete'` Explicitly deleted with `cache.delete()` or
|
|
101
|
-
`cache.clear()`
|
|
102
|
-
|
|
103
|
-
* `noDisposeOnSet` Do not call `dispose()` method when overwriting a key
|
|
104
|
-
with a new value. Defaults to `false`. Overridable on `set()` method.
|
|
102
|
+
- `noDisposeOnSet` Do not call `dispose()` method when overwriting a key
|
|
103
|
+
with a new value. Defaults to `false`. Overridable on `set()` method.
|
|
105
104
|
|
|
106
105
|
When used as an iterator, like `for (const [key, value] of cache)` or
|
|
107
106
|
`[...cache]`, the cache yields the same results as the `entries()` method.
|
|
@@ -120,11 +119,11 @@ Returns the cache object.
|
|
|
120
119
|
|
|
121
120
|
### `cache.get(key, {updateAgeOnGet, checkAgeOnGet, ttl} = {})`
|
|
122
121
|
|
|
123
|
-
Get an item stored in the cache.
|
|
122
|
+
Get an item stored in the cache. Returns `undefined` if the item is not in
|
|
124
123
|
the cache (including if it has expired and been purged).
|
|
125
124
|
|
|
126
125
|
If `updateAgeOnGet` is `true`, then re-add the item into the
|
|
127
|
-
cache with the updated `ttl` value.
|
|
126
|
+
cache with the updated `ttl` value. All options default to the
|
|
128
127
|
settings on the constructor.
|
|
129
128
|
|
|
130
129
|
If `checkAgeOnGet`, then an item will be deleted if it is found
|
|
@@ -133,14 +132,14 @@ has not yet fired to trigger its expiration.
|
|
|
133
132
|
|
|
134
133
|
Note that using `updateAgeOnGet` _can_ effectively simulate a
|
|
135
134
|
"least-recently-used" type of algorithm, by repeatedly updating
|
|
136
|
-
the TTL of items as they are used.
|
|
135
|
+
the TTL of items as they are used. However, if you find yourself
|
|
137
136
|
doing this, consider using
|
|
138
137
|
[`lru-cache`](http://npm.im/lru-cache), as it is much more
|
|
139
138
|
optimized for an LRU use case.
|
|
140
139
|
|
|
141
140
|
### `cache.getRemainingTTL(key)`
|
|
142
141
|
|
|
143
|
-
Return the remaining time before an item expires.
|
|
142
|
+
Return the remaining time before an item expires. Returns `0` if the item
|
|
144
143
|
is not found in the cache or is already expired.
|
|
145
144
|
|
|
146
145
|
### `cache.has(key)`
|
|
@@ -158,7 +157,7 @@ Delete all items from the cache.
|
|
|
158
157
|
### `cache.entries()`
|
|
159
158
|
|
|
160
159
|
Return an iterator that walks through each `[key, value]` from soonest
|
|
161
|
-
expiring to latest expiring.
|
|
160
|
+
expiring to latest expiring. (Items expiring at the same time are walked
|
|
162
161
|
in insertion order.)
|
|
163
162
|
|
|
164
163
|
Default iteration method for the cache object.
|
|
@@ -189,20 +188,20 @@ You should not ever call these, they are managed automatically.
|
|
|
189
188
|
|
|
190
189
|
**Internal**
|
|
191
190
|
|
|
192
|
-
Removes items which have expired.
|
|
191
|
+
Removes items which have expired. Called automatically.
|
|
193
192
|
|
|
194
193
|
### `purgeToCapacity`
|
|
195
194
|
|
|
196
195
|
**Internal**
|
|
197
196
|
|
|
198
|
-
Removes soonest-expiring items when the capacity limit is reached.
|
|
197
|
+
Removes soonest-expiring items when the capacity limit is reached. Called
|
|
199
198
|
automatically.
|
|
200
199
|
|
|
201
200
|
### `dispose`
|
|
202
201
|
|
|
203
202
|
**Internal**
|
|
204
203
|
|
|
205
|
-
Called when an item is removed from the cache and should be disposed.
|
|
204
|
+
Called when an item is removed from the cache and should be disposed. Set
|
|
206
205
|
this on the constructor options.
|
|
207
206
|
|
|
208
207
|
### `setTimer`
|
|
@@ -214,8 +213,8 @@ is setup at once. Called automatically.
|
|
|
214
213
|
|
|
215
214
|
## Algorithm
|
|
216
215
|
|
|
217
|
-
The cache uses two `Map` objects.
|
|
218
|
-
expiration time, and the second maps item keys to their values.
|
|
216
|
+
The cache uses two `Map` objects. The first maps item keys to their
|
|
217
|
+
expiration time, and the second maps item keys to their values. Then, a
|
|
219
218
|
null-prototype object uses the expiration time as keys, with the value
|
|
220
219
|
being an array of all the keys expiring at that time.
|
|
221
220
|
|
|
@@ -226,7 +225,7 @@ fairly good performance:
|
|
|
226
225
|
arbitrary keys.
|
|
227
226
|
- Objects with solely integer-numeric keys are iterated in sorted numeric
|
|
228
227
|
order rather than insertion order, and insertions in the middle of the
|
|
229
|
-
key ordering are still very fast.
|
|
228
|
+
key ordering are still very fast. This is true of all modern JS engines
|
|
230
229
|
tested at the time of this module's creation, but most particularly V8
|
|
231
230
|
(the engine in Node.js).
|
|
232
231
|
|
|
@@ -235,19 +234,19 @@ iteration order, deleting items until we come to the first key greater than
|
|
|
235
234
|
the current time.
|
|
236
235
|
|
|
237
236
|
Thus, the `start` time doesn't need to be tracked, only the expiration
|
|
238
|
-
time.
|
|
237
|
+
time. When an item age is updated (either explicitly on `get()`, or by
|
|
239
238
|
setting to a new value), it is deleted and re-inserted.
|
|
240
239
|
|
|
241
240
|
## Immortality Hazards
|
|
242
241
|
|
|
243
242
|
It is possible to set a TTL of `Infinity`, in which case an item
|
|
244
|
-
will never expire.
|
|
243
|
+
will never expire. As it does not expire, its TTL is not
|
|
245
244
|
tracked, and `getRemainingTTL()` will return `Infinity` for that
|
|
246
245
|
key.
|
|
247
246
|
|
|
248
|
-
If you do this, then the item will never be purged.
|
|
247
|
+
If you do this, then the item will never be purged. Create
|
|
249
248
|
enough immortal values, and the cache will grow to consume all
|
|
250
|
-
available memory.
|
|
249
|
+
available memory. If find yourself doing this, it's _probably_
|
|
251
250
|
better to use a different data structure, such as a `Map` or
|
|
252
251
|
plain old object to store values, as it will have better
|
|
253
252
|
performance and the hazards will be more obvious.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export type DisposeReason = 'set' | 'delete' | 'stale' | 'evict';
|
|
2
|
+
export type DisposeFunction<K, V> = (val: V, key: K, reason: DisposeReason) => unknown;
|
|
3
|
+
export type TTLCacheOptions<K, V> = {
|
|
4
|
+
max?: number;
|
|
5
|
+
ttl?: number;
|
|
6
|
+
updateAgeOnGet?: boolean;
|
|
7
|
+
checkAgeOnGet?: boolean;
|
|
8
|
+
noUpdateTTL?: boolean;
|
|
9
|
+
dispose?: DisposeFunction<K, V>;
|
|
10
|
+
noDisposeOnSet?: boolean;
|
|
11
|
+
};
|
|
12
|
+
export type SetOptions<K, V> = Pick<TTLCacheOptions<K, V>, 'ttl' | 'noUpdateTTL' | 'noDisposeOnSet'>;
|
|
13
|
+
export type GetOptions<K, V> = Pick<TTLCacheOptions<K, V>, 'updateAgeOnGet' | 'ttl' | 'checkAgeOnGet'>;
|
|
14
|
+
export declare class TTLCache<K = unknown, V = unknown> {
|
|
15
|
+
expirations: Record<number, K[]>;
|
|
16
|
+
data: Map<K, V>;
|
|
17
|
+
expirationMap: Map<K, number>;
|
|
18
|
+
ttl?: number;
|
|
19
|
+
max: number;
|
|
20
|
+
updateAgeOnGet: boolean;
|
|
21
|
+
noUpdateTTL: boolean;
|
|
22
|
+
noDisposeOnSet: boolean;
|
|
23
|
+
checkAgeOnGet: boolean;
|
|
24
|
+
dispose: DisposeFunction<K, V>;
|
|
25
|
+
timer?: ReturnType<typeof setTimeout>;
|
|
26
|
+
timerExpiration?: number;
|
|
27
|
+
constructor({ max, ttl, updateAgeOnGet, checkAgeOnGet, noUpdateTTL, dispose, noDisposeOnSet, }?: TTLCacheOptions<K, V>);
|
|
28
|
+
setTimer(expiration: number, ttl: number): void;
|
|
29
|
+
cancelTimer(): void;
|
|
30
|
+
cancelTimers(): void;
|
|
31
|
+
clear(): void;
|
|
32
|
+
setTTL(key: K, ttl?: number | undefined): void;
|
|
33
|
+
set(key: K, val: V, { ttl, noUpdateTTL, noDisposeOnSet, }?: SetOptions<K, V>): this;
|
|
34
|
+
has(key: K): boolean;
|
|
35
|
+
getRemainingTTL(key: K): number;
|
|
36
|
+
get(key: K, { updateAgeOnGet, ttl, checkAgeOnGet, }?: GetOptions<K, V>): V | undefined;
|
|
37
|
+
delete(key: K): boolean;
|
|
38
|
+
purgeToCapacity(): void;
|
|
39
|
+
get size(): number;
|
|
40
|
+
purgeStale(): void;
|
|
41
|
+
entries(): Generator<[K, V], void, unknown>;
|
|
42
|
+
keys(): Generator<K, void, unknown>;
|
|
43
|
+
values(): Generator<V | undefined, void, unknown>;
|
|
44
|
+
[Symbol.iterator](): Generator<[K, V], void, unknown>;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAoBA,MAAM,MAAM,aAAa,GAAG,KAAK,GAAG,QAAQ,GAAG,OAAO,GAAG,OAAO,CAAA;AAEhE,MAAM,MAAM,eAAe,CAAC,CAAC,EAAE,CAAC,IAAI,CAClC,GAAG,EAAE,CAAC,EACN,GAAG,EAAE,CAAC,EACN,MAAM,EAAE,aAAa,KAClB,OAAO,CAAA;AAEZ,MAAM,MAAM,eAAe,CAAC,CAAC,EAAE,CAAC,IAAI;IAClC,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,OAAO,CAAC,EAAE,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;IAC/B,cAAc,CAAC,EAAE,OAAO,CAAA;CACzB,CAAA;AAED,MAAM,MAAM,UAAU,CAAC,CAAC,EAAE,CAAC,IAAI,IAAI,CACjC,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,EACrB,KAAK,GAAG,aAAa,GAAG,gBAAgB,CACzC,CAAA;AACD,MAAM,MAAM,UAAU,CAAC,CAAC,EAAE,CAAC,IAAI,IAAI,CACjC,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,EACrB,gBAAgB,GAAG,KAAK,GAAG,eAAe,CAC3C,CAAA;AAED,qBAAa,QAAQ,CAAC,CAAC,GAAG,OAAO,EAAE,CAAC,GAAG,OAAO;IAC5C,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,CAAsB;IACtD,IAAI,YAAkB;IACtB,aAAa,iBAAuB;IACpC,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,MAAM,CAAA;IACX,cAAc,EAAE,OAAO,CAAA;IACvB,WAAW,EAAE,OAAO,CAAA;IACpB,cAAc,EAAE,OAAO,CAAA;IACvB,aAAa,EAAE,OAAO,CAAA;IACtB,OAAO,EAAE,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;IAC9B,KAAK,CAAC,EAAE,UAAU,CAAC,OAAO,UAAU,CAAC,CAAA;IACrC,eAAe,CAAC,EAAE,MAAM,CAAA;gBAEZ,EACV,GAAc,EACd,GAAG,EACH,cAAsB,EACtB,aAAqB,EACrB,WAAmB,EACnB,OAAO,EACP,cAAsB,GACvB,GAAE,eAAe,CAAC,CAAC,EAAE,CAAC,CAAM;IA4B7B,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM;IA+BxC,WAAW;IASX,YAAY;IAUZ,KAAK;IAaL,MAAM,CAAC,GAAG,EAAE,CAAC,EAAE,GAAG,qBAAW;IAyB7B,GAAG,CACD,GAAG,EAAE,CAAC,EACN,GAAG,EAAE,CAAC,EACN,EACE,GAAc,EACd,WAA8B,EAC9B,cAAoC,GACrC,GAAE,UAAU,CAAC,CAAC,EAAE,CAAC,CAAM;IA6B1B,GAAG,CAAC,GAAG,EAAE,CAAC;IAIV,eAAe,CAAC,GAAG,EAAE,CAAC;IAStB,GAAG,CACD,GAAG,EAAE,CAAC,EACN,EACE,cAAoC,EACpC,GAAc,EACd,aAAkC,GACnC,GAAE,UAAU,CAAC,CAAC,EAAE,CAAC,CAAM;IAa1B,MAAM,CAAC,GAAG,EAAE,CAAC;IAuBb,eAAe;IA8Bf,IAAI,IAAI,WAEP;IAED,UAAU;IA4BT,OAAO;IAOP,IAAI;IAOJ,MAAM;IAOP,CAAC,MAAM,CAAC,QAAQ,CAAC;CAGlB"}
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// A simple TTL cache with max capacity option, ms resolution,
|
|
3
|
+
// autopurge, and reasonably optimized performance
|
|
4
|
+
// Relies on the fact that integer Object keys are kept sorted,
|
|
5
|
+
// and managed very efficiently by V8.
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.TTLCache = void 0;
|
|
8
|
+
/* c8 ignore start */
|
|
9
|
+
const perf = typeof performance === 'object' &&
|
|
10
|
+
performance &&
|
|
11
|
+
typeof performance.now === 'function'
|
|
12
|
+
? performance
|
|
13
|
+
: Date;
|
|
14
|
+
/* c8 ignore stop */
|
|
15
|
+
const now = () => perf.now();
|
|
16
|
+
const isPosInt = (n) => !!n && n === Math.floor(n) && n > 0 && isFinite(n);
|
|
17
|
+
const isPosIntOrInf = (n) => n === Infinity || isPosInt(n);
|
|
18
|
+
class TTLCache {
|
|
19
|
+
expirations = Object.create(null);
|
|
20
|
+
data = new Map();
|
|
21
|
+
expirationMap = new Map();
|
|
22
|
+
ttl;
|
|
23
|
+
max;
|
|
24
|
+
updateAgeOnGet;
|
|
25
|
+
noUpdateTTL;
|
|
26
|
+
noDisposeOnSet;
|
|
27
|
+
checkAgeOnGet;
|
|
28
|
+
dispose;
|
|
29
|
+
timer;
|
|
30
|
+
timerExpiration;
|
|
31
|
+
constructor({ max = Infinity, ttl, updateAgeOnGet = false, checkAgeOnGet = false, noUpdateTTL = false, dispose, noDisposeOnSet = false, } = {}) {
|
|
32
|
+
if (ttl !== undefined && !isPosIntOrInf(ttl)) {
|
|
33
|
+
throw new TypeError('ttl must be positive integer or Infinity if set');
|
|
34
|
+
}
|
|
35
|
+
if (!isPosIntOrInf(max)) {
|
|
36
|
+
throw new TypeError('max must be positive integer or Infinity');
|
|
37
|
+
}
|
|
38
|
+
this.ttl = ttl;
|
|
39
|
+
this.max = max;
|
|
40
|
+
this.updateAgeOnGet = !!updateAgeOnGet;
|
|
41
|
+
this.checkAgeOnGet = !!checkAgeOnGet;
|
|
42
|
+
this.noUpdateTTL = !!noUpdateTTL;
|
|
43
|
+
this.noDisposeOnSet = !!noDisposeOnSet;
|
|
44
|
+
if (dispose !== undefined) {
|
|
45
|
+
if (typeof dispose !== 'function') {
|
|
46
|
+
throw new TypeError('dispose must be function if set');
|
|
47
|
+
}
|
|
48
|
+
this.dispose = dispose;
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
this.dispose = (_, __, ___) => { };
|
|
52
|
+
}
|
|
53
|
+
this.timer = undefined;
|
|
54
|
+
this.timerExpiration = undefined;
|
|
55
|
+
}
|
|
56
|
+
setTimer(expiration, ttl) {
|
|
57
|
+
if (this.timerExpiration && this.timerExpiration < expiration) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (this.timer) {
|
|
61
|
+
clearTimeout(this.timer);
|
|
62
|
+
}
|
|
63
|
+
const t = setTimeout(() => {
|
|
64
|
+
this.timer = undefined;
|
|
65
|
+
this.timerExpiration = undefined;
|
|
66
|
+
this.purgeStale();
|
|
67
|
+
for (const exp in this.expirations) {
|
|
68
|
+
const e = Number(exp);
|
|
69
|
+
this.setTimer(e, e - now());
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
}, ttl);
|
|
73
|
+
/* c8 ignore start - affordance for non-node envs */
|
|
74
|
+
if (t.unref)
|
|
75
|
+
t.unref();
|
|
76
|
+
/* c8 ignore stop */
|
|
77
|
+
this.timerExpiration = expiration;
|
|
78
|
+
this.timer = t;
|
|
79
|
+
}
|
|
80
|
+
// hang onto the timer so we can clearTimeout if all items
|
|
81
|
+
// are deleted. Deno doesn't have Timer.unref(), so it
|
|
82
|
+
// hangs otherwise.
|
|
83
|
+
cancelTimer() {
|
|
84
|
+
if (this.timer) {
|
|
85
|
+
clearTimeout(this.timer);
|
|
86
|
+
this.timerExpiration = undefined;
|
|
87
|
+
this.timer = undefined;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/* c8 ignore start */
|
|
91
|
+
cancelTimers() {
|
|
92
|
+
process.emitWarning('TTLCache.cancelTimers has been renamed to ' +
|
|
93
|
+
'TTLCache.cancelTimer (no "s"), and will be removed in the next ' +
|
|
94
|
+
'major version update');
|
|
95
|
+
return this.cancelTimer();
|
|
96
|
+
}
|
|
97
|
+
/* c8 ignore stop */
|
|
98
|
+
clear() {
|
|
99
|
+
const entries = this.dispose !== TTLCache.prototype.dispose ? [...this] : [];
|
|
100
|
+
this.data.clear();
|
|
101
|
+
this.expirationMap.clear();
|
|
102
|
+
// no need for any purging now
|
|
103
|
+
this.cancelTimer();
|
|
104
|
+
this.expirations = Object.create(null);
|
|
105
|
+
for (const [key, val] of entries) {
|
|
106
|
+
this.dispose(val, key, 'delete');
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
setTTL(key, ttl = this.ttl) {
|
|
110
|
+
const current = this.expirationMap.get(key);
|
|
111
|
+
if (current !== undefined) {
|
|
112
|
+
// remove from the expirations list, so it isn't purged
|
|
113
|
+
const exp = this.expirations[current];
|
|
114
|
+
if (!exp || exp.length <= 1) {
|
|
115
|
+
delete this.expirations[current];
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
this.expirations[current] = exp.filter(k => k !== key);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (ttl && ttl !== Infinity) {
|
|
122
|
+
const expiration = Math.floor(now() + ttl);
|
|
123
|
+
this.expirationMap.set(key, expiration);
|
|
124
|
+
if (!this.expirations[expiration]) {
|
|
125
|
+
this.expirations[expiration] = [];
|
|
126
|
+
this.setTimer(expiration, ttl);
|
|
127
|
+
}
|
|
128
|
+
this.expirations[expiration].push(key);
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
this.expirationMap.set(key, Infinity);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
set(key, val, { ttl = this.ttl, noUpdateTTL = this.noUpdateTTL, noDisposeOnSet = this.noDisposeOnSet, } = {}) {
|
|
135
|
+
if (!isPosIntOrInf(ttl)) {
|
|
136
|
+
throw new TypeError('ttl must be positive integer or Infinity');
|
|
137
|
+
}
|
|
138
|
+
if (this.expirationMap.has(key)) {
|
|
139
|
+
if (!noUpdateTTL) {
|
|
140
|
+
this.setTTL(key, ttl);
|
|
141
|
+
}
|
|
142
|
+
// has old value
|
|
143
|
+
const oldValue = this.data.get(key);
|
|
144
|
+
if (oldValue !== undefined && oldValue !== val) {
|
|
145
|
+
this.data.set(key, val);
|
|
146
|
+
if (!noDisposeOnSet) {
|
|
147
|
+
this.dispose(oldValue, key, 'set');
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
this.setTTL(key, ttl);
|
|
153
|
+
this.data.set(key, val);
|
|
154
|
+
}
|
|
155
|
+
while (this.size > this.max) {
|
|
156
|
+
this.purgeToCapacity();
|
|
157
|
+
}
|
|
158
|
+
return this;
|
|
159
|
+
}
|
|
160
|
+
has(key) {
|
|
161
|
+
return this.data.has(key);
|
|
162
|
+
}
|
|
163
|
+
getRemainingTTL(key) {
|
|
164
|
+
const expiration = this.expirationMap.get(key);
|
|
165
|
+
return expiration === Infinity
|
|
166
|
+
? expiration
|
|
167
|
+
: expiration !== undefined
|
|
168
|
+
? Math.max(0, Math.ceil(expiration - now()))
|
|
169
|
+
: 0;
|
|
170
|
+
}
|
|
171
|
+
get(key, { updateAgeOnGet = this.updateAgeOnGet, ttl = this.ttl, checkAgeOnGet = this.checkAgeOnGet, } = {}) {
|
|
172
|
+
const val = this.data.get(key);
|
|
173
|
+
if (checkAgeOnGet && this.getRemainingTTL(key) === 0) {
|
|
174
|
+
this.delete(key);
|
|
175
|
+
return undefined;
|
|
176
|
+
}
|
|
177
|
+
if (updateAgeOnGet) {
|
|
178
|
+
this.setTTL(key, ttl);
|
|
179
|
+
}
|
|
180
|
+
return val;
|
|
181
|
+
}
|
|
182
|
+
delete(key) {
|
|
183
|
+
const current = this.expirationMap.get(key);
|
|
184
|
+
if (current !== undefined) {
|
|
185
|
+
const value = this.data.get(key);
|
|
186
|
+
this.data.delete(key);
|
|
187
|
+
this.expirationMap.delete(key);
|
|
188
|
+
const exp = this.expirations[current];
|
|
189
|
+
if (exp) {
|
|
190
|
+
if (exp.length <= 1) {
|
|
191
|
+
delete this.expirations[current];
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
this.expirations[current] = exp.filter(k => k !== key);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
this.dispose(value, key, 'delete');
|
|
198
|
+
if (this.size === 0) {
|
|
199
|
+
this.cancelTimer();
|
|
200
|
+
}
|
|
201
|
+
return true;
|
|
202
|
+
}
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
purgeToCapacity() {
|
|
206
|
+
for (const exp in this.expirations) {
|
|
207
|
+
const keys = this.expirations[exp];
|
|
208
|
+
if (this.size - keys.length >= this.max) {
|
|
209
|
+
delete this.expirations[exp];
|
|
210
|
+
const entries = [];
|
|
211
|
+
for (const key of keys) {
|
|
212
|
+
entries.push([key, this.data.get(key)]);
|
|
213
|
+
this.data.delete(key);
|
|
214
|
+
this.expirationMap.delete(key);
|
|
215
|
+
}
|
|
216
|
+
for (const [key, val] of entries) {
|
|
217
|
+
this.dispose(val, key, 'evict');
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
const s = this.size - this.max;
|
|
222
|
+
const entries = [];
|
|
223
|
+
for (const key of keys.splice(0, s)) {
|
|
224
|
+
entries.push([key, this.data.get(key)]);
|
|
225
|
+
this.data.delete(key);
|
|
226
|
+
this.expirationMap.delete(key);
|
|
227
|
+
}
|
|
228
|
+
for (const [key, val] of entries) {
|
|
229
|
+
this.dispose(val, key, 'evict');
|
|
230
|
+
}
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
get size() {
|
|
236
|
+
return this.data.size;
|
|
237
|
+
}
|
|
238
|
+
purgeStale() {
|
|
239
|
+
const n = Math.ceil(now());
|
|
240
|
+
for (const exp in this.expirations) {
|
|
241
|
+
if (exp === 'Infinity' || Number(exp) > n) {
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
/* c8 ignore start
|
|
245
|
+
* mysterious need for a guard here?
|
|
246
|
+
* https://github.com/isaacs/ttlcache/issues/26 */
|
|
247
|
+
const keys = [...(this.expirations[exp] || [])];
|
|
248
|
+
/* c8 ignore stop */
|
|
249
|
+
const entries = [];
|
|
250
|
+
delete this.expirations[exp];
|
|
251
|
+
for (const key of keys) {
|
|
252
|
+
entries.push([key, this.data.get(key)]);
|
|
253
|
+
this.data.delete(key);
|
|
254
|
+
this.expirationMap.delete(key);
|
|
255
|
+
}
|
|
256
|
+
for (const [key, val] of entries) {
|
|
257
|
+
this.dispose(val, key, 'stale');
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
if (this.size === 0) {
|
|
261
|
+
this.cancelTimer();
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
*entries() {
|
|
265
|
+
for (const exp in this.expirations) {
|
|
266
|
+
for (const key of this.expirations[exp]) {
|
|
267
|
+
yield [key, this.data.get(key)];
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
*keys() {
|
|
272
|
+
for (const exp in this.expirations) {
|
|
273
|
+
for (const key of this.expirations[exp]) {
|
|
274
|
+
yield key;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
*values() {
|
|
279
|
+
for (const exp in this.expirations) {
|
|
280
|
+
for (const key of this.expirations[exp]) {
|
|
281
|
+
yield this.data.get(key);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
[Symbol.iterator]() {
|
|
286
|
+
return this.entries();
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
exports.TTLCache = TTLCache;
|
|
290
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["// A simple TTL cache with max capacity option, ms resolution,\n// autopurge, and reasonably optimized performance\n// Relies on the fact that integer Object keys are kept sorted,\n// and managed very efficiently by V8.\n\n/* c8 ignore start */\nconst perf =\n typeof performance === 'object' &&\n performance &&\n typeof performance.now === 'function'\n ? performance\n : Date\n/* c8 ignore stop */\n\nconst now = () => perf.now()\nconst isPosInt = (n: any): n is number =>\n !!n && n === Math.floor(n) && n > 0 && isFinite(n)\nconst isPosIntOrInf = (n: any): n is number =>\n n === Infinity || isPosInt(n)\n\nexport type DisposeReason = 'set' | 'delete' | 'stale' | 'evict'\n\nexport type DisposeFunction<K, V> = (\n val: V,\n key: K,\n reason: DisposeReason,\n) => unknown\n\nexport type TTLCacheOptions<K, V> = {\n max?: number\n ttl?: number\n updateAgeOnGet?: boolean\n checkAgeOnGet?: boolean\n noUpdateTTL?: boolean\n dispose?: DisposeFunction<K, V>\n noDisposeOnSet?: boolean\n}\n\nexport type SetOptions<K, V> = Pick<\n TTLCacheOptions<K, V>,\n 'ttl' | 'noUpdateTTL' | 'noDisposeOnSet'\n>\nexport type GetOptions<K, V> = Pick<\n TTLCacheOptions<K, V>,\n 'updateAgeOnGet' | 'ttl' | 'checkAgeOnGet'\n>\n\nexport class TTLCache<K = unknown, V = unknown> {\n expirations: Record<number, K[]> = Object.create(null)\n data = new Map<K, V>()\n expirationMap = new Map<K, number>()\n ttl?: number\n max: number\n updateAgeOnGet: boolean\n noUpdateTTL: boolean\n noDisposeOnSet: boolean\n checkAgeOnGet: boolean\n dispose: DisposeFunction<K, V>\n timer?: ReturnType<typeof setTimeout>\n timerExpiration?: number\n\n constructor({\n max = Infinity,\n ttl,\n updateAgeOnGet = false,\n checkAgeOnGet = false,\n noUpdateTTL = false,\n dispose,\n noDisposeOnSet = false,\n }: TTLCacheOptions<K, V> = {}) {\n if (ttl !== undefined && !isPosIntOrInf(ttl)) {\n throw new TypeError(\n 'ttl must be positive integer or Infinity if set',\n )\n }\n if (!isPosIntOrInf(max)) {\n throw new TypeError('max must be positive integer or Infinity')\n }\n this.ttl = ttl\n this.max = max\n this.updateAgeOnGet = !!updateAgeOnGet\n this.checkAgeOnGet = !!checkAgeOnGet\n this.noUpdateTTL = !!noUpdateTTL\n this.noDisposeOnSet = !!noDisposeOnSet\n if (dispose !== undefined) {\n if (typeof dispose !== 'function') {\n throw new TypeError('dispose must be function if set')\n }\n this.dispose = dispose\n } else {\n this.dispose = (_, __, ___) => {}\n }\n\n this.timer = undefined\n this.timerExpiration = undefined\n }\n\n setTimer(expiration: number, ttl: number) {\n if (this.timerExpiration && this.timerExpiration < expiration) {\n return\n }\n\n if (this.timer) {\n clearTimeout(this.timer)\n }\n\n const t = setTimeout(() => {\n this.timer = undefined\n this.timerExpiration = undefined\n this.purgeStale()\n for (const exp in this.expirations) {\n const e = Number(exp)\n this.setTimer(e, e - now())\n break\n }\n }, ttl)\n\n /* c8 ignore start - affordance for non-node envs */\n if (t.unref) t.unref()\n /* c8 ignore stop */\n\n this.timerExpiration = expiration\n this.timer = t\n }\n\n // hang onto the timer so we can clearTimeout if all items\n // are deleted. Deno doesn't have Timer.unref(), so it\n // hangs otherwise.\n cancelTimer() {\n if (this.timer) {\n clearTimeout(this.timer)\n this.timerExpiration = undefined\n this.timer = undefined\n }\n }\n\n /* c8 ignore start */\n cancelTimers() {\n process.emitWarning(\n 'TTLCache.cancelTimers has been renamed to ' +\n 'TTLCache.cancelTimer (no \"s\"), and will be removed in the next ' +\n 'major version update',\n )\n return this.cancelTimer()\n }\n /* c8 ignore stop */\n\n clear() {\n const entries =\n this.dispose !== TTLCache.prototype.dispose ? [...this] : []\n this.data.clear()\n this.expirationMap.clear()\n // no need for any purging now\n this.cancelTimer()\n this.expirations = Object.create(null)\n for (const [key, val] of entries as [K, V][]) {\n this.dispose(val, key, 'delete')\n }\n }\n\n setTTL(key: K, ttl = this.ttl) {\n const current = this.expirationMap.get(key)\n if (current !== undefined) {\n // remove from the expirations list, so it isn't purged\n const exp = this.expirations[current]\n if (!exp || exp.length <= 1) {\n delete this.expirations[current]\n } else {\n this.expirations[current] = exp.filter(k => k !== key)\n }\n }\n\n if (ttl && ttl !== Infinity) {\n const expiration = Math.floor(now() + ttl)\n this.expirationMap.set(key, expiration)\n if (!this.expirations[expiration]) {\n this.expirations[expiration] = []\n this.setTimer(expiration, ttl)\n }\n this.expirations[expiration].push(key)\n } else {\n this.expirationMap.set(key, Infinity)\n }\n }\n\n set(\n key: K,\n val: V,\n {\n ttl = this.ttl,\n noUpdateTTL = this.noUpdateTTL,\n noDisposeOnSet = this.noDisposeOnSet,\n }: SetOptions<K, V> = {},\n ) {\n if (!isPosIntOrInf(ttl)) {\n throw new TypeError('ttl must be positive integer or Infinity')\n }\n if (this.expirationMap.has(key)) {\n if (!noUpdateTTL) {\n this.setTTL(key, ttl)\n }\n // has old value\n const oldValue = this.data.get(key)\n if (oldValue !== undefined && oldValue !== val) {\n this.data.set(key, val)\n if (!noDisposeOnSet) {\n this.dispose(oldValue, key, 'set')\n }\n }\n } else {\n this.setTTL(key, ttl)\n this.data.set(key, val)\n }\n\n while (this.size > this.max) {\n this.purgeToCapacity()\n }\n\n return this\n }\n\n has(key: K) {\n return this.data.has(key)\n }\n\n getRemainingTTL(key: K) {\n const expiration = this.expirationMap.get(key)\n return expiration === Infinity\n ? expiration\n : expiration !== undefined\n ? Math.max(0, Math.ceil(expiration - now()))\n : 0\n }\n\n get(\n key: K,\n {\n updateAgeOnGet = this.updateAgeOnGet,\n ttl = this.ttl,\n checkAgeOnGet = this.checkAgeOnGet,\n }: GetOptions<K, V> = {},\n ) {\n const val = this.data.get(key)\n if (checkAgeOnGet && this.getRemainingTTL(key) === 0) {\n this.delete(key)\n return undefined\n }\n if (updateAgeOnGet) {\n this.setTTL(key, ttl)\n }\n return val\n }\n\n delete(key: K) {\n const current = this.expirationMap.get(key)\n if (current !== undefined) {\n const value = this.data.get(key) as V\n this.data.delete(key)\n this.expirationMap.delete(key)\n const exp = this.expirations[current]\n if (exp) {\n if (exp.length <= 1) {\n delete this.expirations[current]\n } else {\n this.expirations[current] = exp.filter(k => k !== key)\n }\n }\n this.dispose(value, key, 'delete')\n if (this.size === 0) {\n this.cancelTimer()\n }\n return true\n }\n return false\n }\n\n purgeToCapacity() {\n for (const exp in this.expirations) {\n const keys = this.expirations[exp] as K[]\n if (this.size - keys.length >= this.max) {\n delete this.expirations[exp]\n const entries: [K, V][] = []\n for (const key of keys) {\n entries.push([key, this.data.get(key) as V])\n this.data.delete(key)\n this.expirationMap.delete(key)\n }\n for (const [key, val] of entries) {\n this.dispose(val, key, 'evict')\n }\n } else {\n const s = this.size - this.max\n const entries: [K, V][] = []\n for (const key of keys.splice(0, s)) {\n entries.push([key, this.data.get(key) as V])\n this.data.delete(key)\n this.expirationMap.delete(key)\n }\n for (const [key, val] of entries) {\n this.dispose(val, key, 'evict')\n }\n return\n }\n }\n }\n\n get size() {\n return this.data.size\n }\n\n purgeStale() {\n const n = Math.ceil(now())\n for (const exp in this.expirations) {\n if (exp === 'Infinity' || Number(exp) > n) {\n return\n }\n\n /* c8 ignore start\n * mysterious need for a guard here?\n * https://github.com/isaacs/ttlcache/issues/26 */\n const keys = [...(this.expirations[exp] || [])]\n /* c8 ignore stop */\n const entries: [K, V][] = []\n delete this.expirations[exp]\n for (const key of keys) {\n entries.push([key, this.data.get(key) as V])\n this.data.delete(key)\n this.expirationMap.delete(key)\n }\n for (const [key, val] of entries) {\n this.dispose(val, key, 'stale')\n }\n }\n if (this.size === 0) {\n this.cancelTimer()\n }\n }\n\n *entries() {\n for (const exp in this.expirations) {\n for (const key of this.expirations[exp] as K[]) {\n yield [key, this.data.get(key)] as [K, V]\n }\n }\n }\n *keys() {\n for (const exp in this.expirations) {\n for (const key of this.expirations[exp] as K[]) {\n yield key\n }\n }\n }\n *values() {\n for (const exp in this.expirations) {\n for (const key of this.expirations[exp] as K[]) {\n yield this.data.get(key)\n }\n }\n }\n [Symbol.iterator]() {\n return this.entries()\n }\n}\n"]}
|