@nocobase/plugin-workflow 2.1.0-alpha.2 → 2.1.0-alpha.20

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 (136) hide show
  1. package/LICENSE +201 -661
  2. package/README.md +79 -10
  3. package/dist/client/214.88c6cd534bd4fc1c.js +10 -0
  4. package/dist/client/261.404a35cf5ae2ceba.js +10 -0
  5. package/dist/client/67.f904ef4520868b8a.js +10 -0
  6. package/dist/client/964.6251d37b35710747.js +10 -0
  7. package/dist/client/hooks/{useWorkflowFilterActionProps.d.ts → useResourceFilterActionProps.d.ts} +1 -1
  8. package/dist/client/index.js +1 -1
  9. package/dist/client/nodes/create.d.ts +10 -0
  10. package/dist/client/nodes/destroy.d.ts +10 -0
  11. package/dist/client/nodes/index.d.ts +3 -0
  12. package/dist/client/nodes/query.d.ts +18 -2
  13. package/dist/client/nodes/update.d.ts +10 -0
  14. package/dist/client/schemas/collection.d.ts +8 -2
  15. package/dist/client/schemas/executions.d.ts +41 -22
  16. package/dist/client/triggers/collection.d.ts +14 -1
  17. package/dist/client/triggers/index.d.ts +4 -0
  18. package/dist/client/triggers/schedule/constants.d.ts +4 -0
  19. package/dist/client/triggers/schedule/index.d.ts +15 -0
  20. package/dist/common/collections/executions.d.ts +22 -22
  21. package/dist/common/collections/executions.js +12 -0
  22. package/dist/common/collections/jobs.js +7 -0
  23. package/dist/common/collections/workflows.d.ts +22 -9
  24. package/dist/common/collections/workflows.js +9 -1
  25. package/dist/externalVersion.js +15 -13
  26. package/dist/locale/zh-CN.json +4 -1
  27. package/dist/node_modules/cron-parser/package.json +1 -1
  28. package/dist/node_modules/joi/dist/joi-browser.min.js +1 -0
  29. package/dist/node_modules/joi/lib/annotate.js +175 -0
  30. package/dist/node_modules/joi/lib/base.js +1069 -0
  31. package/dist/node_modules/joi/lib/cache.js +143 -0
  32. package/dist/node_modules/joi/lib/common.js +216 -0
  33. package/dist/node_modules/joi/lib/compile.js +283 -0
  34. package/dist/node_modules/joi/lib/errors.js +271 -0
  35. package/dist/node_modules/joi/lib/extend.js +312 -0
  36. package/dist/node_modules/joi/lib/index.d.ts +2365 -0
  37. package/dist/node_modules/joi/lib/index.js +1 -0
  38. package/dist/node_modules/joi/lib/manifest.js +476 -0
  39. package/dist/node_modules/joi/lib/messages.js +178 -0
  40. package/dist/node_modules/joi/lib/modify.js +267 -0
  41. package/dist/node_modules/joi/lib/ref.js +414 -0
  42. package/dist/node_modules/joi/lib/schemas.js +302 -0
  43. package/dist/node_modules/joi/lib/state.js +166 -0
  44. package/dist/node_modules/joi/lib/template.js +463 -0
  45. package/dist/node_modules/joi/lib/trace.js +346 -0
  46. package/dist/node_modules/joi/lib/types/alternatives.js +364 -0
  47. package/dist/node_modules/joi/lib/types/any.js +174 -0
  48. package/dist/node_modules/joi/lib/types/array.js +809 -0
  49. package/dist/node_modules/joi/lib/types/binary.js +100 -0
  50. package/dist/node_modules/joi/lib/types/boolean.js +150 -0
  51. package/dist/node_modules/joi/lib/types/date.js +233 -0
  52. package/dist/node_modules/joi/lib/types/function.js +93 -0
  53. package/dist/node_modules/joi/lib/types/keys.js +1067 -0
  54. package/dist/node_modules/joi/lib/types/link.js +168 -0
  55. package/dist/node_modules/joi/lib/types/number.js +363 -0
  56. package/dist/node_modules/joi/lib/types/object.js +22 -0
  57. package/dist/node_modules/joi/lib/types/string.js +850 -0
  58. package/dist/node_modules/joi/lib/types/symbol.js +102 -0
  59. package/dist/node_modules/joi/lib/validator.js +750 -0
  60. package/dist/node_modules/joi/lib/values.js +263 -0
  61. package/dist/node_modules/joi/node_modules/@hapi/topo/lib/index.d.ts +60 -0
  62. package/dist/node_modules/joi/node_modules/@hapi/topo/lib/index.js +225 -0
  63. package/dist/node_modules/joi/node_modules/@hapi/topo/package.json +30 -0
  64. package/dist/node_modules/joi/package.json +1 -0
  65. package/dist/node_modules/lru-cache/dist/commonjs/diagnostics-channel.d.ts +5 -0
  66. package/dist/node_modules/lru-cache/dist/commonjs/diagnostics-channel.js +10 -0
  67. package/dist/node_modules/lru-cache/dist/commonjs/index.d.ts +1381 -0
  68. package/dist/node_modules/lru-cache/dist/commonjs/index.js +1692 -0
  69. package/dist/node_modules/lru-cache/dist/commonjs/index.min.js +1 -0
  70. package/dist/node_modules/lru-cache/dist/esm/browser/diagnostics-channel.d.ts +5 -0
  71. package/dist/node_modules/lru-cache/dist/esm/browser/diagnostics-channel.js +4 -0
  72. package/dist/node_modules/lru-cache/dist/esm/browser/index.d.ts +1381 -0
  73. package/dist/node_modules/lru-cache/dist/{mjs → esm/browser}/index.js +537 -179
  74. package/dist/node_modules/lru-cache/dist/esm/browser/index.min.js +2 -0
  75. package/dist/node_modules/lru-cache/dist/esm/diagnostics-channel.d.ts +5 -0
  76. package/dist/node_modules/lru-cache/dist/esm/diagnostics-channel.js +19 -0
  77. package/dist/node_modules/lru-cache/dist/esm/index.d.ts +1381 -0
  78. package/dist/node_modules/lru-cache/dist/{cjs → esm}/index.js +538 -184
  79. package/dist/node_modules/lru-cache/dist/esm/index.min.js +2 -0
  80. package/dist/node_modules/lru-cache/dist/esm/node/diagnostics-channel.d.ts +5 -0
  81. package/dist/node_modules/lru-cache/dist/esm/node/diagnostics-channel.js +7 -0
  82. package/dist/node_modules/lru-cache/dist/esm/node/index.d.ts +1381 -0
  83. package/dist/node_modules/lru-cache/dist/esm/node/index.js +1688 -0
  84. package/dist/node_modules/lru-cache/dist/esm/node/index.min.js +2 -0
  85. package/dist/node_modules/lru-cache/package.json +1 -1
  86. package/dist/node_modules/nodejs-snowflake/package.json +1 -1
  87. package/dist/server/Dispatcher.d.ts +3 -2
  88. package/dist/server/Dispatcher.js +74 -61
  89. package/dist/server/Plugin.d.ts +1 -0
  90. package/dist/server/Plugin.js +28 -2
  91. package/dist/server/actions/nodes.d.ts +5 -0
  92. package/dist/server/actions/nodes.js +34 -2
  93. package/dist/server/actions/workflows.d.ts +6 -0
  94. package/dist/server/actions/workflows.js +38 -0
  95. package/dist/server/instructions/ConditionInstruction.d.ts +2 -0
  96. package/dist/server/instructions/ConditionInstruction.js +17 -0
  97. package/dist/server/instructions/CreateInstruction.d.ts +3 -0
  98. package/dist/server/instructions/CreateInstruction.js +25 -0
  99. package/dist/server/instructions/DestroyInstruction.d.ts +3 -0
  100. package/dist/server/instructions/DestroyInstruction.js +25 -0
  101. package/dist/server/instructions/EndInstruction.d.ts +2 -0
  102. package/dist/server/instructions/EndInstruction.js +4 -0
  103. package/dist/server/instructions/MultiConditionsInstruction.d.ts +2 -0
  104. package/dist/server/instructions/MultiConditionsInstruction.js +23 -0
  105. package/dist/server/instructions/OutputInstruction.d.ts +2 -0
  106. package/dist/server/instructions/OutputInstruction.js +14 -0
  107. package/dist/server/instructions/QueryInstruction.d.ts +3 -0
  108. package/dist/server/instructions/QueryInstruction.js +32 -7
  109. package/dist/server/instructions/UpdateInstruction.d.ts +3 -0
  110. package/dist/server/instructions/UpdateInstruction.js +27 -0
  111. package/dist/server/instructions/index.d.ts +4 -0
  112. package/dist/server/instructions/index.js +18 -0
  113. package/dist/server/triggers/CollectionTrigger.d.ts +3 -0
  114. package/dist/server/triggers/CollectionTrigger.js +28 -0
  115. package/dist/server/triggers/ScheduleTrigger/index.d.ts +3 -0
  116. package/dist/server/triggers/ScheduleTrigger/index.js +18 -3
  117. package/dist/server/triggers/index.d.ts +3 -0
  118. package/dist/server/triggers/index.js +18 -0
  119. package/dist/server/utils.d.ts +17 -0
  120. package/dist/server/utils.js +51 -2
  121. package/dist/swagger/index.d.ts +830 -109
  122. package/dist/swagger/index.js +947 -208
  123. package/package.json +6 -5
  124. package/dist/client/27bd65abee87cafa.js +0 -10
  125. package/dist/client/478692c1637f2742.js +0 -10
  126. package/dist/client/c1347b9d21f864d9.js +0 -10
  127. package/dist/client/f39e94207f92e352.js +0 -10
  128. package/dist/node_modules/lru-cache/LICENSE +0 -15
  129. package/dist/node_modules/lru-cache/dist/cjs/index-cjs.d.ts +0 -7
  130. package/dist/node_modules/lru-cache/dist/cjs/index-cjs.js +0 -1
  131. package/dist/node_modules/lru-cache/dist/cjs/index.d.ts +0 -807
  132. package/dist/node_modules/lru-cache/dist/cjs/index.min.js +0 -2
  133. package/dist/node_modules/lru-cache/dist/mjs/index.d.ts +0 -807
  134. package/dist/node_modules/lru-cache/dist/mjs/index.min.js +0 -2
  135. /package/dist/node_modules/lru-cache/dist/{cjs → commonjs}/package.json +0 -0
  136. /package/dist/node_modules/lru-cache/dist/{mjs → esm}/package.json +0 -0
