cacheable 0.8.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -15,10 +15,12 @@
15
15
  * Not bloated with additional modules
16
16
  * Extendable to your own caching engine
17
17
  * Scalable and trusted storage engine by Keyv
18
+ * Memory Caching with LRU and Expiration `CacheableMemory`
18
19
  * Resilient to failures with try/catch and offline
19
20
  * Hooks and Events to extend functionality
20
21
  * Comprehensive testing and code coverage
21
22
  * Distributed Caching Sync via Pub/Sub (coming soon)
23
+ * ESM and CommonJS support with TypeScript
22
24
  * Maintained and supported regularly
23
25
 
24
26
  ## Getting Started
@@ -129,11 +131,11 @@ The following options are available for you to configure `cacheable`:
129
131
  * `primary`: The primary store for the cache (layer 1) defaults to in-memory by Keyv.
130
132
  * `secondary`: The secondary store for the cache (layer 2) usually a persistent cache by Keyv.
131
133
  * `nonBlocking`: If the secondary store is non-blocking. Default is `false`.
132
- * `enableStats`: If you want to enable statistics for this instance. Default is `false`.
134
+ * `stats`: To enable statistics for this instance. Default is `false`.
133
135
 
134
136
  ## Cacheable Statistics (Instance Only)
135
137
 
136
- If you want to enable statistics for your instance you can set the `enableStats` property to `true` in the options. This will enable statistics for your instance and you can get the statistics by calling the `stats` property. Here are the following property statistics:
138
+ If you want to enable statistics for your instance you can set the `.stats.enabled` property to `true` in the options. This will enable statistics for your instance and you can get the statistics by calling the `stats` property. Here are the following property statistics:
137
139
 
138
140
  * `hits`: The number of hits in the cache.
139
141
  * `misses`: The number of misses in the cache.
@@ -145,7 +147,7 @@ If you want to enable statistics for your instance you can set the `enableStats`
145
147
  * `vsize`: The estimated byte size of the values in the cache.
146
148
  * `ksize`: The estimated byte size of the keys in the cache.
147
149
 
148
- You can clear the stats by calling the `clearStats()` method.
150
+ You can clear / reset the stats by calling the `.stats.reset()` method.
149
151
 
150
152
  _This does not enable statistics for your layer 2 cache as that is a distributed cache_.
151
153
 
@@ -157,13 +159,11 @@ _This does not enable statistics for your layer 2 cache as that is a distributed
157
159
  * `getMany([keys])`: Gets multiple values from the cache.
158
160
  * `has(key | [key])`: Checks if a value exists in the cache.
159
161
  * `hasMany([keys])`: Checks if multiple values exist in the cache.
160
- * `take(key)`: Takes a value from the cache and deletes it. (coming soon)
161
- * `takeMany([keys])`: Takes multiple values from the cache and deletes them. (coming soon)
162
+ * `take(key)`: Takes a value from the cache and deletes it.
163
+ * `takeMany([keys])`: Takes multiple values from the cache and deletes them.
162
164
  * `delete(key | [key])`: Deletes a value from the cache.
163
165
  * `deleteMany([keys])`: Deletes multiple values from the cache.
164
166
  * `clear()`: Clears the cache stores. Be careful with this as it will clear both layer 1 and layer 2.
165
- * `clearPrimary()`: Clears the primary store. (coming soon)
166
- * `clearSecondary()`: Clears the secondary store. (coming soon)
167
167
  * `wrap(function, options)`: Wraps a function in a cache. (coming soon)
168
168
  * `disconnect()`: Disconnects from the cache stores.
169
169
  * `onHook(hook, callback)`: Sets a hook.
@@ -174,7 +174,37 @@ _This does not enable statistics for your layer 2 cache as that is a distributed
174
174
  * `secondary`: The secondary store for the cache (layer 2) usually a persistent cache by Keyv.
175
175
  * `nonBlocking`: If the secondary store is non-blocking. Default is `false`.
176
176
  * `stats`: The statistics for this instance which includes `hits`, `misses`, `sets`, `deletes`, `clears`, `errors`, `count`, `vsize`, `ksize`.
177
- * `clearStats()`: Clears the statistics for this instance.
177
+
178
+ ## CacheableMemory - In-Memory Cache
179
+
180
+ `cacheable` comes with a built-in in-memory cache called `CacheableMemory`. This is a simple in-memory cache that is used as the primary store for `cacheable`. You can use this as a standalone cache or as a primary store for `cacheable`. Here is an example of how to use `CacheableMemory`:
181
+
182
+ ```javascript
183
+ import { CacheableMemory } from 'cacheable';
184
+ const options = {
185
+ ttl: 60 * 60 * 1000, // 1 hour
186
+ useClones: true, // use clones for the values (default is true)
187
+ lruSize: 1000, // the size of the LRU cache (default is 0 which is unlimited)
188
+ }
189
+ const cache = new CacheableMemory(options);
190
+ await cache.set('key', 'value');
191
+ const value = await cache.get('key'); // value
192
+ ```
193
+
194
+ You can use `CacheableMemory` as a standalone cache or as a primary store for `cacheable`. You can also set the `useClones` property to `false` if you want to use the same reference for the values. This is useful if you are using large objects and want to save memory. The `lruSize` property is the size of the LRU cache and is set to `0` by default which is unlimited. When setting the `lruSize` property it will limit the number of keys in the cache.
195
+
196
+ This simple in-memory cache uses multiple Map objects and a with `expiration` and `lru` policies if set to manage the in memory cache at scale.
197
+
198
+ ### CacheableMemory API
199
+
200
+ * `set(key, value, ttl?)`: Sets a value in the cache.
201
+ * `get(key)`: Gets a value from the cache.
202
+ * `has(key)`: Checks if a value exists in the cache.
203
+ * `delete(key)`: Deletes a value from the cache.
204
+ * `clear()`: Clears the cache.
205
+ * `size()`: The number of keys in the cache.
206
+ * `keys()`: The keys in the cache.
207
+
178
208
 
179
209
  ## How to Contribute
180
210
 
package/dist/index.cjs CHANGED
@@ -22,7 +22,9 @@ var src_exports = {};
22
22
  __export(src_exports, {
23
23
  Cacheable: () => Cacheable,
24
24
  CacheableEvents: () => CacheableEvents,
25
- CacheableHooks: () => CacheableHooks
25
+ CacheableHooks: () => CacheableHooks,
26
+ CacheableMemory: () => CacheableMemory,
27
+ CacheableStats: () => CacheableStats
26
28
  });
27
29
  module.exports = __toCommonJS(src_exports);
28
30
  var import_keyv = require("keyv");
@@ -148,7 +150,7 @@ var CacheableStats = class {
148
150
  }
