@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.
Files changed (37) hide show
  1. package/.jsii +57 -31
  2. package/.jsii.tabl.json +68 -3
  3. package/.warnings.jsii.js +253 -147
  4. package/NOTICE +0 -17
  5. package/lib/integ-tests/test-case.d.ts +47 -42
  6. package/lib/integ-tests/test-case.js +1 -1
  7. package/lib/manifest.js +57 -8
  8. package/node_modules/lru-cache/LICENSE +1 -1
  9. package/node_modules/lru-cache/README.md +632 -99
  10. package/node_modules/lru-cache/index.js +732 -251
  11. package/node_modules/lru-cache/package.json +17 -7
  12. package/node_modules/semver/README.md +3 -1
  13. package/node_modules/semver/bin/semver.js +17 -8
  14. package/node_modules/semver/classes/comparator.js +3 -2
  15. package/node_modules/semver/classes/index.js +1 -1
  16. package/node_modules/semver/classes/range.js +31 -22
  17. package/node_modules/semver/functions/cmp.js +8 -4
  18. package/node_modules/semver/functions/coerce.js +3 -2
  19. package/node_modules/semver/functions/parse.js +1 -1
  20. package/node_modules/semver/internal/constants.js +2 -2
  21. package/node_modules/semver/internal/identifiers.js +1 -1
  22. package/node_modules/semver/internal/parse-options.js +3 -3
  23. package/node_modules/semver/internal/re.js +3 -3
  24. package/node_modules/semver/package.json +46 -13
  25. package/node_modules/semver/ranges/min-version.js +2 -1
  26. package/node_modules/semver/ranges/outside.js +1 -1
  27. package/node_modules/semver/ranges/simplify.js +15 -12
  28. package/node_modules/semver/ranges/subset.js +53 -31
  29. package/package.json +6 -5
  30. package/schema/cloud-assembly.version.json +1 -1
  31. package/schema/integ.schema.json +1 -1
  32. package/node_modules/semver/CHANGELOG.md +0 -111
  33. package/node_modules/yallist/LICENSE +0 -15
  34. package/node_modules/yallist/README.md +0 -204
  35. package/node_modules/yallist/iterator.js +0 -8
  36. package/node_modules/yallist/package.json +0 -29
  37. package/node_modules/yallist/yallist.js +0 -426
