@isaacs/ttlcache 1.2.0 → 1.2.2
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 +15 -2
- package/index.d.ts +10 -5
- package/index.js +60 -21
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -29,7 +29,7 @@ Custom size calculation is not supported. Max capacity is simply the count
|
|
|
29
29
|
of items in the cache.
|
|
30
30
|
|
|
31
31
|
```js
|
|
32
|
-
const TTLCache = require('ttlcache')
|
|
32
|
+
const TTLCache = require('@isaacs/ttlcache')
|
|
33
33
|
const cache = new TTLCache({ max: 10000, ttl: 1000 })
|
|
34
34
|
|
|
35
35
|
// set some value
|
|
@@ -44,13 +44,26 @@ cache.get(1) // returns undefined
|
|
|
44
44
|
cache.has(1) // returns false
|
|
45
45
|
```
|
|
46
46
|
|
|
47
|
+
## Caveat Regarding Timers and Graceful Exits
|
|
48
|
+
|
|
49
|
+
On Node.js, this module uses the `Timeout.unref()` method to
|
|
50
|
+
prevent its internal `setTimeout` calls from keeping the process
|
|
51
|
+
running indefinitely. However, on other systems such as Deno,
|
|
52
|
+
where the `setTimeout` method does not return an object with an
|
|
53
|
+
`unref()` method, the process will stay open as long as any
|
|
54
|
+
unexpired entry exists in the cache.
|
|
55
|
+
|
|
56
|
+
You may delete all entries (by using `cache.clear()` or
|
|
57
|
+
`cache.delete(key)` with every key) in order to clear the
|
|
58
|
+
timeouts and allow the process to exit normally.
|
|
59
|
+
|
|
47
60
|
## API
|
|
48
61
|
|
|
49
62
|
### `const TTLCache = require('@isaacs/ttlcache')` or `import TTLCache from '@isaacs/ttlcache'`
|
|
50
63
|
|
|
51
64
|
Default export is the `TTLCache` class.
|
|
52
65
|
|
|
53
|
-
### `new TTLCache({ ttl, max = Infinty, updateAgeOnGet = false, noUpdateTTL = false })`
|
|
66
|
+
### `new TTLCache({ ttl, max = Infinty, updateAgeOnGet = false, noUpdateTTL = false, noDisposeOnSet = false })`
|
|
54
67
|
|
|
55
68
|
Create a new `TTLCache` object.
|
|
56
69
|
|
package/index.d.ts
CHANGED
|
@@ -89,7 +89,7 @@ declare class TTLCache<K, V> implements Iterable<[K, V]> {
|
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
declare namespace TTLCache {
|
|
92
|
-
type DisposeReason = 'evict' | 'set' | 'delete'
|
|
92
|
+
type DisposeReason = 'evict' | 'set' | 'delete' | 'stale'
|
|
93
93
|
|
|
94
94
|
type Disposer<K, V> = (
|
|
95
95
|
value: V,
|
|
@@ -135,6 +135,13 @@ declare namespace TTLCache {
|
|
|
135
135
|
*/
|
|
136
136
|
updateAgeOnGet?: boolean
|
|
137
137
|
|
|
138
|
+
/**
|
|
139
|
+
* Do not call dispose() function when overwriting a key with a new value
|
|
140
|
+
*
|
|
141
|
+
* @default false
|
|
142
|
+
*/
|
|
143
|
+
noDisposeOnSet?: boolean
|
|
144
|
+
|
|
138
145
|
/**
|
|
139
146
|
* Function that is called on items when they are dropped from the cache.
|
|
140
147
|
* This can be handy if you want to close file descriptors or do other
|
|
@@ -149,10 +156,8 @@ declare namespace TTLCache {
|
|
|
149
156
|
|
|
150
157
|
type SetOptions = {
|
|
151
158
|
/**
|
|
152
|
-
*
|
|
153
|
-
*
|
|
154
|
-
*
|
|
155
|
-
* @default false
|
|
159
|
+
* Do not call dispose() function when overwriting a key with a new value
|
|
160
|
+
* Overrides the value set in the constructor.
|
|
156
161
|
*/
|
|
157
162
|
noDisposeOnSet?: boolean
|
|
158
163
|
|
package/index.js
CHANGED
|
@@ -3,15 +3,15 @@
|
|
|
3
3
|
// Relies on the fact that integer Object keys are kept sorted,
|
|
4
4
|
// and managed very efficiently by V8.
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const now = () =>
|
|
6
|
+
/* istanbul ignore next */
|
|
7
|
+
const perf =
|
|
8
|
+
typeof performance === 'object' &&
|
|
9
|
+
performance &&
|
|
10
|
+
typeof performance.now === 'function'
|
|
11
|
+
? performance
|
|
12
|
+
: Date
|
|
13
|
+
|
|
14
|
+
const now = () => perf.now()
|
|
15
15
|
const isPosInt = n => n && n === Math.floor(n) && n > 0 && isFinite(n)
|
|
16
16
|
const isPosIntOrInf = n => n === Infinity || isPosInt(n)
|
|
17
17
|
|
|
@@ -22,6 +22,7 @@ class TTLCache {
|
|
|
22
22
|
updateAgeOnGet = false,
|
|
23
23
|
noUpdateTTL = false,
|
|
24
24
|
dispose,
|
|
25
|
+
noDisposeOnSet = false,
|
|
25
26
|
} = {}) {
|
|
26
27
|
// {[expirationTime]: [keys]}
|
|
27
28
|
this.expirations = Object.create(null)
|
|
@@ -41,19 +42,35 @@ class TTLCache {
|
|
|
41
42
|
this.max = max
|
|
42
43
|
this.updateAgeOnGet = updateAgeOnGet
|
|
43
44
|
this.noUpdateTTL = noUpdateTTL
|
|
45
|
+
this.noDisposeOnSet = noDisposeOnSet
|
|
44
46
|
if (dispose !== undefined) {
|
|
45
47
|
if (typeof dispose !== 'function') {
|
|
46
48
|
throw new TypeError('dispose must be function if set')
|
|
47
49
|
}
|
|
48
50
|
this.dispose = dispose
|
|
49
51
|
}
|
|
52
|
+
|
|
53
|
+
this.timers = new Set()
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// hang onto the timer so we can clearTimeout if all items
|
|
57
|
+
// are deleted. Deno doesn't have Timer.unref(), so it
|
|
58
|
+
// hangs otherwise.
|
|
59
|
+
cancelTimers() {
|
|
60
|
+
for (const t of this.timers) {
|
|
61
|
+
clearTimeout(t)
|
|
62
|
+
this.timers.delete(t)
|
|
63
|
+
}
|
|
50
64
|
}
|
|
51
65
|
|
|
66
|
+
|
|
52
67
|
clear() {
|
|
53
68
|
const entries =
|
|
54
69
|
this.dispose !== TTLCache.prototype.dispose ? [...this] : []
|
|
55
70
|
this.data.clear()
|
|
56
71
|
this.expirationMap.clear()
|
|
72
|
+
// no need for any purging now
|
|
73
|
+
this.cancelTimers()
|
|
57
74
|
this.expirations = Object.create(null)
|
|
58
75
|
for (const [key, val] of entries) {
|
|
59
76
|
this.dispose(val, key, 'delete')
|
|
@@ -76,9 +93,13 @@ class TTLCache {
|
|
|
76
93
|
const expiration = Math.floor(now() + ttl)
|
|
77
94
|
this.expirationMap.set(key, expiration)
|
|
78
95
|
if (!this.expirations[expiration]) {
|
|
79
|
-
const t = setTimeout(() =>
|
|
96
|
+
const t = setTimeout(() => {
|
|
97
|
+
this.timers.delete(t)
|
|
98
|
+
this.purgeStale()
|
|
99
|
+
}, ttl)
|
|
80
100
|
/* istanbul ignore else - affordance for non-node envs */
|
|
81
101
|
if (t.unref) t.unref()
|
|
102
|
+
this.timers.add(t)
|
|
82
103
|
this.expirations[expiration] = []
|
|
83
104
|
}
|
|
84
105
|
this.expirations[expiration].push(key)
|
|
@@ -156,12 +177,17 @@ class TTLCache {
|
|
|
156
177
|
this.data.delete(key)
|
|
157
178
|
this.expirationMap.delete(key)
|
|
158
179
|
const exp = this.expirations[current]
|
|
159
|
-
if (exp
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
180
|
+
if (exp) {
|
|
181
|
+
if (exp.length <= 1) {
|
|
182
|
+
delete this.expirations[current]
|
|
183
|
+
} else {
|
|
184
|
+
this.expirations[current] = exp.filter(k => k !== key)
|
|
185
|
+
}
|
|
163
186
|
}
|
|
164
187
|
this.dispose(value, key, 'delete')
|
|
188
|
+
if (this.size === 0) {
|
|
189
|
+
this.cancelTimers()
|
|
190
|
+
}
|
|
165
191
|
return true
|
|
166
192
|
}
|
|
167
193
|
return false
|
|
@@ -171,23 +197,29 @@ class TTLCache {
|
|
|
171
197
|
for (const exp in this.expirations) {
|
|
172
198
|
const keys = this.expirations[exp]
|
|
173
199
|
if (this.size - keys.length >= this.max) {
|
|
200
|
+
delete this.expirations[exp]
|
|
201
|
+
const entries = []
|
|
174
202
|
for (const key of keys) {
|
|
175
|
-
|
|
203
|
+
entries.push([key, this.data.get(key)])
|
|
176
204
|
this.data.delete(key)
|
|
177
205
|
this.expirationMap.delete(key)
|
|
206
|
+
}
|
|
207
|
+
for (const [key, val] of entries) {
|
|
178
208
|
this.dispose(val, key, 'evict')
|
|
179
209
|
}
|
|
180
|
-
delete this.expirations[exp]
|
|
181
210
|
} else {
|
|
182
211
|
const s = this.size - this.max
|
|
212
|
+
const entries = []
|
|
183
213
|
for (const key of keys.splice(0, s)) {
|
|
184
|
-
|
|
214
|
+
entries.push([key, this.data.get(key)])
|
|
185
215
|
this.data.delete(key)
|
|
186
216
|
this.expirationMap.delete(key)
|
|
217
|
+
}
|
|
218
|
+
for (const [key, val] of entries) {
|
|
187
219
|
this.dispose(val, key, 'evict')
|
|
188
220
|
}
|
|
221
|
+
return
|
|
189
222
|
}
|
|
190
|
-
return
|
|
191
223
|
}
|
|
192
224
|
}
|
|
193
225
|
|
|
@@ -201,13 +233,20 @@ class TTLCache {
|
|
|
201
233
|
if (exp === 'Infinity' || exp > n) {
|
|
202
234
|
return
|
|
203
235
|
}
|
|
204
|
-
|
|
205
|
-
|
|
236
|
+
const keys = [...this.expirations[exp]]
|
|
237
|
+
const entries = []
|
|
238
|
+
delete this.expirations[exp]
|
|
239
|
+
for (const key of keys) {
|
|
240
|
+
entries.push([key, this.data.get(key)])
|
|
206
241
|
this.data.delete(key)
|
|
207
242
|
this.expirationMap.delete(key)
|
|
243
|
+
}
|
|
244
|
+
for (const [key, val] of entries) {
|
|
208
245
|
this.dispose(val, key, 'stale')
|
|
209
246
|
}
|
|
210
|
-
|
|
247
|
+
}
|
|
248
|
+
if (this.size === 0) {
|
|
249
|
+
this.cancelTimers()
|
|
211
250
|
}
|
|
212
251
|
}
|
|
213
252
|
|