@isaacs/ttlcache 1.2.2 → 1.4.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/README.md +32 -8
- package/index.d.ts +43 -1
- package/index.js +60 -19
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -53,9 +53,9 @@ 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.
|
|
55
55
|
|
|
56
|
-
You may
|
|
57
|
-
|
|
58
|
-
|
|
56
|
+
You may call `cache.cancelTimer()` to clear the timeout and
|
|
57
|
+
allow the process to exit normally. Be advised that canceling the
|
|
58
|
+
timer in this way will of course prevent anything from expiring.
|
|
59
59
|
|
|
60
60
|
## API
|
|
61
61
|
|
|
@@ -63,7 +63,7 @@ timeouts and allow the process to exit normally.
|
|
|
63
63
|
|
|
64
64
|
Default export is the `TTLCache` class.
|
|
65
65
|
|
|
66
|
-
### `new TTLCache({ ttl, max = Infinty, updateAgeOnGet = false, noUpdateTTL = false, noDisposeOnSet = false })`
|
|
66
|
+
### `new TTLCache({ ttl, max = Infinty, updateAgeOnGet = false, checkAgeOnGet = false, noUpdateTTL = false, noDisposeOnSet = false })`
|
|
67
67
|
|
|
68
68
|
Create a new `TTLCache` object.
|
|
69
69
|
|
|
@@ -77,6 +77,11 @@ Create a new `TTLCache` object.
|
|
|
77
77
|
call.
|
|
78
78
|
* `updateAgeOnGet` Should the age of an item be updated when it is
|
|
79
79
|
retrieved? Defaults to `false`. Overridable on the `get()` method.
|
|
80
|
+
* `checkAgeOnGet` Check the TTL whenever an item is retrieved
|
|
81
|
+
with `get()`. If the item is past its ttl, but the timer has
|
|
82
|
+
not yet fired, then delete it and return undefined. By default,
|
|
83
|
+
the cache will return a value if it has one, even if it is
|
|
84
|
+
technically beyond its TTL.
|
|
80
85
|
* `noUpdateTTL` Should setting a new value for an existing key leave the
|
|
81
86
|
TTL unchanged? Defaults to `false`. Overridable on the `set()` method.
|
|
82
87
|
(Note that TTL is _always_ updated if the item is expired, since that is
|
|
@@ -113,14 +118,18 @@ Store a value in the cache for the specified time.
|
|
|
113
118
|
|
|
114
119
|
Returns the cache object.
|
|
115
120
|
|
|
116
|
-
### `cache.get(key, {updateAgeOnGet, ttl} = {})`
|
|
121
|
+
### `cache.get(key, {updateAgeOnGet, checkAgeOnGet, ttl} = {})`
|
|
117
122
|
|
|
118
123
|
Get an item stored in the cache. Returns `undefined` if the item is not in
|
|
119
124
|
the cache (including if it has expired and been purged).
|
|
120
125
|
|
|
121
|
-
If `updateAgeOnGet` is `true`, then re-add the item into the
|
|
122
|
-
updated `ttl` value.
|
|
123
|
-
constructor.
|
|
126
|
+
If `updateAgeOnGet` is `true`, then re-add the item into the
|
|
127
|
+
cache with the updated `ttl` value. All options default to the
|
|
128
|
+
settings on the constructor.
|
|
129
|
+
|
|
130
|
+
If `checkAgeOnGet`, then an item will be deleted if it is found
|
|
131
|
+
to be beyond its TTL, which can happen if the setTimeout timer
|
|
132
|
+
has not yet fired to trigger its expiration.
|
|
124
133
|
|
|
125
134
|
Note that using `updateAgeOnGet` _can_ effectively simulate a
|
|
126
135
|
"least-recently-used" type of algorithm, by repeatedly updating
|
|
@@ -164,6 +173,14 @@ latest expiring.
|
|
|
164
173
|
Return an iterator that walks through each `value` from soonest expiring to
|
|
165
174
|
latest expiring.
|
|
166
175
|
|
|
176
|
+
### `cache.cancelTimer()`
|
|
177
|
+
|
|
178
|
+
Clear the internal timer, and stop automatically expiring items
|
|
179
|
+
when their TTL expires.
|
|
180
|
+
|
|
181
|
+
This allows the process to exit normally on Deno and other
|
|
182
|
+
platforms that lack Node's `Timer.unref()` method.
|
|
183
|
+
|
|
167
184
|
## Internal Methods
|
|
168
185
|
|
|
169
186
|
You should not ever call these, they are managed automatically.
|
|
@@ -188,6 +205,13 @@ automatically.
|
|
|
188
205
|
Called when an item is removed from the cache and should be disposed. Set
|
|
189
206
|
this on the constructor options.
|
|
190
207
|
|
|
208
|
+
### `setTimer`
|
|
209
|
+
|
|
210
|
+
**Internal**
|
|
211
|
+
|
|
212
|
+
Called when an with a ttl is added. This ensures that only one timer
|
|
213
|
+
is setup at once. Called automatically.
|
|
214
|
+
|
|
191
215
|
## Algorithm
|
|
192
216
|
|
|
193
217
|
The cache uses two `Map` objects. The first maps item keys to their
|
package/index.d.ts
CHANGED
|
@@ -6,6 +6,13 @@
|
|
|
6
6
|
declare class TTLCache<K, V> implements Iterable<[K, V]> {
|
|
7
7
|
constructor(options?: TTLCache.Options<K, V>)
|
|
8
8
|
|
|
9
|
+
ttl: number
|
|
10
|
+
max: number
|
|
11
|
+
updateAgeOnGet: boolean
|
|
12
|
+
checkAgeOnGet: boolean
|
|
13
|
+
noUpdateTTL: boolean
|
|
14
|
+
noDisposeOnSet: boolean
|
|
15
|
+
|
|
9
16
|
/**
|
|
10
17
|
* The total number of items held in the cache at the current moment.
|
|
11
18
|
*/
|
|
@@ -86,6 +93,13 @@ declare class TTLCache<K, V> implements Iterable<[K, V]> {
|
|
|
86
93
|
* `cache.entries()`
|
|
87
94
|
*/
|
|
88
95
|
public [Symbol.iterator](): Iterator<[K, V]>
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Cancel the timer and stop automatically expiring entries.
|
|
99
|
+
* This allows the process to gracefully exit where Timer.unref()
|
|
100
|
+
* is not available.
|
|
101
|
+
*/
|
|
102
|
+
public cancelTimer(): void
|
|
89
103
|
}
|
|
90
104
|
|
|
91
105
|
declare namespace TTLCache {
|
|
@@ -135,6 +149,19 @@ declare namespace TTLCache {
|
|
|
135
149
|
*/
|
|
136
150
|
updateAgeOnGet?: boolean
|
|
137
151
|
|
|
152
|
+
/**
|
|
153
|
+
* In the event that an item's expiration timer hasn't yet fired,
|
|
154
|
+
* and an attempt is made to get() it, then return undefined and
|
|
155
|
+
* delete it, rather than returning the cached value.
|
|
156
|
+
*
|
|
157
|
+
* By default, items are only expired when their timer fires, so there's
|
|
158
|
+
* a bit of a "best effort" expiration, and the cache will return a value
|
|
159
|
+
* if it has one, even if it's technically stale.
|
|
160
|
+
*
|
|
161
|
+
* @default false
|
|
162
|
+
*/
|
|
163
|
+
checkAgeOnGet?: boolean
|
|
164
|
+
|
|
138
165
|
/**
|
|
139
166
|
* Do not call dispose() function when overwriting a key with a new value
|
|
140
167
|
*
|
|
@@ -175,10 +202,25 @@ declare namespace TTLCache {
|
|
|
175
202
|
|
|
176
203
|
type GetOptions = {
|
|
177
204
|
/**
|
|
178
|
-
* Update the age of
|
|
205
|
+
* Update the age of items on cache.get(), renewing their TTL
|
|
206
|
+
*
|
|
207
|
+
* @default false
|
|
179
208
|
*/
|
|
180
209
|
updateAgeOnGet?: boolean
|
|
181
210
|
|
|
211
|
+
/**
|
|
212
|
+
* In the event that an item's expiration timer hasn't yet fired,
|
|
213
|
+
* and an attempt is made to get() it, then return undefined and
|
|
214
|
+
* delete it, rather than returning the cached value.
|
|
215
|
+
*
|
|
216
|
+
* By default, items are only expired when their timer fires, so there's
|
|
217
|
+
* a bit of a "best effort" expiration, and the cache will return a value
|
|
218
|
+
* if it has one, even if it's technically stale.
|
|
219
|
+
*
|
|
220
|
+
* @default false
|
|
221
|
+
*/
|
|
222
|
+
checkAgeOnGet?: boolean
|
|
223
|
+
|
|
182
224
|
/**
|
|
183
225
|
* Set new TTL, applied only when `updateAgeOnGet` is true
|
|
184
226
|
*/
|
package/index.js
CHANGED
|
@@ -20,6 +20,7 @@ class TTLCache {
|
|
|
20
20
|
max = Infinity,
|
|
21
21
|
ttl,
|
|
22
22
|
updateAgeOnGet = false,
|
|
23
|
+
checkAgeOnGet = false,
|
|
23
24
|
noUpdateTTL = false,
|
|
24
25
|
dispose,
|
|
25
26
|
noDisposeOnSet = false,
|
|
@@ -40,9 +41,10 @@ class TTLCache {
|
|
|
40
41
|
}
|
|
41
42
|
this.ttl = ttl
|
|
42
43
|
this.max = max
|
|
43
|
-
this.updateAgeOnGet = updateAgeOnGet
|
|
44
|
-
this.
|
|
45
|
-
this.
|
|
44
|
+
this.updateAgeOnGet = !!updateAgeOnGet
|
|
45
|
+
this.checkAgeOnGet = !!checkAgeOnGet
|
|
46
|
+
this.noUpdateTTL = !!noUpdateTTL
|
|
47
|
+
this.noDisposeOnSet = !!noDisposeOnSet
|
|
46
48
|
if (dispose !== undefined) {
|
|
47
49
|
if (typeof dispose !== 'function') {
|
|
48
50
|
throw new TypeError('dispose must be function if set')
|
|
@@ -50,19 +52,56 @@ class TTLCache {
|
|
|
50
52
|
this.dispose = dispose
|
|
51
53
|
}
|
|
52
54
|
|
|
53
|
-
this.
|
|
55
|
+
this.timer = undefined
|
|
56
|
+
this.timerExpiration = undefined
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
setTimer(expiration, ttl) {
|
|
60
|
+
if (this.timerExpiration < expiration) {
|
|
61
|
+
return
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (this.timer) {
|
|
65
|
+
clearTimeout(this.timer)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const t = setTimeout(() => {
|
|
69
|
+
this.timer = undefined
|
|
70
|
+
this.timerExpiration = undefined
|
|
71
|
+
this.purgeStale()
|
|
72
|
+
for (const exp in this.expirations) {
|
|
73
|
+
this.setTimer(exp, exp - now())
|
|
74
|
+
break
|
|
75
|
+
}
|
|
76
|
+
}, ttl)
|
|
77
|
+
|
|
78
|
+
/* istanbul ignore else - affordance for non-node envs */
|
|
79
|
+
if (t.unref) t.unref()
|
|
80
|
+
|
|
81
|
+
this.timerExpiration = expiration
|
|
82
|
+
this.timer = t
|
|
54
83
|
}
|
|
55
84
|
|
|
56
85
|
// hang onto the timer so we can clearTimeout if all items
|
|
57
86
|
// are deleted. Deno doesn't have Timer.unref(), so it
|
|
58
87
|
// hangs otherwise.
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
clearTimeout(
|
|
62
|
-
this.
|
|
88
|
+
cancelTimer() {
|
|
89
|
+
if (this.timer) {
|
|
90
|
+
clearTimeout(this.timer)
|
|
91
|
+
this.timerExpiration = undefined
|
|
92
|
+
this.timer = undefined
|
|
63
93
|
}
|
|
64
94
|
}
|
|
65
95
|
|
|
96
|
+
/* istanbul ignore next */
|
|
97
|
+
cancelTimers() {
|
|
98
|
+
process.emitWarning(
|
|
99
|
+
'TTLCache.cancelTimers has been renamed to ' +
|
|
100
|
+
'TTLCache.cancelTimer (no "s"), and will be removed in the next ' +
|
|
101
|
+
'major version update'
|
|
102
|
+
)
|
|
103
|
+
return this.cancelTimer()
|
|
104
|
+
}
|
|
66
105
|
|
|
67
106
|
clear() {
|
|
68
107
|
const entries =
|
|
@@ -70,7 +109,7 @@ class TTLCache {
|
|
|
70
109
|
this.data.clear()
|
|
71
110
|
this.expirationMap.clear()
|
|
72
111
|
// no need for any purging now
|
|
73
|
-
this.
|
|
112
|
+
this.cancelTimer()
|
|
74
113
|
this.expirations = Object.create(null)
|
|
75
114
|
for (const [key, val] of entries) {
|
|
76
115
|
this.dispose(val, key, 'delete')
|
|
@@ -93,14 +132,8 @@ class TTLCache {
|
|
|
93
132
|
const expiration = Math.floor(now() + ttl)
|
|
94
133
|
this.expirationMap.set(key, expiration)
|
|
95
134
|
if (!this.expirations[expiration]) {
|
|
96
|
-
const t = setTimeout(() => {
|
|
97
|
-
this.timers.delete(t)
|
|
98
|
-
this.purgeStale()
|
|
99
|
-
}, ttl)
|
|
100
|
-
/* istanbul ignore else - affordance for non-node envs */
|
|
101
|
-
if (t.unref) t.unref()
|
|
102
|
-
this.timers.add(t)
|
|
103
135
|
this.expirations[expiration] = []
|
|
136
|
+
this.setTimer(expiration, ttl)
|
|
104
137
|
}
|
|
105
138
|
this.expirations[expiration].push(key)
|
|
106
139
|
} else {
|
|
@@ -159,9 +192,17 @@ class TTLCache {
|
|
|
159
192
|
|
|
160
193
|
get(
|
|
161
194
|
key,
|
|
162
|
-
{
|
|
195
|
+
{
|
|
196
|
+
updateAgeOnGet = this.updateAgeOnGet,
|
|
197
|
+
ttl = this.ttl,
|
|
198
|
+
checkAgeOnGet = this.checkAgeOnGet,
|
|
199
|
+
} = {}
|
|
163
200
|
) {
|
|
164
201
|
const val = this.data.get(key)
|
|
202
|
+
if (checkAgeOnGet && this.getRemainingTTL(key) === 0) {
|
|
203
|
+
this.delete(key)
|
|
204
|
+
return undefined
|
|
205
|
+
}
|
|
165
206
|
if (updateAgeOnGet) {
|
|
166
207
|
this.setTTL(key, ttl)
|
|
167
208
|
}
|
|
@@ -186,7 +227,7 @@ class TTLCache {
|
|
|
186
227
|
}
|
|
187
228
|
this.dispose(value, key, 'delete')
|
|
188
229
|
if (this.size === 0) {
|
|
189
|
-
this.
|
|
230
|
+
this.cancelTimer()
|
|
190
231
|
}
|
|
191
232
|
return true
|
|
192
233
|
}
|
|
@@ -246,7 +287,7 @@ class TTLCache {
|
|
|
246
287
|
}
|
|
247
288
|
}
|
|
248
289
|
if (this.size === 0) {
|
|
249
|
-
this.
|
|
290
|
+
this.cancelTimer()
|
|
250
291
|
}
|
|
251
292
|
}
|
|
252
293
|
|