@@ -1,334 +1,815 @@
1
- 'use strict'
2
-
3
- // A linked list to keep track of recently-used-ness
4
- const Yallist = require('yallist')
5
-
6
- const MAX = Symbol('max')
7
- const LENGTH = Symbol('length')
8
- const LENGTH_CALCULATOR = Symbol('lengthCalculator')
9
- const ALLOW_STALE = Symbol('allowStale')
10
- const MAX_AGE = Symbol('maxAge')
11
- const DISPOSE = Symbol('dispose')
12
- const NO_DISPOSE_ON_SET = Symbol('noDisposeOnSet')
13
- const LRU_LIST = Symbol('lruList')
14
- const CACHE = Symbol('cache')
15
- const UPDATE_AGE_ON_GET = Symbol('updateAgeOnGet')
16
-
17
- const naiveLength = () => 1
18
-
19
- // lruList is a yallist where the head is the youngest
20
- // item, and the tail is the oldest. the list contains the Hit
21
- // objects as the entries.
22
- // Each Hit object has a reference to its Yallist.Node. This
23
- // never changes.
24
- //
25
- // cache is a Map (or PseudoMap) that matches the keys to
26
- // the Yallist.Node object.
27
- class LRUCache {
28
- constructor (options) {
29
- if (typeof options === 'number')
30
- options = { max: options }
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
- if (!options)
33
- options = {}
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
- if (options.max && (typeof options.max !== 'number' || options.max < 0))
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
- const lc = options.length || naiveLength
41
- this[LENGTH_CALCULATOR] = (typeof lc !== 'function') ? naiveLength : lc
42
- this[ALLOW_STALE] = options.stale || false
43
- if (options.maxAge && typeof options.maxAge !== 'number')
44
- throw new TypeError('maxAge must be a number')
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
- // resize the cache when the max changes.
53
- set max (mL) {
54
- if (typeof mL !== 'number' || mL < 0)
55
- throw new TypeError('max must be a non-negative number')
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
- this[MAX] = mL || Infinity
58
- trim(this)
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
- get max () {
61
- return this[MAX]
88
+ pop () {
89
+ return this.heap[--this.length]
62
90
  }
91
+ }
63
92
 
64
- set allowStale (allowStale) {
65
- this[ALLOW_STALE] = !!allowStale
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
- get allowStale () {
68
- return this[ALLOW_STALE]
216
+
217
+ getRemainingTTL (key) {
218
+ return this.has(key, { updateAgeOnHas: false }) ? Infinity : 0
69
219
  }
70
220
 
71
- set maxAge (mA) {
72
- if (typeof mA !== 'number')
73
- throw new TypeError('maxAge must be a non-negative number')
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[MAX_AGE] = mA
76
- trim(this)
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
- get maxAge () {
79
- return this[MAX_AGE]
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
- // resize the cache when the lengthCalculator changes.
83
- set lengthCalculator (lC) {
84
- if (typeof lC !== 'function')
85
- lC = naiveLength
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
- if (lC !== this[LENGTH_CALCULATOR]) {
88
- this[LENGTH_CALCULATOR] = lC
89
- this[LENGTH] = 0
90
- this[LRU_LIST].forEach(hit => {
91
- hit.length = this[LENGTH_CALCULATOR](hit.value, hit.key)
92
- this[LENGTH] += hit.length
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
- get length () { return this[LENGTH] }
100
- get itemCount () { return this[LRU_LIST].length }
361
+ isValidIndex (index) {
362
+ return this.keyMap.get(this.keyList[index]) === index
363
+ }
101
364
 
102
- rforEach (fn, thisp) {
103
- thisp = thisp || this
104
- for (let walker = this[LRU_LIST].tail; walker !== null;) {
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
- forEach (fn, thisp) {
112
- thisp = thisp || this
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
- return this[LRU_LIST].toArray().map(k => k.key)
376
+ *keys () {
377
+ for (const i of this.indexes()) {
378
+ yield this.keyList[i]
379
+ }
122
380
  }
123
-
124
- values () {
125
- return this[LRU_LIST].toArray().map(k => k.value)
381
+ *rkeys () {
382
+ for (const i of this.rindexes()) {
383
+ yield this.keyList[i]
384
+ }
126
385
  }
127
386
 
128
- reset () {
129
- if (this[DISPOSE] &&
130
- this[LRU_LIST] &&
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
- this[CACHE] = new Map() // hash of items by key
136
- this[LRU_LIST] = new Yallist() // list of items in order of use recency
137
- this[LENGTH] = 0 // length of items in the list
398
+ [Symbol.iterator] () {
399
+ return this.entries()
138
400
  }
139
401
 
140
- dump () {
141
- return this[LRU_LIST].map(hit =>
142
- isStale(this, hit) ? false : {
143
- k: hit.key,
144
- v: hit.value,
145
- e: hit.now + (hit.maxAge || 0)
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
- dumpLru () {
150
- return this[LRU_LIST]
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
- set (key, value, maxAge) {
154
- maxAge = maxAge || this[MAX_AGE]
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
- if (maxAge && typeof maxAge !== 'number')
157
- throw new TypeError('maxAge must be a number')
422
+ get prune () {
423
+ deprecatedMethod('prune', 'purgeStale')
424
+ return this.purgeStale
425
+ }
158
426
 
159
- const now = maxAge ? Date.now() : 0
160
- const len = this[LENGTH_CALCULATOR](value, key)
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
- if (this[CACHE].has(key)) {
163
- if (len > this[MAX]) {
164
- del(this, this[CACHE].get(key))
165
- return false
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
- const node = this[CACHE].get(key)
169
- const item = node.value
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
- // dispose of the old one before overwriting
172
- // split out into 2 ifs for better coverage tracking
173
- if (this[DISPOSE]) {
174
- if (!this[NO_DISPOSE_ON_SET])
175
- this[DISPOSE](key, item.value)
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
- item.now = now
179
- item.maxAge = maxAge
180
- item.value = value
181
- this[LENGTH] += len - item.length
182
- item.length = len
183
- this.get(key)
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
- const hit = new Entry(key, value, len, now, maxAge)
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
- // oversized objects fall out of cache automatically.
191
- if (hit.length > this[MAX]) {
192
- if (this[DISPOSE])
193
- this[DISPOSE](key, value)
533
+ pop () {
534
+ if (this.size) {
535
+ const val = this.valList[this.head]
536
+ this.evict()
537
+ return val
538
+ }
539
+ }
194
540
 
195
- return false
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
- this[LENGTH] += hit.length
199
- this[LRU_LIST].unshift(hit)
200
- this[CACHE].set(key, this[LRU_LIST].head)
201
- trim(this)
202
- return true
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
- has (key) {
206
- if (!this[CACHE].has(key)) return false
207
- const hit = this[CACHE].get(key).value
208
- return !isStale(this, hit)
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
- get (key) {
212
- return get(this, key, true)
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
- peek (key) {
216
- return get(this, key, false)
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
- pop () {
220
- const node = this[LRU_LIST].tail
221
- if (!node)
222
- return null
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
- del(this, node)
225
- return node.value
226
- }
627
+ const options = {
628
+ allowStale,
629
+ updateAgeOnGet,
630
+ ttl,
631
+ noDisposeOnSet,
632
+ size,
633
+ sizeCalculation,
634
+ noUpdateTTL,
635
+ }
227
636
 
228
- del (key) {
229
- del(this, this[CACHE].get(key))
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
- load (arr) {
233
- // reset the cache
234
- this.reset()
235
-
236
- const now = Date.now()
237
- // A previous serialized cache has the most recent items first
238
- for (let l = arr.length - 1; l >= 0; l--) {
239
- const hit = arr[l]
240
- const expiresAt = hit.e || 0
241
- if (expiresAt === 0)
242
- // the item was created without expiration in a non aged cache
243
- this.set(hit.k, hit.v)
244
- else {
245
- const maxAge = expiresAt - now
246
- // dont add already expired items
247
- if (maxAge > 0) {
248
- this.set(hit.k, hit.v, maxAge)
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
- prune () {
255
- this[CACHE].forEach((value, key) => get(this, key, false))
696
+ connect (p, n) {
697
+ this.prev[n] = p
698
+ this.next[p] = n
256
699
  }
257
- }
258
700
 
259
- const get = (self, key, doUse) => {
260
- const node = self[CACHE].get(key)
261
- if (node) {
262
- const hit = node.value
263
- if (isStale(self, hit)) {
264
- del(self, node)
265
- if (!self[ALLOW_STALE])
266
- return undefined
267
- } else {
268
- if (doUse) {
269
- if (self[UPDATE_AGE_ON_GET])
270
- node.value.now = Date.now()
271
- self[LRU_LIST].unshiftNode(node)
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
- const diff = Date.now() - hit.now
283
- return hit.maxAge ? diff > hit.maxAge
284
- : self[MAX_AGE] && (diff > self[MAX_AGE])
285
- }
286
-
287
- const trim = self => {
288
- if (self[LENGTH] > self[MAX]) {
289
- for (let walker = self[LRU_LIST].tail;
290
- self[LENGTH] > self[MAX] && walker !== null;) {
291
- // We know that we're about to delete this one, and also
292
- // what the next least recently used key will be, so just
293
- // go ahead and set it now.
294
- const prev = walker.prev
295
- del(self, walker)
296
- walker = prev
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
- const del = (self, node) => {
302
- if (node) {
303
- const hit = node.value
304
- if (self[DISPOSE])
305
- self[DISPOSE](hit.key, hit.value)
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
- self[LENGTH] -= hit.length
308
- self[CACHE].delete(hit.key)
309
- self[LRU_LIST].removeNode(node)
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
- class Entry {
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
- const forEachStep = (self, fn, node, thisp) => {
324
- let hit = node.value
325
- if (isStale(self, hit)) {
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