@zuplo/cli 6.71.21 → 6.71.23

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 (109) hide show
  1. package/node_modules/@posthog/core/dist/error-tracking/exception-steps.d.ts.map +1 -1
  2. package/node_modules/@posthog/core/dist/error-tracking/exception-steps.js +6 -24
  3. package/node_modules/@posthog/core/dist/error-tracking/exception-steps.mjs +7 -25
  4. package/node_modules/@posthog/core/dist/posthog-core-stateless.d.ts +6 -0
  5. package/node_modules/@posthog/core/dist/posthog-core-stateless.d.ts.map +1 -1
  6. package/node_modules/@posthog/core/dist/posthog-core-stateless.js +75 -17
  7. package/node_modules/@posthog/core/dist/posthog-core-stateless.mjs +73 -18
  8. package/node_modules/@posthog/core/dist/posthog-core.d.ts +2 -2
  9. package/node_modules/@posthog/core/dist/posthog-core.d.ts.map +1 -1
  10. package/node_modules/@posthog/core/dist/posthog-core.js +10 -6
  11. package/node_modules/@posthog/core/dist/posthog-core.mjs +11 -7
  12. package/node_modules/@posthog/core/dist/testing/PostHogCoreTestClient.d.ts +1 -0
  13. package/node_modules/@posthog/core/dist/testing/PostHogCoreTestClient.d.ts.map +1 -1
  14. package/node_modules/@posthog/core/dist/testing/PostHogCoreTestClient.js +3 -0
  15. package/node_modules/@posthog/core/dist/testing/PostHogCoreTestClient.mjs +3 -0
  16. package/node_modules/@posthog/core/dist/utils/promise-queue.d.ts +3 -0
  17. package/node_modules/@posthog/core/dist/utils/promise-queue.d.ts.map +1 -1
  18. package/node_modules/@posthog/core/dist/utils/promise-queue.js +15 -3
  19. package/node_modules/@posthog/core/dist/utils/promise-queue.mjs +15 -3
  20. package/node_modules/@posthog/core/dist/utils/string-utils.d.ts +1 -0
  21. package/node_modules/@posthog/core/dist/utils/string-utils.d.ts.map +1 -1
  22. package/node_modules/@posthog/core/dist/utils/string-utils.js +21 -0
  23. package/node_modules/@posthog/core/dist/utils/string-utils.mjs +19 -1
  24. package/node_modules/@posthog/core/package.json +1 -1
  25. package/node_modules/@posthog/core/src/error-tracking/exception-steps.ts +5 -42
  26. package/node_modules/@posthog/core/src/posthog-core-stateless.ts +118 -23
  27. package/node_modules/@posthog/core/src/posthog-core.ts +18 -7
  28. package/node_modules/@posthog/core/src/testing/PostHogCoreTestClient.ts +4 -0
  29. package/node_modules/@posthog/core/src/utils/promise-queue.ts +17 -4
  30. package/node_modules/@posthog/core/src/utils/string-utils.spec.ts +38 -1
  31. package/node_modules/@posthog/core/src/utils/string-utils.ts +42 -0
  32. package/node_modules/@posthog/types/dist/posthog.d.ts +12 -0
  33. package/node_modules/@posthog/types/dist/posthog.d.ts.map +1 -1
  34. package/node_modules/@posthog/types/package.json +1 -1
  35. package/node_modules/@posthog/types/src/posthog.ts +13 -0
  36. package/node_modules/@types/node/README.md +1 -1
  37. package/node_modules/@types/node/buffer.d.ts +64 -25
  38. package/node_modules/@types/node/crypto.d.ts +18 -5
  39. package/node_modules/@types/node/diagnostics_channel.d.ts +237 -3
  40. package/node_modules/@types/node/dns.d.ts +1 -1
  41. package/node_modules/@types/node/ffi.d.ts +486 -0
  42. package/node_modules/@types/node/fs/promises.d.ts +3 -0
  43. package/node_modules/@types/node/fs.d.ts +21 -6
  44. package/node_modules/@types/node/http.d.ts +25 -0
  45. package/node_modules/@types/node/index.d.ts +1 -0
  46. package/node_modules/@types/node/package.json +2 -2
  47. package/node_modules/@types/node/process.d.ts +14 -1
  48. package/node_modules/@types/node/quic.d.ts +92 -11
  49. package/node_modules/@types/node/sqlite.d.ts +55 -0
  50. package/node_modules/@types/node/stream/iter.d.ts +150 -0
  51. package/node_modules/@types/node/stream.d.ts +32 -0
  52. package/node_modules/@types/node/test.d.ts +112 -2
  53. package/node_modules/@types/node/ts5.6/index.d.ts +1 -0
  54. package/node_modules/@types/node/ts5.7/index.d.ts +1 -0
  55. package/node_modules/@types/node/util.d.ts +19 -2
  56. package/node_modules/@types/node/v8.d.ts +84 -2
  57. package/node_modules/@types/node/worker_threads.d.ts +8 -7
  58. package/node_modules/@zuplo/core/customer.cli.minified.js +2 -2
  59. package/node_modules/@zuplo/core/index.minified.js +2 -2
  60. package/node_modules/@zuplo/core/package.json +1 -1
  61. package/node_modules/@zuplo/graphql/out/esm/index.js +11 -11
  62. package/node_modules/@zuplo/graphql/out/esm/index.js.map +1 -1
  63. package/node_modules/@zuplo/graphql/package.json +1 -1
  64. package/node_modules/@zuplo/openapi-tools/package.json +1 -1
  65. package/node_modules/@zuplo/otel/package.json +1 -1
  66. package/node_modules/@zuplo/runtime/out/esm/{chunk-DQ4ANJLR.js → chunk-4MNJC7E2.js} +2 -2
  67. package/node_modules/@zuplo/runtime/out/esm/chunk-4MNJC7E2.js.map +1 -0
  68. package/node_modules/@zuplo/runtime/out/esm/{chunk-2Y72LML3.js → chunk-4QJJMELB.js} +2 -2
  69. package/node_modules/@zuplo/runtime/out/esm/{chunk-2Y72LML3.js.map → chunk-4QJJMELB.js.map} +1 -1
  70. package/node_modules/@zuplo/runtime/out/esm/chunk-5CYWMN74.js +402 -0
  71. package/node_modules/@zuplo/runtime/out/esm/chunk-5CYWMN74.js.map +1 -0
  72. package/node_modules/@zuplo/runtime/out/esm/{chunk-L3MZGNQA.js → chunk-DSZS6PZJ.js} +10 -10
  73. package/node_modules/@zuplo/runtime/out/esm/chunk-DSZS6PZJ.js.map +1 -0
  74. package/node_modules/@zuplo/runtime/out/esm/index.js +1 -1
  75. package/node_modules/@zuplo/runtime/out/esm/index.js.map +1 -1
  76. package/node_modules/@zuplo/runtime/out/esm/internal/index.js +1 -1
  77. package/node_modules/@zuplo/runtime/out/esm/mcp-gateway/index.js +7 -7
  78. package/node_modules/@zuplo/runtime/out/esm/mcp-gateway/index.js.map +1 -1
  79. package/node_modules/@zuplo/runtime/out/esm/mocks/index.js +1 -1
  80. package/node_modules/@zuplo/runtime/out/types/index.d.ts +1050 -18
  81. package/node_modules/@zuplo/runtime/out/types/mcp-gateway/index.d.ts +33 -7
  82. package/node_modules/@zuplo/runtime/package.json +1 -1
  83. package/node_modules/iconv-lite/encodings/sbcs-data.js +2 -0
  84. package/node_modules/iconv-lite/encodings/utf32.js +10 -3
  85. package/node_modules/iconv-lite/package.json +2 -2
  86. package/node_modules/iconv-lite/types/encodings.d.ts +2 -0
  87. package/node_modules/protobufjs/dist/light/protobuf.js +2 -2
  88. package/node_modules/protobufjs/dist/light/protobuf.min.js +2 -2
  89. package/node_modules/protobufjs/dist/minimal/protobuf.js +2 -2
  90. package/node_modules/protobufjs/dist/minimal/protobuf.min.js +2 -2
  91. package/node_modules/protobufjs/dist/protobuf.js +5 -2
  92. package/node_modules/protobufjs/dist/protobuf.js.map +1 -1
  93. package/node_modules/protobufjs/dist/protobuf.min.js +3 -3
  94. package/node_modules/protobufjs/dist/protobuf.min.js.map +1 -1
  95. package/node_modules/protobufjs/package.json +1 -1
  96. package/node_modules/protobufjs/src/parse.js +3 -0
  97. package/node_modules/toad-cache/README.md +10 -9
  98. package/node_modules/toad-cache/dist/toad-cache.cjs +139 -139
  99. package/node_modules/toad-cache/dist/toad-cache.mjs +136 -140
  100. package/node_modules/toad-cache/package.json +8 -8
  101. package/node_modules/toad-cache/toad-cache.d.cts +20 -14
  102. package/node_modules/toad-cache/toad-cache.d.ts +18 -14
  103. package/package.json +6 -6
  104. package/node_modules/@zuplo/runtime/out/esm/chunk-DQ4ANJLR.js.map +0 -1
  105. package/node_modules/@zuplo/runtime/out/esm/chunk-I5HLAHUY.js +0 -357
  106. package/node_modules/@zuplo/runtime/out/esm/chunk-I5HLAHUY.js.map +0 -1
  107. package/node_modules/@zuplo/runtime/out/esm/chunk-L3MZGNQA.js.map +0 -1
  108. /package/node_modules/@zuplo/runtime/out/esm/{chunk-I5HLAHUY.js.LEGAL.txt → chunk-5CYWMN74.js.LEGAL.txt} +0 -0
  109. /package/node_modules/@zuplo/runtime/out/esm/{chunk-L3MZGNQA.js.LEGAL.txt → chunk-DSZS6PZJ.js.LEGAL.txt} +0 -0
