@nocobase/plugin-workflow 2.1.0-beta.15 → 2.1.0-beta.17

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 (125) hide show
  1. package/dist/client/214.7e602cfe7a8251b8.js +10 -0
  2. package/dist/client/261.7722d7400942730e.js +10 -0
  3. package/dist/client/67.f904ef4520868b8a.js +10 -0
  4. package/dist/client/964.6251d37b35710747.js +10 -0
  5. package/dist/client/index.js +1 -1
  6. package/dist/client/nodes/create.d.ts +10 -0
  7. package/dist/client/nodes/destroy.d.ts +10 -0
  8. package/dist/client/nodes/index.d.ts +3 -0
  9. package/dist/client/nodes/query.d.ts +10 -0
  10. package/dist/client/nodes/update.d.ts +10 -0
  11. package/dist/client/triggers/collection.d.ts +14 -1
  12. package/dist/client/triggers/index.d.ts +4 -0
  13. package/dist/client/triggers/schedule/constants.d.ts +4 -0
  14. package/dist/client/triggers/schedule/index.d.ts +15 -0
  15. package/dist/common/collections/jobs.js +7 -0
  16. package/dist/externalVersion.js +12 -12
  17. package/dist/locale/zh-CN.json +1 -0
  18. package/dist/node_modules/cron-parser/lib/parser.js +1 -1
  19. package/dist/node_modules/cron-parser/package.json +1 -1
  20. package/dist/node_modules/joi/dist/joi-browser.min.js +1 -0
  21. package/dist/node_modules/joi/lib/annotate.js +175 -0
  22. package/dist/node_modules/joi/lib/base.js +1069 -0
  23. package/dist/node_modules/joi/lib/cache.js +143 -0
  24. package/dist/node_modules/joi/lib/common.js +216 -0
  25. package/dist/node_modules/joi/lib/compile.js +283 -0
  26. package/dist/node_modules/joi/lib/errors.js +271 -0
  27. package/dist/node_modules/joi/lib/extend.js +312 -0
  28. package/dist/node_modules/joi/lib/index.d.ts +2365 -0
  29. package/dist/node_modules/joi/lib/index.js +1 -0
  30. package/dist/node_modules/joi/lib/manifest.js +476 -0
  31. package/dist/node_modules/joi/lib/messages.js +178 -0
  32. package/dist/node_modules/joi/lib/modify.js +267 -0
  33. package/dist/node_modules/joi/lib/ref.js +414 -0
  34. package/dist/node_modules/joi/lib/schemas.js +302 -0
  35. package/dist/node_modules/joi/lib/state.js +166 -0
  36. package/dist/node_modules/joi/lib/template.js +463 -0
  37. package/dist/node_modules/joi/lib/trace.js +346 -0
  38. package/dist/node_modules/joi/lib/types/alternatives.js +364 -0
  39. package/dist/node_modules/joi/lib/types/any.js +174 -0
  40. package/dist/node_modules/joi/lib/types/array.js +809 -0
  41. package/dist/node_modules/joi/lib/types/binary.js +100 -0
  42. package/dist/node_modules/joi/lib/types/boolean.js +150 -0
  43. package/dist/node_modules/joi/lib/types/date.js +233 -0
  44. package/dist/node_modules/joi/lib/types/function.js +93 -0
  45. package/dist/node_modules/joi/lib/types/keys.js +1067 -0
  46. package/dist/node_modules/joi/lib/types/link.js +168 -0
  47. package/dist/node_modules/joi/lib/types/number.js +363 -0
  48. package/dist/node_modules/joi/lib/types/object.js +22 -0
  49. package/dist/node_modules/joi/lib/types/string.js +850 -0
  50. package/dist/node_modules/joi/lib/types/symbol.js +102 -0
  51. package/dist/node_modules/joi/lib/validator.js +750 -0
  52. package/dist/node_modules/joi/lib/values.js +263 -0
  53. package/dist/node_modules/joi/node_modules/@hapi/topo/lib/index.d.ts +60 -0
  54. package/dist/node_modules/joi/node_modules/@hapi/topo/lib/index.js +225 -0
  55. package/dist/node_modules/joi/node_modules/@hapi/topo/package.json +30 -0
  56. package/dist/node_modules/joi/package.json +1 -0
  57. package/dist/node_modules/lru-cache/dist/commonjs/diagnostics-channel.d.ts +5 -0
  58. package/dist/node_modules/lru-cache/dist/commonjs/diagnostics-channel.js +10 -0
  59. package/dist/node_modules/lru-cache/dist/commonjs/index.d.ts +1381 -0
  60. package/dist/node_modules/lru-cache/dist/commonjs/index.js +1692 -0
  61. package/dist/node_modules/lru-cache/dist/commonjs/index.min.js +1 -0
  62. package/dist/node_modules/lru-cache/dist/esm/browser/diagnostics-channel.d.ts +5 -0
  63. package/dist/node_modules/lru-cache/dist/esm/browser/diagnostics-channel.js +4 -0
  64. package/dist/node_modules/lru-cache/dist/esm/browser/index.d.ts +1381 -0
  65. package/dist/node_modules/lru-cache/dist/{mjs → esm/browser}/index.js +537 -179
  66. package/dist/node_modules/lru-cache/dist/esm/browser/index.min.js +2 -0
  67. package/dist/node_modules/lru-cache/dist/esm/diagnostics-channel.d.ts +5 -0
  68. package/dist/node_modules/lru-cache/dist/esm/diagnostics-channel.js +19 -0
  69. package/dist/node_modules/lru-cache/dist/esm/index.d.ts +1381 -0
  70. package/dist/node_modules/lru-cache/dist/{cjs → esm}/index.js +538 -184
  71. package/dist/node_modules/lru-cache/dist/esm/index.min.js +2 -0
  72. package/dist/node_modules/lru-cache/dist/esm/node/diagnostics-channel.d.ts +5 -0
  73. package/dist/node_modules/lru-cache/dist/esm/node/diagnostics-channel.js +7 -0
  74. package/dist/node_modules/lru-cache/dist/esm/node/index.d.ts +1381 -0
  75. package/dist/node_modules/lru-cache/dist/esm/node/index.js +1688 -0
  76. package/dist/node_modules/lru-cache/dist/esm/node/index.min.js +2 -0
  77. package/dist/node_modules/lru-cache/package.json +1 -1
  78. package/dist/node_modules/nodejs-snowflake/nodejs_snowflake.js +1 -1
  79. package/dist/node_modules/nodejs-snowflake/package.json +1 -1
  80. package/dist/server/Plugin.d.ts +1 -0
  81. package/dist/server/Plugin.js +28 -3
  82. package/dist/server/actions/nodes.d.ts +5 -0
  83. package/dist/server/actions/nodes.js +38 -5
  84. package/dist/server/actions/workflows.d.ts +6 -0
  85. package/dist/server/actions/workflows.js +38 -0
  86. package/dist/server/instructions/ConditionInstruction.d.ts +2 -0
  87. package/dist/server/instructions/ConditionInstruction.js +17 -0
  88. package/dist/server/instructions/CreateInstruction.d.ts +3 -0
  89. package/dist/server/instructions/CreateInstruction.js +25 -0
  90. package/dist/server/instructions/DestroyInstruction.d.ts +3 -0
  91. package/dist/server/instructions/DestroyInstruction.js +25 -0
  92. package/dist/server/instructions/EndInstruction.d.ts +2 -0
  93. package/dist/server/instructions/EndInstruction.js +4 -0
  94. package/dist/server/instructions/MultiConditionsInstruction.d.ts +2 -0
  95. package/dist/server/instructions/MultiConditionsInstruction.js +23 -0
  96. package/dist/server/instructions/OutputInstruction.d.ts +2 -0
  97. package/dist/server/instructions/OutputInstruction.js +14 -0
  98. package/dist/server/instructions/QueryInstruction.d.ts +3 -0
  99. package/dist/server/instructions/QueryInstruction.js +30 -0
  100. package/dist/server/instructions/UpdateInstruction.d.ts +3 -0
  101. package/dist/server/instructions/UpdateInstruction.js +27 -0
  102. package/dist/server/instructions/index.d.ts +6 -1
  103. package/dist/server/instructions/index.js +18 -0
  104. package/dist/server/triggers/CollectionTrigger.d.ts +3 -0
  105. package/dist/server/triggers/CollectionTrigger.js +28 -0
  106. package/dist/server/triggers/ScheduleTrigger/index.d.ts +3 -0
  107. package/dist/server/triggers/ScheduleTrigger/index.js +18 -3
  108. package/dist/server/triggers/index.d.ts +3 -0
  109. package/dist/server/triggers/index.js +18 -0
  110. package/dist/server/utils.d.ts +2 -0
  111. package/dist/server/utils.js +22 -2
  112. package/package.json +4 -3
  113. package/dist/client/10.54a0831f49cae121.js +0 -10
  114. package/dist/client/626.7d24ff2a47742f1b.js +0 -10
  115. package/dist/client/771.5e7be8b4d4ac579f.js +0 -10
  116. package/dist/client/781.d46db08dcfead1b0.js +0 -10
  117. package/dist/node_modules/lru-cache/LICENSE +0 -15
  118. package/dist/node_modules/lru-cache/dist/cjs/index-cjs.d.ts +0 -7
  119. package/dist/node_modules/lru-cache/dist/cjs/index-cjs.js +0 -1
  120. package/dist/node_modules/lru-cache/dist/cjs/index.d.ts +0 -807
  121. package/dist/node_modules/lru-cache/dist/cjs/index.min.js +0 -2
  122. package/dist/node_modules/lru-cache/dist/mjs/index.d.ts +0 -807
  123. package/dist/node_modules/lru-cache/dist/mjs/index.min.js +0 -2
  124. /package/dist/node_modules/lru-cache/dist/{cjs → commonjs}/package.json +0 -0
  125. /package/dist/node_modules/lru-cache/dist/{mjs → esm}/package.json +0 -0