@@ -1,23 +1,31 @@
1
1
  /**
2
2
  * @module LRUCache
3
3
  */
4
- const perf = typeof performance === 'object' &&
4
+ import { metrics, tracing } from './diagnostics-channel.js';
5
+ const hasSubscribers = () => metrics.hasSubscribers || tracing.hasSubscribers;
6
+ const defaultPerf = (typeof performance === 'object' &&
5
7
  performance &&
6
- typeof performance.now === 'function'
7
- ? performance
8
+ typeof performance.now === 'function') ?
9
+ performance
8
10
  : Date;
9
11
  const warned = new Set();
12
+ /* c8 ignore start */
13
+ const PROCESS = (typeof process === 'object' && !!process ?
14
+ process
15
+ : {});
16
+ /* c8 ignore stop */
10
17
  const emitWarning = (msg, type, code, fn) => {
11
- typeof process === 'object' &&
12
- process &&
13
- typeof process.emitWarning === 'function'
14
- ? process.emitWarning(msg, type, code, fn)
15
- : console.error(`[${code}] ${type}: ${msg}`);
18
+ if (typeof PROCESS.emitWarning === 'function') {
19
+ PROCESS.emitWarning(msg, type, code, fn);
20
+ }
21
+ else {
22
+ //oxlint-disable-next-line no-console
23
+ console.error(`[${code}] ${type}: ${msg}`);
24
+ }
16
25
  };
17
26
  const shouldWarn = (code) => !warned.has(code);
18
27
  const TYPE = Symbol('type');
19
- const isPosInt = (n) => n && n === Math.floor(n) && n > 0 && isFinite(n);
20
- /* c8 ignore start */
28
+ const isPosInt = (n) => !!n && n === Math.floor(n) && n > 0 && isFinite(n);
21
29
  // This is a little bit ridiculous, tbh.
22
30
  // The maximum array length is 2^32-1 or thereabouts on most JS impls.
23
31
  // And well before that point, you're caching the entire world, I mean,
@@ -26,16 +34,12 @@ const isPosInt = (n) => n && n === Math.floor(n) && n > 0 && isFinite(n);
26
34
  // zeroes at init time is brutal when you get that big.
27
35
  // But why not be complete?
28
36
  // Maybe in the future, these limits will have expanded.
29
- const getUintArray = (max) => !isPosInt(max)
30
- ? null
31
- : max <= Math.pow(2, 8)
32
- ? Uint8Array
33
- : max <= Math.pow(2, 16)
34
- ? Uint16Array
35
- : max <= Math.pow(2, 32)
36
- ? Uint32Array
37
- : max <= Number.MAX_SAFE_INTEGER
38
- ? ZeroArray
37
+ /* c8 ignore start */
38
+ const getUintArray = (max) => !isPosInt(max) ? null
39
+ : max <= Math.pow(2, 8) ? Uint8Array
40
+ : max <= Math.pow(2, 16) ? Uint16Array
41
+ : max <= Math.pow(2, 32) ? Uint32Array
42
+ : max <= Number.MAX_SAFE_INTEGER ? ZeroArray
39
43
  : null;
40
44
  /* c8 ignore stop */
