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