cacheable 1.10.4 → 2.0.1

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/dist/index.js CHANGED
@@ -1,1208 +1,23 @@
1
1
  // src/index.ts
2
- import { Hookified as Hookified2 } from "hookified";
3
- import { Keyv as Keyv2 } from "keyv";
4
-
5
- // src/hash.ts
6
- import * as crypto from "crypto";
7
- function hash(object, algorithm = "sha256") {
8
- const objectString = JSON.stringify(object);
9
- if (!crypto.getHashes().includes(algorithm)) {
10
- throw new Error(`Unsupported hash algorithm: '${algorithm}'`);
11
- }
12
- const hasher = crypto.createHash(algorithm);
13
- hasher.update(objectString);
14
- return hasher.digest("hex");
15
- }
16
- function hashToNumber(object, min = 0, max = 10, algorithm = "sha256") {
17
- const objectString = JSON.stringify(object);
18
- if (!crypto.getHashes().includes(algorithm)) {
19
- throw new Error(`Unsupported hash algorithm: '${algorithm}'`);
20
- }
21
- const hasher = crypto.createHash(algorithm);
22
- hasher.update(objectString);
23
- const hashHex = hasher.digest("hex");
24
- const hashNumber = Number.parseInt(hashHex, 16);
25
- const range = max - min + 1;
26
- return min + hashNumber % range;
27
- }
28
- function djb2Hash(string_, min = 0, max = 10) {
29
- let hash2 = 5381;
30
- for (let i = 0; i < string_.length; i++) {
31
- hash2 = hash2 * 33 ^ string_.charCodeAt(i);
32
- }
33
- const range = max - min + 1;
34
- return min + Math.abs(hash2) % range;
35
- }
36
-
37
- // src/keyv-memory.ts
38
- import { Keyv } from "keyv";
39
-
40
- // src/memory.ts
41
- import { Hookified } from "hookified";
42
-
43
- // src/memory-lru.ts
44
- var ListNode = class {
45
- value;
46
- prev = void 0;
47
- next = void 0;
48
- constructor(value) {
49
- this.value = value;
50
- }
51
- };
52
- var DoublyLinkedList = class {
53
- head = void 0;
54
- tail = void 0;
55
- nodesMap = /* @__PURE__ */ new Map();
56
- // Add a new node to the front (most recently used)
57
- addToFront(value) {
58
- const newNode = new ListNode(value);
59
- if (this.head) {
60
- newNode.next = this.head;
61
- this.head.prev = newNode;
62
- this.head = newNode;
63
- } else {
64
- this.head = this.tail = newNode;
65
- }
66
- this.nodesMap.set(value, newNode);
67
- }
68
- // Move an existing node to the front (most recently used)
69
- moveToFront(value) {
70
- const node = this.nodesMap.get(value);
71
- if (!node || this.head === node) {
72
- return;
73
- }
74
- if (node.prev) {
75
- node.prev.next = node.next;
76
- }
77
- if (node.next) {
78
- node.next.prev = node.prev;
79
- }
80
- if (node === this.tail) {
81
- this.tail = node.prev;
82
- }
83
- node.prev = void 0;
84
- node.next = this.head;
85
- if (this.head) {
86
- this.head.prev = node;
87
- }
88
- this.head = node;
89
- this.tail ??= node;
90
- }
91
- // Get the oldest node (tail)
92
- getOldest() {
93
- return this.tail ? this.tail.value : void 0;
94
- }
95
- // Remove the oldest node (tail)
96
- removeOldest() {
97
- if (!this.tail) {
98
- return void 0;
99
- }
100
- const oldValue = this.tail.value;
101
- if (this.tail.prev) {
102
- this.tail = this.tail.prev;
103
- this.tail.next = void 0;
104
- } else {
105
- this.head = this.tail = void 0;
106
- }
107
- this.nodesMap.delete(oldValue);
108
- return oldValue;
109
- }
110
- get size() {
111
- return this.nodesMap.size;
112
- }
113
- };
114
-
115
- // src/shorthand-time.ts
116
- var shorthandToMilliseconds = (shorthand) => {
117
- let milliseconds;
118
- if (shorthand === void 0) {
119
- return void 0;
120
- }
121
- if (typeof shorthand === "number") {
122
- milliseconds = shorthand;
123
- } else if (typeof shorthand === "string") {
124
- shorthand = shorthand.trim();
125
- if (Number.isNaN(Number(shorthand))) {
126
- const match = /^([\d.]+)\s*(ms|s|m|h|hr|d)$/i.exec(shorthand);
127
- if (!match) {
128
- throw new Error(
129
- `Unsupported time format: "${shorthand}". Use 'ms', 's', 'm', 'h', 'hr', or 'd'.`
130
- );
131
- }
132
- const [, value, unit] = match;
133
- const numericValue = Number.parseFloat(value);
134
- const unitLower = unit.toLowerCase();
135
- switch (unitLower) {
136
- case "ms": {
137
- milliseconds = numericValue;
138
- break;
139
- }
140
- case "s": {
141
- milliseconds = numericValue * 1e3;
142
- break;
143
- }
144
- case "m": {
145
- milliseconds = numericValue * 1e3 * 60;
146
- break;
147
- }
148
- case "h": {
149
- milliseconds = numericValue * 1e3 * 60 * 60;
150
- break;
151
- }
152
- case "hr": {
153
- milliseconds = numericValue * 1e3 * 60 * 60;
154
- break;
155
- }
156
- case "d": {
157
- milliseconds = numericValue * 1e3 * 60 * 60 * 24;
158
- break;
159
- }
160
- /* c8 ignore next 3 */
161
- default: {
162
- milliseconds = Number(shorthand);
163
- }
164
- }
165
- } else {
166
- milliseconds = Number(shorthand);
167
- }
168
- } else {
169
- throw new TypeError("Time must be a string or a number.");
170
- }
171
- return milliseconds;
172
- };
173
- var shorthandToTime = (shorthand, fromDate) => {
174
- fromDate ??= /* @__PURE__ */ new Date();
175
- const milliseconds = shorthandToMilliseconds(shorthand);
176
- if (milliseconds === void 0) {
177
- return fromDate.getTime();
178
- }
179
- return fromDate.getTime() + milliseconds;
180
- };
181
-
182
- // src/coalesce-async.ts
183
- var callbacks = /* @__PURE__ */ new Map();
184
- function hasKey(key) {
185
- return callbacks.has(key);
186
- }
187
- function addKey(key) {
188
- callbacks.set(key, []);
189
- }
190
- function removeKey(key) {
191
- callbacks.delete(key);
192
- }
193
- function addCallbackToKey(key, callback) {
194
- const stash = getCallbacksByKey(key);
195
- stash.push(callback);
196
- callbacks.set(key, stash);
197
- }
198
- function getCallbacksByKey(key) {
199
- return callbacks.get(key) ?? [];
200
- }
201
- async function enqueue(key) {
202
- return new Promise((resolve, reject) => {
203
- const callback = { resolve, reject };
204
- addCallbackToKey(key, callback);
205
- });
206
- }
207
- function dequeue(key) {
208
- const stash = getCallbacksByKey(key);
209
- removeKey(key);
210
- return stash;
211
- }
212
- function coalesce(options) {
213
- const { key, error, result } = options;
214
- for (const callback of dequeue(key)) {
215
- if (error) {
216
- callback.reject(error);
217
- } else {
218
- callback.resolve(result);
219
- }
220
- }
221
- }
222
- async function coalesceAsync(key, fnc) {
223
- if (!hasKey(key)) {
224
- addKey(key);
225
- try {
226
- const result = await Promise.resolve(fnc());
227
- coalesce({ key, result });
228
- return result;
229
- } catch (error) {
230
- coalesce({ key, error });
231
- throw error;
232
- }
233
- }
234
- return enqueue(key);
235
- }
236
-
237
- // src/wrap.ts
238
- function wrapSync(function_, options) {
239
- const { ttl, keyPrefix, cache } = options;
240
- return (...arguments_) => {
241
- let cacheKey = createWrapKey(function_, arguments_, keyPrefix);
242
- if (options.createKey) {
243
- cacheKey = options.createKey(function_, arguments_, options);
244
- }
245
- let value = cache.get(cacheKey);
246
- if (value === void 0) {
247
- try {
248
- value = function_(...arguments_);
249
- cache.set(cacheKey, value, ttl);
250
- } catch (error) {
251
- cache.emit("error", error);
252
- if (options.cacheErrors) {
253
- cache.set(cacheKey, error, ttl);
254
- }
255
- }
256
- }
257
- return value;
258
- };
259
- }
260
- async function getOrSet(key, function_, options) {
261
- const keyString = typeof key === "function" ? key(options) : key;
262
- let value = await options.cache.get(keyString);
263
- if (value === void 0) {
264
- const cacheId = options.cacheId ?? "default";
265
- const coalesceKey = `${cacheId}::${keyString}`;
266
- value = await coalesceAsync(coalesceKey, async () => {
267
- try {
268
- const result = await function_();
269
- await options.cache.set(keyString, result, options.ttl);
270
- return result;
271
- } catch (error) {
272
- options.cache.emit("error", error);
273
- if (options.cacheErrors) {
274
- await options.cache.set(keyString, error, options.ttl);
275
- }
276
- if (options.throwErrors) {
277
- throw error;
278
- }
279
- }
280
- });
281
- }
282
- return value;
283
- }
284
- function wrap(function_, options) {
285
- const { keyPrefix, cache } = options;
286
- return async (...arguments_) => {
287
- let cacheKey = createWrapKey(function_, arguments_, keyPrefix);
288
- if (options.createKey) {
289
- cacheKey = options.createKey(function_, arguments_, options);
290
- }
291
- return cache.getOrSet(
292
- cacheKey,
293
- async () => function_(...arguments_),
294
- options
295
- );
296
- };
297
- }
298
- function createWrapKey(function_, arguments_, keyPrefix) {
299
- if (!keyPrefix) {
300
- return `${function_.name}::${hash(arguments_)}`;
301
- }
302
- return `${keyPrefix}::${function_.name}::${hash(arguments_)}`;
303
- }
304
-
305
- // src/memory.ts
306
- var defaultStoreHashSize = 16;
307
- var maximumMapSize = 16777216;
308
- var CacheableMemory = class extends Hookified {
309
- _lru = new DoublyLinkedList();
310
- _storeHashSize = defaultStoreHashSize;
311
- _storeHashAlgorithm = "djb2Hash" /* djb2Hash */;
312
- // Default is djb2Hash
313
- _store = Array.from(
314
- { length: this._storeHashSize },
315
- () => /* @__PURE__ */ new Map()
316
- );
317
- _ttl;
318
- // Turned off by default
319
- _useClone = true;
320
- // Turned on by default
321
- _lruSize = 0;
322
- // Turned off by default
323
- _checkInterval = 0;
324
- // Turned off by default
325
- _interval = 0;
326
- // Turned off by default
327
- /**
328
- * @constructor
329
- * @param {CacheableMemoryOptions} [options] - The options for the CacheableMemory
330
- */
331
- constructor(options) {
332
- super();
333
- if (options?.ttl) {
334
- this.setTtl(options.ttl);
335
- }
336
- if (options?.useClone !== void 0) {
337
- this._useClone = options.useClone;
338
- }
339
- if (options?.storeHashSize && options.storeHashSize > 0) {
340
- this._storeHashSize = options.storeHashSize;
341
- }
342
- if (options?.lruSize) {
343
- if (options.lruSize > maximumMapSize) {
344
- this.emit(
345
- "error",
346
- new Error(
347
- `LRU size cannot be larger than ${maximumMapSize} due to Map limitations.`
348
- )
349
- );
350
- } else {
351
- this._lruSize = options.lruSize;
352
- }
353
- }
354
- if (options?.checkInterval) {
355
- this._checkInterval = options.checkInterval;
356
- }
357
- if (options?.storeHashAlgorithm) {
358
- this._storeHashAlgorithm = options.storeHashAlgorithm;
359
- }
360
- this._store = Array.from(
361
- { length: this._storeHashSize },
362
- () => /* @__PURE__ */ new Map()
363
- );
364
- this.startIntervalCheck();
365
- }
366
- /**
367
- * Gets the time-to-live
368
- * @returns {number|string|undefined} - The time-to-live in miliseconds or a human-readable format. If undefined, it will not have a time-to-live.
369
- */
370
- get ttl() {
371
- return this._ttl;
372
- }
373
- /**
374
- * Sets the time-to-live
375
- * @param {number|string|undefined} value - The time-to-live in miliseconds or a human-readable format (example '1s' = 1 second, '1h' = 1 hour). If undefined, it will not have a time-to-live.
376
- */
377
- set ttl(value) {
378
- this.setTtl(value);
379
- }
380
- /**
381
- * Gets whether to use clone
382
- * @returns {boolean} - If true, it will clone the value before returning it. If false, it will return the value directly. Default is true.
383
- */
384
- get useClone() {
385
- return this._useClone;
386
- }
387
- /**
388
- * Sets whether to use clone
389
- * @param {boolean} value - If true, it will clone the value before returning it. If false, it will return the value directly. Default is true.
390
- */
391
- set useClone(value) {
392
- this._useClone = value;
393
- }
394
- /**
395
- * Gets the size of the LRU cache
396
- * @returns {number} - The size of the LRU cache. If set to 0, it will not use LRU cache. Default is 0. If you are using LRU then the limit is based on Map() size 17mm.
397
- */
398
- get lruSize() {
399
- return this._lruSize;
400
- }
401
- /**
402
- * Sets the size of the LRU cache
403
- * @param {number} value - The size of the LRU cache. If set to 0, it will not use LRU cache. Default is 0. If you are using LRU then the limit is based on Map() size 17mm.
404
- */
405
- set lruSize(value) {
406
- if (value > maximumMapSize) {
407
- this.emit(
408
- "error",
409
- new Error(
410
- `LRU size cannot be larger than ${maximumMapSize} due to Map limitations.`
411
- )
412
- );
413
- return;
414
- }
415
- this._lruSize = value;
416
- if (this._lruSize === 0) {
417
- this._lru = new DoublyLinkedList();
418
- return;
419
- }
420
- this.lruResize();
421
- }
422
- /**
423
- * Gets the check interval
424
- * @returns {number} - The interval to check for expired items. If set to 0, it will not check for expired items. Default is 0.
425
- */
426
- get checkInterval() {
427
- return this._checkInterval;
428
- }
429
- /**
430
- * Sets the check interval
431
- * @param {number} value - The interval to check for expired items. If set to 0, it will not check for expired items. Default is 0.
432
- */
433
- set checkInterval(value) {
434
- this._checkInterval = value;
435
- }
436
- /**
437
- * Gets the size of the cache
438
- * @returns {number} - The size of the cache
439
- */
440
- get size() {
441
- let size = 0;
442
- for (const store of this._store) {
443
- size += store.size;
444
- }
445
- return size;
446
- }
447
- /**
448
- * Gets the number of hash stores
449
- * @returns {number} - The number of hash stores
450
- */
451
- get storeHashSize() {
452
- return this._storeHashSize;
453
- }
454
- /**
455
- * Sets the number of hash stores. This will recreate the store and all data will be cleared
456
- * @param {number} value - The number of hash stores
457
- */
458
- set storeHashSize(value) {
459
- if (value === this._storeHashSize) {
460
- return;
461
- }
462
- this._storeHashSize = value;
463
- this._store = Array.from(
464
- { length: this._storeHashSize },
465
- () => /* @__PURE__ */ new Map()
466
- );
467
- }
468
- /**
469
- * Gets the store hash algorithm
470
- * @returns {StoreHashAlgorithm | StoreHashAlgorithmFunction} - The store hash algorithm
471
- */
472
- get storeHashAlgorithm() {
473
- return this._storeHashAlgorithm;
474
- }
475
- /**
476
- * Sets the store hash algorithm. This will recreate the store and all data will be cleared
477
- * @param {StoreHashAlgorithm | StoreHashAlgorithmFunction} value - The store hash algorithm
478
- */
479
- set storeHashAlgorithm(value) {
480
- this._storeHashAlgorithm = value;
481
- }
482
- /**
483
- * Gets the keys
484
- * @returns {IterableIterator<string>} - The keys
485
- */
486
- get keys() {
487
- const keys = [];
488
- for (const store of this._store) {
489
- for (const key of store.keys()) {
490
- const item = store.get(key);
491
- if (item && this.hasExpired(item)) {
492
- store.delete(key);
493
- continue;
494
- }
495
- keys.push(key);
496
- }
497
- }
498
- return keys.values();
499
- }
500
- /**
501
- * Gets the items
502
- * @returns {IterableIterator<CacheableStoreItem>} - The items
503
- */
504
- get items() {
505
- const items = [];
506
- for (const store of this._store) {
507
- for (const item of store.values()) {
508
- if (this.hasExpired(item)) {
509
- store.delete(item.key);
510
- continue;
511
- }
512
- items.push(item);
513
- }
514
- }
515
- return items.values();
516
- }
517
- /**
518
- * Gets the store
519
- * @returns {Array<Map<string, CacheableStoreItem>>} - The store
520
- */
521
- get store() {
522
- return this._store;
523
- }
524
- /**
525
- * Gets the value of the key
526
- * @param {string} key - The key to get the value
527
- * @returns {T | undefined} - The value of the key
528
- */
529
- get(key) {
530
- const store = this.getStore(key);
531
- const item = store.get(key);
532
- if (!item) {
533
- return void 0;
534
- }
535
- if (item.expires && Date.now() > item.expires) {
536
- store.delete(key);
537
- return void 0;
538
- }
539
- this.lruMoveToFront(key);
540
- if (!this._useClone) {
541
- return item.value;
542
- }
543
- return this.clone(item.value);
544
- }
545
- /**
546
- * Gets the values of the keys
547
- * @param {string[]} keys - The keys to get the values
548
- * @returns {T[]} - The values of the keys
549
- */
550
- getMany(keys) {
551
- const result = [];
552
- for (const key of keys) {
553
- result.push(this.get(key));
554
- }
555
- return result;
556
- }
557
- /**
558
- * Gets the raw value of the key
559
- * @param {string} key - The key to get the value
560
- * @returns {CacheableStoreItem | undefined} - The raw value of the key
561
- */
562
- getRaw(key) {
563
- const store = this.getStore(key);
564
- const item = store.get(key);
565
- if (!item) {
566
- return void 0;
567
- }
568
- if (item.expires && item.expires && Date.now() > item.expires) {
569
- store.delete(key);
570
- return void 0;
571
- }
572
- this.lruMoveToFront(key);
573
- return item;
574
- }
575
- /**
576
- * Gets the raw values of the keys
577
- * @param {string[]} keys - The keys to get the values
578
- * @returns {CacheableStoreItem[]} - The raw values of the keys
579
- */
580
- getManyRaw(keys) {
581
- const result = [];
582
- for (const key of keys) {
583
- result.push(this.getRaw(key));
584
- }
585
- return result;
586
- }
587
- /**
588
- * Sets the value of the key
589
- * @param {string} key - The key to set the value
590
- * @param {any} value - The value to set
591
- * @param {number|string|SetOptions} [ttl] - Time to Live - If you set a number it is miliseconds, if you set a string it is a human-readable.
592
- * If you want to set expire directly you can do that by setting the expire property in the SetOptions.
593
- * If you set undefined, it will use the default time-to-live. If both are undefined then it will not have a time-to-live.
594
- * @returns {void}
595
- */
596
- set(key, value, ttl) {
597
- const store = this.getStore(key);
598
- let expires;
599
- if (ttl !== void 0 || this._ttl !== void 0) {
600
- if (typeof ttl === "object") {
601
- if (ttl.expire) {
602
- expires = typeof ttl.expire === "number" ? ttl.expire : ttl.expire.getTime();
603
- }
604
- if (ttl.ttl) {
605
- const finalTtl = shorthandToTime(ttl.ttl);
606
- if (finalTtl !== void 0) {
607
- expires = finalTtl;
608
- }
609
- }
610
- } else {
611
- const finalTtl = shorthandToTime(ttl ?? this._ttl);
612
- if (finalTtl !== void 0) {
613
- expires = finalTtl;
614
- }
615
- }
616
- }
617
- if (this._lruSize > 0) {
618
- if (store.has(key)) {
619
- this.lruMoveToFront(key);
620
- } else {
621
- this.lruAddToFront(key);
622
- if (this._lru.size > this._lruSize) {
623
- const oldestKey = this._lru.getOldest();
624
- if (oldestKey) {
625
- this._lru.removeOldest();
626
- this.delete(oldestKey);
627
- }
628
- }
629
- }
630
- }
631
- const item = { key, value, expires };
632
- store.set(key, item);
633
- }
634
- /**
635
- * Sets the values of the keys
636
- * @param {CacheableItem[]} items - The items to set
637
- * @returns {void}
638
- */
639
- setMany(items) {
640
- for (const item of items) {
641
- this.set(item.key, item.value, item.ttl);
642
- }
643
- }
644
- /**
645
- * Checks if the key exists
646
- * @param {string} key - The key to check
647
- * @returns {boolean} - If true, the key exists. If false, the key does not exist.
648
- */
649
- has(key) {
650
- const item = this.get(key);
651
- return Boolean(item);
652
- }
653
- /**
654
- * @function hasMany
655
- * @param {string[]} keys - The keys to check
656
- * @returns {boolean[]} - If true, the key exists. If false, the key does not exist.
657
- */
658
- hasMany(keys) {
659
- const result = [];
660
- for (const key of keys) {
661
- const item = this.get(key);
662
- result.push(Boolean(item));
663
- }
664
- return result;
665
- }
666
- /**
667
- * Take will get the key and delete the entry from cache
668
- * @param {string} key - The key to take
669
- * @returns {T | undefined} - The value of the key
670
- */
671
- take(key) {
672
- const item = this.get(key);
673
- if (!item) {
674
- return void 0;
675
- }
676
- this.delete(key);
677
- return item;
678
- }
679
- /**
680
- * TakeMany will get the keys and delete the entries from cache
681
- * @param {string[]} keys - The keys to take
682
- * @returns {T[]} - The values of the keys
683
- */
684
- takeMany(keys) {
685
- const result = [];
686
- for (const key of keys) {
687
- result.push(this.take(key));
688
- }
689
- return result;
690
- }
691
- /**
692
- * Delete the key
693
- * @param {string} key - The key to delete
694
- * @returns {void}
695
- */
696
- delete(key) {
697
- const store = this.getStore(key);
698
- store.delete(key);
699
- }
700
- /**
701
- * Delete the keys
702
- * @param {string[]} keys - The keys to delete
703
- * @returns {void}
704
- */
705
- deleteMany(keys) {
706
- for (const key of keys) {
707
- this.delete(key);
708
- }
709
- }
710
- /**
711
- * Clear the cache
712
- * @returns {void}
713
- */
714
- clear() {
715
- this._store = Array.from(
716
- { length: this._storeHashSize },
717
- () => /* @__PURE__ */ new Map()
718
- );
719
- this._lru = new DoublyLinkedList();
720
- }
721
- /**
722
- * Get the store based on the key (internal use)
723
- * @param {string} key - The key to get the store
724
- * @returns {CacheableHashStore} - The store
725
- */
726
- getStore(key) {
727
- const hash2 = this.getKeyStoreHash(key);
728
- this._store[hash2] ||= /* @__PURE__ */ new Map();
729
- return this._store[hash2];
730
- }
731
- /**
732
- * Hash the key for which store to go to (internal use)
733
- * @param {string} key - The key to hash
734
- * Available algorithms are: SHA256, SHA1, MD5, and djb2Hash.
735
- * @returns {number} - The hashed key as a number
736
- */
737
- getKeyStoreHash(key) {
738
- if (this._store.length === 1) {
739
- return 0;
740
- }
741
- if (this._storeHashAlgorithm === "djb2Hash" /* djb2Hash */) {
742
- return djb2Hash(key, 0, this._storeHashSize);
743
- }
744
- if (typeof this._storeHashAlgorithm === "function") {
745
- return this._storeHashAlgorithm(key, this._storeHashSize);
746
- }
747
- return hashToNumber(key, 0, this._storeHashSize, this._storeHashAlgorithm);
748
- }
749
- /**
750
- * Clone the value. This is for internal use
751
- * @param {any} value - The value to clone
752
- * @returns {any} - The cloned value
753
- */
754
- clone(value) {
755
- if (this.isPrimitive(value)) {
756
- return value;
757
- }
758
- return structuredClone(value);
759
- }
760
- /**
761
- * Add to the front of the LRU cache. This is for internal use
762
- * @param {string} key - The key to add to the front
763
- * @returns {void}
764
- */
765
- lruAddToFront(key) {
766
- if (this._lruSize === 0) {
767
- return;
768
- }
769
- this._lru.addToFront(key);
770
- }
771
- /**
772
- * Move to the front of the LRU cache. This is for internal use
773
- * @param {string} key - The key to move to the front
774
- * @returns {void}
775
- */
776
- lruMoveToFront(key) {
777
- if (this._lruSize === 0) {
778
- return;
779
- }
780
- this._lru.moveToFront(key);
781
- }
782
- /**
783
- * Resize the LRU cache. This is for internal use.
784
- * @returns {void}
785
- */
786
- lruResize() {
787
- while (this._lru.size > this._lruSize) {
788
- const oldestKey = this._lru.getOldest();
789
- if (oldestKey) {
790
- this._lru.removeOldest();
791
- this.delete(oldestKey);
792
- }
793
- }
794
- }
795
- /**
796
- * Check for expiration. This is for internal use
797
- * @returns {void}
798
- */
799
- checkExpiration() {
800
- for (const store of this._store) {
801
- for (const item of store.values()) {
802
- if (item.expires && Date.now() > item.expires) {
803
- store.delete(item.key);
804
- }
805
- }
806
- }
807
- }
808
- /**
809
- * Start the interval check. This is for internal use
810
- * @returns {void}
811
- */
812
- startIntervalCheck() {
813
- if (this._checkInterval > 0) {
814
- if (this._interval) {
815
- clearInterval(this._interval);
816
- }
817
- this._interval = setInterval(() => {
818
- this.checkExpiration();
819
- }, this._checkInterval).unref();
820
- }
821
- }
822
- /**
823
- * Stop the interval check. This is for internal use
824
- * @returns {void}
825
- */
826
- stopIntervalCheck() {
827
- if (this._interval) {
828
- clearInterval(this._interval);
829
- }
830
- this._interval = 0;
831
- this._checkInterval = 0;
832
- }
833
- /**
834
- * Wrap the function for caching
835
- * @param {Function} function_ - The function to wrap
836
- * @param {Object} [options] - The options to wrap
837
- * @returns {Function} - The wrapped function
838
- */
839
- wrap(function_, options) {
840
- const wrapOptions = {
841
- ttl: options?.ttl ?? this._ttl,
842
- keyPrefix: options?.keyPrefix,
843
- cache: this
844
- };
845
- return wrapSync(function_, wrapOptions);
846
- }
847
- isPrimitive(value) {
848
- const result = false;
849
- if (value === null || value === void 0) {
850
- return true;
851
- }
852
- if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
853
- return true;
854
- }
855
- return result;
856
- }
857
- setTtl(ttl) {
858
- if (typeof ttl === "string" || ttl === void 0) {
859
- this._ttl = ttl;
860
- } else if (ttl > 0) {
861
- this._ttl = ttl;
862
- } else {
863
- this._ttl = void 0;
864
- }
865
- }
866
- hasExpired(item) {
867
- if (item.expires && Date.now() > item.expires) {
868
- return true;
869
- }
870
- return false;
871
- }
872
- };
873
-
874
- // src/keyv-memory.ts
875
- var KeyvCacheableMemory = class {
876
- opts = {
877
- ttl: 0,
878
- useClone: true,
879
- lruSize: 0,
880
- checkInterval: 0
881
- };
882
- _defaultCache = new CacheableMemory();
883
- _nCache = /* @__PURE__ */ new Map();
884
- _namespace;
885
- constructor(options) {
886
- if (options) {
887
- this.opts = options;
888
- this._defaultCache = new CacheableMemory(options);
889
- if (options.namespace) {
890
- this._namespace = options.namespace;
891
- this._nCache.set(this._namespace, new CacheableMemory(options));
892
- }
893
- }
894
- }
895
- get namespace() {
896
- return this._namespace;
897
- }
898
- set namespace(value) {
899
- this._namespace = value;
900
- }
901
- get store() {
902
- return this.getStore(this._namespace);
903
- }
904
- async get(key) {
905
- const result = this.getStore(this._namespace).get(key);
906
- if (result) {
907
- return result;
908
- }
909
- return void 0;
910
- }
911
- async getMany(keys) {
912
- const result = this.getStore(this._namespace).getMany(keys);
913
- return result;
914
- }
915
- async set(key, value, ttl) {
916
- this.getStore(this._namespace).set(key, value, ttl);
917
- }
918
- async setMany(values) {
919
- this.getStore(this._namespace).setMany(values);
920
- }
921
- async delete(key) {
922
- this.getStore(this._namespace).delete(key);
923
- return true;
924
- }
925
- async deleteMany(key) {
926
- this.getStore(this._namespace).deleteMany(key);
927
- return true;
928
- }
929
- async clear() {
930
- this.getStore(this._namespace).clear();
931
- }
932
- async has(key) {
933
- return this.getStore(this._namespace).has(key);
934
- }
935
- on(event, listener) {
936
- this.getStore(this._namespace).on(event, listener);
937
- return this;
938
- }
939
- getStore(namespace) {
940
- if (!namespace) {
941
- return this._defaultCache;
942
- }
943
- if (!this._nCache.has(namespace)) {
944
- this._nCache.set(namespace, new CacheableMemory(this.opts));
945
- }
946
- return this._nCache.get(namespace);
947
- }
948
- };
949
- function createKeyv(options) {
950
- const store = new KeyvCacheableMemory(options);
951
- const namespace = options?.namespace;
952
- let ttl;
953
- if (options?.ttl && Number.isInteger(options.ttl)) {
954
- ttl = options?.ttl;
955
- }
956
- const keyv = new Keyv({ store, namespace, ttl });
957
- keyv.serialize = void 0;
958
- keyv.deserialize = void 0;
959
- return keyv;
960
- }
961
-
962
- // src/stats.ts
963
- var CacheableStats = class {
964
- _hits = 0;
965
- _misses = 0;
966
- _gets = 0;
967
- _sets = 0;
968
- _deletes = 0;
969
- _clears = 0;
970
- _vsize = 0;
971
- _ksize = 0;
972
- _count = 0;
973
- _enabled = false;
974
- constructor(options) {
975
- if (options?.enabled) {
976
- this._enabled = options.enabled;
977
- }
978
- }
979
- /**
980
- * @returns {boolean} - Whether the stats are enabled
981
- */
982
- get enabled() {
983
- return this._enabled;
984
- }
985
- /**
986
- * @param {boolean} enabled - Whether to enable the stats
987
- */
988
- set enabled(enabled) {
989
- this._enabled = enabled;
990
- }
991
- /**
992
- * @returns {number} - The number of hits
993
- * @readonly
994
- */
995
- get hits() {
996
- return this._hits;
997
- }
998
- /**
999
- * @returns {number} - The number of misses
1000
- * @readonly
1001
- */
1002
- get misses() {
1003
- return this._misses;
1004
- }
1005
- /**
1006
- * @returns {number} - The number of gets
1007
- * @readonly
1008
- */
1009
- get gets() {
1010
- return this._gets;
1011
- }
1012
- /**
1013
- * @returns {number} - The number of sets
1014
- * @readonly
1015
- */
1016
- get sets() {
1017
- return this._sets;
1018
- }
1019
- /**
1020
- * @returns {number} - The number of deletes
1021
- * @readonly
1022
- */
1023
- get deletes() {
1024
- return this._deletes;
1025
- }
1026
- /**
1027
- * @returns {number} - The number of clears
1028
- * @readonly
1029
- */
1030
- get clears() {
1031
- return this._clears;
1032
- }
1033
- /**
1034
- * @returns {number} - The vsize (value size) of the cache instance
1035
- * @readonly
1036
- */
1037
- get vsize() {
1038
- return this._vsize;
1039
- }
1040
- /**
1041
- * @returns {number} - The ksize (key size) of the cache instance
1042
- * @readonly
1043
- */
1044
- get ksize() {
1045
- return this._ksize;
1046
- }
1047
- /**
1048
- * @returns {number} - The count of the cache instance
1049
- * @readonly
1050
- */
1051
- get count() {
1052
- return this._count;
1053
- }
1054
- incrementHits() {
1055
- if (!this._enabled) {
1056
- return;
1057
- }
1058
- this._hits++;
1059
- }
1060
- incrementMisses() {
1061
- if (!this._enabled) {
1062
- return;
1063
- }
1064
- this._misses++;
1065
- }
1066
- incrementGets() {
1067
- if (!this._enabled) {
1068
- return;
1069
- }
1070
- this._gets++;
1071
- }
1072
- incrementSets() {
1073
- if (!this._enabled) {
1074
- return;
1075
- }
1076
- this._sets++;
1077
- }
1078
- incrementDeletes() {
1079
- if (!this._enabled) {
1080
- return;
1081
- }
1082
- this._deletes++;
1083
- }
1084
- incrementClears() {
1085
- if (!this._enabled) {
1086
- return;
1087
- }
1088
- this._clears++;
1089
- }
1090
- incrementVSize(value) {
1091
- if (!this._enabled) {
1092
- return;
1093
- }
1094
- this._vsize += this.roughSizeOfObject(value);
1095
- }
1096
- decreaseVSize(value) {
1097
- if (!this._enabled) {
1098
- return;
1099
- }
1100
- this._vsize -= this.roughSizeOfObject(value);
1101
- }
1102
- incrementKSize(key) {
1103
- if (!this._enabled) {
1104
- return;
1105
- }
1106
- this._ksize += this.roughSizeOfString(key);
1107
- }
1108
- decreaseKSize(key) {
1109
- if (!this._enabled) {
1110
- return;
1111
- }
1112
- this._ksize -= this.roughSizeOfString(key);
1113
- }
1114
- incrementCount() {
1115
- if (!this._enabled) {
1116
- return;
1117
- }
1118
- this._count++;
1119
- }
1120
- decreaseCount() {
1121
- if (!this._enabled) {
1122
- return;
1123
- }
1124
- this._count--;
1125
- }
1126
- setCount(count) {
1127
- if (!this._enabled) {
1128
- return;
1129
- }
1130
- this._count = count;
1131
- }
1132
- roughSizeOfString(value) {
1133
- return value.length * 2;
1134
- }
1135
- roughSizeOfObject(object) {
1136
- const objectList = [];
1137
- const stack = [object];
1138
- let bytes = 0;
1139
- while (stack.length > 0) {
1140
- const value = stack.pop();
1141
- if (typeof value === "boolean") {
1142
- bytes += 4;
1143
- } else if (typeof value === "string") {
1144
- bytes += value.length * 2;
1145
- } else if (typeof value === "number") {
1146
- bytes += 8;
1147
- } else if (typeof value === "object" && value !== null && !objectList.includes(value)) {
1148
- objectList.push(value);
1149
- for (const key in value) {
1150
- bytes += key.length * 2;
1151
- stack.push(value[key]);
1152
- }
1153
- }
1154
- }
1155
- return bytes;
1156
- }
1157
- reset() {
1158
- this._hits = 0;
1159
- this._misses = 0;
1160
- this._gets = 0;
1161
- this._sets = 0;
1162
- this._deletes = 0;
1163
- this._clears = 0;
1164
- this._vsize = 0;
1165
- this._ksize = 0;
1166
- this._count = 0;
1167
- }
1168
- resetStoreValues() {
1169
- this._vsize = 0;
1170
- this._ksize = 0;
1171
- this._count = 0;
1172
- }
1173
- };
1174
-
1175
- // src/ttl.ts
1176
- function getTtlFromExpires(expires) {
1177
- if (expires === void 0 || expires === null) {
1178
- return void 0;
1179
- }
1180
- const now = Date.now();
1181
- if (expires < now) {
1182
- return void 0;
1183
- }
1184
- return expires - now;
1185
- }
1186
- function getCascadingTtl(cacheableTtl, primaryTtl, secondaryTtl) {
1187
- return secondaryTtl ?? primaryTtl ?? shorthandToMilliseconds(cacheableTtl);
1188
- }
1189
- function calculateTtlFromExpiration(ttl, expires) {
1190
- const ttlFromExpires = getTtlFromExpires(expires);
1191
- const expiresFromTtl = ttl ? Date.now() + ttl : void 0;
1192
- if (ttlFromExpires === void 0) {
1193
- return ttl;
1194
- }
1195
- if (expiresFromTtl === void 0) {
1196
- return ttlFromExpires;
1197
- }
1198
- if (expires > expiresFromTtl) {
1199
- return ttl;
1200
- }
1201
- return ttlFromExpires;
1202
- }
2
+ import {
3
+ getOrSet,
4
+ wrap
5
+ } from "@cacheable/memoize";
6
+ import { createKeyv } from "@cacheable/memory";
7
+ import {
8
+ Stats as CacheableStats,
9
+ calculateTtlFromExpiration,
10
+ getCascadingTtl,
11
+ HashAlgorithm,
12
+ hash,
13
+ shorthandToMilliseconds
14
+ } from "@cacheable/utils";
15
+ import { Hookified } from "hookified";
16
+ import {
17
+ Keyv
18
+ } from "keyv";
1203
19
 