149
151
  this._count++;
150
152
  }
151
- descreaseCount() {
153
+ decreaseCount() {
152
154
  if (!this._enabled) {
153
155
  return;
154
156
  }
@@ -196,6 +198,304 @@ var CacheableStats = class {
196
198
  this._ksize = 0;
197
199
  this._count = 0;
198
200
  }
201
+ resetStoreValues() {
202
+ this._vsize = 0;
203
+ this._ksize = 0;
204
+ this._count = 0;
205
+ }
206
+ };
207
+
208
+ // src/memory-lru.ts
209
+ var ListNode = class {
210
+ // eslint-disable-next-line @typescript-eslint/parameter-properties
211
+ value;
212
+ prev = void 0;
213
+ next = void 0;
214
+ constructor(value) {
215
+ this.value = value;
216
+ }
217
+ };
218
+ var DoublyLinkedList = class {
219
+ head = void 0;
220
+ tail = void 0;
221
+ nodesMap = /* @__PURE__ */ new Map();
222
+ // Add a new node to the front (most recently used)
223
+ addToFront(value) {
224
+ const newNode = new ListNode(value);
225
+ if (this.head) {
226
+ newNode.next = this.head;
227
+ this.head.prev = newNode;
228
+ this.head = newNode;
229
+ } else {
230
+ this.head = this.tail = newNode;
231
+ }
232
+ this.nodesMap.set(value, newNode);
233
+ }
234
+ // Move an existing node to the front (most recently used)
235
+ moveToFront(value) {
236
+ const node = this.nodesMap.get(value);
237
+ if (!node || this.head === node) {
238
+ return;
239
+ }
240
+ if (node.prev) {
241
+ node.prev.next = node.next;
242
+ }
243
+ if (node.next) {
244
+ node.next.prev = node.prev;
245
+ }
246
+ if (node === this.tail) {
247
+ this.tail = node.prev;
248
+ }
249
+ node.prev = void 0;
250
+ node.next = this.head;
251
+ if (this.head) {
252
+ this.head.prev = node;
253
+ }
254
+ this.head = node;
255
+ this.tail ||= node;
256
+ }
257
+ // Get the oldest node (tail)
258
+ getOldest() {
259
+ return this.tail ? this.tail.value : void 0;
260
+ }
261
+ // Remove the oldest node (tail)
262
+ removeOldest() {
263
+ if (!this.tail) {
264
+ return void 0;
265
+ }
266
+ const oldValue = this.tail.value;
267
+ if (this.tail.prev) {
268
+ this.tail = this.tail.prev;
269
+ this.tail.next = void 0;
270
+ } else {
271
+ this.head = this.tail = void 0;
272
+ }
273
+ this.nodesMap.delete(oldValue);
274
+ return oldValue;
275
+ }
276
+ get size() {
277
+ return this.nodesMap.size;
278
+ }
279
+ };
280
+
281
+ // src/memory.ts
282
+ var CacheableMemory = class {
283
+ _hashCache = /* @__PURE__ */ new Map();
284
+ _hash0 = /* @__PURE__ */ new Map();
285
+ _hash1 = /* @__PURE__ */ new Map();
286
+ _hash2 = /* @__PURE__ */ new Map();
287
+ _hash3 = /* @__PURE__ */ new Map();
288
+ _hash4 = /* @__PURE__ */ new Map();
289
+ _hash5 = /* @__PURE__ */ new Map();
290
+ _hash6 = /* @__PURE__ */ new Map();
291
+ _hash7 = /* @__PURE__ */ new Map();
292
+ _hash8 = /* @__PURE__ */ new Map();
293
+ _hash9 = /* @__PURE__ */ new Map();
294
+ _lru = new DoublyLinkedList();
295
+ _ttl = 0;
296
+ _useClone = true;
297
+ _lruSize = 0;
298
+ constructor(options) {
299
+ if (options?.ttl) {
300
+ this._ttl = options.ttl;
301
+ }
302
+ if (options?.useClone !== void 0) {
303
+ this._useClone = options.useClone;
304
+ }
305
+ if (options?.lruSize) {
306
+ this._lruSize = options.lruSize;
307
+ }
308
+ }
309
+ get ttl() {
310
+ return this._ttl;
311
+ }
312
+ set ttl(value) {
313
+ this._ttl = value;
314
+ }
315
+ get useClone() {
316
+ return this._useClone;
317
+ }
318
+ set useClone(value) {
319
+ this._useClone = value;
320
+ }
321
+ get lruSize() {
322
+ return this._lruSize;
323
+ }
324
+ set lruSize(value) {
325
+ this._lruSize = value;
326
+ this.lruResize();
327
+ }
328
+ get size() {
329
+ return this._hash0.size + this._hash1.size + this._hash2.size + this._hash3.size + this._hash4.size + this._hash5.size + this._hash6.size + this._hash7.size + this._hash8.size + this._hash9.size;
330
+ }
331
+ get keys() {
332
+ return this.concatStores().keys();
333
+ }
334
+ get(key) {
335
+ const store = this.getStore(key);
336
+ const item = store.get(key);
337
+ if (!item) {
338
+ return void 0;
339
+ }
340
+ if (item.expires && item.expires && Date.now() > item.expires) {
341
+ store.delete(key);
342
+ return void 0;
343
+ }
344
+ this.lruMoveToFront(key);
345
+ if (!this._useClone) {
346
+ return item.value;
347
+ }
348
+ return this.clone(item.value);
349
+ }
350
+ set(key, value, ttl) {
351
+ const store = this.getStore(key);
352
+ let expires;
353
+ if (ttl !== void 0 || this._ttl !== 0) {
354
+ expires = Date.now() + (ttl ?? this._ttl);
355
+ }
356
+ if (this._lruSize > 0) {
357
+ if (store.has(key)) {
358
+ this.lruMoveToFront(key);
359
+ } else {
360
+ this.lruAddToFront(key);
361
+ if (this._lru.size > this._lruSize) {
362
+ const oldestKey = this._lru.getOldest();
363
+ if (oldestKey) {
364
+ this._lru.removeOldest();
365
+ this.delete(oldestKey);
366
+ }
367
+ }
368
+ }
369
+ }
370
+ store.set(key, {
371
+ key,
372
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
373
+ value,
374
+ expires
375
+ });
376
+ }
377
+ has(key) {
378
+ const item = this.get(key);
379
+ return Boolean(item);
380
+ }
381
+ take(key) {
382
+ const item = this.get(key);
383
+ if (!item) {
384
+ return void 0;
385
+ }
386
+ this.delete(key);
387
+ return item;
388
+ }
389
+ delete(key) {
390
+ const store = this.getStore(key);
391
+ store.delete(key);
392
+ }
393
+ clear() {
394
+ this._hash0.clear();
395
+ this._hash1.clear();
396
+ this._hash2.clear();
397
+ this._hash3.clear();
398
+ this._hash4.clear();
399
+ this._hash5.clear();
400
+ this._hash6.clear();
401
+ this._hash7.clear();
402
+ this._hash8.clear();
403
+ this._hash9.clear();
404
+ this._hashCache.clear();
405
+ }
406
+ hashKey(key) {
407
+ const cacheHashNumber = this._hashCache.get(key);
408
+ if (cacheHashNumber) {
409
+ return cacheHashNumber;
410
+ }
411
+ let hash = 0;
412
+ const primeMultiplier = 31;
413
+ for (let i = 0; i < key.length; i++) {
414
+ hash = hash * primeMultiplier + key.charCodeAt(i);
415
+ }
416
+ const result = Math.abs(hash) % 10;
417
+ this._hashCache.set(key, result);
418
+ return result;
419
+ }
420
+ getStore(key) {
421
+ const hashKey = this.hashKey(key);
422
+ switch (hashKey) {
423
+ case 1: {
424
+ return this._hash1;
425
+ }
426
+ case 2: {
427
+ return this._hash2;
428
+ }
429
+ case 3: {
430
+ return this._hash3;
431
+ }
432
+ case 4: {
433
+ return this._hash4;
434
+ }
435
+ case 5: {
436
+ return this._hash5;
437
+ }
438
+ case 6: {
439
+ return this._hash6;
440
+ }
441
+ case 7: {
442
+ return this._hash7;
443
+ }
444
+ case 8: {
445
+ return this._hash8;
446
+ }
447
+ case 9: {
448
+ return this._hash9;
449
+ }
450
+ default: {
451
+ return this._hash0;
452
+ }
453
+ }
454
+ }
455
+ clone(value) {
456
+ if (this.isPrimitive(value)) {
457
+ return value;
458
+ }
459
+ return structuredClone(value);
460
+ }
461
+ lruAddToFront(key) {
462
+ if (this._lruSize === 0) {
463
+ return;
464
+ }
465
+ this._lru.addToFront(key);
466
+ }
467
+ lruMoveToFront(key) {
468
+ if (this._lruSize === 0) {
469
+ return;
470
+ }
471
+ this._lru.moveToFront(key);
472
+ }
473
+ lruResize() {
474
+ if (this._lruSize === 0) {
475
+ return;
476
+ }
477
+ while (this._lru.size > this._lruSize) {
478
+ const oldestKey = this._lru.getOldest();
479
+ if (oldestKey) {
480
+ this._lru.removeOldest();
481
+ this.delete(oldestKey);
482
+ }
483
+ }
484
+ }
485
+ isPrimitive(value) {
486
+ const result = false;
487
+ if (value === null || value === void 0) {
488
+ return true;
489
+ }
490
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
491
+ return true;
492
+ }
493
+ return result;
494
+ }
495
+ concatStores() {
496
+ const result = new Map([...this._hash0, ...this._hash1, ...this._hash2, ...this._hash3, ...this._hash4, ...this._hash5, ...this._hash6, ...this._hash7, ...this._hash8, ...this._hash9]);
497
+ return result;
498
+ }
199
499
  };