@@ -1,26 +1,31 @@
1
- "use strict";
2
1
  /**
3
2
  * @module LRUCache
4
3
  */
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.LRUCache = void 0;
7
- 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' &&
8
7
  performance &&
9
- typeof performance.now === 'function'
10
- ? performance
8
+ typeof performance.now === 'function') ?
9
+ performance
11
10
  : Date;
12
11
  const warned = new Set();
12
+ /* c8 ignore start */
13
+ const PROCESS = (typeof process === 'object' && !!process ?
14
+ process
15
+ : {});
16
+ /* c8 ignore stop */
13
17
  const emitWarning = (msg, type, code, fn) => {
14
- typeof process === 'object' &&
15
- process &&
16
- typeof process.emitWarning === 'function'
17
- ? process.emitWarning(msg, type, code, fn)
18
- : 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
+ }
19
25
  };
20
26
  const shouldWarn = (code) => !warned.has(code);
21
27
  const TYPE = Symbol('type');
22
- const isPosInt = (n) => n && n === Math.floor(n) && n > 0 && isFinite(n);
23
- /* c8 ignore start */
28
+ const isPosInt = (n) => !!n && n === Math.floor(n) && n > 0 && isFinite(n);
24
29
  // This is a little bit ridiculous, tbh.
25
30
  // The maximum array length is 2^32-1 or thereabouts on most JS impls.