@@ -3,17 +3,26 @@
3
3
  *
4
4
  * @copyright 2026 Igor Savin <kibertoad@gmail.com>
5
5
  * @license MIT
6
- * @version 3.7.0
6
+ * @version 3.7.3
7
7
  */
8
- class FifoMap {
9
- constructor(max = 1000, ttlInMsecs = 0) {
10
- if (isNaN(max) || max < 0) {
11
- throw new Error('Invalid max value')
12
- }
8
+ /**
9
+ * Validates the shared cache constructor parameters.
10
+ * Both values must be non-negative integers.
11
+ *
12
+ * @param {number} max
13
+ * @param {number} ttlInMsecs
14
+ */
15
+ function validateCacheParams(max, ttlInMsecs) {
16
+ if (typeof max !== 'number' || !Number.isInteger(max) || max < 0) {
17
+ throw new Error('Invalid max value')
18
+ }
13
19
 
14
- if (isNaN(ttlInMsecs) || ttlInMsecs < 0) {
15
- throw new Error('Invalid ttl value')
16
- }
20
+ if (typeof ttlInMsecs !== 'number' || !Number.isInteger(ttlInMsecs) || ttlInMsecs < 0) {
21
+ throw new Error('Invalid ttl value')
22
+ }
23
+ }class FifoMap {
24
+ constructor(max = 1000, ttlInMsecs = 0) {
25
+ validateCacheParams(max, ttlInMsecs);
17
26
 
18
27
  this.first = null;
19
28
  this.items = new Map();
@@ -27,15 +36,15 @@ class FifoMap {
27
36
  }
28
37
 
29
38
  clear() {
30
- this.items = new Map();
39
+ this.items.clear();
31
40
  this.first = null;
32
41
  this.last = null;
33
42
  }
34
43
 
35
44
  delete(key) {
36
- if (this.items.has(key)) {
37
- const deletedItem = this.items.get(key);
45
+ const deletedItem = this.items.get(key);
38
46
 
47
+ if (deletedItem !== undefined) {
39
48
  this.items.delete(key);
40
49
 
41
50
  if (deletedItem.prev !== null) {
@@ -79,15 +88,17 @@ class FifoMap {
79
88
  }
80
89
 
81
90
  expiresAt(key) {
82
- if (this.items.has(key)) {
83
- return this.items.get(key).expiry
91
+ const item = this.items.get(key);
92
+
93
+ if (item !== undefined) {
94
+ return item.expiry
84
95
  }
85
96
  }
86
97
 
87
98
  get(key) {
88
- if (this.items.has(key)) {
89
- const item = this.items.get(key);
99
+ const item = this.items.get(key);
90
100
 
101
+ if (item !== undefined) {
91
102
  if (this.ttl > 0 && item.expiry <= Date.now()) {
92
103
  this.delete(key);
93
104
  return
@@ -98,10 +109,10 @@ class FifoMap {
98
109
  }
99
110
 
100
111
  getMany(keys) {
101
- const result = [];
112
+ const result = new Array(keys.length);
102
113
 
103
114
  for (var i = 0; i < keys.length; i++) {
104
- result.push(this.get(keys[i]));
115
+ result[i] = this.get(keys[i]);
105
116
  }
106
117
 
107
118
  return result
@@ -113,17 +124,17 @@ class FifoMap {
113
124
 
114
125
  set(key, value) {
115
126
  // Replace existing item
116
- if (this.items.has(key)) {
117
- const item = this.items.get(key);
118
- item.value = value;
127
+ const existing = this.items.get(key);
119
128
 
120
- item.expiry = this.ttl > 0 ? Date.now() + this.ttl : this.ttl;
129
+ if (existing !== undefined) {
130
+ existing.value = value;
131
+ existing.expiry = this.ttl > 0 ? Date.now() + this.ttl : this.ttl;
121
132
 
122
133
  return
123
134
  }
124
135
 
125
136
  // Add new item
126
- if (this.max > 0 && this.size === this.max) {
137
+ if (this.max > 0 && this.size >= this.max) {
127
138
  this.evict();
128
139
  }
129
140
 
@@ -146,13 +157,7 @@ class FifoMap {
146
157
  }
147
158
  }class FifoObject {
148
159
  constructor(max = 1000, ttlInMsecs = 0) {
149
- if (isNaN(max) || max < 0) {
150
- throw new Error('Invalid max value')
151
- }
152
-
153
- if (isNaN(ttlInMsecs) || ttlInMsecs < 0) {
154
- throw new Error('Invalid ttl value')
155
- }
160
+ validateCacheParams(max, ttlInMsecs);
156
161
 
157
162
  this.first = null;
158
163
  this.items = Object.create(null);
@@ -170,9 +175,9 @@ class FifoMap {
170
175
  }
171
176
 
172
177
  delete(key) {
173
- if (Object.prototype.hasOwnProperty.call(this.items, key)) {
174
- const deletedItem = this.items[key];
178
+ const deletedItem = this.items[key];
175
179
 
180
+ if (deletedItem !== undefined) {
176
181
  delete this.items[key];
177
182
  this.size--;
178
183
 
@@ -217,15 +222,17 @@ class FifoMap {
217
222
  }
218
223
 
219
224
  expiresAt(key) {
220
- if (Object.prototype.hasOwnProperty.call(this.items, key)) {
221
- return this.items[key].expiry
225
+ const item = this.items[key];
226
+
227
+ if (item !== undefined) {
228
+ return item.expiry
222
229
  }
223
230
  }
224
231
 
225
232
  get(key) {
226
- if (Object.prototype.hasOwnProperty.call(this.items, key)) {
227
- const item = this.items[key];
233
+ const item = this.items[key];
228
234
 
235
+ if (item !== undefined) {
229
236
  if (this.ttl > 0 && item.expiry <= Date.now()) {
230
237
  this.delete(key);
231
238
  return
@@ -236,10 +243,10 @@ class FifoMap {
236
243
  }
237
244
 
238
245
  getMany(keys) {
239
- const result = [];
246
+ const result = new Array(keys.length);
240
247
 
241
248
  for (var i = 0; i < keys.length; i++) {
242
- result.push(this.get(keys[i]));
249
+ result[i] = this.get(keys[i]);
243
250
  }
244
251
 
245
252
  return result
@@ -251,17 +258,17 @@ class FifoMap {
251
258
 
252
259
  set(key, value) {
253
260
  // Replace existing item
254
- if (Object.prototype.hasOwnProperty.call(this.items, key)) {
255
- const item = this.items[key];
256
- item.value = value;
261
+ const existing = this.items[key];
257
262
 
258
- item.expiry = this.ttl > 0 ? Date.now() + this.ttl : this.ttl;
263
+ if (existing !== undefined) {
264
+ existing.value = value;
265
+ existing.expiry = this.ttl > 0 ? Date.now() + this.ttl : this.ttl;
259
266
 
260
267
  return
261
268
  }
262
269
 
263
270
  // Add new item
264
- if (this.max > 0 && this.size === this.max) {
271
+ if (this.max > 0 && this.size >= this.max) {
265
272
  this.evict();
266
273
  }
267
274
 
@@ -282,6 +289,24 @@ class FifoMap {
282
289
 
283
290
  this.last = item;
284
291
  }
292
+ }/**
293
+ * Creates a zeroed statistics record for a single collection window.
294
+ *
295
+ * @returns {object}
296
+ */
297
+ function createEmptyStatisticsRecord() {
298
+ return {
299
+ cacheSize: 0,
300
+ hits: 0,
301
+ falsyHits: 0,
302
+ emptyHits: 0,
303
+ misses: 0,
304
+ expirations: 0,
305
+ evictions: 0,
306
+ invalidateOne: 0,
307
+ invalidateAll: 0,
308
+ sets: 0,
309
+ }
285
310
  }class HitStatisticsRecord {
286
311
  constructor() {
287
312
  this.records = {};
@@ -289,35 +314,17 @@ class FifoMap {
289
314
 
290
315
  initForCache(cacheId, currentTimeStamp) {
291
316
  this.records[cacheId] = {
292
- [currentTimeStamp]: {
293
- cacheSize: 0,
294
- hits: 0,
295
- falsyHits: 0,
296
- emptyHits: 0,
297
- misses: 0,
298
- expirations: 0,
299
- evictions: 0,
300
- invalidateOne: 0,
301
- invalidateAll: 0,
302
- sets: 0,
303
- },
317
+ [currentTimeStamp]: createEmptyStatisticsRecord(),
304
318
  };
305
319
  }
306
320
 
307
321
  resetForCache(cacheId) {
322
+ if (!this.records[cacheId]) {
323
+ return
324
+ }
325
+
308
326
  for (let key of Object.keys(this.records[cacheId])) {
309
- this.records[cacheId][key] = {
310
- cacheSize: 0,
311
- hits: 0,
312
- falsyHits: 0,
313
- emptyHits: 0,
314
- misses: 0,
315
- expirations: 0,
316
- evictions: 0,
317
- invalidateOne: 0,
318
- invalidateAll: 0,
319
- sets: 0,
320
- };
327
+ this.records[cacheId][key] = createEmptyStatisticsRecord();
321
328
  }
322
329
  }
323
330
 
@@ -326,13 +333,7 @@ class FifoMap {
326
333
  }
327
334
  }class LruMap {
328
335
  constructor(max = 1000, ttlInMsecs = 0) {
329
- if (isNaN(max) || max < 0) {
330
- throw new Error('Invalid max value')
331
- }
332
-
333
- if (isNaN(ttlInMsecs) || ttlInMsecs < 0) {
334
- throw new Error('Invalid ttl value')
335
- }
336
+ validateCacheParams(max, ttlInMsecs);
336
337
 
337
338
  this.first = null;
338
339
  this.items = new Map();
@@ -366,6 +367,7 @@ class FifoMap {
366
367
  prev.next = next;
367
368
  }
368
369
 
370
+ /* v8 ignore next 3 -- next is always non-null here: the early return above guarantees item !== this.last in a well-formed list */
369
371
  if (next !== null) {
370
372
  next.prev = prev;
371
373
  }
@@ -374,15 +376,15 @@ class FifoMap {
374
376
  }
375
377
 
376
378
  clear() {
377
- this.items = new Map();
379
+ this.items.clear();
378
380
  this.first = null;
379
381
  this.last = null;
380
382
  }
381
383
 
382
384
  delete(key) {
383
- if (this.items.has(key)) {
384
- const item = this.items.get(key);
385
+ const item = this.items.get(key);
385
386
 
387
+ if (item !== undefined) {
386
388
  this.items.delete(key);
387
389
 
388
390
  if (item.prev !== null) {
@@ -426,15 +428,17 @@ class FifoMap {
426
428
  }
427
429
 
428
430
  expiresAt(key) {
429
- if (this.items.has(key)) {
430
- return this.items.get(key).expiry
431
+ const item = this.items.get(key);
432
+
433
+ if (item !== undefined) {
434
+ return item.expiry
431
435
  }
432
436
  }
433
437
 
434
438
  get(key) {
435
- if (this.items.has(key)) {
436
- const item = this.items.get(key);
439
+ const item = this.items.get(key);
437
440
 
441
+ if (item !== undefined) {
438
442
  // Item has already expired
439
443
  if (this.ttl > 0 && item.expiry <= Date.now()) {
440
444
  this.delete(key);
@@ -448,10 +452,10 @@ class FifoMap {
448
452
  }
449
453
 
450
454
  getMany(keys) {
451
- const result = [];
455
+ const result = new Array(keys.length);
452
456
 
453
457
  for (var i = 0; i < keys.length; i++) {
454
- result.push(this.get(keys[i]));
458
+ result[i] = this.get(keys[i]);
455
459
  }
456
460
 
457
461
  return result
@@ -463,21 +467,18 @@ class FifoMap {
463
467
 
464
468
  set(key, value) {
465
469
  // Replace existing item
466
- if (this.items.has(key)) {
467
- const item = this.items.get(key);
468
- item.value = value;
469
-
470
- item.expiry = this.ttl > 0 ? Date.now() + this.ttl : this.ttl;
470
+ const existing = this.items.get(key);
471
471
 
472
- if (this.last !== item) {
473
- this.bumpLru(item);
474
- }
472
+ if (existing !== undefined) {
473
+ existing.value = value;
474
+ existing.expiry = this.ttl > 0 ? Date.now() + this.ttl : this.ttl;
475
+ this.bumpLru(existing);
475
476
 
476
477
  return
477
478
  }
478
479
 
479
480
  // Add new item
480
- if (this.max > 0 && this.size === this.max) {
481
+ if (this.max > 0 && this.size >= this.max) {
481
482
  this.evict();
482
483
  }
483
484
 
@@ -500,13 +501,7 @@ class FifoMap {
500
501
  }
501
502
  }class LruObject {
502
503
  constructor(max = 1000, ttlInMsecs = 0) {
503
- if (isNaN(max) || max < 0) {
504
- throw new Error('Invalid max value')
505
- }
506
-
507
- if (isNaN(ttlInMsecs) || ttlInMsecs < 0) {
508
- throw new Error('Invalid ttl value')
509
- }
504
+ validateCacheParams(max, ttlInMsecs);
510
505
 
511
506
  this.first = null;
512
507
  this.items = Object.create(null);
@@ -537,6 +532,7 @@ class FifoMap {
537
532
  prev.next = next;
538
533
  }
539
534
 
535
+ /* v8 ignore next 3 -- next is always non-null here: the early return above guarantees item !== this.last in a well-formed list */
540
536
  if (next !== null) {
541
537
  next.prev = prev;
542
538
  }
@@ -552,9 +548,9 @@ class FifoMap {
552
548
  }
553
549
 
554
550
  delete(key) {
555
- if (Object.prototype.hasOwnProperty.call(this.items, key)) {
556
- const item = this.items[key];
551
+ const item = this.items[key];
557
552
 
553
+ if (item !== undefined) {
558
554
  delete this.items[key];
559
555
  this.size--;
560
556
 
@@ -599,15 +595,17 @@ class FifoMap {
599
595
  }
600
596
 
601
597
  expiresAt(key) {
602
- if (Object.prototype.hasOwnProperty.call(this.items, key)) {
603
- return this.items[key].expiry
598
+ const item = this.items[key];
599
+
600
+ if (item !== undefined) {
601
+ return item.expiry
604
602
  }
605
603
  }
606
604
 
607
605
  get(key) {
608
- if (Object.prototype.hasOwnProperty.call(this.items, key)) {
609
- const item = this.items[key];
606
+ const item = this.items[key];
610
607
 
608
+ if (item !== undefined) {
611
609
  // Item has already expired
612
610
  if (this.ttl > 0 && item.expiry <= Date.now()) {
613
611
  this.delete(key);
@@ -621,10 +619,10 @@ class FifoMap {
621
619
  }
622
620
 
623
621
  getMany(keys) {
624
- const result = [];
622
+ const result = new Array(keys.length);
625
623
 
626
624
  for (var i = 0; i < keys.length; i++) {
627
- result.push(this.get(keys[i]));
625
+ result[i] = this.get(keys[i]);
628
626
  }
629
627
 
630
628
  return result
@@ -636,21 +634,18 @@ class FifoMap {
636
634
 
637
635
  set(key, value) {
638
636
  // Replace existing item
639
- if (Object.prototype.hasOwnProperty.call(this.items, key)) {
640
- const item = this.items[key];
641
- item.value = value;
637
+ const existing = this.items[key];
642
638
 
643
- item.expiry = this.ttl > 0 ? Date.now() + this.ttl : this.ttl;
644
-
645
- if (this.last !== item) {
646
- this.bumpLru(item);
647
- }
639
+ if (existing !== undefined) {
640
+ existing.value = value;
641
+ existing.expiry = this.ttl > 0 ? Date.now() + this.ttl : this.ttl;
642
+ this.bumpLru(existing);
648
643
 
649
644
  return
650
645
  }
651
646
 
652
647
  // Add new item
653
- if (this.max > 0 && this.size === this.max) {
648
+ if (this.max > 0 && this.size >= this.max) {
654
649
  this.evict();
655
650
  }
656
651
 
@@ -688,32 +683,24 @@ function getTimestamp(date) {
688
683
 
689
684
  this.collectionStart = new Date();
690
685
  this.currentTimeStamp = getTimestamp(this.collectionStart);
686
+ this.archiveAfter = this.collectionStart.getTime() + this.statisticTtlInHours * 3_600_000;
691
687
 
692
688
  this.records = globalStatisticsRecord || new HitStatisticsRecord();
693
689
  this.records.initForCache(this.cacheId, this.currentTimeStamp);
694
690
  }
695
691
 
696
692
  get currentRecord() {
693
+ const cacheRecords = this.records.records[this.cacheId];
697
694
  // safety net
698
- /* c8 ignore next 14 */
699
- if (!this.records.records[this.cacheId][this.currentTimeStamp]) {
700
- this.records.records[this.cacheId][this.currentTimeStamp] = {
701
- cacheSize: 0,
702
- hits: 0,
703
- falsyHits: 0,
704
- emptyHits: 0,
705
- misses: 0,
706
- expirations: 0,
707
- evictions: 0,
708
- sets: 0,
709
- invalidateOne: 0,
710
- invalidateAll: 0,
711
- };
695
+ /* c8 ignore next 3 */
696
+ if (!cacheRecords[this.currentTimeStamp]) {
697
+ cacheRecords[this.currentTimeStamp] = createEmptyStatisticsRecord();
712
698
  }
713
699
 
714
- return this.records.records[this.cacheId][this.currentTimeStamp]
700
+ return cacheRecords[this.currentTimeStamp]
715
701
  }
716
702
 
703
+ /* v8 ignore next 3 -- kept for compatibility, no longer used internally */
717
704
  hoursPassed() {
718
705
  return (Date.now() - this.collectionStart) / 1000 / 60 / 60
719
706
  }
@@ -772,15 +759,19 @@ function getTimestamp(date) {
772
759
  }
773
760
 
774
761
  archiveIfNeeded() {
775
- if (this.hoursPassed() >= this.statisticTtlInHours) {
762
+ if (Date.now() >= this.archiveAfter) {
776
763
  this.collectionStart = new Date();
777
764
  this.currentTimeStamp = getTimestamp(this.collectionStart);
765
+ this.archiveAfter = this.collectionStart.getTime() + this.statisticTtlInHours * 3_600_000;
778
766
  this.records.initForCache(this.cacheId, this.currentTimeStamp);
779
767
  }
780
768
  }
781
769
  }class LruObjectHitStatistics extends LruObject {
782
770
  constructor(max, ttlInMsecs, cacheId, globalStatisticsRecord, statisticTtlInHours) {
783
- super(max || 1000, ttlInMsecs || 0);
771
+ // Pass through as-is: the base constructor applies the 1000/0 defaults for
772
+ // omitted (undefined) values and validates everything else, so explicit 0
773
+ // stays unlimited and null/NaN are rejected the same way as the base class.
774
+ super(max, ttlInMsecs);
784
775
 
785
776
  if (!cacheId) {
786
777
  throw new Error('Cache id is mandatory')
@@ -804,15 +795,19 @@ function getTimestamp(date) {
804
795
  }
805
796
 
806
797
  evict() {
798
+ const hadItems = this.size > 0;
807
799
  super.evict();
808
- this.hitStatistics.addEviction();
800
+ if (hadItems) {
801
+ this.hitStatistics.addEviction();
802
+ }
809
803
  this.hitStatistics.setCacheSize(this.size);
810
804
  }
811
805
 
812
806
  delete(key, isExpiration = false) {
807
+ const existed = this.items[key] !== undefined;
813
808
  super.delete(key);
814
809
 
815
- if (!isExpiration) {
810
+ if (existed && !isExpiration) {
816
811
  this.hitStatistics.addInvalidateOne();
817
812
  }
818
813
  this.hitStatistics.setCacheSize(this.size);
@@ -826,9 +821,9 @@ function getTimestamp(date) {
826
821
  }
827
822
 
828
823
  get(key) {
829
- if (Object.prototype.hasOwnProperty.call(this.items, key)) {
830
- const item = this.items[key];
824
+ const item = this.items[key];
831
825
 
826
+ if (item !== undefined) {
832
827
  // Item has already expired
833
828
  if (this.ttl > 0 && item.expiry <= Date.now()) {
834
829
  this.delete(key, true);
@@ -840,9 +835,10 @@ function getTimestamp(date) {
840
835
  this.bumpLru(item);
841
836
  if (!item.value) {
842
837
  this.hitStatistics.addFalsyHit();
843
- }
844
- if (item.value === undefined || item.value === null || item.value === '') {
845
- this.hitStatistics.addEmptyHit();
838
+ // Empty values are a subset of falsy values
839
+ if (item.value === undefined || item.value === null || item.value === '') {
840
+ this.hitStatistics.addEmptyHit();
841
+ }
846
842
  }
847
843
  this.hitStatistics.addHit();
848
844
  return item.value
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "toad-cache",
3
3
  "description": "LRU and FIFO caches for Client or Server",
4
- "version": "3.7.1",
4
+ "version": "3.7.4",
5
5
  "homepage": "https://github.com/kibertoad/toad-cache",
6
6
  "author": "Igor Savin <kibertoad@gmail.com>",
7
7
  "repository": {
@@ -41,20 +41,20 @@
41
41
  "devEngines": {
42
42
  "runtime": {
43
43
  "name": "node",
44
- "version": ">=20",
44
+ "version": ">=22.13",
45
45
  "onFail": "error"
46
46
  }
47
47
  },
48
48
  "devDependencies": {
49
- "@biomejs/biome": "^2.4.15",
50
- "@vitest/coverage-v8": "^4.1.6",
51
- "auto-changelog": "^2.5.0",
49
+ "@biomejs/biome": "^2.5.2",
50
+ "@vitest/coverage-v8": "^4.1.9",
51
+ "auto-changelog": "^2.6.0",
52
52
  "del-cli": "^7.0.0",
53
53
  "precise": "^5.0.1",
54
- "rollup": "^4.60.4",
54
+ "rollup": "^4.62.2",
55
55
  "tsd": "^0.33.0",
56
56
  "typescript": "~6.0.3",
57
- "vitest": "^4.1.6"
57
+ "vitest": "^4.1.9"
58
58
  },
59
59
  "keywords": [
60
60
  "LRU",
@@ -76,7 +76,7 @@
76
76
  "lint:fix": "biome check --apply index.js benchmark.js src test biome.json",
77
77
  "rollup": "rollup --config",
78
78
  "test": "vitest",
79
- "test:coverage": "pnpm run rollup && pnpm run test -- --coverage",
79
+ "test:coverage": "pnpm run rollup && vitest --coverage",
80
80
  "test:ci": "pnpm run lint && pnpm run test:coverage && pnpm run test:typescript",
81
81
  "test:typescript": "tsd",
82
82
  "types:generate": "pnpm exec tsc index.js --declaration --allowJs --emitDeclarationOnly --outDir ."
@@ -8,6 +8,19 @@ type CacheEntry<T> = {
8
8
  value: T
9
9
  }
10
10
 
11
+ type CacheStatistics = {
12
+ expirations: number,
13
+ evictions: number,
14
+ hits: number,
15
+ emptyHits: number,
16
+ falsyHits: number,
17
+ misses: number,
18
+ invalidateAll: number,
19
+ invalidateOne: number,
20
+ cacheSize: number,
21
+ sets: number,
22
+ }
23
+
11
24
  interface ToadCache<T> {
12
25
  first: any;
13
26
  last: any;
@@ -28,7 +41,7 @@ interface ToadCache<T> {
28
41
  declare class FifoMap<T> implements ToadCache<T>{
29
42
  constructor(max?: number, ttlInMsecs?: number);
30
43
  first: any;
31
- items: Map<any, T>;
44
+ items: Map<any, CacheEntry<T>>;
32
45
  last: any;
33
46
  max: number;
34
47
  ttl: number;
@@ -66,7 +79,7 @@ declare class FifoObject<T> implements ToadCache<T> {
66
79
  declare class LruMap<T> implements ToadCache<T> {
67
80
  constructor(max?: number, ttlInMsecs?: number);
68
81
  first: any;
69
- items: Map<any, T>;
82
+ items: Map<any, CacheEntry<T>>;
70
83
  last: any;
71
84
  max: number;
72
85
  ttl: number;
@@ -103,29 +116,22 @@ declare class LruObject<T> implements ToadCache<T> {
103
116
  }
104
117
 
105
118
  declare class HitStatisticsRecord {
106
- records: Record<string, Record<string, {
107
- expirations: number,
108
- evictions: number,
109
- hits: number,
110
- emptyHits: number,
111
- falsyHits: number,
112
- misses: number,
113
- invalidateAll: number,
114
- invalidateOne: number,
115
- cacheSize: number,
116
- sets: number,
117
- }>>
119
+ records: Record<string, Record<string, CacheStatistics>>
118
120
 
119
121
  initForCache(cacheId: string, currentTimeStamp: string): void
120
122
  resetForCache(cacheId: string): void
123
+ getStatistics(): Record<string, Record<string, CacheStatistics>>
121
124
  }
122
125
 
123
126
  declare class LruObjectHitStatistics<T> extends LruObject<T>{
124
127
  constructor(max?: number, ttlInMsecs?: number, cacheId?: string, globalStatisticsRecord?: HitStatisticsRecord, statisticTtlInHours?: number);
128
+ getStatistics(): Record<string, Record<string, CacheStatistics>>
125
129
  }
126
130
 
127
131
  export {
128
132
  CacheConstructor,
133
+ CacheEntry,
134
+ CacheStatistics,
129
135
  ToadCache,
130
136
  LruObject,
131
137
  LruMap,