1204
- // src/index.ts
1205
- import { Keyv as Keyv3, KeyvHooks } from "keyv";
20
+ // src/enums.ts
1206
21
  var CacheableHooks = /* @__PURE__ */ ((CacheableHooks2) => {
1207
22
  CacheableHooks2["BEFORE_SET"] = "BEFORE_SET";
1208
23
  CacheableHooks2["AFTER_SET"] = "AFTER_SET";
@@ -1217,9 +32,33 @@ var CacheableHooks = /* @__PURE__ */ ((CacheableHooks2) => {
1217
32
  })(CacheableHooks || {});
1218
33
  var CacheableEvents = /* @__PURE__ */ ((CacheableEvents2) => {
1219
34
  CacheableEvents2["ERROR"] = "error";
35
+ CacheableEvents2["CACHE_HIT"] = "cache:hit";
36
+ CacheableEvents2["CACHE_MISS"] = "cache:miss";
1220
37
  return CacheableEvents2;
1221
38
  })(CacheableEvents || {});
1222
- var Cacheable = class extends Hookified2 {
39
+
40
+ // src/index.ts
41
+ import {
42
+ getOrSet as getOrSet2,
43
+ wrap as wrap2,
44
+ wrapSync
45
+ } from "@cacheable/memoize";
46
+ import {
47
+ CacheableMemory,
48
+ createKeyv as createKeyv2,
49
+ KeyvCacheableMemory
50
+ } from "@cacheable/memory";
51
+ import {
52
+ calculateTtlFromExpiration as calculateTtlFromExpiration2,
53
+ getCascadingTtl as getCascadingTtl2,
54
+ HashAlgorithm as HashAlgorithm2,
55
+ hash as hash2,
56
+ Stats,
57
+ shorthandToMilliseconds as shorthandToMilliseconds2,
58
+ shorthandToTime
59
+ } from "@cacheable/utils";
60
+ import { Keyv as Keyv2, KeyvHooks } from "keyv";
61
+ var Cacheable = class extends Hookified {
1223
62
  _primary = createKeyv();
1224
63
  _secondary;
1225
64
  _nonBlocking = false;
@@ -1402,7 +241,7 @@ var Cacheable = class extends Hookified2 {
1402
241
  if (this.isKeyvInstance(primary)) {
1403
242
  this._primary = primary;
1404
243
  } else {
1405
- this._primary = new Keyv2(primary);
244
+ this._primary = new Keyv(primary);
1406
245
  }
1407
246
  this._primary.on("error", (error) => {
1408
247
  this.emit("error" /* ERROR */, error);
@@ -1417,7 +256,7 @@ var Cacheable = class extends Hookified2 {
1417
256
  if (this.isKeyvInstance(secondary)) {
1418
257
  this._secondary = secondary;
1419
258
  } else {
1420
- this._secondary = new Keyv2(secondary);
259
+ this._secondary = new Keyv(secondary);
1421
260
  }
1422
261
  this._secondary.on("error", (error) => {
1423
262
  this.emit("error" /* ERROR */, error);
@@ -1425,7 +264,7 @@ var Cacheable = class extends Hookified2 {
1425
264
  }
1426
265
  // biome-ignore lint/suspicious/noExplicitAny: type format
1427
266
  isKeyvInstance(keyv) {
1428
- if (keyv instanceof Keyv2) {
267
+ if (keyv instanceof Keyv) {
1429
268
  return true;
1430
269
  }
1431
270
  const keyvMethods = [
@@ -1451,26 +290,68 @@ var Cacheable = class extends Hookified2 {
1451
290
  }
1452
291
  return this._namespace;
1453
292
  }
1454
- async get(key, options = {}) {
293
+ /**
294
+ * Retrieves an entry from the cache.
295
+ *
296
+ * Checks the primary store first; if not found and a secondary store is configured,
297
+ * it will fetch from the secondary, repopulate the primary, and return the result.
298
+ *
299
+ * @typeParam T - The expected type of the stored value.
300
+ * @param {string} key - The cache key to retrieve.
301
+ * @param {GetOptions} - options such as to bypass `nonBlocking` for this call
302
+ * @returns {Promise<T | undefined>}
303
+ * A promise that resolves to the cached value if found, or `undefined`.
304
+ */
305
+ async get(key, options) {
306
+ const result = await this.getRaw(key, options);
307
+ return result?.value;
308
+ }
309
+ /**
310
+ * Retrieves the raw entry from the cache including metadata like expiration.
311
+ *
312
+ * Checks the primary store first; if not found and a secondary store is configured,
313
+ * it will fetch from the secondary, repopulate the primary, and return the result.
314
+ *
315
+ * @typeParam T - The expected type of the stored value.
316
+ * @param {string} key - The cache key to retrieve.
317
+ * @param {GetOptions} - options such as to bypass `nonBlocking` for this call
318
+ * @returns {Promise<StoredDataRaw<T>>}
319
+ * A promise that resolves to the full raw data object if found, or undefined.
320
+ */
321
+ async getRaw(key, options) {
1455
322
  let result;
1456
- const { raw = false } = options;
1457
323
  try {
1458
324
  await this.hook("BEFORE_GET" /* BEFORE_GET */, key);
1459
- result = await this._primary.get(key, { raw: true });
325
+ result = await this._primary.getRaw(key);
1460
326
  let ttl;
327
+ if (result) {
328
+ this.emit("cache:hit" /* CACHE_HIT */, {
329
+ key,
330
+ value: result.value,
331
+ store: "primary"
332
+ });
333
+ } else {
334
+ this.emit("cache:miss" /* CACHE_MISS */, { key, store: "primary" });
335
+ }
336
+ const nonBlocking = options?.nonBlocking ?? this._nonBlocking;
1461
337
  if (!result && this._secondary) {
1462
- const secondaryResult = await this.getSecondaryRawResults(key);
1463
- if (secondaryResult?.value) {
1464
- result = secondaryResult;
1465
- const cascadeTtl = getCascadingTtl(this._ttl, this._primary.ttl);
1466
- const expires = secondaryResult.expires ?? void 0;
1467
- ttl = calculateTtlFromExpiration(cascadeTtl, expires);
1468
- const setItem = { key, value: result.value, ttl };
1469
- await this.hook(
1470
- "BEFORE_SECONDARY_SETS_PRIMARY" /* BEFORE_SECONDARY_SETS_PRIMARY */,
1471
- setItem
338
+ let secondaryProcessResult;
339
+ if (nonBlocking) {
340
+ secondaryProcessResult = await this.processSecondaryForGetRawNonBlocking(
341
+ this._primary,
342
+ this._secondary,
343
+ key
344
+ );
345
+ } else {
346
+ secondaryProcessResult = await this.processSecondaryForGetRaw(
347
+ this._primary,
348
+ this._secondary,
349
+ key
1472
350
  );
1473
- await this._primary.set(setItem.key, setItem.value, setItem.ttl);
351
+ }
352
+ if (secondaryProcessResult) {
353
+ result = secondaryProcessResult.result;
354
+ ttl = secondaryProcessResult.ttl;
1474
355
  }
1475
356
  }
1476
357
  await this.hook("AFTER_GET" /* AFTER_GET */, { key, result, ttl });
@@ -1485,38 +366,52 @@ var Cacheable = class extends Hookified2 {
1485
366
  }
1486
367
  this.stats.incrementGets();
1487
368
  }
1488
- return raw ? result : result?.value;
369
+ return result;
1489
370
  }
1490
- async getMany(keys, options = {}) {
371
+ /**
372
+ * Retrieves multiple raw entries from the cache including metadata like expiration.
373
+ *
374
+ * Checks the primary store for each key; if a key is missing and a secondary store is configured,
375
+ * it will fetch from the secondary store, repopulate the primary store, and return the results.
376
+ *
377
+ * @typeParam T - The expected type of the stored values.
378
+ * @param {string[]} keys - The cache keys to retrieve.
379
+ * @param {GetOptions} - options such as to bypass `nonBlocking` on this call
380
+ * @returns {Promise<Array<StoredDataRaw<T>>>}
381
+ * A promise that resolves to an array of raw data objects.
382
+ */
383
+ async getManyRaw(keys, options) {
1491
384
  let result = [];
1492
- const { raw = false } = options;
1493
385
  try {
1494
386
  await this.hook("BEFORE_GET_MANY" /* BEFORE_GET_MANY */, keys);
1495
- result = await this._primary.get(keys, { raw: true });
1496
- if (this._secondary) {
1497
- const missingKeys = [];
1498
- for (const [i, key] of keys.entries()) {
1499
- if (!result[i]) {
1500
- missingKeys.push(key);
1501
- }
387
+ result = await this._primary.getManyRaw(keys);
388
+ for (const [i, key] of keys.entries()) {
389
+ if (result[i]) {
390
+ this.emit("cache:hit" /* CACHE_HIT */, {
391
+ key,
392
+ value: result[i].value,
393
+ store: "primary"
394
+ });
395
+ } else {
396
+ this.emit("cache:miss" /* CACHE_MISS */, { key, store: "primary" });
1502
397
  }
1503
- const secondaryResults = await this.getManySecondaryRawResults(missingKeys);
1504
- for await (const [i, key] of keys.entries()) {
1505
- if (!result[i] && secondaryResults[i]) {
1506
- result[i] = secondaryResults[i];
1507
- const cascadeTtl = getCascadingTtl(this._ttl, this._primary.ttl);
1508
- let { expires } = secondaryResults[i];
1509
- if (expires === null) {
1510
- expires = void 0;
1511
- }
1512
- const ttl = calculateTtlFromExpiration(cascadeTtl, expires);
1513
- const setItem = { key, value: result[i].value, ttl };
1514
- await this.hook(
1515
- "BEFORE_SECONDARY_SETS_PRIMARY" /* BEFORE_SECONDARY_SETS_PRIMARY */,
1516
- setItem
1517
- );
1518
- await this._primary.set(setItem.key, setItem.value, setItem.ttl);
1519
- }
398
+ }
399
+ const nonBlocking = options?.nonBlocking ?? this._nonBlocking;
400
+ if (this._secondary) {
401
+ if (nonBlocking) {
402
+ await this.processSecondaryForGetManyRawNonBlocking(
403
+ this._primary,
404
+ this._secondary,
405
+ keys,
406
+ result
407
+ );
408
+ } else {
409
+ await this.processSecondaryForGetManyRaw(
410
+ this._primary,
411
+ this._secondary,
412
+ keys,
413
+ result
414
+ );
1520
415
  }
1521
416
  }
1522
417
  await this.hook("AFTER_GET_MANY" /* AFTER_GET_MANY */, { keys, result });
@@ -1533,7 +428,22 @@ var Cacheable = class extends Hookified2 {
1533
428
  }
1534
429
  this.stats.incrementGets();
1535
430
  }
1536
- return raw ? result : result.map((item) => item?.value);
431
+ return result;
432
+ }
433
+ /**
434
+ * Retrieves multiple entries from the cache.
435
+ * Checks the primary store for each key; if a key is missing and a secondary store is configured,
436
+ * it will fetch from the secondary store, repopulate the primary store, and return the results.
437
+ *
438
+ * @typeParam T - The expected type of the stored values.
439
+ * @param {string[]} keys - The cache keys to retrieve.
440
+ * @param {GetOptions} - options such as to bypass `nonBlocking` on this call
441
+ * @returns {Promise<Array<T | undefined>>}
442
+ * A promise that resolves to an array of cached values or `undefined` for misses.
443
+ */
444
+ async getMany(keys, options) {
445
+ const result = await this.getManyRaw(keys, options);
446
+ return result.map((item) => item?.value);
1537
447
  }
1538
448
  /**
1539
449
  * Sets the value of the key. If the secondary store is set then it will also set the value in the secondary store.
@@ -1556,6 +466,11 @@ var Cacheable = class extends Hookified2 {
1556
466
  }
1557
467
  if (this._nonBlocking) {
1558
468
  result = await Promise.race(promises);
469
+ for (const promise of promises) {
470
+ promise.catch((error) => {
471
+ this.emit("error" /* ERROR */, error);
472
+ });
473
+ }
1559
474
  } else {
1560
475
  const results = await Promise.all(promises);
1561
476
  result = results[0];
@@ -1584,7 +499,9 @@ var Cacheable = class extends Hookified2 {
1584
499
  result = await this.setManyKeyv(this._primary, items);
1585
500
  if (this._secondary) {
1586
501
  if (this._nonBlocking) {
1587
- this.setManyKeyv(this._secondary, items);
502
+ this.setManyKeyv(this._secondary, items).catch((error) => {
503
+ this.emit("error" /* ERROR */, error);
504
+ });
1588
505
  } else {
1589
506
  await this.setManyKeyv(this._secondary, items);
1590
507
  }
@@ -1687,6 +604,11 @@ var Cacheable = class extends Hookified2 {
1687
604
  }
1688
605
  if (this.nonBlocking) {
1689
606
  result = await Promise.race(promises);
607
+ for (const promise of promises) {
608
+ promise.catch((error) => {
609
+ this.emit("error" /* ERROR */, error);
610
+ });
611
+ }
1690
612
  } else {
1691
613
  const resultAll = await Promise.all(promises);
1692
614
  result = resultAll[0];
@@ -1708,12 +630,14 @@ var Cacheable = class extends Hookified2 {
1708
630
  this.stats.incrementDeletes();
1709
631
  }
1710
632
  }
1711
- const result = await this.deleteManyKeyv(this._primary, keys);
633
+ const result = await this._primary.deleteMany(keys);
1712
634
  if (this._secondary) {
1713
635
  if (this._nonBlocking) {
1714
- this.deleteManyKeyv(this._secondary, keys);
636
+ this._secondary.deleteMany(keys).catch((error) => {
637
+ this.emit("error" /* ERROR */, error);
638
+ });
1715
639
  } else {
1716
- await this.deleteManyKeyv(this._secondary, keys);
640
+ await this._secondary.deleteMany(keys);
1717
641
  }
1718
642
  }
1719
643
  return result;
@@ -1756,11 +680,30 @@ var Cacheable = class extends Hookified2 {
1756
680
  */
1757
681
  // biome-ignore lint/suspicious/noExplicitAny: type format
1758
682
  wrap(function_, options) {
683
+ const cacheAdapter = {
684
+ get: async (key) => this.get(key),
685
+ has: async (key) => this.has(key),
686
+ // biome-ignore lint/suspicious/noExplicitAny: CacheInstance requires any type
687
+ set: async (key, value, ttl) => {
688
+ await this.set(key, value, ttl);
689
+ },
690
+ /* c8 ignore start */
691
+ // biome-ignore lint/suspicious/noExplicitAny: CacheInstance interface
692
+ on: (event, listener) => {
693
+ this.on(event, listener);
694
+ },
695
+ /* c8 ignore stop */
696
+ // biome-ignore lint/suspicious/noExplicitAny: CacheInstance requires any type
697
+ emit: (event, ...args) => this.emit(event, ...args)
698
+ };
1759
699
  const wrapOptions = {
1760
700
  ttl: options?.ttl ?? this._ttl,
1761
701
  keyPrefix: options?.keyPrefix,
1762
- cache: this,
1763
- cacheId: this._cacheId
702
+ createKey: options?.createKey,
703
+ cacheErrors: options?.cacheErrors,
704
+ cache: cacheAdapter,
705
+ cacheId: this._cacheId,
706
+ serialize: options?.serialize
1764
707
  };
1765
708
  return wrap(function_, wrapOptions);
1766
709
  }
@@ -1775,8 +718,24 @@ var Cacheable = class extends Hookified2 {
1775
718
  * @return {Promise<T | undefined>} - A promise that resolves to the cached or newly computed value, or undefined if an error occurs and caching is not configured for errors.
1776
719
  */
1777
720
  async getOrSet(key, function_, options) {
721
+ const cacheAdapter = {
722
+ get: async (key2) => this.get(key2),
723
+ has: async (key2) => this.has(key2),
724
+ // biome-ignore lint/suspicious/noExplicitAny: CacheInstance requires any type
725
+ set: async (key2, value, ttl) => {
726
+ await this.set(key2, value, ttl);
727
+ },
728
+ /* c8 ignore start */
729
+ // biome-ignore lint/suspicious/noExplicitAny: CacheInstance interface
730
+ on: (event, listener) => {
731
+ this.on(event, listener);
732
+ },
733
+ /* c8 ignore stop */
734
+ // biome-ignore lint/suspicious/noExplicitAny: CacheInstance requires any type
735
+ emit: (event, ...args) => this.emit(event, ...args)
736
+ };
1778
737
  const getOrSetOptions = {
1779
- cache: this,
738
+ cache: cacheAdapter,
1780
739
  cacheId: this._cacheId,
1781
740
  ttl: options?.ttl ?? this._ttl,
1782
741
  cacheErrors: options?.cacheErrors,
@@ -1790,39 +749,17 @@ var Cacheable = class extends Hookified2 {
1790
749
  * @param {string} algorithm the hash algorithm to use. The default is 'sha256'
1791
750
  * @returns {string} the hash of the object
1792
751
  */
1793
- // biome-ignore lint/suspicious/noExplicitAny: type format
1794
- hash(object, algorithm = "sha256") {
1795
- return hash(object, algorithm);
1796
- }
1797
- async getSecondaryRawResults(key) {
1798
- let result;
1799
- if (this._secondary) {
1800
- result = await this._secondary.get(key, { raw: true });
1801
- }
1802
- return result;
1803
- }
1804
- async getManySecondaryRawResults(keys) {
1805
- let result = [];
1806
- if (this._secondary) {
1807
- result = await this._secondary.get(keys, { raw: true });
1808
- }
1809
- return result;
1810
- }
1811
- async deleteManyKeyv(keyv, keys) {
1812
- const promises = [];
1813
- for (const key of keys) {
1814
- promises.push(keyv.delete(key));
1815
- }
1816
- await Promise.all(promises);
1817
- return true;
752
+ hash(object, algorithm = HashAlgorithm.SHA256) {
753
+ const validAlgorithm = Object.values(HashAlgorithm).includes(algorithm) ? algorithm : HashAlgorithm.SHA256;
754
+ return hash(object, { algorithm: validAlgorithm });
1818
755
  }
1819
756
  async setManyKeyv(keyv, items) {
1820
- const promises = [];
757
+ const entries = [];
1821
758
  for (const item of items) {
1822
759
  const finalTtl = shorthandToMilliseconds(item.ttl ?? this._ttl);
1823
- promises.push(keyv.set(item.key, item.value, finalTtl));
760
+ entries.push({ key: item.key, value: item.value, ttl: finalTtl });
1824
761
  }
1825
- await Promise.all(promises);
762
+ await keyv.setMany(entries);
1826
763
  return true;
1827
764
  }
1828
765
  async hasManyKeyv(keyv, keys) {
@@ -1832,6 +769,162 @@ var Cacheable = class extends Hookified2 {
1832
769
  }
1833
770
  return Promise.all(promises);
1834
771
  }
772
+ /**
773
+ * Processes a single key from secondary store for getRaw operation
774
+ * @param primary - the primary store to use
775
+ * @param secondary - the secondary store to use
776
+ * @param key - The key to retrieve from secondary store
777
+ * @returns Promise containing the result and TTL information
778
+ */
779
+ async processSecondaryForGetRaw(primary, secondary, key) {
780
+ const secondaryResult = await secondary.getRaw(key);
781
+ if (secondaryResult?.value) {
782
+ this.emit("cache:hit" /* CACHE_HIT */, {
783
+ key,
784
+ value: secondaryResult.value,
785
+ store: "secondary"
786
+ });
787
+ const cascadeTtl = getCascadingTtl(this._ttl, this._primary.ttl);
788
+ const expires = secondaryResult.expires ?? void 0;
789
+ const ttl = calculateTtlFromExpiration(cascadeTtl, expires);
790
+ const setItem = { key, value: secondaryResult.value, ttl };
791
+ await this.hook("BEFORE_SECONDARY_SETS_PRIMARY" /* BEFORE_SECONDARY_SETS_PRIMARY */, setItem);
792
+ await primary.set(setItem.key, setItem.value, setItem.ttl);
793
+ return { result: secondaryResult, ttl };
794
+ } else {
795
+ this.emit("cache:miss" /* CACHE_MISS */, { key, store: "secondary" });
796
+ return void 0;
797
+ }
798
+ }
799
+ /**
800
+ * Processes a single key from secondary store for getRaw operation in non-blocking mode
801
+ * Non-blocking mode means we don't wait for secondary operations that update primary store
802
+ * @param primary - the primary store to use
803
+ * @param secondary - the secondary store to use
804
+ * @param key - The key to retrieve from secondary store
805
+ * @returns Promise containing the result and TTL information
806
+ */
807
+ async processSecondaryForGetRawNonBlocking(primary, secondary, key) {
808
+ const secondaryResult = await secondary.getRaw(key);
809
+ if (secondaryResult?.value) {
810
+ this.emit("cache:hit" /* CACHE_HIT */, {
811
+ key,
812
+ value: secondaryResult.value,
813
+ store: "secondary"
814
+ });
815
+ const cascadeTtl = getCascadingTtl(this._ttl, this._primary.ttl);
816
+ const expires = secondaryResult.expires ?? void 0;
817
+ const ttl = calculateTtlFromExpiration(cascadeTtl, expires);
818
+ const setItem = { key, value: secondaryResult.value, ttl };
819
+ this.hook("BEFORE_SECONDARY_SETS_PRIMARY" /* BEFORE_SECONDARY_SETS_PRIMARY */, setItem).then(async () => {
820
+ await primary.set(setItem.key, setItem.value, setItem.ttl);
821
+ }).catch((error) => {
822
+ this.emit("error" /* ERROR */, error);
823
+ });
824
+ return { result: secondaryResult, ttl };
825
+ } else {
826
+ this.emit("cache:miss" /* CACHE_MISS */, { key, store: "secondary" });
827
+ return void 0;
828
+ }
829
+ }
830
+ /**
831
+ * Processes missing keys from secondary store for getManyRaw operation
832
+ * @param primary - the primary store to use
833
+ * @param secondary - the secondary store to use
834
+ * @param keys - The original array of keys requested
835
+ * @param result - The result array from primary store (will be modified)
836
+ * @returns Promise<void>
837
+ */
838
+ async processSecondaryForGetManyRaw(primary, secondary, keys, result) {
839
+ const missingKeys = [];
840
+ for (const [i, key] of keys.entries()) {
841
+ if (!result[i]) {
842
+ missingKeys.push(key);
843
+ }
844
+ }
845
+ const secondaryResults = await secondary.getManyRaw(missingKeys);
846
+ let secondaryIndex = 0;
847
+ for await (const [i, key] of keys.entries()) {
848
+ if (!result[i]) {
849
+ const secondaryResult = secondaryResults[secondaryIndex];
850
+ if (secondaryResult && secondaryResult.value !== void 0) {
851
+ result[i] = secondaryResult;
852
+ this.emit("cache:hit" /* CACHE_HIT */, {
853
+ key,
854
+ value: secondaryResult.value,
855
+ store: "secondary"
856
+ });
857
+ const cascadeTtl = getCascadingTtl(this._ttl, this._primary.ttl);
858
+ let { expires } = secondaryResult;
859
+ if (expires === null) {
860
+ expires = void 0;
861
+ }
862
+ const ttl = calculateTtlFromExpiration(cascadeTtl, expires);
863
+ const setItem = { key, value: secondaryResult.value, ttl };
864
+ await this.hook(
865
+ "BEFORE_SECONDARY_SETS_PRIMARY" /* BEFORE_SECONDARY_SETS_PRIMARY */,
866
+ setItem
867
+ );
868
+ await primary.set(setItem.key, setItem.value, setItem.ttl);
869
+ } else {
870
+ this.emit("cache:miss" /* CACHE_MISS */, {
871
+ key,
872
+ store: "secondary"
873
+ });
874
+ }
875
+ secondaryIndex++;
876
+ }
877
+ }
878
+ }
879
+ /**
880
+ * Processes missing keys from secondary store for getManyRaw operation in non-blocking mode
881
+ * Non-blocking mode means we don't wait for secondary operations that update primary store
882
+ * @param secondary - the secondary store to use
883
+ * @param keys - The original array of keys requested
884
+ * @param result - The result array from primary store (will be modified)
885
+ * @returns Promise<void>
886
+ */
887
+ async processSecondaryForGetManyRawNonBlocking(primary, secondary, keys, result) {
888
+ const missingKeys = [];
889
+ for (const [i, key] of keys.entries()) {
890
+ if (!result[i]) {
891
+ missingKeys.push(key);
892
+ }
893
+ }
894
+ const secondaryResults = await secondary.getManyRaw(missingKeys);
895
+ let secondaryIndex = 0;
896
+ for await (const [i, key] of keys.entries()) {
897
+ if (!result[i]) {
898
+ const secondaryResult = secondaryResults[secondaryIndex];
899
+ if (secondaryResult && secondaryResult.value !== void 0) {
900
+ result[i] = secondaryResult;
901
+ this.emit("cache:hit" /* CACHE_HIT */, {
902
+ key,
903
+ value: secondaryResult.value,
904
+ store: "secondary"
905
+ });
906
+ const cascadeTtl = getCascadingTtl(this._ttl, this._primary.ttl);
907
+ let { expires } = secondaryResult;
908
+ if (expires === null) {
909
+ expires = void 0;
910
+ }
911
+ const ttl = calculateTtlFromExpiration(cascadeTtl, expires);
912
+ const setItem = { key, value: secondaryResult.value, ttl };
913
+ this.hook("BEFORE_SECONDARY_SETS_PRIMARY" /* BEFORE_SECONDARY_SETS_PRIMARY */, setItem).then(async () => {
914
+ await primary.set(setItem.key, setItem.value, setItem.ttl);
915
+ }).catch((error) => {
916
+ this.emit("error" /* ERROR */, error);
917
+ });
918
+ } else {
919
+ this.emit("cache:miss" /* CACHE_MISS */, {
920
+ key,
921
+ store: "secondary"
922
+ });
923
+ }
924
+ secondaryIndex++;
925
+ }
926
+ }
927
+ }
1835
928
  setTtl(ttl) {
1836
929
  if (typeof ttl === "string" || ttl === void 0) {
1837
930
  this._ttl = ttl;
@@ -1847,14 +940,18 @@ export {
1847
940
  CacheableEvents,
1848
941
  CacheableHooks,
1849
942
  CacheableMemory,
1850
- CacheableStats,
1851
- Keyv3 as Keyv,
943
+ Stats as CacheableStats,
944
+ HashAlgorithm2 as HashAlgorithm,
945
+ Keyv2 as Keyv,
1852
946
  KeyvCacheableMemory,
1853
947
  KeyvHooks,
1854
- createKeyv,
1855
- getOrSet,
1856
- shorthandToMilliseconds,
948
+ calculateTtlFromExpiration2 as calculateTtlFromExpiration,
949
+ createKeyv2 as createKeyv,
950
+ getCascadingTtl2 as getCascadingTtl,
951
+ getOrSet2 as getOrSet,
952
+ hash2 as hash,
953
+ shorthandToMilliseconds2 as shorthandToMilliseconds,
1857
954
  shorthandToTime,
1858
- wrap,
955
+ wrap2 as wrap,
1859
956
  wrapSync
1860
957
  };