26
31
  // And well before that point, you're caching the entire world, I mean,
@@ -29,16 +34,12 @@ const isPosInt = (n) => n && n === Math.floor(n) && n > 0 && isFinite(n);
29
34
  // zeroes at init time is brutal when you get that big.
30
35
  // But why not be complete?
31
36
  // Maybe in the future, these limits will have expanded.
32
- const getUintArray = (max) => !isPosInt(max)
33
- ? null
34
- : max <= Math.pow(2, 8)
35
- ? Uint8Array
36
- : max <= Math.pow(2, 16)
37
- ? Uint16Array
38
- : max <= Math.pow(2, 32)
39
- ? Uint32Array
40
- : max <= Number.MAX_SAFE_INTEGER
41
- ? 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
42
43
  : null;
43
44
  /* c8 ignore stop */
44
45
  class ZeroArray extends Array {
@@ -80,21 +81,34 @@ class Stack {
80
81
  /**
81
82
  * Default export, the thing you're using this module to get.
82
83
  *
83
- * All properties from the options object (with the exception of
84
- * {@link OptionsBase.max} and {@link OptionsBase.maxSize}) are added as
85
- * normal public members. (`max` and `maxBase` are read-only getters.)
86
- * Changing any of these will alter the defaults for subsequent method calls,
87
- * 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.
88
95
  */
89
- class LRUCache {
90
- // properties coming in from the options of these, only max and maxSize
91
- // really *need* to be protected. The rest can be modified, as they just
92
- // set defaults for various methods.
96
+ export class LRUCache {
97
+ // options that cannot be changed without disaster
93
98
  #max;
94
99
  #maxSize;
95
100
  #dispose;
101
+ #onInsert;
96
102
  #disposeAfter;
97
103
  #fetchMethod;
104
+ #memoMethod;
105
+ #perf;
106
+ /**
107
+ * {@link LRUCache.OptionsBase.perf}
108
+ */
109
+ get perf() {
110
+ return this.#perf;
111
+ }
98
112
  /**
99
113
  * {@link LRUCache.OptionsBase.ttl}
100
114
  */
@@ -170,9 +184,11 @@ class LRUCache {
170
184
  #sizes;
171
185
  #starts;
172
186
  #ttls;
187
+ #autopurgeTimers;
173
188
  #hasDispose;
174
189
  #hasFetchMethod;
175
190
  #hasDisposeAfter;
191
+ #hasOnInsert;
176
192
  /**
177
193
  * Do not call this method unless you need to inspect the
178
194
  * inner workings of the cache. If anything returned by this
@@ -187,6 +203,7 @@ class LRUCache {
187
203
  // properties
188
204
  starts: c.#starts,
189
205
  ttls: c.#ttls,
206
+ autopurgeTimers: c.#autopurgeTimers,
190
207
  sizes: c.#sizes,
191
208
  keyMap: c.#keyMap,
192
209
  keyList: c.#keyList,
@@ -240,12 +257,21 @@ class LRUCache {
240
257
  get fetchMethod() {
241
258
  return this.#fetchMethod;
242
259
  }
260
+ get memoMethod() {
261
+ return this.#memoMethod;
262
+ }
243
263
  /**
244
264
  * {@link LRUCache.OptionsBase.dispose} (read-only)
245
265
  */
246
266
  get dispose() {
247
267
  return this.#dispose;
248
268
  }
269
+ /**
270
+ * {@link LRUCache.OptionsBase.onInsert} (read-only)
271
+ */
272
+ get onInsert() {
273
+ return this.#onInsert;
274
+ }
249
275
  /**
250
276
  * {@link LRUCache.OptionsBase.disposeAfter} (read-only)
251
277
  */
@@ -253,7 +279,13 @@ class LRUCache {
253
279
  return this.#disposeAfter;
254
280
  }
255
281
  constructor(options) {
256
- 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;
257
289
  if (max !== 0 && !isPosInt(max)) {
258
290
  throw new TypeError('max option must be a nonnegative integer');
259
291
  }
@@ -273,15 +305,18 @@ class LRUCache {
273
305
  throw new TypeError('sizeCalculation set to non-function');
274
306
  }
275
307
  }
276
- if (fetchMethod !== undefined &&
277
- 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') {
278
313
  throw new TypeError('fetchMethod must be a function if specified');
279
314
  }
280
315
  this.#fetchMethod = fetchMethod;
281
316
  this.#hasFetchMethod = !!fetchMethod;
282
317
  this.#keyMap = new Map();
283
- this.#keyList = new Array(max).fill(undefined);
284
- 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);
285
320
  this.#next = new UintArray(max);
286
321
  this.#prev = new UintArray(max);
287
322
  this.#head = 0;
@@ -292,6 +327,9 @@ class LRUCache {
292
327
  if (typeof dispose === 'function') {
293
328
  this.#dispose = dispose;
294
329
  }
330
+ if (typeof onInsert === 'function') {
331
+ this.#onInsert = onInsert;
332
+ }
295
333
  if (typeof disposeAfter === 'function') {
296
334
  this.#disposeAfter = disposeAfter;
297
335
  this.#disposed = [];
@@ -301,6 +339,7 @@ class LRUCache {
301
339
  this.#disposed = undefined;
302
340
  }
303
341
  this.#hasDispose = !!this.#dispose;
342
+ this.#hasOnInsert = !!this.#onInsert;
304
343
  this.#hasDisposeAfter = !!this.#disposeAfter;
305
344
  this.noDisposeOnSet = !!noDisposeOnSet;
306
345
  this.noUpdateTTL = !!noUpdateTTL;
@@ -325,9 +364,7 @@ class LRUCache {
325
364
  this.updateAgeOnGet = !!updateAgeOnGet;
326
365
  this.updateAgeOnHas = !!updateAgeOnHas;
327
366
  this.ttlResolution =
328
- isPosInt(ttlResolution) || ttlResolution === 0
329
- ? ttlResolution
330
- : 1;
367
+ isPosInt(ttlResolution) || ttlResolution === 0 ? ttlResolution : 1;
331
368
  this.ttlAutopurge = !!ttlAutopurge;
332
369
  this.ttl = ttl || 0;
333
370
  if (this.ttl) {
@@ -351,7 +388,8 @@ class LRUCache {
351
388
  }
352
389
  }
353
390
  /**
354
- * 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.
355
393
  */
356
394
  getRemainingTTL(key) {
357
395
  return this.#keyMap.has(key) ? Infinity : 0;
@@ -361,41 +399,68 @@ class LRUCache {
361
399
  const starts = new ZeroArray(this.#max);
362
400
  this.#ttls = ttls;
363
401
  this.#starts = starts;
364
- 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()) => {
365
409
  starts[index] = ttl !== 0 ? start : 0;
366
410
  ttls[index] = ttl;
367
- if (ttl !== 0 && this.ttlAutopurge) {
368
- const t = setTimeout(() => {
369
- if (this.#isStale(index)) {
370
- this.delete(this.#keyList[index]);
371
- }
372
- }, ttl + 1);
373
- // unref() not supported on all platforms
374
- /* c8 ignore start */
375
- if (t.unref) {
376
- t.unref();
377
- }
378
- /* c8 ignore stop */
379
- }
411
+ setPurgetTimer(index, ttl);
380
412
  };
381
413
  this.#updateItemAge = index => {
382
- starts[index] = ttls[index] !== 0 ? perf.now() : 0;
414
+ starts[index] = ttls[index] !== 0 ? this.#perf.now() : 0;
415
+ setPurgetTimer(index, ttls[index]);
383
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
+ };
384
443
  this.#statusTTL = (status, index) => {
385
444
  if (ttls[index]) {
386
445
  const ttl = ttls[index];
387
446
  const start = starts[index];
447
+ /* c8 ignore start */
448
+ if (!ttl || !start) {
449
+ return;
450
+ }
451
+ /* c8 ignore stop */
388
452
  status.ttl = ttl;
389
453
  status.start = start;
390
454
  status.now = cachedNow || getNow();
391
- status.remainingTTL = status.now + ttl - start;
455
+ const age = status.now - start;
456
+ status.remainingTTL = ttl - age;
392
457
  }
393
458
  };
394
459
  // debounce calls to perf.now() to 1s so we're not hitting
395
460
  // that costly call repeatedly.
396
461
  let cachedNow = 0;
397
462
  const getNow = () => {
398
- const n = perf.now();
463
+ const n = this.#perf.now();
399
464
  if (this.ttlResolution > 0) {
400
465
  cachedNow = n;
401
466
  const t = setTimeout(() => (cachedNow = 0), this.ttlResolution);
@@ -413,14 +478,18 @@ class LRUCache {
413
478
  if (index === undefined) {
414
479
  return 0;
415
480
  }
416
- return ttls[index] === 0 || starts[index] === 0
417
- ? Infinity
418
- : 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;
419
488
  };
420
489
  this.#isStale = index => {
421
- return (ttls[index] !== 0 &&
422
- starts[index] !== 0 &&
423
- (cachedNow || getNow()) - starts[index] > ttls[index]);
490
+ const s = starts[index];
491
+ const t = ttls[index];
492
+ return !!t && !!s && (cachedNow || getNow()) - s > t;
424
493
  };
425
494
  }
426
495
  // conditionally set private methods related to TTL
@@ -486,10 +555,7 @@ class LRUCache {
486
555
  };
487
556
  *#indexes({ allowStale = this.allowStale } = {}) {
488
557
  if (this.#size) {
489
- for (let i = this.#tail; true;) {
490
- if (!this.#isValidIndex(i)) {
491
- break;
492
- }
558
+ for (let i = this.#tail; this.#isValidIndex(i);) {
493
559
  if (allowStale || !this.#isStale(i)) {
494
560
  yield i;
495
561
  }
@@ -504,10 +570,7 @@ class LRUCache {
504
570
  }
505
571
  *#rindexes({ allowStale = this.allowStale } = {}) {
506
572
  if (this.#size) {
507
- for (let i = this.#head; true;) {
508
- if (!this.#isValidIndex(i)) {
509
- break;
510
- }
573
+ for (let i = this.#head; this.#isValidIndex(i);) {
511
574
  if (allowStale || !this.#isStale(i)) {
512
575
  yield i;
513
576
  }
@@ -559,8 +622,7 @@ class LRUCache {
559
622
  *keys() {
560
623
  for (const i of this.#indexes()) {
561
624
  const k = this.#keyList[i];
562
- if (k !== undefined &&
563
- !this.#isBackgroundFetch(this.#valList[i])) {
625
+ if (k !== undefined && !this.#isBackgroundFetch(this.#valList[i])) {
564
626
  yield k;
565
627
  }
566
628
  }
@@ -574,8 +636,7 @@ class LRUCache {
574
636
  *rkeys() {
575
637
  for (const i of this.#rindexes()) {
576
638
  const k = this.#keyList[i];
577
- if (k !== undefined &&
578
- !this.#isBackgroundFetch(this.#valList[i])) {
639
+ if (k !== undefined && !this.#isBackgroundFetch(this.#valList[i])) {
579
640
  yield k;
580
641
  }
581
642
  }
@@ -587,8 +648,7 @@ class LRUCache {
587
648
  *values() {
588
649
  for (const i of this.#indexes()) {
589
650
  const v = this.#valList[i];
590
- if (v !== undefined &&
591
- !this.#isBackgroundFetch(this.#valList[i])) {
651
+ if (v !== undefined && !this.#isBackgroundFetch(this.#valList[i])) {
592
652
  yield this.#valList[i];
593
653
  }
594
654
  }
@@ -602,8 +662,7 @@ class LRUCache {
602
662
  *rvalues() {
603
663
  for (const i of this.#rindexes()) {
604
664
  const v = this.#valList[i];
605
- if (v !== undefined &&
606
- !this.#isBackgroundFetch(this.#valList[i])) {
665
+ if (v !== undefined && !this.#isBackgroundFetch(this.#valList[i])) {
607
666
  yield this.#valList[i];
608
667
  }
609
668
  }
@@ -615,35 +674,42 @@ class LRUCache {
615
674
  [Symbol.iterator]() {
616
675
  return this.entries();
617
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';
618
683
  /**
619
684
  * Find a value for which the supplied fn method returns a truthy value,
620
- * 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)`.
621
686
  */
622
687
  find(fn, getOptions = {}) {
623
688
  for (const i of this.#indexes()) {
624
689
  const v = this.#valList[i];
625
- const value = this.#isBackgroundFetch(v)
626
- ? v.__staleWhileFetching
627
- : v;
690
+ const value = this.#isBackgroundFetch(v) ? v.__staleWhileFetching : v;
628
691
  if (value === undefined)
629
692
  continue;
630
693
  if (fn(value, this.#keyList[i], this)) {
631
- return this.get(this.#keyList[i], getOptions);
694
+ return this.#get(this.#keyList[i], getOptions);
632
695
  }
633
696
  }
634
697
  }
635
698
  /**
636
- * Call the supplied function on each item in the cache, in order from
637
- * most recently used to least recently used. fn is called as
638
- * fn(value, key, cache). Does not update age or recenty of use.
639
- * 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.
640
708
  */
641
709
  forEach(fn, thisp = this) {
642
710
  for (const i of this.#indexes()) {
643
711
  const v = this.#valList[i];
644
- const value = this.#isBackgroundFetch(v)
645
- ? v.__staleWhileFetching
646
- : v;
712
+ const value = this.#isBackgroundFetch(v) ? v.__staleWhileFetching : v;
647
713
  if (value === undefined)
648
714
  continue;
649
715
  fn.call(thisp, value, this.#keyList[i], this);
@@ -656,9 +722,7 @@ class LRUCache {
656
722
  rforEach(fn, thisp = this) {
657
723
  for (const i of this.#rindexes()) {
658
724
  const v = this.#valList[i];
659
- const value = this.#isBackgroundFetch(v)
660
- ? v.__staleWhileFetching
661
- : v;
725
+ const value = this.#isBackgroundFetch(v) ? v.__staleWhileFetching : v;
662
726
  if (value === undefined)
663
727
  continue;
664
728
  fn.call(thisp, value, this.#keyList[i], this);
@@ -672,24 +736,69 @@ class LRUCache {
672
736
  let deleted = false;
673
737
  for (const i of this.#rindexes({ allowStale: true })) {
674
738
  if (this.#isStale(i)) {
675
- this.delete(this.#keyList[i]);
739
+ this.#delete(this.#keyList[i], 'expire');
676
740
  deleted = true;
677
741
  }
678
742
  }
679
743
  return deleted;
680
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
+ }
681
783
  /**
682
784
  * Return an array of [key, {@link LRUCache.Entry}] tuples which can be
683
- * 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.
684
795
  */
685
796
  dump() {
686
797
  const arr = [];
687
798
  for (const i of this.#indexes({ allowStale: true })) {
688
799
  const key = this.#keyList[i];
689
800
  const v = this.#valList[i];
690
- const value = this.#isBackgroundFetch(v)
691
- ? v.__staleWhileFetching
692
- : v;
801
+ const value = this.#isBackgroundFetch(v) ? v.__staleWhileFetching : v;
693
802
  if (value === undefined || key === undefined)
694
803
  continue;
695
804
  const entry = { value };
@@ -697,7 +806,7 @@ class LRUCache {
697
806
  entry.ttl = this.#ttls[i];
698
807
  // always dump the start relative to a portable timestamp
699
808
  // it's ok for this to be a bit slow, it's a rare operation.
700
- const age = perf.now() - this.#starts[i];
809
+ const age = this.#perf.now() - this.#starts[i];
701
810
  entry.start = Math.floor(Date.now() - age);
702
811
  }
703
812
  if (this.#sizes) {
@@ -709,8 +818,12 @@ class LRUCache {
709
818
  }
710
819
  /**
711
820
  * Reset the cache and load in the items in entries in the order listed.
712
- * Note that the shape of the resulting cache may be different if the
713
- * 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.
714
827
  */
715
828
  load(arr) {
716
829
  this.clear();
@@ -723,38 +836,85 @@ class LRUCache {
723
836
  //
724
837
  // it's ok for this to be a bit slow, it's a rare operation.
725
838
  const age = Date.now() - entry.start;
726
- entry.start = perf.now() - age;
839
+ entry.start = this.#perf.now() - age;
727
840
  }
728
- this.set(key, entry.value, entry);
841
+ this.#set(key, entry.value, entry);
729
842
  }
730
843
  }
731
844
  /**
732
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.
733
873
  */
734
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 = {}) {
735
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
+ }
736
897
  let { noUpdateTTL = this.noUpdateTTL } = setOptions;
737
- 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);
738
901
  // if the item doesn't fit, don't do anything
739
902
  // NB: maxEntrySize set to maxSize by default
740
903
  if (this.maxEntrySize && size > this.maxEntrySize) {
904
+ // have to delete, in case something is there already.
905
+ this.#delete(k, 'set');
741
906
  if (status) {
742
907
  status.set = 'miss';
743
908
  status.maxEntrySizeExceeded = true;
744
909
  }
745
- // have to delete, in case something is there already.
746
- this.delete(k);
747
910
  return this;
748
911
  }
749
912
  let index = this.#size === 0 ? undefined : this.#keyMap.get(k);
750
913
  if (index === undefined) {
751
914
  // addition
752
- index = (this.#size === 0
753
- ? this.#tail
754
- : this.#free.length !== 0
755
- ? this.#free.pop()
756
- : this.#size === this.#max
757
- ? 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)
758
918
  : this.#size);
759
919
  this.#keyList[index] = k;
760
920
  this.#valList[index] = v;
@@ -767,6 +927,9 @@ class LRUCache {
767
927
  if (status)
768
928
  status.set = 'add';
769
929
  noUpdateTTL = false;
930
+ if (this.#hasOnInsert) {
931
+ this.#onInsert?.(v, k, 'add');
932
+ }
770
933
  }
771
934
  else {
772
935
  // update
@@ -775,6 +938,15 @@ class LRUCache {
775
938
  if (v !== oldVal) {
776
939
  if (this.#hasFetchMethod && this.#isBackgroundFetch(oldVal)) {
777
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
+ }
778
950
  }
779
951
  else if (!noDisposeOnSet) {
780
952
  if (this.#hasDispose) {
@@ -789,8 +961,8 @@ class LRUCache {
789
961
  this.#valList[index] = v;
790
962
  if (status) {
791
963
  status.set = 'replace';
792
- const oldValue = oldVal && this.#isBackgroundFetch(oldVal)
793
- ? oldVal.__staleWhileFetching
964
+ const oldValue = oldVal && this.#isBackgroundFetch(oldVal) ?
965
+ oldVal.__staleWhileFetching
794
966
  : oldVal;
795
967
  if (oldValue !== undefined)
796
968
  status.oldValue = oldValue;
@@ -799,6 +971,9 @@ class LRUCache {
799
971
  else if (status) {
800
972
  status.set = 'update';
801
973
  }
974
+ if (this.#hasOnInsert) {
975
+ this.onInsert?.(v, k, v === oldVal ? 'update' : 'replace');
976
+ }
802
977
  }
803
978
  if (ttl !== 0 && !this.#ttls) {
804
979
  this.#initializeTTLTracking();
@@ -864,6 +1039,10 @@ class LRUCache {
864
1039
  }
865
1040
  }
866
1041
  this.#removeItemSize(head);
1042
+ if (this.#autopurgeTimers?.[head]) {
1043
+ clearTimeout(this.#autopurgeTimers[head]);
1044
+ this.#autopurgeTimers[head] = undefined;
1045
+ }
867
1046
  // if we aren't about to use the index, then null these out
868
1047
  if (free) {
869
1048
  this.#keyList[head] = undefined;
@@ -886,10 +1065,30 @@ class LRUCache {
886
1065
  * Will return false if the item is stale, even though it is technically
887
1066
  * in the cache.
888
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
+ *
889
1076
  * Will not update item age unless
890
1077
  * {@link LRUCache.OptionsBase.updateAgeOnHas} is set.
891
1078
  */
892
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 = {}) {
893
1092
  const { updateAgeOnHas = this.updateAgeOnHas, status } = hasOptions;
894
1093
  const index = this.#keyMap.get(k);
895
1094
  if (index !== undefined) {
@@ -926,14 +1125,38 @@ class LRUCache {
926
1125
  * {@link LRUCache.OptionsBase.allowStale} is set.
927
1126
  */
928
1127
  peek(k, peekOptions = {}) {
929
- 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;
930
1142
  const index = this.#keyMap.get(k);
931
- if (index !== undefined &&
932
- (allowStale || !this.#isStale(index))) {
933
- const v = this.#valList[index];
934
- // either stale and allowed, or forcing a refresh of non-stale value
935
- 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
+ }
936
1158
  }
1159
+ return val;
937
1160
  }
938
1161
  #backgroundFetch(k, index, options, context) {
939
1162
  const v = index === undefined ? undefined : this.#valList[index];
@@ -954,6 +1177,8 @@ class LRUCache {
954
1177
  const cb = (v, updateCache = false) => {
955
1178
  const { aborted } = ac.signal;
956
1179
  const ignoreAbort = options.ignoreFetchAbort && v !== undefined;
1180
+ const proceed = options.ignoreFetchAbort ||
1181
+ !!(options.allowStaleOnFetchAbort && v !== undefined);
957
1182
  if (options.status) {
958
1183
  if (aborted && !updateCache) {
959
1184
  options.status.fetchAborted = true;
@@ -966,23 +1191,27 @@ class LRUCache {
966
1191
  }
967
1192
  }
968
1193
  if (aborted && !ignoreAbort && !updateCache) {
969
- return fetchFail(ac.signal.reason);
1194
+ return fetchFail(ac.signal.reason, proceed);
970
1195
  }
971
1196
  // either we didn't abort, and are still here, or we did, and ignored
972
1197
  const bf = p;
973
- 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)) {
974
1203
  if (v === undefined) {
975
- if (bf.__staleWhileFetching) {
1204
+ if (bf.__staleWhileFetching !== undefined) {
976
1205
  this.#valList[index] = bf.__staleWhileFetching;
977
1206
  }
978
1207
  else {
979
- this.delete(k);
1208
+ this.#delete(k, 'fetch');
980
1209
  }
981
1210
  }
982
1211
  else {
983
1212
  if (options.status)
984
1213
  options.status.fetchUpdated = true;
985
- this.set(k, v, fetchOpts.options);
1214
+ this.#set(k, v, fetchOpts.options);
986
1215
  }
987
1216
  }
988
1217
  return v;
@@ -992,9 +1221,10 @@ class LRUCache {
992
1221
  options.status.fetchRejected = true;
993
1222
  options.status.fetchError = er;
994
1223
  }
995
- return fetchFail(er);
1224
+ // do not pass go, do not collect $200
1225
+ return fetchFail(er, false);
996
1226
  };
997
- const fetchFail = (er) => {
1227
+ const fetchFail = (er, proceed) => {
998
1228
  const { aborted } = ac.signal;
999
1229
  const allowStaleAborted = aborted && options.allowStaleOnFetchAbort;
1000
1230
  const allowStale = allowStaleAborted || options.allowStaleOnFetchRejection;
@@ -1003,9 +1233,9 @@ class LRUCache {
1003
1233
  if (this.#valList[index] === p) {
1004
1234
  // if we allow stale on fetch rejections, then we need to ensure that
1005
1235
  // the stale value is not removed from the cache when the fetch fails.
1006
- const del = !noDelete || bf.__staleWhileFetching === undefined;
1236
+ const del = !noDelete || (!proceed && bf.__staleWhileFetching === undefined);
1007
1237
  if (del) {
1008
- this.delete(k);
1238
+ this.#delete(k, 'fetch');
1009
1239
  }
1010
1240
  else if (!allowStaleAborted) {
1011
1241
  // still replace the *promise* with the stale value,
@@ -1028,15 +1258,14 @@ class LRUCache {
1028
1258
  const pcall = (res, rej) => {
1029
1259
  const fmp = this.#fetchMethod?.(k, v, fetchOpts);
1030
1260
  if (fmp && fmp instanceof Promise) {
1031
- fmp.then(v => res(v), rej);
1261
+ fmp.then(v => res(v === undefined ? undefined : v), rej);
1032
1262
  }
1033
1263
  // ignored, we go until we finish, regardless.
1034
1264
  // defer check until we are actually aborting,
1035
1265
  // so fetchMethod can override.
1036
1266
  ac.signal.addEventListener('abort', () => {
1037
- if (!options.ignoreFetchAbort ||
1038
- options.allowStaleOnFetchAbort) {
1039
- res();
1267
+ if (!options.ignoreFetchAbort || options.allowStaleOnFetchAbort) {
1268
+ res(undefined);
1040
1269
  // when it eventually resolves, update the cache.
1041
1270
  if (options.allowStaleOnFetchAbort) {
1042
1271
  res = v => cb(v, true);
@@ -1054,7 +1283,7 @@ class LRUCache {
1054
1283
  });
1055
1284
  if (index === undefined) {
1056
1285
  // internal, don't expose status.
1057
- this.set(k, bf, { ...fetchOpts.options, status: undefined });
1286
+ this.#set(k, bf, { ...fetchOpts.options, status: undefined });
1058
1287
  index = this.#keyMap.get(k);
1059
1288
  }
1060
1289
  else {
@@ -1071,7 +1300,23 @@ class LRUCache {
1071
1300
  b.hasOwnProperty('__staleWhileFetching') &&
1072
1301
  b.__abortController instanceof AbortController);
1073
1302
  }
1074
- 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 = {}) {
1075
1320
  const {
1076
1321
  // get options
1077
1322
  allowStale = this.allowStale, updateAgeOnGet = this.updateAgeOnGet, noDeleteOnStaleGet = this.noDeleteOnStaleGet,
@@ -1079,10 +1324,16 @@ class LRUCache {
1079
1324
  ttl = this.ttl, noDisposeOnSet = this.noDisposeOnSet, size = 0, sizeCalculation = this.sizeCalculation, noUpdateTTL = this.noUpdateTTL,
1080
1325
  // fetch exclusive options
1081
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
+ }
1082
1333
  if (!this.#hasFetchMethod) {
1083
1334
  if (status)
1084
1335
  status.fetch = 'get';
1085
- return this.get(k, {
1336
+ return this.#get(k, {
1086
1337
  allowStale,
1087
1338
  updateAgeOnGet,
1088
1339
  noDeleteOnStaleGet,
@@ -1151,6 +1402,71 @@ class LRUCache {
1151
1402
  return staleVal ? p.__staleWhileFetching : (p.__returned = p);
1152
1403
  }
1153
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
+ }
1154
1470
  /**
1155
1471
  * Return a value from the cache. Will update the recency of the cache
1156
1472
  * entry found.
@@ -1158,55 +1474,70 @@ class LRUCache {
1158
1474
  * If the key is not found, get() will return `undefined`.
1159
1475
  */
1160
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 = {}) {
1161
1493
  const { allowStale = this.allowStale, updateAgeOnGet = this.updateAgeOnGet, noDeleteOnStaleGet = this.noDeleteOnStaleGet, status, } = getOptions;
1162
1494
  const index = this.#keyMap.get(k);
1163
- if (index !== undefined) {
1164
- const value = this.#valList[index];
1165
- const fetching = this.#isBackgroundFetch(value);
1495
+ if (index === undefined) {
1166
1496
  if (status)
1167
- this.#statusTTL(status, index);
1168
- 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
+ }
1169
1510
  if (status)
1170
1511
  status.get = 'stale';
1171
- // delete only if not an in-flight background fetch
1172
- if (!fetching) {
1173
- if (!noDeleteOnStaleGet) {
1174
- this.delete(k);
1175
- }
1176
- if (status && allowStale)
1177
- status.returnedStale = true;
1178
- return allowStale ? value : undefined;
1179
- }
1180
- else {
1181
- if (status &&
1182
- allowStale &&
1183
- value.__staleWhileFetching !== undefined) {
1512
+ if (allowStale) {
1513
+ if (status)
1184
1514
  status.returnedStale = true;
1185
- }
1186
- return allowStale ? value.__staleWhileFetching : undefined;
1515
+ return value;
1187
1516
  }
1517
+ return undefined;
1188
1518
  }
1189
- else {
1519
+ if (status)
1520
+ status.get = 'stale-fetching';
1521
+ if (allowStale && value.__staleWhileFetching !== undefined) {
1190
1522
  if (status)
1191
- status.get = 'hit';
1192
- // if we're currently fetching it, we don't actually have it yet
1193
- // it's not stale, which means this isn't a staleWhileRefetching.
1194
- // If it's not stale, and fetching, AND has a __staleWhileFetching
1195
- // value, then that means the user fetched with {forceRefresh:true},
1196
- // so it's safe to return that value.
1197
- if (fetching) {
1198
- return value.__staleWhileFetching;
1199
- }
1200
- this.#moveToTail(index);
1201
- if (updateAgeOnGet) {
1202
- this.#updateItemAge(index);
1203
- }
1204
- return value;
1523
+ status.returnedStale = true;
1524
+ return value.__staleWhileFetching;
1205
1525
  }
1526
+ return undefined;
1206
1527
  }
1207
- else if (status) {
1208
- 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);
1209
1539
  }
1540
+ return fetching ? value.__staleWhileFetching : value;
1210
1541
  }
1211
1542
  #connect(p, n) {
1212
1543
  this.#prev[n] = p;
@@ -1234,16 +1565,31 @@ class LRUCache {
1234
1565
  }
1235
1566
  /**
1236
1567
  * Deletes a key out of the cache.
1568
+ *
1237
1569
  * Returns true if the key was deleted, false otherwise.
1238
1570
  */
1239
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
+ }
1240
1582
  let deleted = false;
1241
1583
  if (this.#size !== 0) {
1242
1584
  const index = this.#keyMap.get(k);
1243
1585
  if (index !== undefined) {
1586
+ if (this.#autopurgeTimers?.[index]) {
1587
+ clearTimeout(this.#autopurgeTimers?.[index]);
1588
+ this.#autopurgeTimers[index] = undefined;
1589
+ }
1244
1590
  deleted = true;
1245
1591
  if (this.#size === 1) {
1246
- this.clear();
1592
+ this.#clear(reason);
1247
1593
  }
1248
1594
  else {
1249
1595
  this.#removeItemSize(index);
@@ -1253,10 +1599,10 @@ class LRUCache {
1253
1599
  }
1254
1600
  else if (this.#hasDispose || this.#hasDisposeAfter) {
1255
1601
  if (this.#hasDispose) {
1256
- this.#dispose?.(v, k, 'delete');
1602
+ this.#dispose?.(v, k, reason);
1257
1603
  }
1258
1604
  if (this.#hasDisposeAfter) {
1259
- this.#disposed?.push([v, k, 'delete']);
1605
+ this.#disposed?.push([v, k, reason]);
1260
1606
  }
1261
1607
  }
1262
1608
  this.#keyMap.delete(k);
@@ -1269,8 +1615,10 @@ class LRUCache {
1269
1615
  this.#head = this.#next[index];
1270
1616
  }
1271
1617
  else {
1272
- this.#next[this.#prev[index]] = this.#next[index];
1273
- 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];
1274
1622
  }
1275
1623
  this.#size--;
1276
1624
  this.#free.push(index);
@@ -1290,6 +1638,9 @@ class LRUCache {
1290
1638
  * Clear the cache entirely, throwing away all values.
1291
1639
  */
1292
1640
  clear() {
1641
+ return this.#clear('delete');
1642
+ }
1643
+ #clear(reason) {
1293
1644
  for (const index of this.#rindexes({ allowStale: true })) {
1294
1645
  const v = this.#valList[index];
1295
1646
  if (this.#isBackgroundFetch(v)) {
@@ -1298,10 +1649,10 @@ class LRUCache {
1298
1649
  else {
1299
1650
  const k = this.#keyList[index];
1300
1651
  if (this.#hasDispose) {
1301
- this.#dispose?.(v, k, 'delete');
1652
+ this.#dispose?.(v, k, reason);
1302
1653
  }
1303
1654
  if (this.#hasDisposeAfter) {
1304
- this.#disposed?.push([v, k, 'delete']);
1655
+ this.#disposed?.push([v, k, reason]);
1305
1656
  }
1306
1657
  }
1307
1658
  }
@@ -1311,6 +1662,11 @@ class LRUCache {
1311
1662
  if (this.#ttls && this.#starts) {
1312
1663
  this.#ttls.fill(0);
1313
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);
1314
1670
  }
1315
1671
  if (this.#sizes) {
1316
1672
  this.#sizes.fill(0);
@@ -1329,6 +1685,4 @@ class LRUCache {
1329
1685
  }
1330
1686
  }
1331
1687
  }
1332
- exports.LRUCache = LRUCache;
1333
- exports.default = LRUCache;
1334
1688
  //# sourceMappingURL=index.js.map