200
500
 
201
501
  // src/index.ts
@@ -215,9 +515,10 @@ var CacheableEvents = /* @__PURE__ */ ((CacheableEvents2) => {
215
515
  return CacheableEvents2;
216
516
  })(CacheableEvents || {});
217
517
  var Cacheable = class extends import_hookified.Hookified {
218
- _primary = new import_keyv.Keyv();
518
+ _primary = new import_keyv.Keyv({ store: new CacheableMemory() });
219
519
  _secondary;
220
520
  _nonBlocking = false;
521
+ _ttl;
221
522
  _stats = new CacheableStats({ enabled: false });
222
523
  constructor(options) {
223
524
  super();
@@ -233,6 +534,9 @@ var Cacheable = class extends import_hookified.Hookified {
233
534
  if (options?.stats) {
234
535
  this._stats.enabled = options.stats;
235
536
  }
537
+ if (options?.ttl) {
538
+ this._ttl = options.ttl;
539
+ }
236
540
  }
237
541
  get stats() {
238
542
  return this._stats;
@@ -255,6 +559,12 @@ var Cacheable = class extends import_hookified.Hookified {
255
559
  set nonBlocking(nonBlocking) {
256
560
  this._nonBlocking = nonBlocking;
257
561
  }
562
+ get ttl() {
563
+ return this._ttl;
564
+ }
565
+ set ttl(ttl) {
566
+ this._ttl = ttl;
567
+ }
258
568
  setPrimary(primary) {
259
569
  this._primary = primary instanceof import_keyv.Keyv ? primary : new import_keyv.Keyv(primary);
260
570
  }
@@ -269,13 +579,21 @@ var Cacheable = class extends import_hookified.Hookified {
269
579
  if (!result && this._secondary) {
270
580
  result = await this._secondary.get(key);
271
581
  if (result) {
272
- await this._primary.set(key, result);
582
+ await this._primary.set(key, result, this._ttl);
273
583
  }
274
584
  }
275
585
  await this.hook("AFTER_GET" /* AFTER_GET */, { key, result });
276
586
  } catch (error) {
277
587
  await this.emit("error" /* ERROR */, error);
278
588
  }
589
+ if (this.stats.enabled) {
590
+ if (result) {
591
+ this._stats.incrementHits();
592
+ } else {
593
+ this._stats.incrementMisses();
594
+ }
595
+ this.stats.incrementGets();
596
+ }
279
597
  return result;
280
598
  }
281
599
  async getMany(keys) {
@@ -294,7 +612,7 @@ var Cacheable = class extends import_hookified.Hookified {
294
612
  for (const [i, key] of keys.entries()) {
295
613
  if (!result[i] && secondaryResult[i]) {
296
614
  result[i] = secondaryResult[i];
297
- await this._primary.set(key, secondaryResult[i]);
615
+ await this._primary.set(key, secondaryResult[i], this._ttl);
298
616
  }
299
617
  }
300
618
  }
@@ -302,16 +620,27 @@ var Cacheable = class extends import_hookified.Hookified {
302
620
  } catch (error) {
303
621
  await this.emit("error" /* ERROR */, error);
304
622
  }
623
+ if (this.stats.enabled) {
624
+ for (const item of result) {
625
+ if (item) {
626
+ this._stats.incrementHits();
627
+ } else {
628
+ this._stats.incrementMisses();
629
+ }
630
+ }
631
+ this.stats.incrementGets();
632
+ }
305
633
  return result;
306
634
  }
307
635
  async set(key, value, ttl) {
308
636
  let result = false;
637
+ const finalTtl = ttl ?? this._ttl;
309
638
  try {
310
- await this.hook("BEFORE_SET" /* BEFORE_SET */, { key, value, ttl });
639
+ await this.hook("BEFORE_SET" /* BEFORE_SET */, { key, value, finalTtl });
311
640
  const promises = [];
312
- promises.push(this._primary.set(key, value, ttl));
641
+ promises.push(this._primary.set(key, value, finalTtl));
313
642
  if (this._secondary) {
314
- promises.push(this._secondary.set(key, value, ttl));
643
+ promises.push(this._secondary.set(key, value, finalTtl));
315
644
  }
316
645
  if (this._nonBlocking) {
317
646
  result = await Promise.race(promises);
@@ -319,10 +648,16 @@ var Cacheable = class extends import_hookified.Hookified {
319
648
  const results = await Promise.all(promises);
320
649
  result = results[0];
321
650
  }
322
- await this.hook("AFTER_SET" /* AFTER_SET */, { key, value, ttl });
651
+ await this.hook("AFTER_SET" /* AFTER_SET */, { key, value, finalTtl });
323
652
  } catch (error) {
324
653
  await this.emit("error" /* ERROR */, error);
325
654
  }
655
+ if (this.stats.enabled) {
656
+ this.stats.incrementKSize(key);
657
+ this.stats.incrementCount();
658
+ this.stats.incrementVSize(value);
659
+ this.stats.incrementSets();
660
+ }
326
661
  return result;
327
662
  }
328
663
  async setMany(items) {
@@ -341,6 +676,13 @@ var Cacheable = class extends import_hookified.Hookified {
341
676
  } catch (error) {
342
677
  await this.emit("error" /* ERROR */, error);
343
678
  }
679
+ if (this.stats.enabled) {
680
+ for (const item of items) {
681
+ this.stats.incrementKSize(item.key);
682
+ this.stats.incrementCount();
683
+ this.stats.incrementVSize(item.value);
684
+ }
685
+ }
344
686
  return result;
345
687
  }
346
688
  async take(key) {
@@ -354,11 +696,18 @@ var Cacheable = class extends import_hookified.Hookified {
354
696
  return result;
355
697
  }
356
698
  async has(key) {
357
- let result = await this._primary.has(key);
358
- if (!result && this._secondary) {
359
- result = await this._secondary.has(key);
699
+ const promises = [];
700
+ promises.push(this._primary.has(key));
701
+ if (this._secondary) {
702
+ promises.push(this._secondary.has(key));
360
703
  }
361
- return result;
704
+ const resultAll = await Promise.all(promises);
705
+ for (const result of resultAll) {
706
+ if (result) {
707
+ return true;
708
+ }
709
+ }
710
+ return false;
362
711
  }
363
712
  async hasMany(keys) {
364
713
  const result = await this.hasManyKeyv(this._primary, keys);
@@ -379,17 +728,39 @@ var Cacheable = class extends import_hookified.Hookified {
379
728
  return result;
380
729
  }
381
730
  async delete(key) {
382
- const result = await this._primary.delete(key);
383
- if (this._secondary) {
384
- if (this._nonBlocking) {
385
- this._secondary.delete(key);
386
- } else {
387
- await this._secondary.delete(key);
731
+ let result = false;
732
+ const promises = [];
733
+ if (this.stats.enabled) {
734
+ const statResult = await this._primary.get(key);
735
+ if (statResult) {
736
+ this.stats.decreaseKSize(key);
737
+ this.stats.decreaseVSize(statResult);
738
+ this.stats.decreaseCount();
739
+ this.stats.incrementDeletes();
388
740
  }
389
741
  }
742
+ promises.push(this._primary.delete(key));
743
+ if (this._secondary) {
744
+ promises.push(this._secondary.delete(key));
745
+ }
746
+ if (this.nonBlocking) {
747
+ result = await Promise.race(promises);
748
+ } else {
749
+ const resultAll = await Promise.all(promises);
750
+ result = resultAll[0];
751
+ }
390
752
  return result;
391
753
  }
392
754
  async deleteMany(keys) {
755
+ if (this.stats.enabled) {
756
+ const statResult = await this._primary.get(keys);
757
+ for (const key of keys) {
758
+ this.stats.decreaseKSize(key);
759
+ this.stats.decreaseVSize(statResult);
760
+ this.stats.decreaseCount();
761
+ this.stats.incrementDeletes();
762
+ }
763
+ }
393
764
  const result = await this.deleteManyKeyv(this._primary, keys);
394
765
  if (this._secondary) {
395
766
  if (this._nonBlocking) {
@@ -407,6 +778,10 @@ var Cacheable = class extends import_hookified.Hookified {
407
778
  promises.push(this._secondary.clear());
408
779
  }
409
780
  await (this._nonBlocking ? Promise.race(promises) : Promise.all(promises));
781
+ if (this.stats.enabled) {
782
+ this._stats.resetStoreValues();
783
+ this._stats.incrementClears();
784
+ }
410
785
  }
411
786
  async disconnect() {
412
787
  const promises = [];
@@ -427,7 +802,8 @@ var Cacheable = class extends import_hookified.Hookified {
427
802
  async setManyKeyv(keyv, items) {
428
803
  const promises = [];
429
804
  for (const item of items) {
430
- promises.push(keyv.set(item.key, item.value, item.ttl));
805
+ const finalTtl = item.ttl ?? this._ttl;
806
+ promises.push(keyv.set(item.key, item.value, finalTtl));
431
807
  }
432
808
  await Promise.all(promises);
433
809
  return true;
@@ -444,5 +820,7 @@ var Cacheable = class extends import_hookified.Hookified {
444
820
  0 && (module.exports = {
445
821
  Cacheable,
446
822
  CacheableEvents,
447
- CacheableHooks
823
+ CacheableHooks,
824
+ CacheableMemory,
825
+ CacheableStats
448
826
  });
package/dist/index.d.cts CHANGED
@@ -38,11 +38,58 @@ declare class CacheableStats {
38
38
  incrementKSize(key: string): void;
39
39
  decreaseKSize(key: string): void;
40
40
  incrementCount(): void;
41
- descreaseCount(): void;
41
+ decreaseCount(): void;
42
42
  setCount(count: number): void;
43
43
  roughSizeOfString(value: string): number;
44
44
  roughSizeOfObject(object: any): number;
45
45
  reset(): void;
46
+ resetStoreValues(): void;
47
+ }
48
+
49
+ type CacheableMemoryOptions = {
50
+ ttl?: number;
51
+ useClone?: boolean;
52
+ lruSize?: number;
53
+ };
54
+ declare class CacheableMemory {
55
+ private readonly _hashCache;
56
+ private readonly _hash0;
57
+ private readonly _hash1;
58
+ private readonly _hash2;
59
+ private readonly _hash3;
60
+ private readonly _hash4;
61
+ private readonly _hash5;
62
+ private readonly _hash6;
63
+ private readonly _hash7;
64
+ private readonly _hash8;
65
+ private readonly _hash9;
66
+ private readonly _lru;
67
+ private _ttl;
68
+ private _useClone;
69
+ private _lruSize;
70
+ constructor(options?: CacheableMemoryOptions);
71
+ get ttl(): number;
72
+ set ttl(value: number);
73
+ get useClone(): boolean;
74
+ set useClone(value: boolean);
75
+ get lruSize(): number;
76
+ set lruSize(value: number);
77
+ get size(): number;
78
+ get keys(): IterableIterator<string>;
79
+ get<T>(key: string): any;
80
+ set(key: string, value: any, ttl?: number): void;
81
+ has(key: string): boolean;
82
+ take<T>(key: string): any;
83
+ delete(key: string): void;
84
+ clear(): void;
85
+ hashKey(key: string): number;
86
+ getStore(key: string): Map<string, any>;
87
+ clone(value: any): any;
88
+ lruAddToFront(key: string): void;
89
+ lruMoveToFront(key: string): void;
90
+ lruResize(): void;
91
+ private isPrimitive;
92
+ private concatStores;
46
93
  }
47
94
 
48
95
  declare enum CacheableHooks {
@@ -68,11 +115,13 @@ type CacheableOptions = {
68
115
  secondary?: Keyv | KeyvStoreAdapter;
69
116
  stats?: boolean;
70
117
  nonBlocking?: boolean;
118
+ ttl?: number;
71
119
  };
72
120
  declare class Cacheable extends Hookified {
73
121
  private _primary;
74
122
  private _secondary;
75
123
  private _nonBlocking;
124
+ private _ttl?;
76
125
  private readonly _stats;
77
126
  constructor(options?: CacheableOptions);
78
127
  get stats(): CacheableStats;
@@ -82,6 +131,8 @@ declare class Cacheable extends Hookified {
82
131
  set secondary(secondary: Keyv | undefined);
83
132
  get nonBlocking(): boolean;
84
133
  set nonBlocking(nonBlocking: boolean);
134
+ get ttl(): number | undefined;
135
+ set ttl(ttl: number | undefined);
85
136
  setPrimary(primary: Keyv | KeyvStoreAdapter): void;
86
137
  setSecondary(secondary: Keyv | KeyvStoreAdapter): void;
87
138
  get<T>(key: string): Promise<T | undefined>;
@@ -101,4 +152,4 @@ declare class Cacheable extends Hookified {
101
152
  private hasManyKeyv;
102
153
  }
103
154
 
104
- export { Cacheable, CacheableEvents, CacheableHooks, type CacheableItem, type CacheableOptions };
155
+ export { Cacheable, CacheableEvents, CacheableHooks, type CacheableItem, CacheableMemory, type CacheableOptions, CacheableStats };
package/dist/index.d.ts CHANGED
@@ -38,11 +38,58 @@ declare class CacheableStats {
38
38
  incrementKSize(key: string): void;
39
39
  decreaseKSize(key: string): void;
40
40
  incrementCount(): void;
41
- descreaseCount(): void;
41
+ decreaseCount(): void;
42
42
  setCount(count: number): void;
43
43
  roughSizeOfString(value: string): number;
44
44
  roughSizeOfObject(object: any): number;
45
45
  reset(): void;
46
+ resetStoreValues(): void;
47
+ }
48
+
49
+ type CacheableMemoryOptions = {
50
+ ttl?: number;
51
+ useClone?: boolean;
52
+ lruSize?: number;
53
+ };
54
+ declare class CacheableMemory {
55
+ private readonly _hashCache;
56
+ private readonly _hash0;
57
+ private readonly _hash1;
58
+ private readonly _hash2;
59
+ private readonly _hash3;
60
+ private readonly _hash4;
61
+ private readonly _hash5;
62
+ private readonly _hash6;
63
+ private readonly _hash7;
64
+ private readonly _hash8;
65
+ private readonly _hash9;
66
+ private readonly _lru;
67
+ private _ttl;
68
+ private _useClone;
69
+ private _lruSize;
70
+ constructor(options?: CacheableMemoryOptions);
71
+ get ttl(): number;
72
+ set ttl(value: number);
73
+ get useClone(): boolean;
74
+ set useClone(value: boolean);
75
+ get lruSize(): number;
76
+ set lruSize(value: number);
77
+ get size(): number;
78
+ get keys(): IterableIterator<string>;
79
+ get<T>(key: string): any;
80
+ set(key: string, value: any, ttl?: number): void;
81
+ has(key: string): boolean;
82
+ take<T>(key: string): any;
83
+ delete(key: string): void;
84
+ clear(): void;
85
+ hashKey(key: string): number;
86
+ getStore(key: string): Map<string, any>;
87
+ clone(value: any): any;
88
+ lruAddToFront(key: string): void;
89
+ lruMoveToFront(key: string): void;
90
+ lruResize(): void;
91
+ private isPrimitive;
92
+ private concatStores;
46
93
  }
47
94
 
48
95
  declare enum CacheableHooks {
@@ -68,11 +115,13 @@ type CacheableOptions = {
68
115
  secondary?: Keyv | KeyvStoreAdapter;
69
116
  stats?: boolean;
70
117
  nonBlocking?: boolean;
118
+ ttl?: number;
71
119
  };
72
120
  declare class Cacheable extends Hookified {
73
121
  private _primary;
74
122
  private _secondary;
75
123
  private _nonBlocking;
124
+ private _ttl?;
76
125
  private readonly _stats;
77
126
  constructor(options?: CacheableOptions);
78
127
  get stats(): CacheableStats;
@@ -82,6 +131,8 @@ declare class Cacheable extends Hookified {
82
131
  set secondary(secondary: Keyv | undefined);
83
132
  get nonBlocking(): boolean;
84
133
  set nonBlocking(nonBlocking: boolean);
134
+ get ttl(): number | undefined;
135
+ set ttl(ttl: number | undefined);
85
136
  setPrimary(primary: Keyv | KeyvStoreAdapter): void;
86
137
  setSecondary(secondary: Keyv | KeyvStoreAdapter): void;
87
138
  get<T>(key: string): Promise<T | undefined>;
@@ -101,4 +152,4 @@ declare class Cacheable extends Hookified {
101
152
  private hasManyKeyv;
102
153
  }
103
154
 
104
- export { Cacheable, CacheableEvents, CacheableHooks, type CacheableItem, type CacheableOptions };
155
+ export { Cacheable, CacheableEvents, CacheableHooks, type CacheableItem, CacheableMemory, type CacheableOptions, CacheableStats };
package/dist/index.js CHANGED
@@ -122,7 +122,7 @@ var CacheableStats = class {
122
122
  }
123
123
  this._count++;
124
124
  }
125
- descreaseCount() {
125
+ decreaseCount() {
126
126
  if (!this._enabled) {
127
127
  return;
128
128
  }
@@ -170,6 +170,304 @@ var CacheableStats = class {
170
170
  this._ksize = 0;
171
171
  this._count = 0;
172
172
  }
173
+ resetStoreValues() {
174
+ this._vsize = 0;
175
+ this._ksize = 0;
176
+ this._count = 0;
177
+ }
178
+ };
179
+
180
+ // src/memory-lru.ts
181
+ var ListNode = class {
182
+ // eslint-disable-next-line @typescript-eslint/parameter-properties
183
+ value;
184
+ prev = void 0;
185
+ next = void 0;
186
+ constructor(value) {
187
+ this.value = value;
188
+ }
189
+ };
190
+ var DoublyLinkedList = class {
191
+ head = void 0;
192
+ tail = void 0;
193
+ nodesMap = /* @__PURE__ */ new Map();
194
+ // Add a new node to the front (most recently used)
195
+ addToFront(value) {
196
+ const newNode = new ListNode(value);
197
+ if (this.head) {
198
+ newNode.next = this.head;
199
+ this.head.prev = newNode;
200
+ this.head = newNode;
201
+ } else {
202
+ this.head = this.tail = newNode;
203
+ }
204
+ this.nodesMap.set(value, newNode);
205
+ }
206
+ // Move an existing node to the front (most recently used)
207
+ moveToFront(value) {
208
+ const node = this.nodesMap.get(value);
209
+ if (!node || this.head === node) {
210
+ return;
211
+ }
212
+ if (node.prev) {
213
+ node.prev.next = node.next;
214
+ }
215
+ if (node.next) {
216
+ node.next.prev = node.prev;
217
+ }
218
+ if (node === this.tail) {
219
+ this.tail = node.prev;
220
+ }
221
+ node.prev = void 0;
222
+ node.next = this.head;
223
+ if (this.head) {
224
+ this.head.prev = node;
225
+ }
226
+ this.head = node;
227
+ this.tail ||= node;
228
+ }
229
+ // Get the oldest node (tail)
230
+ getOldest() {
231
+ return this.tail ? this.tail.value : void 0;
232
+ }
233
+ // Remove the oldest node (tail)
234
+ removeOldest() {
235
+ if (!this.tail) {
236
+ return void 0;
237
+ }
238
+ const oldValue = this.tail.value;
239
+ if (this.tail.prev) {
240
+ this.tail = this.tail.prev;
241
+ this.tail.next = void 0;
242
+ } else {
243
+ this.head = this.tail = void 0;
244
+ }
245
+ this.nodesMap.delete(oldValue);
246
+ return oldValue;
247
+ }
248
+ get size() {
249
+ return this.nodesMap.size;
250
+ }
251
+ };
252
+
253
+ // src/memory.ts
254
+ var CacheableMemory = class {
255
+ _hashCache = /* @__PURE__ */ new Map();
256
+ _hash0 = /* @__PURE__ */ new Map();
257
+ _hash1 = /* @__PURE__ */ new Map();
258
+ _hash2 = /* @__PURE__ */ new Map();
259
+ _hash3 = /* @__PURE__ */ new Map();
260
+ _hash4 = /* @__PURE__ */ new Map();
261
+ _hash5 = /* @__PURE__ */ new Map();
262
+ _hash6 = /* @__PURE__ */ new Map();
263
+ _hash7 = /* @__PURE__ */ new Map();
264
+ _hash8 = /* @__PURE__ */ new Map();
265
+ _hash9 = /* @__PURE__ */ new Map();
266
+ _lru = new DoublyLinkedList();
267
+ _ttl = 0;
268
+ _useClone = true;
269
+ _lruSize = 0;
270
+ constructor(options) {
271
+ if (options?.ttl) {
272
+ this._ttl = options.ttl;
273
+ }
274
+ if (options?.useClone !== void 0) {
275
+ this._useClone = options.useClone;
276
+ }
277
+ if (options?.lruSize) {
278
+ this._lruSize = options.lruSize;
279
+ }
280
+ }
281
+ get ttl() {
282
+ return this._ttl;
283
+ }
284
+ set ttl(value) {
285
+ this._ttl = value;
286
+ }
287
+ get useClone() {
288
+ return this._useClone;
289
+ }
290
+ set useClone(value) {
291
+ this._useClone = value;
292
+ }
293
+ get lruSize() {
294
+ return this._lruSize;
295
+ }
296
+ set lruSize(value) {
297
+ this._lruSize = value;
298
+ this.lruResize();
299
+ }
300
+ get size() {
301
+ return this._hash0.size + this._hash1.size + this._hash2.size + this._hash3.size + this._hash4.size + this._hash5.size + this._hash6.size + this._hash7.size + this._hash8.size + this._hash9.size;
302
+ }
303
+ get keys() {
304
+ return this.concatStores().keys();
305
+ }
306
+ get(key) {
307
+ const store = this.getStore(key);
308
+ const item = store.get(key);
309
+ if (!item) {
310
+ return void 0;
311
+ }
312
+ if (item.expires && item.expires && Date.now() > item.expires) {
313
+ store.delete(key);
314
+ return void 0;
315
+ }
316
+ this.lruMoveToFront(key);
317
+ if (!this._useClone) {
318
+ return item.value;
319
+ }
320
+ return this.clone(item.value);
321
+ }
322
+ set(key, value, ttl) {
323
+ const store = this.getStore(key);
324
+ let expires;
325
+ if (ttl !== void 0 || this._ttl !== 0) {
326
+ expires = Date.now() + (ttl ?? this._ttl);
327
+ }
328
+ if (this._lruSize > 0) {
329
+ if (store.has(key)) {
330
+ this.lruMoveToFront(key);
331
+ } else {
332
+ this.lruAddToFront(key);
333
+ if (this._lru.size > this._lruSize) {
334
+ const oldestKey = this._lru.getOldest();
335
+ if (oldestKey) {
336
+ this._lru.removeOldest();
337
+ this.delete(oldestKey);
338
+ }
339
+ }
340
+ }
341
+ }
342
+ store.set(key, {
343
+ key,
344
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
345
+ value,
346
+ expires
347
+ });
348
+ }
349
+ has(key) {
350
+ const item = this.get(key);
351
+ return Boolean(item);
352
+ }
353
+ take(key) {
354
+ const item = this.get(key);
355
+ if (!item) {
356
+ return void 0;
357
+ }
358
+ this.delete(key);
359
+ return item;
360
+ }
361
+ delete(key) {
362
+ const store = this.getStore(key);
363
+ store.delete(key);
364
+ }
365
+ clear() {
366
+ this._hash0.clear();
367
+ this._hash1.clear();
368
+ this._hash2.clear();
369
+ this._hash3.clear();
370
+ this._hash4.clear();
371
+ this._hash5.clear();
372
+ this._hash6.clear();
373
+ this._hash7.clear();
374
+ this._hash8.clear();
375
+ this._hash9.clear();
376
+ this._hashCache.clear();
377
+ }
378
+ hashKey(key) {
379
+ const cacheHashNumber = this._hashCache.get(key);
380
+ if (cacheHashNumber) {
381
+ return cacheHashNumber;
382
+ }
383
+ let hash = 0;
384
+ const primeMultiplier = 31;
385
+ for (let i = 0; i < key.length; i++) {
386
+ hash = hash * primeMultiplier + key.charCodeAt(i);
387
+ }
388
+ const result = Math.abs(hash) % 10;
389
+ this._hashCache.set(key, result);
390
+ return result;
391
+ }
392
+ getStore(key) {
393
+ const hashKey = this.hashKey(key);
394
+ switch (hashKey) {
395
+ case 1: {
396
+ return this._hash1;
397
+ }
398
+ case 2: {
399
+ return this._hash2;
400
+ }
401
+ case 3: {
402
+ return this._hash3;
403
+ }
404
+ case 4: {
405
+ return this._hash4;
406
+ }
407
+ case 5: {
408
+ return this._hash5;
409
+ }
410
+ case 6: {
411
+ return this._hash6;
412
+ }
413
+ case 7: {
414
+ return this._hash7;
415
+ }
416
+ case 8: {
417
+ return this._hash8;
418
+ }
419
+ case 9: {
420
+ return this._hash9;
421
+ }
422
+ default: {
423
+ return this._hash0;
424
+ }
425
+ }
426
+ }
427
+ clone(value) {
428
+ if (this.isPrimitive(value)) {
429
+ return value;
430
+ }
431
+ return structuredClone(value);
432
+ }
433
+ lruAddToFront(key) {
434
+ if (this._lruSize === 0) {
435
+ return;
436
+ }
437
+ this._lru.addToFront(key);
438
+ }
439
+ lruMoveToFront(key) {
440
+ if (this._lruSize === 0) {
441
+ return;
442
+ }
443
+ this._lru.moveToFront(key);
444
+ }
445
+ lruResize() {
446
+ if (this._lruSize === 0) {
447
+ return;
448
+ }
449
+ while (this._lru.size > this._lruSize) {
450
+ const oldestKey = this._lru.getOldest();
451
+ if (oldestKey) {
452
+ this._lru.removeOldest();
453
+ this.delete(oldestKey);
454
+ }
455
+ }
456
+ }
457
+ isPrimitive(value) {
458
+ const result = false;
459
+ if (value === null || value === void 0) {
460
+ return true;
461
+ }
462
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
463
+ return true;
464
+ }
465
+ return result;
466
+ }
467
+ concatStores() {
468
+ const result = new Map([...this._hash0, ...this._hash1, ...this._hash2, ...this._hash3, ...this._hash4, ...this._hash5, ...this._hash6, ...this._hash7, ...this._hash8, ...this._hash9]);
469
+ return result;
470
+ }
173
471
  };
174
472
 
175
473
  // src/index.ts
@@ -189,9 +487,10 @@ var CacheableEvents = /* @__PURE__ */ ((CacheableEvents2) => {
189
487
  return CacheableEvents2;
190
488
  })(CacheableEvents || {});
191
489
  var Cacheable = class extends Hookified {
192
- _primary = new Keyv();
490
+ _primary = new Keyv({ store: new CacheableMemory() });
193
491
  _secondary;
194
492
  _nonBlocking = false;
493
+ _ttl;
195
494
  _stats = new CacheableStats({ enabled: false });
196
495
  constructor(options) {
197
496
  super();
@@ -207,6 +506,9 @@ var Cacheable = class extends Hookified {
207
506
  if (options?.stats) {
208
507
  this._stats.enabled = options.stats;
209
508
  }
509
+ if (options?.ttl) {
510
+ this._ttl = options.ttl;
511
+ }
210
512
  }
211
513
  get stats() {
212
514
  return this._stats;
@@ -229,6 +531,12 @@ var Cacheable = class extends Hookified {
229
531
  set nonBlocking(nonBlocking) {
230
532
  this._nonBlocking = nonBlocking;
231
533
  }
534
+ get ttl() {
535
+ return this._ttl;
536
+ }
537
+ set ttl(ttl) {
538
+ this._ttl = ttl;
539
+ }
232
540
  setPrimary(primary) {
233
541
  this._primary = primary instanceof Keyv ? primary : new Keyv(primary);
234
542
  }
@@ -243,13 +551,21 @@ var Cacheable = class extends Hookified {
243
551
  if (!result && this._secondary) {
244
552
  result = await this._secondary.get(key);
245
553
  if (result) {
246
- await this._primary.set(key, result);
554
+ await this._primary.set(key, result, this._ttl);
247
555
  }
248
556
  }
249
557
  await this.hook("AFTER_GET" /* AFTER_GET */, { key, result });
250
558
  } catch (error) {
251
559
  await this.emit("error" /* ERROR */, error);
252
560
  }
561
+ if (this.stats.enabled) {
562
+ if (result) {
563
+ this._stats.incrementHits();
564
+ } else {
565
+ this._stats.incrementMisses();
566
+ }
567
+ this.stats.incrementGets();
568
+ }
253
569
  return result;
254
570
  }
255
571
  async getMany(keys) {
@@ -268,7 +584,7 @@ var Cacheable = class extends Hookified {
268
584
  for (const [i, key] of keys.entries()) {
269
585
  if (!result[i] && secondaryResult[i]) {
270
586
  result[i] = secondaryResult[i];
271
- await this._primary.set(key, secondaryResult[i]);
587
+ await this._primary.set(key, secondaryResult[i], this._ttl);
272
588
  }
273
589
  }
274
590
  }
@@ -276,16 +592,27 @@ var Cacheable = class extends Hookified {
276
592
  } catch (error) {
277
593
  await this.emit("error" /* ERROR */, error);
278
594
  }
595
+ if (this.stats.enabled) {
596
+ for (const item of result) {
597
+ if (item) {
598
+ this._stats.incrementHits();
599
+ } else {
600
+ this._stats.incrementMisses();
601
+ }
602
+ }
603
+ this.stats.incrementGets();
604
+ }
279
605
  return result;
280
606
  }
281
607
  async set(key, value, ttl) {
282
608
  let result = false;
609
+ const finalTtl = ttl ?? this._ttl;
283
610
  try {
284
- await this.hook("BEFORE_SET" /* BEFORE_SET */, { key, value, ttl });
611
+ await this.hook("BEFORE_SET" /* BEFORE_SET */, { key, value, finalTtl });
285
612
  const promises = [];
286
- promises.push(this._primary.set(key, value, ttl));
613
+ promises.push(this._primary.set(key, value, finalTtl));
287
614
  if (this._secondary) {
288
- promises.push(this._secondary.set(key, value, ttl));
615
+ promises.push(this._secondary.set(key, value, finalTtl));
289
616
  }
290
617
  if (this._nonBlocking) {
291
618
  result = await Promise.race(promises);
@@ -293,10 +620,16 @@ var Cacheable = class extends Hookified {
293
620
  const results = await Promise.all(promises);
294
621
  result = results[0];
295
622
  }
296
- await this.hook("AFTER_SET" /* AFTER_SET */, { key, value, ttl });
623
+ await this.hook("AFTER_SET" /* AFTER_SET */, { key, value, finalTtl });
297
624
  } catch (error) {
298
625
  await this.emit("error" /* ERROR */, error);
299
626
  }
627
+ if (this.stats.enabled) {
628
+ this.stats.incrementKSize(key);
629
+ this.stats.incrementCount();
630
+ this.stats.incrementVSize(value);
631
+ this.stats.incrementSets();
632
+ }
300
633
  return result;
301
634
  }
302
635
  async setMany(items) {
@@ -315,6 +648,13 @@ var Cacheable = class extends Hookified {
315
648
  } catch (error) {
316
649
  await this.emit("error" /* ERROR */, error);
317
650
  }
651
+ if (this.stats.enabled) {
652
+ for (const item of items) {
653
+ this.stats.incrementKSize(item.key);
654
+ this.stats.incrementCount();
655
+ this.stats.incrementVSize(item.value);
656
+ }
657
+ }
318
658
  return result;
319
659
  }
320
660
  async take(key) {
@@ -328,11 +668,18 @@ var Cacheable = class extends Hookified {
328
668
  return result;
329
669
  }
330
670
  async has(key) {
331
- let result = await this._primary.has(key);
332
- if (!result && this._secondary) {
333
- result = await this._secondary.has(key);
671
+ const promises = [];
672
+ promises.push(this._primary.has(key));
673
+ if (this._secondary) {
674
+ promises.push(this._secondary.has(key));
334
675
  }
335
- return result;
676
+ const resultAll = await Promise.all(promises);
677
+ for (const result of resultAll) {
678
+ if (result) {
679
+ return true;
680
+ }
681
+ }
682
+ return false;
336
683
  }
337
684
  async hasMany(keys) {
338
685
  const result = await this.hasManyKeyv(this._primary, keys);
@@ -353,17 +700,39 @@ var Cacheable = class extends Hookified {
353
700
  return result;
354
701
  }
355
702
  async delete(key) {
356
- const result = await this._primary.delete(key);
357
- if (this._secondary) {
358
- if (this._nonBlocking) {
359
- this._secondary.delete(key);
360
- } else {
361
- await this._secondary.delete(key);
703
+ let result = false;
704
+ const promises = [];
705
+ if (this.stats.enabled) {
706
+ const statResult = await this._primary.get(key);
707
+ if (statResult) {
708
+ this.stats.decreaseKSize(key);
709
+ this.stats.decreaseVSize(statResult);
710
+ this.stats.decreaseCount();
711
+ this.stats.incrementDeletes();
362
712
  }
363
713
  }
714
+ promises.push(this._primary.delete(key));
715
+ if (this._secondary) {
716
+ promises.push(this._secondary.delete(key));
717
+ }
718
+ if (this.nonBlocking) {
719
+ result = await Promise.race(promises);
720
+ } else {
721
+ const resultAll = await Promise.all(promises);
722
+ result = resultAll[0];
723
+ }
364
724
  return result;
365
725
  }
366
726
  async deleteMany(keys) {
727
+ if (this.stats.enabled) {
728
+ const statResult = await this._primary.get(keys);
729
+ for (const key of keys) {
730
+ this.stats.decreaseKSize(key);
731
+ this.stats.decreaseVSize(statResult);
732
+ this.stats.decreaseCount();
733
+ this.stats.incrementDeletes();
734
+ }
735
+ }
367
736
  const result = await this.deleteManyKeyv(this._primary, keys);
368
737
  if (this._secondary) {
369
738
  if (this._nonBlocking) {
@@ -381,6 +750,10 @@ var Cacheable = class extends Hookified {
381
750
  promises.push(this._secondary.clear());
382
751
  }
383
752
  await (this._nonBlocking ? Promise.race(promises) : Promise.all(promises));
753
+ if (this.stats.enabled) {
754
+ this._stats.resetStoreValues();
755
+ this._stats.incrementClears();
756
+ }
384
757
  }
385
758
  async disconnect() {
386
759
  const promises = [];
@@ -401,7 +774,8 @@ var Cacheable = class extends Hookified {
401
774
  async setManyKeyv(keyv, items) {
402
775
  const promises = [];
403
776
  for (const item of items) {
404
- promises.push(keyv.set(item.key, item.value, item.ttl));
777
+ const finalTtl = item.ttl ?? this._ttl;
778
+ promises.push(keyv.set(item.key, item.value, finalTtl));
405
779
  }
406
780
  await Promise.all(promises);
407
781
  return true;
@@ -417,5 +791,7 @@ var Cacheable = class extends Hookified {
417
791
  export {
418
792
  Cacheable,
419
793
  CacheableEvents,
420
- CacheableHooks
794
+ CacheableHooks,
795
+ CacheableMemory,
796
+ CacheableStats
421
797
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cacheable",
3
- "version": "0.8.0",
3
+ "version": "1.2.0",
4
4
  "description": "Simple Caching Engine using Keyv",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -19,11 +19,11 @@
19
19
  "devDependencies": {
20
20
  "@keyv/redis": "^3.0.1",
21
21
  "@vitest/coverage-v8": "^2.1.1",
22
- "lru-cache": "^10.4.3",
22
+ "lru-cache": "^11.0.1",
23
23
  "rimraf": "^6.0.1",
24
24
  "ts-node": "^10.9.2",
25
- "tsup": "^8.2.4",
26
- "typescript": "^5.5.4",
25
+ "tsup": "^8.3.0",
26
+ "typescript": "^5.6.2",
27
27
  "vitest": "^2.1.1",
28
28
  "xo": "^0.59.3"
29
29
  },