41
45
  class ZeroArray extends Array {
@@ -77,21 +81,34 @@ class Stack {
77
81
  /**
78
82
  * Default export, the thing you're using this module to get.
79
83
  *
80
- * All properties from the options object (with the exception of
81
- * {@link OptionsBase.max} and {@link OptionsBase.maxSize}) are added as
82
- * normal public members. (`max` and `maxBase` are read-only getters.)
83
- * Changing any of these will alter the defaults for subsequent method calls,
84
- * but is otherwise safe.
84
+ * The `K` and `V` types define the key and value types, respectively. The
85
+ * optional `FC` type defines the type of the `context` object passed to
86
+ * `cache.fetch()` and `cache.memo()`.
87
+ *
88
+ * Keys and values **must not** be `null` or `undefined`.
89
+ *
90
+ * All properties from the options object (with the exception of `max`,
91
+ * `maxSize`, `fetchMethod`, `memoMethod`, `dispose` and `disposeAfter`) are
92
+ * added as normal public members. (The listed options are read-only getters.)
93
+ *
94
+ * Changing any of these will alter the defaults for subsequent method calls.
85
95
  */
86
96
  export class LRUCache {
87
- // properties coming in from the options of these, only max and maxSize
88
- // really *need* to be protected. The rest can be modified, as they just
89
- // set defaults for various methods.
97
+ // options that cannot be changed without disaster
90
98
  #max;
91
99
  #maxSize;
92
100
  #dispose;
101
+ #onInsert;
93
102
  #disposeAfter;
94
103
  #fetchMethod;
104
+ #memoMethod;
105
+ #perf;
106
+ /**
107
+ * {@link LRUCache.OptionsBase.perf}
108
+ */
109
+ get perf() {
110
+ return this.#perf;
111
+ }
95
112
  /**
96
113
  * {@link LRUCache.OptionsBase.ttl}
97
114
  */
@@ -167,9 +184,11 @@ export class LRUCache {
167
184
  #sizes;
168
185
  #starts;
169
186
  #ttls;
187
+ #autopurgeTimers;
170
188
  #hasDispose;
171
189
  #hasFetchMethod;
172
190
  #hasDisposeAfter;
191
+ #hasOnInsert;
173
192
  /**
174
193
  * Do not call this method unless you need to inspect the
175
194
  * inner workings of the cache. If anything returned by this
@@ -184,6 +203,7 @@ export class LRUCache {
184
203
  // properties
185
204
  starts: c.#starts,
186
205
  ttls: c.#ttls,
206
+ autopurgeTimers: c.#autopurgeTimers,
187
207
  sizes: c.#sizes,
188
208
  keyMap: c.#keyMap,
189
209
  keyList: c.#keyList,
@@ -237,12 +257,21 @@ export class LRUCache {
237
257
  get fetchMethod() {
238
258
  return this.#fetchMethod;
239
259
  }
260
+ get memoMethod() {
261
+ return this.#memoMethod;
262
+ }
240
263
  /**
241
264
  * {@link LRUCache.OptionsBase.dispose} (read-only)
242
265
  */
243
266
  get dispose() {
244
267
  return this.#dispose;
245
268
  }
269
+ /**
270
+ * {@link LRUCache.OptionsBase.onInsert} (read-only)
271
+ */
272
+ get onInsert() {
273
+ return this.#onInsert;
274
+ }
246
275
  /**
247
276
  * {@link LRUCache.OptionsBase.disposeAfter} (read-only)
248
277
  */
@@ -250,7 +279,13 @@ export class LRUCache {
250
279
  return this.#disposeAfter;
251
280
  }
252
281
  constructor(options) {
253
- const { max = 0, ttl, ttlResolution = 1, ttlAutopurge, updateAgeOnGet, updateAgeOnHas, allowStale, dispose, disposeAfter, noDisposeOnSet, noUpdateTTL, maxSize = 0, maxEntrySize = 0, sizeCalculation, fetchMethod, noDeleteOnFetchRejection, noDeleteOnStaleGet, allowStaleOnFetchRejection, allowStaleOnFetchAbort, ignoreFetchAbort, } = options;
282
+ const { max = 0, ttl, ttlResolution = 1, ttlAutopurge, updateAgeOnGet, updateAgeOnHas, allowStale, dispose, onInsert, disposeAfter, noDisposeOnSet, noUpdateTTL, maxSize = 0, maxEntrySize = 0, sizeCalculation, fetchMethod, memoMethod, noDeleteOnFetchRejection, noDeleteOnStaleGet, allowStaleOnFetchRejection, allowStaleOnFetchAbort, ignoreFetchAbort, perf, } = options;
283
+ if (perf !== undefined) {
284
+ if (typeof perf?.now !== 'function') {
285
+ throw new TypeError('perf option must have a now() method if specified');
286
+ }
287
+ }
288
+ this.#perf = perf ?? defaultPerf;
254
289
  if (max !== 0 && !isPosInt(max)) {
255
290
  throw new TypeError('max option must be a nonnegative integer');
256
291
  }
@@ -270,15 +305,18 @@ export class LRUCache {
270
305
  throw new TypeError('sizeCalculation set to non-function');
271
306
  }
272
307
  }
273
- if (fetchMethod !== undefined &&
274
- typeof fetchMethod !== 'function') {
308
+ if (memoMethod !== undefined && typeof memoMethod !== 'function') {
309
+ throw new TypeError('memoMethod must be a function if defined');
310
+ }
311
+ this.#memoMethod = memoMethod;
312
+ if (fetchMethod !== undefined && typeof fetchMethod !== 'function') {
275
313
  throw new TypeError('fetchMethod must be a function if specified');
276
314
  }
277
315
  this.#fetchMethod = fetchMethod;
278
316
  this.#hasFetchMethod = !!fetchMethod;
279
317
  this.#keyMap = new Map();
280
- this.#keyList = new Array(max).fill(undefined);
281
- this.#valList = new Array(max).fill(undefined);
318
+ this.#keyList = Array.from({ length: max }).fill(undefined);
319
+ this.#valList = Array.from({ length: max }).fill(undefined);
282
320
  this.#next = new UintArray(max);
283
321
  this.#prev = new UintArray(max);
284
322
  this.#head = 0;
@@ -289,6 +327,9 @@ export class LRUCache {
289
327
  if (typeof dispose === 'function') {
290
328
  this.#dispose = dispose;
291
329
  }
330
+ if (typeof onInsert === 'function') {
331
+ this.#onInsert = onInsert;
332
+ }
292
333
  if (typeof disposeAfter === 'function') {
293
334
  this.#disposeAfter = disposeAfter;
294
335
  this.#disposed = [];
@@ -298,6 +339,7 @@ export class LRUCache {
298
339
  this.#disposed = undefined;
299
340
  }
300
341
  this.#hasDispose = !!this.#dispose;
342
+ this.#hasOnInsert = !!this.#onInsert;
301
343
  this.#hasDisposeAfter = !!this.#disposeAfter;
302
344
  this.noDisposeOnSet = !!noDisposeOnSet;
303
345
  this.noUpdateTTL = !!noUpdateTTL;
@@ -322,9 +364,7 @@ export class LRUCache {
322
364
  this.updateAgeOnGet = !!updateAgeOnGet;
323
365
  this.updateAgeOnHas = !!updateAgeOnHas;
324
366
  this.ttlResolution =
325
- isPosInt(ttlResolution) || ttlResolution === 0
326
- ? ttlResolution
327
- : 1;
367
+ isPosInt(ttlResolution) || ttlResolution === 0 ? ttlResolution : 1;
328
368
  this.ttlAutopurge = !!ttlAutopurge;
329
369
  this.ttl = ttl || 0;
330
370
  if (this.ttl) {
@@ -348,7 +388,8 @@ export class LRUCache {
348
388
  }
349
389
  }
350
390
  /**
351
- * Return the remaining TTL time for a given entry key
391
+ * Return the number of ms left in the item's TTL. If item is not in cache,
392
+ * returns `0`. Returns `Infinity` if item is in cache without a defined TTL.
352
393
  */
353
394
  getRemainingTTL(key) {
354
395
  return this.#keyMap.has(key) ? Infinity : 0;
@@ -358,41 +399,68 @@ export class LRUCache {
358
399
  const starts = new ZeroArray(this.#max);
359
400
  this.#ttls = ttls;
360
401
  this.#starts = starts;
361
- this.#setItemTTL = (index, ttl, start = perf.now()) => {
402
+ const purgeTimers = this.ttlAutopurge ?
403
+ Array.from({
404
+ length: this.#max,
405
+ })
406
+ : undefined;
407
+ this.#autopurgeTimers = purgeTimers;
408
+ this.#setItemTTL = (index, ttl, start = this.#perf.now()) => {
362
409
  starts[index] = ttl !== 0 ? start : 0;
363
410
  ttls[index] = ttl;
364
- if (ttl !== 0 && this.ttlAutopurge) {
365
- const t = setTimeout(() => {
366
- if (this.#isStale(index)) {
367
- this.delete(this.#keyList[index]);
368
- }
369
- }, ttl + 1);
370
- // unref() not supported on all platforms
371
- /* c8 ignore start */
372
- if (t.unref) {
373
- t.unref();
374
- }
375
- /* c8 ignore stop */
376
- }
411
+ setPurgetTimer(index, ttl);
377
412
  };
378
413
  this.#updateItemAge = index => {
379
- starts[index] = ttls[index] !== 0 ? perf.now() : 0;
414
+ starts[index] = ttls[index] !== 0 ? this.#perf.now() : 0;
415
+ setPurgetTimer(index, ttls[index]);
380
416
  };
417
+ // clear out the purge timer if we're setting TTL to 0, and
418
+ // previously had a ttl purge timer running, so it doesn't
419
+ // fire unnecessarily. Don't need to do this if we're not doing
420
+ // autopurge.
421
+ const setPurgetTimer = !this.ttlAutopurge ?
422
+ () => { }
423
+ : (index, ttl) => {
424
+ if (purgeTimers?.[index]) {
425
+ clearTimeout(purgeTimers[index]);
426
+ purgeTimers[index] = undefined;
427
+ }
428
+ if (ttl && ttl !== 0 && purgeTimers) {
429
+ const t = setTimeout(() => {
430
+ if (this.#isStale(index)) {
431
+ this.#delete(this.#keyList[index], 'expire');
432
+ }
433
+ }, ttl + 1);
434
+ // unref() not supported on all platforms
435
+ /* c8 ignore start */
436
+ if (t.unref) {
437
+ t.unref();
438
+ }
439
+ /* c8 ignore stop */
440
+ purgeTimers[index] = t;
441
+ }
442
+ };
381
443
  this.#statusTTL = (status, index) => {
382
444
  if (ttls[index]) {
383
445
  const ttl = ttls[index];
384
446
  const start = starts[index];
447
+ /* c8 ignore start */
448
+ if (!ttl || !start) {
449
+ return;
450
+ }
451
+ /* c8 ignore stop */
385
452
  status.ttl = ttl;
386
453
  status.start = start;
387
454
  status.now = cachedNow || getNow();
388
- status.remainingTTL = status.now + ttl - start;
455
+ const age = status.now - start;
456
+ status.remainingTTL = ttl - age;
389
457
  }
390
458
  };
391
459
  // debounce calls to perf.now() to 1s so we're not hitting
392
460
  // that costly call repeatedly.
393
461
  let cachedNow = 0;
394
462
  const getNow = () => {
395
- const n = perf.now();
463
+ const n = this.#perf.now();
396
464
  if (this.ttlResolution > 0) {
397
465
  cachedNow = n;
398
466
  const t = setTimeout(() => (cachedNow = 0), this.ttlResolution);
@@ -410,14 +478,18 @@ export class LRUCache {
410
478
  if (index === undefined) {
411
479
  return 0;
412
480
  }
413
- return ttls[index] === 0 || starts[index] === 0
414
- ? Infinity
415
- : starts[index] + ttls[index] - (cachedNow || getNow());
481
+ const ttl = ttls[index];
482
+ const start = starts[index];
483
+ if (!ttl || !start) {
484
+ return Infinity;
485
+ }
486
+ const age = (cachedNow || getNow()) - start;
487
+ return ttl - age;
416
488
  };
417
489
  this.#isStale = index => {
418
- return (ttls[index] !== 0 &&
419
- starts[index] !== 0 &&
420
- (cachedNow || getNow()) - starts[index] > ttls[index]);
490
+ const s = starts[index];
491
+ const t = ttls[index];
492
+ return !!t && !!s && (cachedNow || getNow()) - s > t;
421
493
  };
422
494
  }
423
495
  // conditionally set private methods related to TTL
@@ -483,10 +555,7 @@ export class LRUCache {
483
555
  };
484
556
  *#indexes({ allowStale = this.allowStale } = {}) {
485
557
  if (this.#size) {
486
- for (let i = this.#tail; true;) {
487
- if (!this.#isValidIndex(i)) {
488
- break;
489
- }
558
+ for (let i = this.#tail; this.#isValidIndex(i);) {
490
559
  if (allowStale || !this.#isStale(i)) {
491
560
  yield i;
492
561
  }
@@ -501,10 +570,7 @@ export class LRUCache {
501
570
  }
502
571
  *#rindexes({ allowStale = this.allowStale } = {}) {
503
572
  if (this.#size) {
504
- for (let i = this.#head; true;) {
505
- if (!this.#isValidIndex(i)) {
506
- break;
507
- }
573
+ for (let i = this.#head; this.#isValidIndex(i);) {
508
574
  if (allowStale || !this.#isStale(i)) {
509
575
  yield i;
510
576
  }
@@ -556,8 +622,7 @@ export class LRUCache {
556
622
  *keys() {
557
623
  for (const i of this.#indexes()) {
558
624
  const k = this.#keyList[i];
559
- if (k !== undefined &&
560
- !this.#isBackgroundFetch(this.#valList[i])) {
625
+ if (k !== undefined && !this.#isBackgroundFetch(this.#valList[i])) {
561
626
  yield k;
562
627
  }
563
628
  }
@@ -571,8 +636,7 @@ export class LRUCache {
571
636
  *rkeys() {
572
637
  for (const i of this.#rindexes()) {
573
638
  const k = this.#keyList[i];
574
- if (k !== undefined &&
575
- !this.#isBackgroundFetch(this.#valList[i])) {
639
+ if (k !== undefined && !this.#isBackgroundFetch(this.#valList[i])) {
576
640
  yield k;
577
641
  }
578
642
  }
@@ -584,8 +648,7 @@ export class LRUCache {
584
648
  *values() {
585
649
  for (const i of this.#indexes()) {
586
650
  const v = this.#valList[i];
587
- if (v !== undefined &&
588
- !this.#isBackgroundFetch(this.#valList[i])) {
651
+ if (v !== undefined && !this.#isBackgroundFetch(this.#valList[i])) {
589
652
  yield this.#valList[i];
590
653
  }
591
654
  }
@@ -599,8 +662,7 @@ export class LRUCache {
599
662
  *rvalues() {
600
663
  for (const i of this.#rindexes()) {
601
664
  const v = this.#valList[i];
602
- if (v !== undefined &&
603
- !this.#isBackgroundFetch(this.#valList[i])) {
665
+ if (v !== undefined && !this.#isBackgroundFetch(this.#valList[i])) {
604
666
  yield this.#valList[i];
605
667
  }
606
668
  }
@@ -612,35 +674,42 @@ export class LRUCache {
612
674
  [Symbol.iterator]() {
613
675
  return this.entries();
614
676
  }
677
+ /**
678
+ * A String value that is used in the creation of the default string
679
+ * description of an object. Called by the built-in method
680
+ * `Object.prototype.toString`.
681
+ */
682
+ [Symbol.toStringTag] = 'LRUCache';
615
683
  /**
616
684
  * Find a value for which the supplied fn method returns a truthy value,
617
- * similar to Array.find(). fn is called as fn(value, key, cache).
685
+ * similar to `Array.find()`. fn is called as `fn(value, key, cache)`.
618
686
  */
619
687
  find(fn, getOptions = {}) {
620
688
  for (const i of this.#indexes()) {
621
689
  const v = this.#valList[i];
622
- const value = this.#isBackgroundFetch(v)
623
- ? v.__staleWhileFetching
624
- : v;
690
+ const value = this.#isBackgroundFetch(v) ? v.__staleWhileFetching : v;
625
691
  if (value === undefined)
626
692
  continue;
627
693
  if (fn(value, this.#keyList[i], this)) {
628
- return this.get(this.#keyList[i], getOptions);
694
+ return this.#get(this.#keyList[i], getOptions);
629
695
  }
630
696
  }
631
697
  }
632
698
  /**
633
- * Call the supplied function on each item in the cache, in order from
634
- * most recently used to least recently used. fn is called as
635
- * fn(value, key, cache). Does not update age or recenty of use.
636
- * Does not iterate over stale values.
699
+ * Call the supplied function on each item in the cache, in order from most
700
+ * recently used to least recently used.
701
+ *
702
+ * `fn` is called as `fn(value, key, cache)`.
703
+ *
704
+ * If `thisp` is provided, function will be called in the `this`-context of
705
+ * the provided object, or the cache if no `thisp` object is provided.
706
+ *
707
+ * Does not update age or recenty of use, or iterate over stale values.
637
708
  */
638
709
  forEach(fn, thisp = this) {
639
710
  for (const i of this.#indexes()) {
640
711
  const v = this.#valList[i];
641
- const value = this.#isBackgroundFetch(v)
642
- ? v.__staleWhileFetching
643
- : v;
712
+ const value = this.#isBackgroundFetch(v) ? v.__staleWhileFetching : v;
644
713
  if (value === undefined)
645
714
  continue;
646
715
  fn.call(thisp, value, this.#keyList[i], this);
@@ -653,9 +722,7 @@ export class LRUCache {
653
722
  rforEach(fn, thisp = this) {
654
723
  for (const i of this.#rindexes()) {
655
724
  const v = this.#valList[i];
656
- const value = this.#isBackgroundFetch(v)
657
- ? v.__staleWhileFetching
658
- : v;
725
+ const value = this.#isBackgroundFetch(v) ? v.__staleWhileFetching : v;
659
726
  if (value === undefined)
660
727
  continue;
661
728
  fn.call(thisp, value, this.#keyList[i], this);
@@ -669,24 +736,69 @@ export class LRUCache {
669
736
  let deleted = false;
670
737
  for (const i of this.#rindexes({ allowStale: true })) {
671
738
  if (this.#isStale(i)) {
672
- this.delete(this.#keyList[i]);
739
+ this.#delete(this.#keyList[i], 'expire');
673
740
  deleted = true;
674
741
  }
675
742
  }
676
743
  return deleted;
677
744
  }
745
+ /**
746
+ * Get the extended info about a given entry, to get its value, size, and
747
+ * TTL info simultaneously. Returns `undefined` if the key is not present.
748
+ *
749
+ * Unlike {@link LRUCache#dump}, which is designed to be portable and survive
750
+ * serialization, the `start` value is always the current timestamp, and the
751
+ * `ttl` is a calculated remaining time to live (negative if expired).
752
+ *
753
+ * Always returns stale values, if their info is found in the cache, so be
754
+ * sure to check for expirations (ie, a negative {@link LRUCache.Entry#ttl})
755
+ * if relevant.
756
+ */
757
+ info(key) {
758
+ const i = this.#keyMap.get(key);
759
+ if (i === undefined)
760
+ return undefined;
761
+ const v = this.#valList[i];
762
+ /* c8 ignore start - this isn't tested for the info function,
763
+ * but it's the same logic as found in other places. */
764
+ const value = this.#isBackgroundFetch(v) ? v.__staleWhileFetching : v;
765
+ if (value === undefined)
766
+ return undefined;
767
+ /* c8 ignore stop */
768
+ const entry = { value };
769
+ if (this.#ttls && this.#starts) {
770
+ const ttl = this.#ttls[i];
771
+ const start = this.#starts[i];
772
+ if (ttl && start) {
773
+ const remain = ttl - (this.#perf.now() - start);
774
+ entry.ttl = remain;
775
+ entry.start = Date.now();
776
+ }
777
+ }
778
+ if (this.#sizes) {
779
+ entry.size = this.#sizes[i];
780
+ }
781
+ return entry;
782
+ }
678
783
  /**
679
784
  * Return an array of [key, {@link LRUCache.Entry}] tuples which can be
680
- * passed to cache.load()
785
+ * passed to {@link LRUCache#load}.
786
+ *
787
+ * The `start` fields are calculated relative to a portable `Date.now()`
788
+ * timestamp, even if `performance.now()` is available.
789
+ *
790
+ * Stale entries are always included in the `dump`, even if
791
+ * {@link LRUCache.OptionsBase.allowStale} is false.
792
+ *
793
+ * Note: this returns an actual array, not a generator, so it can be more
794
+ * easily passed around.
681
795
  */
682
796
  dump() {
683
797
  const arr = [];
684
798
  for (const i of this.#indexes({ allowStale: true })) {
685
799
  const key = this.#keyList[i];
686
800
  const v = this.#valList[i];
687
- const value = this.#isBackgroundFetch(v)
688
- ? v.__staleWhileFetching
689
- : v;
801
+ const value = this.#isBackgroundFetch(v) ? v.__staleWhileFetching : v;
690
802
  if (value === undefined || key === undefined)
691
803
  continue;
692
804
  const entry = { value };
@@ -694,7 +806,7 @@ export class LRUCache {
694
806
  entry.ttl = this.#ttls[i];
695
807
  // always dump the start relative to a portable timestamp
696
808
  // it's ok for this to be a bit slow, it's a rare operation.
697
- const age = perf.now() - this.#starts[i];
809
+ const age = this.#perf.now() - this.#starts[i];
698
810
  entry.start = Math.floor(Date.now() - age);
699
811
  }
700
812
  if (this.#sizes) {
@@ -706,8 +818,12 @@ export class LRUCache {
706
818
  }
707
819
  /**
708
820
  * Reset the cache and load in the items in entries in the order listed.
709
- * Note that the shape of the resulting cache may be different if the
710
- * same options are not used in both caches.
821
+ *
822
+ * The shape of the resulting cache may be different if the same options are
823
+ * not used in both caches.
824
+ *
825
+ * The `start` fields are assumed to be calculated relative to a portable
826
+ * `Date.now()` timestamp, even if `performance.now()` is available.
711
827
  */
712
828
  load(arr) {
713
829
  this.clear();
@@ -720,38 +836,85 @@ export class LRUCache {
720
836
  //
721
837
  // it's ok for this to be a bit slow, it's a rare operation.
722
838
  const age = Date.now() - entry.start;
723
- entry.start = perf.now() - age;
839
+ entry.start = this.#perf.now() - age;
724
840
  }
725
- this.set(key, entry.value, entry);
841
+ this.#set(key, entry.value, entry);
726
842
  }
727
843
  }
728
844
  /**
729
845
  * Add a value to the cache.
846
+ *
847
+ * Note: if `undefined` is specified as a value, this is an alias for
848
+ * {@link LRUCache#delete}
849
+ *
850
+ * Fields on the {@link LRUCache.SetOptions} options param will override
851
+ * their corresponding values in the constructor options for the scope
852
+ * of this single `set()` operation.
853
+ *
854
+ * If `start` is provided, then that will set the effective start
855
+ * time for the TTL calculation. Note that this must be a previous
856
+ * value of `performance.now()` if supported, or a previous value of
857
+ * `Date.now()` if not.
858
+ *
859
+ * Options object may also include `size`, which will prevent
860
+ * calling the `sizeCalculation` function and just use the specified
861
+ * number if it is a positive integer, and `noDisposeOnSet` which
862
+ * will prevent calling a `dispose` function in the case of
863
+ * overwrites.
864
+ *
865
+ * If the `size` (or return value of `sizeCalculation`) for a given
866
+ * entry is greater than `maxEntrySize`, then the item will not be
867
+ * added to the cache.
868
+ *
869
+ * Will update the recency of the entry.
870
+ *
871
+ * If the value is `undefined`, then this is an alias for
872
+ * `cache.delete(key)`. `undefined` is never stored in the cache.
730
873
  */
731
874
  set(k, v, setOptions = {}) {
875
+ const { status = metrics.hasSubscribers ? {} : undefined } = setOptions;
876
+ setOptions.status = status;
877
+ if (status) {
878
+ status.op = 'set';
879
+ status.key = k;
880
+ if (v !== undefined)
881
+ status.value = v;
882
+ }
883
+ const result = this.#set(k, v, setOptions);
884
+ if (status && metrics.hasSubscribers) {
885
+ metrics.publish(status);
886
+ }
887
+ return result;
888
+ }
889
+ #set(k, v, setOptions = {}) {
732
890
  const { ttl = this.ttl, start, noDisposeOnSet = this.noDisposeOnSet, sizeCalculation = this.sizeCalculation, status, } = setOptions;
891
+ if (v === undefined) {
892
+ if (status)
893
+ status.set = 'deleted';
894
+ this.delete(k);
895
+ return this;
896
+ }
733
897
  let { noUpdateTTL = this.noUpdateTTL } = setOptions;
734
- const size = this.#requireSize(k, v, setOptions.size || 0, sizeCalculation);
898
+ if (status && !this.#isBackgroundFetch(v))
899
+ status.value = v;
900
+ const size = this.#requireSize(k, v, setOptions.size || 0, sizeCalculation, status);
735
901
  // if the item doesn't fit, don't do anything
736
902
  // NB: maxEntrySize set to maxSize by default
737
903
  if (this.maxEntrySize && size > this.maxEntrySize) {
904
+ // have to delete, in case something is there already.
905
+ this.#delete(k, 'set');
738
906
  if (status) {
739
907
  status.set = 'miss';
740
908
  status.maxEntrySizeExceeded = true;
741
909
  }
742
- // have to delete, in case something is there already.
743
- this.delete(k);
744
910
  return this;
745
911
  }
746
912
  let index = this.#size === 0 ? undefined : this.#keyMap.get(k);
747
913
  if (index === undefined) {
748
914
  // addition
749
- index = (this.#size === 0
750
- ? this.#tail
751
- : this.#free.length !== 0
752
- ? this.#free.pop()
753
- : this.#size === this.#max
754
- ? this.#evict(false)
915
+ index = (this.#size === 0 ? this.#tail
916
+ : this.#free.length !== 0 ? this.#free.pop()
917
+ : this.#size === this.#max ? this.#evict(false)
755
918
  : this.#size);
756
919
  this.#keyList[index] = k;
757
920
  this.#valList[index] = v;
@@ -764,6 +927,9 @@ export class LRUCache {
764
927
  if (status)
765
928
  status.set = 'add';
766
929
  noUpdateTTL = false;
930
+ if (this.#hasOnInsert) {
931
+ this.#onInsert?.(v, k, 'add');
932
+ }
767
933
  }
768
934
  else {
769
935
  // update
@@ -772,6 +938,15 @@ export class LRUCache {
772
938
  if (v !== oldVal) {
773
939
  if (this.#hasFetchMethod && this.#isBackgroundFetch(oldVal)) {
774
940
  oldVal.__abortController.abort(new Error('replaced'));
941
+ const { __staleWhileFetching: s } = oldVal;
942
+ if (s !== undefined && !noDisposeOnSet) {
943
+ if (this.#hasDispose) {
944
+ this.#dispose?.(s, k, 'set');
945
+ }
946
+ if (this.#hasDisposeAfter) {
947
+ this.#disposed?.push([s, k, 'set']);
948
+ }
949
+ }
775
950
  }
776
951
  else if (!noDisposeOnSet) {
777
952
  if (this.#hasDispose) {
@@ -786,8 +961,8 @@ export class LRUCache {
786
961
  this.#valList[index] = v;
787
962
  if (status) {
788
963
  status.set = 'replace';
789
- const oldValue = oldVal && this.#isBackgroundFetch(oldVal)
790
- ? oldVal.__staleWhileFetching
964
+ const oldValue = oldVal && this.#isBackgroundFetch(oldVal) ?
965
+ oldVal.__staleWhileFetching
791
966
  : oldVal;
792
967
  if (oldValue !== undefined)
793
968
  status.oldValue = oldValue;
@@ -796,6 +971,9 @@ export class LRUCache {
796
971
  else if (status) {
797
972
  status.set = 'update';
798
973
  }
974
+ if (this.#hasOnInsert) {
975
+ this.onInsert?.(v, k, v === oldVal ? 'update' : 'replace');
976
+ }
799
977
  }
800
978
  if (ttl !== 0 && !this.#ttls) {
801
979
  this.#initializeTTLTracking();
@@ -861,6 +1039,10 @@ export class LRUCache {
861
1039
  }
862
1040
  }
863
1041
  this.#removeItemSize(head);
1042
+ if (this.#autopurgeTimers?.[head]) {
1043
+ clearTimeout(this.#autopurgeTimers[head]);
1044
+ this.#autopurgeTimers[head] = undefined;
1045
+ }
864
1046
  // if we aren't about to use the index, then null these out
865
1047
  if (free) {
866
1048
  this.#keyList[head] = undefined;
@@ -883,10 +1065,30 @@ export class LRUCache {
883
1065
  * Will return false if the item is stale, even though it is technically
884
1066
  * in the cache.
885
1067
  *
1068
+ * Check if a key is in the cache, without updating the recency of
1069
+ * use. Age is updated if {@link LRUCache.OptionsBase.updateAgeOnHas} is set
1070
+ * to `true` in either the options or the constructor.
1071
+ *
1072
+ * Will return `false` if the item is stale, even though it is technically in
1073
+ * the cache. The difference can be determined (if it matters) by using a
1074
+ * `status` argument, and inspecting the `has` field.
1075
+ *
886
1076
  * Will not update item age unless
887
1077
  * {@link LRUCache.OptionsBase.updateAgeOnHas} is set.
888
1078
  */
889
1079
  has(k, hasOptions = {}) {
1080
+ const { status = metrics.hasSubscribers ? {} : undefined } = hasOptions;
1081
+ hasOptions.status = status;
1082
+ if (status) {
1083
+ status.op = 'has';
1084
+ status.key = k;
1085
+ }
1086
+ const result = this.#has(k, hasOptions);
1087
+ if (metrics.hasSubscribers)
1088
+ metrics.publish(status);
1089
+ return result;
1090
+ }
1091
+ #has(k, hasOptions = {}) {
890
1092
  const { updateAgeOnHas = this.updateAgeOnHas, status } = hasOptions;
891
1093
  const index = this.#keyMap.get(k);
892
1094
  if (index !== undefined) {
@@ -923,14 +1125,38 @@ export class LRUCache {
923
1125
  * {@link LRUCache.OptionsBase.allowStale} is set.
924
1126
  */
925
1127
  peek(k, peekOptions = {}) {
926
- const { allowStale = this.allowStale } = peekOptions;
1128
+ const { status = hasSubscribers() ? {} : undefined } = peekOptions;
1129
+ if (status) {
1130
+ status.op = 'peek';
1131
+ status.key = k;
1132
+ }
1133
+ peekOptions.status = status;
1134
+ const result = this.#peek(k, peekOptions);
1135
+ if (metrics.hasSubscribers) {
1136
+ metrics.publish(status);
1137
+ }
1138
+ return result;
1139
+ }
1140
+ #peek(k, peekOptions) {
1141
+ const { status, allowStale = this.allowStale } = peekOptions;
927
1142
  const index = this.#keyMap.get(k);
928
- if (index !== undefined &&
929
- (allowStale || !this.#isStale(index))) {
930
- const v = this.#valList[index];
931
- // either stale and allowed, or forcing a refresh of non-stale value
932
- return this.#isBackgroundFetch(v) ? v.__staleWhileFetching : v;
1143
+ if (index === undefined || (!allowStale && this.#isStale(index))) {
1144
+ if (status)
1145
+ status.peek = index === undefined ? 'miss' : 'stale';
1146
+ return undefined;
1147
+ }
1148
+ const v = this.#valList[index];
1149
+ const val = this.#isBackgroundFetch(v) ? v.__staleWhileFetching : v;
1150
+ if (status) {
1151
+ if (val !== undefined) {
1152
+ status.peek = 'hit';
1153
+ status.value = val;
1154
+ }
1155
+ else {
1156
+ status.peek = 'miss';
1157
+ }
933
1158
  }
1159
+ return val;
934
1160
  }
935
1161
  #backgroundFetch(k, index, options, context) {
936
1162
  const v = index === undefined ? undefined : this.#valList[index];
@@ -951,6 +1177,8 @@ export class LRUCache {
951
1177
  const cb = (v, updateCache = false) => {
952
1178
  const { aborted } = ac.signal;
953
1179
  const ignoreAbort = options.ignoreFetchAbort && v !== undefined;
1180
+ const proceed = options.ignoreFetchAbort ||
1181
+ !!(options.allowStaleOnFetchAbort && v !== undefined);
954
1182
  if (options.status) {
955
1183
  if (aborted && !updateCache) {
956
1184
  options.status.fetchAborted = true;
@@ -963,23 +1191,27 @@ export class LRUCache {
963
1191
  }
964
1192
  }
965
1193
  if (aborted && !ignoreAbort && !updateCache) {
966
- return fetchFail(ac.signal.reason);
1194
+ return fetchFail(ac.signal.reason, proceed);
967
1195
  }
968
1196
  // either we didn't abort, and are still here, or we did, and ignored
969
1197
  const bf = p;
970
- if (this.#valList[index] === p) {
1198
+ // if nothing else has been written there but we're set to update the
1199
+ // cache and ignore the abort, or if it's still pending on this specific
1200
+ // background request, then write it to the cache.
1201
+ const vl = this.#valList[index];
1202
+ if (vl === p || (vl === undefined && ignoreAbort && updateCache)) {
971
1203
  if (v === undefined) {
972
- if (bf.__staleWhileFetching) {
1204
+ if (bf.__staleWhileFetching !== undefined) {
973
1205
  this.#valList[index] = bf.__staleWhileFetching;
974
1206
  }
975
1207
  else {
976
- this.delete(k);
1208
+ this.#delete(k, 'fetch');
977
1209
  }
978
1210
  }
979
1211
  else {
980
1212
  if (options.status)
981
1213
  options.status.fetchUpdated = true;
982
- this.set(k, v, fetchOpts.options);
1214
+ this.#set(k, v, fetchOpts.options);
983
1215
  }
984
1216
  }
985
1217
  return v;
@@ -989,9 +1221,10 @@ export class LRUCache {
989
1221
  options.status.fetchRejected = true;
990
1222
  options.status.fetchError = er;
991
1223
  }
992
- return fetchFail(er);
1224
+ // do not pass go, do not collect $200
1225
+ return fetchFail(er, false);
993
1226
  };
994
- const fetchFail = (er) => {
1227
+ const fetchFail = (er, proceed) => {
995
1228
  const { aborted } = ac.signal;
996
1229
  const allowStaleAborted = aborted && options.allowStaleOnFetchAbort;
997
1230
  const allowStale = allowStaleAborted || options.allowStaleOnFetchRejection;
@@ -1000,9 +1233,9 @@ export class LRUCache {
1000
1233
  if (this.#valList[index] === p) {
1001
1234
  // if we allow stale on fetch rejections, then we need to ensure that
1002
1235
  // the stale value is not removed from the cache when the fetch fails.
1003
- const del = !noDelete || bf.__staleWhileFetching === undefined;
1236
+ const del = !noDelete || (!proceed && bf.__staleWhileFetching === undefined);
1004
1237
  if (del) {
1005
- this.delete(k);
1238
+ this.#delete(k, 'fetch');
1006
1239
  }
1007
1240
  else if (!allowStaleAborted) {
1008
1241
  // still replace the *promise* with the stale value,
@@ -1025,15 +1258,14 @@ export class LRUCache {
1025
1258
  const pcall = (res, rej) => {
1026
1259
  const fmp = this.#fetchMethod?.(k, v, fetchOpts);
1027
1260
  if (fmp && fmp instanceof Promise) {
1028
- fmp.then(v => res(v), rej);
1261
+ fmp.then(v => res(v === undefined ? undefined : v), rej);
1029
1262
  }
1030
1263
  // ignored, we go until we finish, regardless.
1031
1264
  // defer check until we are actually aborting,
1032
1265
  // so fetchMethod can override.
1033
1266
  ac.signal.addEventListener('abort', () => {
1034
- if (!options.ignoreFetchAbort ||
1035
- options.allowStaleOnFetchAbort) {
1036
- res();
1267
+ if (!options.ignoreFetchAbort || options.allowStaleOnFetchAbort) {
1268
+ res(undefined);
1037
1269
  // when it eventually resolves, update the cache.
1038
1270
  if (options.allowStaleOnFetchAbort) {
1039
1271
  res = v => cb(v, true);
@@ -1051,7 +1283,7 @@ export class LRUCache {
1051
1283
  });
1052
1284
  if (index === undefined) {
1053
1285
  // internal, don't expose status.
1054
- this.set(k, bf, { ...fetchOpts.options, status: undefined });
1286
+ this.#set(k, bf, { ...fetchOpts.options, status: undefined });
1055
1287
  index = this.#keyMap.get(k);
1056
1288
  }
1057
1289
  else {
@@ -1068,7 +1300,23 @@ export class LRUCache {
1068
1300
  b.hasOwnProperty('__staleWhileFetching') &&
1069
1301
  b.__abortController instanceof AbortController);
1070
1302
  }
1071
- async fetch(k, fetchOptions = {}) {
1303
+ fetch(k, fetchOptions = {}) {
1304
+ const ths = tracing.hasSubscribers;
1305
+ const { status = hasSubscribers() ? {} : undefined } = fetchOptions;
1306
+ fetchOptions.status = status;
1307
+ if (status && fetchOptions.context) {
1308
+ status.context = fetchOptions.context;
1309
+ }
1310
+ const p = this.#fetch(k, fetchOptions);
1311
+ if (status && hasSubscribers()) {
1312
+ if (ths) {
1313
+ status.trace = true;
1314
+ tracing.tracePromise(() => p, status).catch(() => { });
1315
+ }
1316
+ }
1317
+ return p;
1318
+ }
1319
+ async #fetch(k, fetchOptions = {}) {
1072
1320
  const {
1073
1321
  // get options
1074
1322
  allowStale = this.allowStale, updateAgeOnGet = this.updateAgeOnGet, noDeleteOnStaleGet = this.noDeleteOnStaleGet,
@@ -1076,10 +1324,16 @@ export class LRUCache {
1076
1324
  ttl = this.ttl, noDisposeOnSet = this.noDisposeOnSet, size = 0, sizeCalculation = this.sizeCalculation, noUpdateTTL = this.noUpdateTTL,
1077
1325
  // fetch exclusive options
1078
1326
  noDeleteOnFetchRejection = this.noDeleteOnFetchRejection, allowStaleOnFetchRejection = this.allowStaleOnFetchRejection, ignoreFetchAbort = this.ignoreFetchAbort, allowStaleOnFetchAbort = this.allowStaleOnFetchAbort, context, forceRefresh = false, status, signal, } = fetchOptions;
1327
+ if (status) {
1328
+ status.op = 'fetch';
1329
+ status.key = k;
1330
+ if (forceRefresh)
1331
+ status.forceRefresh = true;
1332
+ }
1079
1333
  if (!this.#hasFetchMethod) {
1080
1334
  if (status)
1081
1335
  status.fetch = 'get';
1082
- return this.get(k, {
1336
+ return this.#get(k, {
1083
1337
  allowStale,
1084
1338
  updateAgeOnGet,
1085
1339
  noDeleteOnStaleGet,
@@ -1148,6 +1402,71 @@ export class LRUCache {
1148
1402
  return staleVal ? p.__staleWhileFetching : (p.__returned = p);
1149
1403
  }
1150
1404
  }
1405
+ forceFetch(k, fetchOptions = {}) {
1406
+ const ths = tracing.hasSubscribers;
1407
+ const { status = hasSubscribers() ? {} : undefined } = fetchOptions;
1408
+ fetchOptions.status = status;
1409
+ if (status && fetchOptions.context) {
1410
+ status.context = fetchOptions.context;
1411
+ }
1412
+ const p = this.#forceFetch(k, fetchOptions);
1413
+ if (status && hasSubscribers()) {
1414
+ if (ths) {
1415
+ status.trace = true;
1416
+ tracing.tracePromise(() => p, status).catch(() => { });
1417
+ }
1418
+ }
1419
+ return p;
1420
+ }
1421
+ async #forceFetch(k, fetchOptions = {}) {
1422
+ const v = await this.#fetch(k, fetchOptions);
1423
+ if (v === undefined)
1424
+ throw new Error('fetch() returned undefined');
1425
+ return v;
1426
+ }
1427
+ memo(k, memoOptions = {}) {
1428
+ const { status = metrics.hasSubscribers ? {} : undefined } = memoOptions;
1429
+ memoOptions.status = status;
1430
+ if (status) {
1431
+ status.op = 'memo';
1432
+ status.key = k;
1433
+ if (memoOptions.context) {
1434
+ status.context = memoOptions.context;
1435
+ }
1436
+ }
1437
+ const result = this.#memo(k, memoOptions);
1438
+ if (status)
1439
+ status.value = result;
1440
+ if (metrics.hasSubscribers)
1441
+ metrics.publish(status);
1442
+ return result;
1443
+ }
1444
+ #memo(k, memoOptions = {}) {
1445
+ const memoMethod = this.#memoMethod;
1446
+ if (!memoMethod) {
1447
+ throw new Error('no memoMethod provided to constructor');
1448
+ }
1449
+ const { context, status, forceRefresh, ...options } = memoOptions;
1450
+ if (status && forceRefresh)
1451
+ status.forceRefresh = true;
1452
+ const v = this.#get(k, options);
1453
+ const refresh = forceRefresh || v === undefined;
1454
+ if (status) {
1455
+ status.memo = refresh ? 'miss' : 'hit';
1456
+ if (!refresh)
1457
+ status.value = v;
1458
+ }
1459
+ if (!refresh)
1460
+ return v;
1461
+ const vv = memoMethod(k, v, {
1462
+ options,
1463
+ context,
1464
+ });
1465
+ if (status)
1466
+ status.value = vv;
1467
+ this.#set(k, vv, options);
1468
+ return vv;
1469
+ }
1151
1470
  /**
1152
1471
  * Return a value from the cache. Will update the recency of the cache
1153
1472
  * entry found.
@@ -1155,55 +1474,70 @@ export class LRUCache {
1155
1474
  * If the key is not found, get() will return `undefined`.
1156
1475
  */
1157
1476
  get(k, getOptions = {}) {
1477
+ const { status = metrics.hasSubscribers ? {} : undefined } = getOptions;
1478
+ getOptions.status = status;
1479
+ if (status) {
1480
+ status.op = 'get';
1481
+ status.key = k;
1482
+ }
1483
+ const result = this.#get(k, getOptions);
1484
+ if (status) {
1485
+ if (result !== undefined)
1486
+ status.value = result;
1487
+ if (metrics.hasSubscribers)
1488
+ metrics.publish(status);
1489
+ }
1490
+ return result;
1491
+ }
1492
+ #get(k, getOptions = {}) {
1158
1493
  const { allowStale = this.allowStale, updateAgeOnGet = this.updateAgeOnGet, noDeleteOnStaleGet = this.noDeleteOnStaleGet, status, } = getOptions;
1159
1494
  const index = this.#keyMap.get(k);
1160
- if (index !== undefined) {
1161
- const value = this.#valList[index];
1162
- const fetching = this.#isBackgroundFetch(value);
1495
+ if (index === undefined) {
1163
1496
  if (status)
1164
- this.#statusTTL(status, index);
1165
- if (this.#isStale(index)) {
1497
+ status.get = 'miss';
1498
+ return undefined;
1499
+ }
1500
+ const value = this.#valList[index];
1501
+ const fetching = this.#isBackgroundFetch(value);
1502
+ if (status)
1503
+ this.#statusTTL(status, index);
1504
+ if (this.#isStale(index)) {
1505
+ // delete only if not an in-flight background fetch
1506
+ if (!fetching) {
1507
+ if (!noDeleteOnStaleGet) {
1508
+ this.#delete(k, 'expire');
1509
+ }
1166
1510
  if (status)
1167
1511
  status.get = 'stale';
1168
- // delete only if not an in-flight background fetch
1169
- if (!fetching) {
1170
- if (!noDeleteOnStaleGet) {
1171
- this.delete(k);
1172
- }
1173
- if (status && allowStale)
1174
- status.returnedStale = true;
1175
- return allowStale ? value : undefined;
1176
- }
1177
- else {
1178
- if (status &&
1179
- allowStale &&
1180
- value.__staleWhileFetching !== undefined) {
1512
+ if (allowStale) {
1513
+ if (status)
1181
1514
  status.returnedStale = true;
1182
- }
1183
- return allowStale ? value.__staleWhileFetching : undefined;
1515
+ return value;
1184
1516
  }
1517
+ return undefined;
1185
1518
  }
1186
- else {
1519
+ if (status)
1520
+ status.get = 'stale-fetching';
1521
+ if (allowStale && value.__staleWhileFetching !== undefined) {
1187
1522
  if (status)
1188
- status.get = 'hit';
1189
- // if we're currently fetching it, we don't actually have it yet
1190
- // it's not stale, which means this isn't a staleWhileRefetching.
1191
- // If it's not stale, and fetching, AND has a __staleWhileFetching
1192
- // value, then that means the user fetched with {forceRefresh:true},
1193
- // so it's safe to return that value.
1194
- if (fetching) {
1195
- return value.__staleWhileFetching;
1196
- }
1197
- this.#moveToTail(index);
1198
- if (updateAgeOnGet) {
1199
- this.#updateItemAge(index);
1200
- }
1201
- return value;
1523
+ status.returnedStale = true;
1524
+ return value.__staleWhileFetching;
1202
1525
  }
1526
+ return undefined;
1203
1527
  }
1204
- else if (status) {
1205
- status.get = 'miss';
1528
+ // not stale
1529
+ if (status)
1530
+ status.get = fetching ? 'fetching' : 'hit';
1531
+ // if we're currently fetching it, we don't actually have it yet
1532
+ // it's not stale, which means this isn't a staleWhileRefetching.
1533
+ // If it's not stale, and fetching, AND has a __staleWhileFetching
1534
+ // value, then that means the user fetched with {forceRefresh:true},
1535
+ // so it's safe to return that value.
1536
+ this.#moveToTail(index);
1537
+ if (updateAgeOnGet) {
1538
+ this.#updateItemAge(index);
1206
1539
  }
1540
+ return fetching ? value.__staleWhileFetching : value;
1207
1541
  }
1208
1542
  #connect(p, n) {
1209
1543
  this.#prev[n] = p;
@@ -1231,16 +1565,31 @@ export class LRUCache {
1231
1565
  }
1232
1566
  /**
1233
1567
  * Deletes a key out of the cache.
1568
+ *
1234
1569
  * Returns true if the key was deleted, false otherwise.
1235
1570
  */
1236
1571
  delete(k) {
1572
+ return this.#delete(k, 'delete');
1573
+ }
1574
+ #delete(k, reason) {
1575
+ if (metrics.hasSubscribers) {
1576
+ metrics.publish({
1577
+ op: 'delete',
1578
+ delete: reason,
1579
+ key: k,
1580
+ });
1581
+ }
1237
1582
  let deleted = false;
1238
1583
  if (this.#size !== 0) {
1239
1584
  const index = this.#keyMap.get(k);
1240
1585
  if (index !== undefined) {
1586
+ if (this.#autopurgeTimers?.[index]) {
1587
+ clearTimeout(this.#autopurgeTimers?.[index]);
1588
+ this.#autopurgeTimers[index] = undefined;
1589
+ }
1241
1590
  deleted = true;
1242
1591
  if (this.#size === 1) {
1243
- this.clear();
1592
+ this.#clear(reason);
1244
1593
  }
1245
1594
  else {
1246
1595
  this.#removeItemSize(index);
@@ -1250,10 +1599,10 @@ export class LRUCache {
1250
1599
  }
1251
1600
  else if (this.#hasDispose || this.#hasDisposeAfter) {
1252
1601
  if (this.#hasDispose) {
1253
- this.#dispose?.(v, k, 'delete');
1602
+ this.#dispose?.(v, k, reason);
1254
1603
  }
1255
1604
  if (this.#hasDisposeAfter) {
1256
- this.#disposed?.push([v, k, 'delete']);
1605
+ this.#disposed?.push([v, k, reason]);
1257
1606
  }
1258
1607
  }
1259
1608
  this.#keyMap.delete(k);
@@ -1266,8 +1615,10 @@ export class LRUCache {
1266
1615
  this.#head = this.#next[index];
1267
1616
  }
1268
1617
  else {
1269
- this.#next[this.#prev[index]] = this.#next[index];
1270
- this.#prev[this.#next[index]] = this.#prev[index];
1618
+ const pi = this.#prev[index];
1619
+ this.#next[pi] = this.#next[index];
1620
+ const ni = this.#next[index];
1621
+ this.#prev[ni] = this.#prev[index];
1271
1622
  }
1272
1623
  this.#size--;
1273
1624
  this.#free.push(index);
@@ -1287,6 +1638,9 @@ export class LRUCache {
1287
1638
  * Clear the cache entirely, throwing away all values.
1288
1639
  */
1289
1640
  clear() {
1641
+ return this.#clear('delete');
1642
+ }
1643
+ #clear(reason) {
1290
1644
  for (const index of this.#rindexes({ allowStale: true })) {
1291
1645
  const v = this.#valList[index];
1292
1646
  if (this.#isBackgroundFetch(v)) {
@@ -1295,10 +1649,10 @@ export class LRUCache {
1295
1649
  else {
1296
1650
  const k = this.#keyList[index];
1297
1651
  if (this.#hasDispose) {
1298
- this.#dispose?.(v, k, 'delete');
1652
+ this.#dispose?.(v, k, reason);
1299
1653
  }
1300
1654
  if (this.#hasDisposeAfter) {
1301
- this.#disposed?.push([v, k, 'delete']);
1655
+ this.#disposed?.push([v, k, reason]);
1302
1656
  }
1303
1657
  }
1304
1658
  }
@@ -1308,6 +1662,11 @@ export class LRUCache {
1308
1662
  if (this.#ttls && this.#starts) {
1309
1663
  this.#ttls.fill(0);
1310
1664
  this.#starts.fill(0);
1665
+ for (const t of this.#autopurgeTimers ?? []) {
1666
+ if (t !== undefined)
1667
+ clearTimeout(t);
1668
+ }
1669
+ this.#autopurgeTimers?.fill(undefined);
1311
1670
  }
1312
1671
  if (this.#sizes) {
1313
1672
  this.#sizes.fill(0);
@@ -1326,5 +1685,4 @@ export class LRUCache {
1326
1685
  }
1327
1686
  }
1328
1687
  }
1329
- export default LRUCache;
1330
1688
  //# sourceMappingURL=index.js.map