@aws-cdk/cloud-assembly-schema 2.20.0 → 2.22.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/.jsii +57 -31
- package/.jsii.tabl.json +68 -3
- package/.warnings.jsii.js +253 -147
- package/NOTICE +0 -17
- package/lib/integ-tests/test-case.d.ts +47 -42
- package/lib/integ-tests/test-case.js +1 -1
- package/lib/manifest.js +57 -8
- package/node_modules/lru-cache/LICENSE +1 -1
- package/node_modules/lru-cache/README.md +632 -99
- package/node_modules/lru-cache/index.js +732 -251
- package/node_modules/lru-cache/package.json +17 -7
- package/node_modules/semver/README.md +3 -1
- package/node_modules/semver/bin/semver.js +17 -8
- package/node_modules/semver/classes/comparator.js +3 -2
- package/node_modules/semver/classes/index.js +1 -1
- package/node_modules/semver/classes/range.js +31 -22
- package/node_modules/semver/functions/cmp.js +8 -4
- package/node_modules/semver/functions/coerce.js +3 -2
- package/node_modules/semver/functions/parse.js +1 -1
- package/node_modules/semver/internal/constants.js +2 -2
- package/node_modules/semver/internal/identifiers.js +1 -1
- package/node_modules/semver/internal/parse-options.js +3 -3
- package/node_modules/semver/internal/re.js +3 -3
- package/node_modules/semver/package.json +46 -13
- package/node_modules/semver/ranges/min-version.js +2 -1
- package/node_modules/semver/ranges/outside.js +1 -1
- package/node_modules/semver/ranges/simplify.js +15 -12
- package/node_modules/semver/ranges/subset.js +53 -31
- package/package.json +6 -5
- package/schema/cloud-assembly.version.json +1 -1
- package/schema/integ.schema.json +1 -1
- package/node_modules/semver/CHANGELOG.md +0 -111
- package/node_modules/yallist/LICENSE +0 -15
- package/node_modules/yallist/README.md +0 -204
- package/node_modules/yallist/iterator.js +0 -8
- package/node_modules/yallist/package.json +0 -29
- package/node_modules/yallist/yallist.js +0 -426
|
@@ -1,334 +1,815 @@
|
|
|
1
|
-
'
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
1
|
+
const perf = typeof performance === 'object' && performance &&
|
|
2
|
+
typeof performance.now === 'function' ? performance : Date
|
|
3
|
+
|
|
4
|
+
const hasAbortController = typeof AbortController !== 'undefined'
|
|
5
|
+
|
|
6
|
+
// minimal backwards-compatibility polyfill
|
|
7
|
+
const AC = hasAbortController ? AbortController : Object.assign(
|
|
8
|
+
class AbortController {
|
|
9
|
+
constructor () { this.signal = new AC.AbortSignal }
|
|
10
|
+
abort () { this.signal.aborted = true }
|
|
11
|
+
},
|
|
12
|
+
{ AbortSignal: class AbortSignal { constructor () { this.aborted = false }}}
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
const warned = new Set()
|
|
16
|
+
const deprecatedOption = (opt, instead) => {
|
|
17
|
+
const code = `LRU_CACHE_OPTION_${opt}`
|
|
18
|
+
if (shouldWarn(code)) {
|
|
19
|
+
warn(code, `${opt} option`, `options.${instead}`, LRUCache)
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
const deprecatedMethod = (method, instead) => {
|
|
23
|
+
const code = `LRU_CACHE_METHOD_${method}`
|
|
24
|
+
if (shouldWarn(code)) {
|
|
25
|
+
const { prototype } = LRUCache
|
|
26
|
+
const { get } = Object.getOwnPropertyDescriptor(prototype, method)
|
|
27
|
+
warn(code, `${method} method`, `cache.${instead}()`, get)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
const deprecatedProperty = (field, instead) => {
|
|
31
|
+
const code = `LRU_CACHE_PROPERTY_${field}`
|
|
32
|
+
if (shouldWarn(code)) {
|
|
33
|
+
const { prototype } = LRUCache
|
|
34
|
+
const { get } = Object.getOwnPropertyDescriptor(prototype, field)
|
|
35
|
+
warn(code, `${field} property`, `cache.${instead}`, get)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
31
38
|
|
|
32
|
-
|
|
33
|
-
|
|
39
|
+
const emitWarning = (...a) => {
|
|
40
|
+
typeof process === 'object' &&
|
|
41
|
+
process &&
|
|
42
|
+
typeof process.emitWarning === 'function'
|
|
43
|
+
? process.emitWarning(...a)
|
|
44
|
+
: console.error(...a)
|
|
45
|
+
}
|
|
34
46
|
|
|
35
|
-
|
|
36
|
-
throw new TypeError('max must be a non-negative number')
|
|
37
|
-
// Kind of weird to have a default max of Infinity, but oh well.
|
|
38
|
-
const max = this[MAX] = options.max || Infinity
|
|
47
|
+
const shouldWarn = code => !warned.has(code)
|
|
39
48
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
this[MAX_AGE] = options.maxAge || 0
|
|
46
|
-
this[DISPOSE] = options.dispose
|
|
47
|
-
this[NO_DISPOSE_ON_SET] = options.noDisposeOnSet || false
|
|
48
|
-
this[UPDATE_AGE_ON_GET] = options.updateAgeOnGet || false
|
|
49
|
-
this.reset()
|
|
50
|
-
}
|
|
49
|
+
const warn = (code, what, instead, fn) => {
|
|
50
|
+
warned.add(code)
|
|
51
|
+
const msg = `The ${what} is deprecated. Please use ${instead} instead.`
|
|
52
|
+
emitWarning(msg, 'DeprecationWarning', code, fn)
|
|
53
|
+
}
|
|
51
54
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
55
|
+
const isPosInt = n => n && n === Math.floor(n) && n > 0 && isFinite(n)
|
|
56
|
+
|
|
57
|
+
/* istanbul ignore next - This is a little bit ridiculous, tbh.
|
|
58
|
+
* The maximum array length is 2^32-1 or thereabouts on most JS impls.
|
|
59
|
+
* And well before that point, you're caching the entire world, I mean,
|
|
60
|
+
* that's ~32GB of just integers for the next/prev links, plus whatever
|
|
61
|
+
* else to hold that many keys and values. Just filling the memory with
|
|
62
|
+
* zeroes at init time is brutal when you get that big.
|
|
63
|
+
* But why not be complete?
|
|
64
|
+
* Maybe in the future, these limits will have expanded. */
|
|
65
|
+
const getUintArray = max => !isPosInt(max) ? null
|
|
66
|
+
: max <= Math.pow(2, 8) ? Uint8Array
|
|
67
|
+
: max <= Math.pow(2, 16) ? Uint16Array
|
|
68
|
+
: max <= Math.pow(2, 32) ? Uint32Array
|
|
69
|
+
: max <= Number.MAX_SAFE_INTEGER ? ZeroArray
|
|
70
|
+
: null
|
|
71
|
+
|
|
72
|
+
class ZeroArray extends Array {
|
|
73
|
+
constructor (size) {
|
|
74
|
+
super(size)
|
|
75
|
+
this.fill(0)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
56
78
|
|
|
57
|
-
|
|
58
|
-
|
|
79
|
+
class Stack {
|
|
80
|
+
constructor (max) {
|
|
81
|
+
const UintArray = max ? getUintArray(max) : Array
|
|
82
|
+
this.heap = new UintArray(max)
|
|
83
|
+
this.length = 0
|
|
84
|
+
}
|
|
85
|
+
push (n) {
|
|
86
|
+
this.heap[this.length++] = n
|
|
59
87
|
}
|
|
60
|
-
|
|
61
|
-
return this[
|
|
88
|
+
pop () {
|
|
89
|
+
return this.heap[--this.length]
|
|
62
90
|
}
|
|
91
|
+
}
|
|
63
92
|
|
|
64
|
-
|
|
65
|
-
|
|
93
|
+
class LRUCache {
|
|
94
|
+
constructor (options = {}) {
|
|
95
|
+
const {
|
|
96
|
+
max = 0,
|
|
97
|
+
ttl,
|
|
98
|
+
ttlResolution = 1,
|
|
99
|
+
ttlAutopurge,
|
|
100
|
+
updateAgeOnGet,
|
|
101
|
+
updateAgeOnHas,
|
|
102
|
+
allowStale,
|
|
103
|
+
dispose,
|
|
104
|
+
disposeAfter,
|
|
105
|
+
noDisposeOnSet,
|
|
106
|
+
noUpdateTTL,
|
|
107
|
+
maxSize = 0,
|
|
108
|
+
sizeCalculation,
|
|
109
|
+
fetchMethod,
|
|
110
|
+
} = options
|
|
111
|
+
|
|
112
|
+
// deprecated options, don't trigger a warning for getting them if
|
|
113
|
+
// the thing being passed in is another LRUCache we're copying.
|
|
114
|
+
const {
|
|
115
|
+
length,
|
|
116
|
+
maxAge,
|
|
117
|
+
stale,
|
|
118
|
+
} = options instanceof LRUCache ? {} : options
|
|
119
|
+
|
|
120
|
+
if (max !== 0 && !isPosInt(max)) {
|
|
121
|
+
throw new TypeError('max option must be a nonnegative integer')
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const UintArray = max ? getUintArray(max) : Array
|
|
125
|
+
if (!UintArray) {
|
|
126
|
+
throw new Error('invalid max value: ' + max)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
this.max = max
|
|
130
|
+
this.maxSize = maxSize
|
|
131
|
+
this.sizeCalculation = sizeCalculation || length
|
|
132
|
+
if (this.sizeCalculation) {
|
|
133
|
+
if (!this.maxSize) {
|
|
134
|
+
throw new TypeError('cannot set sizeCalculation without setting maxSize')
|
|
135
|
+
}
|
|
136
|
+
if (typeof this.sizeCalculation !== 'function') {
|
|
137
|
+
throw new TypeError('sizeCalculation set to non-function')
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
this.fetchMethod = fetchMethod || null
|
|
142
|
+
if (this.fetchMethod && typeof this.fetchMethod !== 'function') {
|
|
143
|
+
throw new TypeError('fetchMethod must be a function if specified')
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
this.keyMap = new Map()
|
|
148
|
+
this.keyList = new Array(max).fill(null)
|
|
149
|
+
this.valList = new Array(max).fill(null)
|
|
150
|
+
this.next = new UintArray(max)
|
|
151
|
+
this.prev = new UintArray(max)
|
|
152
|
+
this.head = 0
|
|
153
|
+
this.tail = 0
|
|
154
|
+
this.free = new Stack(max)
|
|
155
|
+
this.initialFill = 1
|
|
156
|
+
this.size = 0
|
|
157
|
+
|
|
158
|
+
if (typeof dispose === 'function') {
|
|
159
|
+
this.dispose = dispose
|
|
160
|
+
}
|
|
161
|
+
if (typeof disposeAfter === 'function') {
|
|
162
|
+
this.disposeAfter = disposeAfter
|
|
163
|
+
this.disposed = []
|
|
164
|
+
} else {
|
|
165
|
+
this.disposeAfter = null
|
|
166
|
+
this.disposed = null
|
|
167
|
+
}
|
|
168
|
+
this.noDisposeOnSet = !!noDisposeOnSet
|
|
169
|
+
this.noUpdateTTL = !!noUpdateTTL
|
|
170
|
+
|
|
171
|
+
if (this.maxSize !== 0) {
|
|
172
|
+
if (!isPosInt(this.maxSize)) {
|
|
173
|
+
throw new TypeError('maxSize must be a positive integer if specified')
|
|
174
|
+
}
|
|
175
|
+
this.initializeSizeTracking()
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
this.allowStale = !!allowStale || !!stale
|
|
179
|
+
this.updateAgeOnGet = !!updateAgeOnGet
|
|
180
|
+
this.updateAgeOnHas = !!updateAgeOnHas
|
|
181
|
+
this.ttlResolution = isPosInt(ttlResolution) || ttlResolution === 0
|
|
182
|
+
? ttlResolution : 1
|
|
183
|
+
this.ttlAutopurge = !!ttlAutopurge
|
|
184
|
+
this.ttl = ttl || maxAge || 0
|
|
185
|
+
if (this.ttl) {
|
|
186
|
+
if (!isPosInt(this.ttl)) {
|
|
187
|
+
throw new TypeError('ttl must be a positive integer if specified')
|
|
188
|
+
}
|
|
189
|
+
this.initializeTTLTracking()
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// do not allow completely unbounded caches
|
|
193
|
+
if (this.max === 0 && this.ttl === 0 && this.maxSize === 0) {
|
|
194
|
+
throw new TypeError('At least one of max, maxSize, or ttl is required')
|
|
195
|
+
}
|
|
196
|
+
if (!this.ttlAutopurge && !this.max && !this.maxSize) {
|
|
197
|
+
const code = 'LRU_CACHE_UNBOUNDED'
|
|
198
|
+
if (shouldWarn(code)) {
|
|
199
|
+
warned.add(code)
|
|
200
|
+
const msg = 'TTL caching without ttlAutopurge, max, or maxSize can ' +
|
|
201
|
+
'result in unbounded memory consumption.'
|
|
202
|
+
emitWarning(msg, 'UnboundedCacheWarning', code, LRUCache)
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (stale) {
|
|
207
|
+
deprecatedOption('stale', 'allowStale')
|
|
208
|
+
}
|
|
209
|
+
if (maxAge) {
|
|
210
|
+
deprecatedOption('maxAge', 'ttl')
|
|
211
|
+
}
|
|
212
|
+
if (length) {
|
|
213
|
+
deprecatedOption('length', 'sizeCalculation')
|
|
214
|
+
}
|
|
66
215
|
}
|
|
67
|
-
|
|
68
|
-
|
|
216
|
+
|
|
217
|
+
getRemainingTTL (key) {
|
|
218
|
+
return this.has(key, { updateAgeOnHas: false }) ? Infinity : 0
|
|
69
219
|
}
|
|
70
220
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
221
|
+
initializeTTLTracking () {
|
|
222
|
+
this.ttls = new ZeroArray(this.max)
|
|
223
|
+
this.starts = new ZeroArray(this.max)
|
|
224
|
+
|
|
225
|
+
this.setItemTTL = (index, ttl) => {
|
|
226
|
+
this.starts[index] = ttl !== 0 ? perf.now() : 0
|
|
227
|
+
this.ttls[index] = ttl
|
|
228
|
+
if (ttl !== 0 && this.ttlAutopurge) {
|
|
229
|
+
const t = setTimeout(() => {
|
|
230
|
+
if (this.isStale(index)) {
|
|
231
|
+
this.delete(this.keyList[index])
|
|
232
|
+
}
|
|
233
|
+
}, ttl + 1)
|
|
234
|
+
/* istanbul ignore else - unref() not supported on all platforms */
|
|
235
|
+
if (t.unref) {
|
|
236
|
+
t.unref()
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
74
240
|
|
|
75
|
-
this
|
|
76
|
-
|
|
241
|
+
this.updateItemAge = (index) => {
|
|
242
|
+
this.starts[index] = this.ttls[index] !== 0 ? perf.now() : 0
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// debounce calls to perf.now() to 1s so we're not hitting
|
|
246
|
+
// that costly call repeatedly.
|
|
247
|
+
let cachedNow = 0
|
|
248
|
+
const getNow = () => {
|
|
249
|
+
const n = perf.now()
|
|
250
|
+
if (this.ttlResolution > 0) {
|
|
251
|
+
cachedNow = n
|
|
252
|
+
const t = setTimeout(() => cachedNow = 0, this.ttlResolution)
|
|
253
|
+
/* istanbul ignore else - not available on all platforms */
|
|
254
|
+
if (t.unref) {
|
|
255
|
+
t.unref()
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
return n
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
this.getRemainingTTL = (key) => {
|
|
262
|
+
const index = this.keyMap.get(key)
|
|
263
|
+
if (index === undefined) {
|
|
264
|
+
return 0
|
|
265
|
+
}
|
|
266
|
+
return this.ttls[index] === 0 || this.starts[index] === 0 ? Infinity
|
|
267
|
+
: ((this.starts[index] + this.ttls[index]) - (cachedNow || getNow()))
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
this.isStale = (index) => {
|
|
271
|
+
return this.ttls[index] !== 0 && this.starts[index] !== 0 &&
|
|
272
|
+
((cachedNow || getNow()) - this.starts[index] > this.ttls[index])
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
updateItemAge (index) {}
|
|
276
|
+
setItemTTL (index, ttl) {}
|
|
277
|
+
isStale (index) { return false }
|
|
278
|
+
|
|
279
|
+
initializeSizeTracking () {
|
|
280
|
+
this.calculatedSize = 0
|
|
281
|
+
this.sizes = new ZeroArray(this.max)
|
|
282
|
+
this.removeItemSize = index => this.calculatedSize -= this.sizes[index]
|
|
283
|
+
this.requireSize = (k, v, size, sizeCalculation) => {
|
|
284
|
+
if (!isPosInt(size)) {
|
|
285
|
+
if (sizeCalculation) {
|
|
286
|
+
if (typeof sizeCalculation !== 'function') {
|
|
287
|
+
throw new TypeError('sizeCalculation must be a function')
|
|
288
|
+
}
|
|
289
|
+
size = sizeCalculation(v, k)
|
|
290
|
+
if (!isPosInt(size)) {
|
|
291
|
+
throw new TypeError('sizeCalculation return invalid (expect positive integer)')
|
|
292
|
+
}
|
|
293
|
+
} else {
|
|
294
|
+
throw new TypeError('invalid size value (must be positive integer)')
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
return size
|
|
298
|
+
}
|
|
299
|
+
this.addItemSize = (index, v, k, size) => {
|
|
300
|
+
this.sizes[index] = size
|
|
301
|
+
const maxSize = this.maxSize - this.sizes[index]
|
|
302
|
+
while (this.calculatedSize > maxSize) {
|
|
303
|
+
this.evict()
|
|
304
|
+
}
|
|
305
|
+
this.calculatedSize += this.sizes[index]
|
|
306
|
+
}
|
|
307
|
+
this.delete = k => {
|
|
308
|
+
if (this.size !== 0) {
|
|
309
|
+
const index = this.keyMap.get(k)
|
|
310
|
+
if (index !== undefined) {
|
|
311
|
+
this.calculatedSize -= this.sizes[index]
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
return LRUCache.prototype.delete.call(this, k)
|
|
315
|
+
}
|
|
77
316
|
}
|
|
78
|
-
|
|
79
|
-
|
|
317
|
+
removeItemSize (index) {}
|
|
318
|
+
addItemSize (index, v, k, size) {}
|
|
319
|
+
requireSize (k, v, size, sizeCalculation) {
|
|
320
|
+
if (size || sizeCalculation) {
|
|
321
|
+
throw new TypeError('cannot set size without setting maxSize on cache')
|
|
322
|
+
}
|
|
80
323
|
}
|
|
81
324
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
325
|
+
*indexes ({ allowStale = this.allowStale } = {}) {
|
|
326
|
+
if (this.size) {
|
|
327
|
+
for (let i = this.tail; true; ) {
|
|
328
|
+
if (!this.isValidIndex(i)) {
|
|
329
|
+
break
|
|
330
|
+
}
|
|
331
|
+
if (allowStale || !this.isStale(i)) {
|
|
332
|
+
yield i
|
|
333
|
+
}
|
|
334
|
+
if (i === this.head) {
|
|
335
|
+
break
|
|
336
|
+
} else {
|
|
337
|
+
i = this.prev[i]
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
86
342
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
343
|
+
*rindexes ({ allowStale = this.allowStale } = {}) {
|
|
344
|
+
if (this.size) {
|
|
345
|
+
for (let i = this.head; true; ) {
|
|
346
|
+
if (!this.isValidIndex(i)) {
|
|
347
|
+
break
|
|
348
|
+
}
|
|
349
|
+
if (allowStale || !this.isStale(i)) {
|
|
350
|
+
yield i
|
|
351
|
+
}
|
|
352
|
+
if (i === this.tail) {
|
|
353
|
+
break
|
|
354
|
+
} else {
|
|
355
|
+
i = this.next[i]
|
|
356
|
+
}
|
|
357
|
+
}
|
|
94
358
|
}
|
|
95
|
-
trim(this)
|
|
96
359
|
}
|
|
97
|
-
get lengthCalculator () { return this[LENGTH_CALCULATOR] }
|
|
98
360
|
|
|
99
|
-
|
|
100
|
-
|
|
361
|
+
isValidIndex (index) {
|
|
362
|
+
return this.keyMap.get(this.keyList[index]) === index
|
|
363
|
+
}
|
|
101
364
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
const prev = walker.prev
|
|
106
|
-
forEachStep(this, fn, walker, thisp)
|
|
107
|
-
walker = prev
|
|
365
|
+
*entries () {
|
|
366
|
+
for (const i of this.indexes()) {
|
|
367
|
+
yield [this.keyList[i], this.valList[i]]
|
|
108
368
|
}
|
|
109
369
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
for (let walker = this[LRU_LIST].head; walker !== null;) {
|
|
114
|
-
const next = walker.next
|
|
115
|
-
forEachStep(this, fn, walker, thisp)
|
|
116
|
-
walker = next
|
|
370
|
+
*rentries () {
|
|
371
|
+
for (const i of this.rindexes()) {
|
|
372
|
+
yield [this.keyList[i], this.valList[i]]
|
|
117
373
|
}
|
|
118
374
|
}
|
|
119
375
|
|
|
120
|
-
keys () {
|
|
121
|
-
|
|
376
|
+
*keys () {
|
|
377
|
+
for (const i of this.indexes()) {
|
|
378
|
+
yield this.keyList[i]
|
|
379
|
+
}
|
|
122
380
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
381
|
+
*rkeys () {
|
|
382
|
+
for (const i of this.rindexes()) {
|
|
383
|
+
yield this.keyList[i]
|
|
384
|
+
}
|
|
126
385
|
}
|
|
127
386
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
this[LRU_LIST].length) {
|
|
132
|
-
this[LRU_LIST].forEach(hit => this[DISPOSE](hit.key, hit.value))
|
|
387
|
+
*values () {
|
|
388
|
+
for (const i of this.indexes()) {
|
|
389
|
+
yield this.valList[i]
|
|
133
390
|
}
|
|
391
|
+
}
|
|
392
|
+
*rvalues () {
|
|
393
|
+
for (const i of this.rindexes()) {
|
|
394
|
+
yield this.valList[i]
|
|
395
|
+
}
|
|
396
|
+
}
|
|
134
397
|
|
|
135
|
-
|
|
136
|
-
this
|
|
137
|
-
this[LENGTH] = 0 // length of items in the list
|
|
398
|
+
[Symbol.iterator] () {
|
|
399
|
+
return this.entries()
|
|
138
400
|
}
|
|
139
401
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
}).toArray().filter(h => h)
|
|
402
|
+
find (fn, getOptions = {}) {
|
|
403
|
+
for (const i of this.indexes()) {
|
|
404
|
+
if (fn(this.valList[i], this.keyList[i], this)) {
|
|
405
|
+
return this.get(this.keyList[i], getOptions)
|
|
406
|
+
}
|
|
407
|
+
}
|
|
147
408
|
}
|
|
148
409
|
|
|
149
|
-
|
|
150
|
-
|
|
410
|
+
forEach (fn, thisp = this) {
|
|
411
|
+
for (const i of this.indexes()) {
|
|
412
|
+
fn.call(thisp, this.valList[i], this.keyList[i], this)
|
|
413
|
+
}
|
|
151
414
|
}
|
|
152
415
|
|
|
153
|
-
|
|
154
|
-
|
|
416
|
+
rforEach (fn, thisp = this) {
|
|
417
|
+
for (const i of this.rindexes()) {
|
|
418
|
+
fn.call(thisp, this.valList[i], this.keyList[i], this)
|
|
419
|
+
}
|
|
420
|
+
}
|
|
155
421
|
|
|
156
|
-
|
|
157
|
-
|
|
422
|
+
get prune () {
|
|
423
|
+
deprecatedMethod('prune', 'purgeStale')
|
|
424
|
+
return this.purgeStale
|
|
425
|
+
}
|
|
158
426
|
|
|
159
|
-
|
|
160
|
-
|
|
427
|
+
purgeStale () {
|
|
428
|
+
let deleted = false
|
|
429
|
+
for (const i of this.rindexes({ allowStale: true })) {
|
|
430
|
+
if (this.isStale(i)) {
|
|
431
|
+
this.delete(this.keyList[i])
|
|
432
|
+
deleted = true
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
return deleted
|
|
436
|
+
}
|
|
161
437
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
438
|
+
dump () {
|
|
439
|
+
const arr = []
|
|
440
|
+
for (const i of this.indexes()) {
|
|
441
|
+
const key = this.keyList[i]
|
|
442
|
+
const value = this.valList[i]
|
|
443
|
+
const entry = { value }
|
|
444
|
+
if (this.ttls) {
|
|
445
|
+
entry.ttl = this.ttls[i]
|
|
446
|
+
}
|
|
447
|
+
if (this.sizes) {
|
|
448
|
+
entry.size = this.sizes[i]
|
|
166
449
|
}
|
|
450
|
+
arr.unshift([key, entry])
|
|
451
|
+
}
|
|
452
|
+
return arr
|
|
453
|
+
}
|
|
167
454
|
|
|
168
|
-
|
|
169
|
-
|
|
455
|
+
load (arr) {
|
|
456
|
+
this.clear()
|
|
457
|
+
for (const [key, entry] of arr) {
|
|
458
|
+
this.set(key, entry.value, entry)
|
|
459
|
+
}
|
|
460
|
+
}
|
|
170
461
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
462
|
+
dispose (v, k, reason) {}
|
|
463
|
+
|
|
464
|
+
set (k, v, {
|
|
465
|
+
ttl = this.ttl,
|
|
466
|
+
noDisposeOnSet = this.noDisposeOnSet,
|
|
467
|
+
size = 0,
|
|
468
|
+
sizeCalculation = this.sizeCalculation,
|
|
469
|
+
noUpdateTTL = this.noUpdateTTL,
|
|
470
|
+
} = {}) {
|
|
471
|
+
size = this.requireSize(k, v, size, sizeCalculation)
|
|
472
|
+
let index = this.size === 0 ? undefined : this.keyMap.get(k)
|
|
473
|
+
if (index === undefined) {
|
|
474
|
+
// addition
|
|
475
|
+
index = this.newIndex()
|
|
476
|
+
this.keyList[index] = k
|
|
477
|
+
this.valList[index] = v
|
|
478
|
+
this.keyMap.set(k, index)
|
|
479
|
+
this.next[this.tail] = index
|
|
480
|
+
this.prev[index] = this.tail
|
|
481
|
+
this.tail = index
|
|
482
|
+
this.size ++
|
|
483
|
+
this.addItemSize(index, v, k, size)
|
|
484
|
+
noUpdateTTL = false
|
|
485
|
+
} else {
|
|
486
|
+
// update
|
|
487
|
+
const oldVal = this.valList[index]
|
|
488
|
+
if (v !== oldVal) {
|
|
489
|
+
if (this.isBackgroundFetch(oldVal)) {
|
|
490
|
+
oldVal.__abortController.abort()
|
|
491
|
+
} else {
|
|
492
|
+
if (!noDisposeOnSet) {
|
|
493
|
+
this.dispose(oldVal, k, 'set')
|
|
494
|
+
if (this.disposeAfter) {
|
|
495
|
+
this.disposed.push([oldVal, k, 'set'])
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
this.removeItemSize(index)
|
|
500
|
+
this.valList[index] = v
|
|
501
|
+
this.addItemSize(index, v, k, size)
|
|
176
502
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
this.
|
|
184
|
-
trim(this)
|
|
185
|
-
return true
|
|
503
|
+
this.moveToTail(index)
|
|
504
|
+
}
|
|
505
|
+
if (ttl !== 0 && this.ttl === 0 && !this.ttls) {
|
|
506
|
+
this.initializeTTLTracking()
|
|
507
|
+
}
|
|
508
|
+
if (!noUpdateTTL) {
|
|
509
|
+
this.setItemTTL(index, ttl)
|
|
186
510
|
}
|
|
511
|
+
if (this.disposeAfter) {
|
|
512
|
+
while (this.disposed.length) {
|
|
513
|
+
this.disposeAfter(...this.disposed.shift())
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
return this
|
|
517
|
+
}
|
|
187
518
|
|
|
188
|
-
|
|
519
|
+
newIndex () {
|
|
520
|
+
if (this.size === 0) {
|
|
521
|
+
return this.tail
|
|
522
|
+
}
|
|
523
|
+
if (this.size === this.max) {
|
|
524
|
+
return this.evict()
|
|
525
|
+
}
|
|
526
|
+
if (this.free.length !== 0) {
|
|
527
|
+
return this.free.pop()
|
|
528
|
+
}
|
|
529
|
+
// initial fill, just keep writing down the list
|
|
530
|
+
return this.initialFill++
|
|
531
|
+
}
|
|
189
532
|
|
|
190
|
-
|
|
191
|
-
if (
|
|
192
|
-
|
|
193
|
-
|
|
533
|
+
pop () {
|
|
534
|
+
if (this.size) {
|
|
535
|
+
const val = this.valList[this.head]
|
|
536
|
+
this.evict()
|
|
537
|
+
return val
|
|
538
|
+
}
|
|
539
|
+
}
|
|
194
540
|
|
|
195
|
-
|
|
541
|
+
evict () {
|
|
542
|
+
const head = this.head
|
|
543
|
+
const k = this.keyList[head]
|
|
544
|
+
const v = this.valList[head]
|
|
545
|
+
if (this.isBackgroundFetch(v)) {
|
|
546
|
+
v.__abortController.abort()
|
|
547
|
+
} else {
|
|
548
|
+
this.dispose(v, k, 'evict')
|
|
549
|
+
if (this.disposeAfter) {
|
|
550
|
+
this.disposed.push([v, k, 'evict'])
|
|
551
|
+
}
|
|
196
552
|
}
|
|
553
|
+
this.removeItemSize(head)
|
|
554
|
+
this.head = this.next[head]
|
|
555
|
+
this.keyMap.delete(k)
|
|
556
|
+
this.size --
|
|
557
|
+
return head
|
|
558
|
+
}
|
|
197
559
|
|
|
198
|
-
|
|
199
|
-
this
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
560
|
+
has (k, { updateAgeOnHas = this.updateAgeOnHas } = {}) {
|
|
561
|
+
const index = this.keyMap.get(k)
|
|
562
|
+
if (index !== undefined) {
|
|
563
|
+
if (!this.isStale(index)) {
|
|
564
|
+
if (updateAgeOnHas) {
|
|
565
|
+
this.updateItemAge(index)
|
|
566
|
+
}
|
|
567
|
+
return true
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
return false
|
|
203
571
|
}
|
|
204
572
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
const
|
|
208
|
-
|
|
573
|
+
// like get(), but without any LRU updating or TTL expiration
|
|
574
|
+
peek (k, { allowStale = this.allowStale } = {}) {
|
|
575
|
+
const index = this.keyMap.get(k)
|
|
576
|
+
if (index !== undefined && (allowStale || !this.isStale(index))) {
|
|
577
|
+
return this.valList[index]
|
|
578
|
+
}
|
|
209
579
|
}
|
|
210
580
|
|
|
211
|
-
|
|
212
|
-
|
|
581
|
+
backgroundFetch (k, index, options) {
|
|
582
|
+
const v = index === undefined ? undefined : this.valList[index]
|
|
583
|
+
if (this.isBackgroundFetch(v)) {
|
|
584
|
+
return v
|
|
585
|
+
}
|
|
586
|
+
const ac = new AC()
|
|
587
|
+
const fetchOpts = {
|
|
588
|
+
signal: ac.signal,
|
|
589
|
+
options,
|
|
590
|
+
}
|
|
591
|
+
const p = Promise.resolve(this.fetchMethod(k, v, fetchOpts)).then(v => {
|
|
592
|
+
if (!ac.signal.aborted) {
|
|
593
|
+
this.set(k, v, fetchOpts.options)
|
|
594
|
+
}
|
|
595
|
+
return v
|
|
596
|
+
})
|
|
597
|
+
p.__abortController = ac
|
|
598
|
+
p.__staleWhileFetching = v
|
|
599
|
+
if (index === undefined) {
|
|
600
|
+
this.set(k, p, fetchOpts.options)
|
|
601
|
+
index = this.keyMap.get(k)
|
|
602
|
+
} else {
|
|
603
|
+
this.valList[index] = p
|
|
604
|
+
}
|
|
605
|
+
return p
|
|
213
606
|
}
|
|
214
607
|
|
|
215
|
-
|
|
216
|
-
return
|
|
608
|
+
isBackgroundFetch (p) {
|
|
609
|
+
return p && typeof p === 'object' && typeof p.then === 'function' &&
|
|
610
|
+
Object.prototype.hasOwnProperty.call(p, '__staleWhileFetching')
|
|
217
611
|
}
|
|
218
612
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
613
|
+
// this takes the union of get() and set() opts, because it does both
|
|
614
|
+
async fetch (k, {
|
|
615
|
+
allowStale = this.allowStale,
|
|
616
|
+
updateAgeOnGet = this.updateAgeOnGet,
|
|
617
|
+
ttl = this.ttl,
|
|
618
|
+
noDisposeOnSet = this.noDisposeOnSet,
|
|
619
|
+
size = 0,
|
|
620
|
+
sizeCalculation = this.sizeCalculation,
|
|
621
|
+
noUpdateTTL = this.noUpdateTTL,
|
|
622
|
+
} = {}) {
|
|
623
|
+
if (!this.fetchMethod) {
|
|
624
|
+
return this.get(k, {allowStale, updateAgeOnGet})
|
|
625
|
+
}
|
|
223
626
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
627
|
+
const options = {
|
|
628
|
+
allowStale,
|
|
629
|
+
updateAgeOnGet,
|
|
630
|
+
ttl,
|
|
631
|
+
noDisposeOnSet,
|
|
632
|
+
size,
|
|
633
|
+
sizeCalculation,
|
|
634
|
+
noUpdateTTL,
|
|
635
|
+
}
|
|
227
636
|
|
|
228
|
-
|
|
229
|
-
|
|
637
|
+
let index = this.keyMap.get(k)
|
|
638
|
+
if (index === undefined) {
|
|
639
|
+
return this.backgroundFetch(k, index, options)
|
|
640
|
+
} else {
|
|
641
|
+
// in cache, maybe already fetching
|
|
642
|
+
const v = this.valList[index]
|
|
643
|
+
if (this.isBackgroundFetch(v)) {
|
|
644
|
+
return allowStale && v.__staleWhileFetching !== undefined
|
|
645
|
+
? v.__staleWhileFetching : v
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
if (!this.isStale(index)) {
|
|
649
|
+
this.moveToTail(index)
|
|
650
|
+
if (updateAgeOnGet) {
|
|
651
|
+
this.updateItemAge(index)
|
|
652
|
+
}
|
|
653
|
+
return v
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
// ok, it is stale, and not already fetching
|
|
657
|
+
// refresh the cache.
|
|
658
|
+
const p = this.backgroundFetch(k, index, options)
|
|
659
|
+
return allowStale && p.__staleWhileFetching !== undefined
|
|
660
|
+
? p.__staleWhileFetching : p
|
|
661
|
+
}
|
|
230
662
|
}
|
|
231
663
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
this.
|
|
235
|
-
|
|
236
|
-
const
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
const
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
664
|
+
get (k, {
|
|
665
|
+
allowStale = this.allowStale,
|
|
666
|
+
updateAgeOnGet = this.updateAgeOnGet,
|
|
667
|
+
} = {}) {
|
|
668
|
+
const index = this.keyMap.get(k)
|
|
669
|
+
if (index !== undefined) {
|
|
670
|
+
const value = this.valList[index]
|
|
671
|
+
const fetching = this.isBackgroundFetch(value)
|
|
672
|
+
if (this.isStale(index)) {
|
|
673
|
+
// delete only if not an in-flight background fetch
|
|
674
|
+
if (!fetching) {
|
|
675
|
+
this.delete(k)
|
|
676
|
+
return allowStale ? value : undefined
|
|
677
|
+
} else {
|
|
678
|
+
return allowStale ? value.__staleWhileFetching : undefined
|
|
679
|
+
}
|
|
680
|
+
} else {
|
|
681
|
+
// if we're currently fetching it, we don't actually have it yet
|
|
682
|
+
// it's not stale, which means this isn't a staleWhileRefetching,
|
|
683
|
+
// so we just return undefined
|
|
684
|
+
if (fetching) {
|
|
685
|
+
return undefined
|
|
686
|
+
}
|
|
687
|
+
this.moveToTail(index)
|
|
688
|
+
if (updateAgeOnGet) {
|
|
689
|
+
this.updateItemAge(index)
|
|
249
690
|
}
|
|
691
|
+
return value
|
|
250
692
|
}
|
|
251
693
|
}
|
|
252
694
|
}
|
|
253
695
|
|
|
254
|
-
|
|
255
|
-
this[
|
|
696
|
+
connect (p, n) {
|
|
697
|
+
this.prev[n] = p
|
|
698
|
+
this.next[p] = n
|
|
256
699
|
}
|
|
257
|
-
}
|
|
258
700
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
701
|
+
moveToTail (index) {
|
|
702
|
+
// if tail already, nothing to do
|
|
703
|
+
// if head, move head to next[index]
|
|
704
|
+
// else
|
|
705
|
+
// move next[prev[index]] to next[index] (head has no prev)
|
|
706
|
+
// move prev[next[index]] to prev[index]
|
|
707
|
+
// prev[index] = tail
|
|
708
|
+
// next[tail] = index
|
|
709
|
+
// tail = index
|
|
710
|
+
if (index !== this.tail) {
|
|
711
|
+
if (index === this.head) {
|
|
712
|
+
this.head = this.next[index]
|
|
713
|
+
} else {
|
|
714
|
+
this.connect(this.prev[index], this.next[index])
|
|
272
715
|
}
|
|
716
|
+
this.connect(this.tail, index)
|
|
717
|
+
this.tail = index
|
|
273
718
|
}
|
|
274
|
-
return hit.value
|
|
275
719
|
}
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
const isStale = (self, hit) => {
|
|
279
|
-
if (!hit || (!hit.maxAge && !self[MAX_AGE]))
|
|
280
|
-
return false
|
|
281
720
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
721
|
+
get del () {
|
|
722
|
+
deprecatedMethod('del', 'delete')
|
|
723
|
+
return this.delete
|
|
724
|
+
}
|
|
725
|
+
delete (k) {
|
|
726
|
+
let deleted = false
|
|
727
|
+
if (this.size !== 0) {
|
|
728
|
+
const index = this.keyMap.get(k)
|
|
729
|
+
if (index !== undefined) {
|
|
730
|
+
deleted = true
|
|
731
|
+
if (this.size === 1) {
|
|
732
|
+
this.clear()
|
|
733
|
+
} else {
|
|
734
|
+
this.removeItemSize(index)
|
|
735
|
+
const v = this.valList[index]
|
|
736
|
+
if (this.isBackgroundFetch(v)) {
|
|
737
|
+
v.__abortController.abort()
|
|
738
|
+
} else {
|
|
739
|
+
this.dispose(v, k, 'delete')
|
|
740
|
+
if (this.disposeAfter) {
|
|
741
|
+
this.disposed.push([v, k, 'delete'])
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
this.keyMap.delete(k)
|
|
745
|
+
this.keyList[index] = null
|
|
746
|
+
this.valList[index] = null
|
|
747
|
+
if (index === this.tail) {
|
|
748
|
+
this.tail = this.prev[index]
|
|
749
|
+
} else if (index === this.head) {
|
|
750
|
+
this.head = this.next[index]
|
|
751
|
+
} else {
|
|
752
|
+
this.next[this.prev[index]] = this.next[index]
|
|
753
|
+
this.prev[this.next[index]] = this.prev[index]
|
|
754
|
+
}
|
|
755
|
+
this.size --
|
|
756
|
+
this.free.push(index)
|
|
757
|
+
}
|
|
758
|
+
}
|
|
297
759
|
}
|
|
760
|
+
if (this.disposed) {
|
|
761
|
+
while (this.disposed.length) {
|
|
762
|
+
this.disposeAfter(...this.disposed.shift())
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
return deleted
|
|
298
766
|
}
|
|
299
|
-
}
|
|
300
767
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
768
|
+
clear () {
|
|
769
|
+
for (const index of this.rindexes({ allowStale: true })) {
|
|
770
|
+
const v = this.valList[index]
|
|
771
|
+
if (this.isBackgroundFetch(v)) {
|
|
772
|
+
v.__abortController.abort()
|
|
773
|
+
} else {
|
|
774
|
+
const k = this.keyList[index]
|
|
775
|
+
this.dispose(v, k, 'delete')
|
|
776
|
+
if (this.disposeAfter) {
|
|
777
|
+
this.disposed.push([v, k, 'delete'])
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
}
|
|
306
781
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
782
|
+
this.keyMap.clear()
|
|
783
|
+
this.valList.fill(null)
|
|
784
|
+
this.keyList.fill(null)
|
|
785
|
+
if (this.ttls) {
|
|
786
|
+
this.ttls.fill(0)
|
|
787
|
+
this.starts.fill(0)
|
|
788
|
+
}
|
|
789
|
+
if (this.sizes) {
|
|
790
|
+
this.sizes.fill(0)
|
|
791
|
+
}
|
|
792
|
+
this.head = 0
|
|
793
|
+
this.tail = 0
|
|
794
|
+
this.initialFill = 1
|
|
795
|
+
this.free.length = 0
|
|
796
|
+
this.calculatedSize = 0
|
|
797
|
+
this.size = 0
|
|
798
|
+
if (this.disposed) {
|
|
799
|
+
while (this.disposed.length) {
|
|
800
|
+
this.disposeAfter(...this.disposed.shift())
|
|
801
|
+
}
|
|
802
|
+
}
|
|
310
803
|
}
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
constructor (key, value, length, now, maxAge) {
|
|
315
|
-
this.key = key
|
|
316
|
-
this.value = value
|
|
317
|
-
this.length = length
|
|
318
|
-
this.now = now
|
|
319
|
-
this.maxAge = maxAge || 0
|
|
804
|
+
get reset () {
|
|
805
|
+
deprecatedMethod('reset', 'clear')
|
|
806
|
+
return this.clear
|
|
320
807
|
}
|
|
321
|
-
}
|
|
322
808
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
del(self, node)
|
|
327
|
-
if (!self[ALLOW_STALE])
|
|
328
|
-
hit = undefined
|
|
809
|
+
get length () {
|
|
810
|
+
deprecatedProperty('length', 'size')
|
|
811
|
+
return this.size
|
|
329
812
|
}
|
|
330
|
-
if (hit)
|
|
331
|
-
fn.call(thisp, hit.value, hit.key, self)
|
|
332
813
|
}
|
|
333
814
|
|
|
334
815
|
module.exports = LRUCache
|