cacheable 1.8.10 → 1.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -78,7 +78,7 @@ import {
78
78
  import { Hookified } from "hookified";
79
79
 
80
80
  // src/hash.ts
81
- import * as crypto from "node:crypto";
81
+ import * as crypto from "crypto";
82
82
  function hash(object, algorithm = "sha256") {
83
83
  const objectString = JSON.stringify(object);
84
84
  if (!crypto.getHashes().includes(algorithm)) {
@@ -88,6 +88,26 @@ function hash(object, algorithm = "sha256") {
88
88
  hasher.update(objectString);
89
89
  return hasher.digest("hex");
90
90
  }
91
+ function hashToNumber(object, min = 0, max = 10, algorithm = "sha256") {
92
+ const objectString = JSON.stringify(object);
93
+ if (!crypto.getHashes().includes(algorithm)) {
94
+ throw new Error(`Unsupported hash algorithm: '${algorithm}'`);
95
+ }
96
+ const hasher = crypto.createHash(algorithm);
97
+ hasher.update(objectString);
98
+ const hashHex = hasher.digest("hex");
99
+ const hashNumber = Number.parseInt(hashHex, 16);
100
+ const range = max - min + 1;
101
+ return min + hashNumber % range;
102
+ }
103
+ function djb2Hash(string_, min = 0, max = 10) {
104
+ let hash2 = 5381;
105
+ for (let i = 0; i < string_.length; i++) {
106
+ hash2 = hash2 * 33 ^ string_.charCodeAt(i);
107
+ }
108
+ const range = max - min + 1;
109
+ return min + Math.abs(hash2) % range;
110
+ }
91
111
 
92
112
  // src/coalesce-async.ts
93
113
  var callbacks = /* @__PURE__ */ new Map();
@@ -164,29 +184,31 @@ function wrapSync(function_, options) {
164
184
  return value;
165
185
  };
166
186
  }
187
+ async function getOrSet(key, function_, options) {
188
+ let value = await options.cache.get(key);
189
+ if (value === void 0) {
190
+ const cacheId = options.cacheId ?? "default";
191
+ const coalesceKey = `${cacheId}::${key}`;
192
+ value = await coalesceAsync(coalesceKey, async () => {
193
+ try {
194
+ const result = await function_();
195
+ await options.cache.set(key, result, options.ttl);
196
+ return result;
197
+ } catch (error) {
198
+ options.cache.emit("error", error);
199
+ if (options.cacheErrors) {
200
+ await options.cache.set(key, error, options.ttl);
201
+ }
202
+ }
203
+ });
204
+ }
205
+ return value;
206
+ }
167
207
  function wrap(function_, options) {
168
- const { ttl, keyPrefix, cache } = options;
208
+ const { keyPrefix, cache } = options;
169
209
  return async function(...arguments_) {
170
- let value;
171
210
  const cacheKey = createWrapKey(function_, arguments_, keyPrefix);
172
- value = await cache.get(cacheKey);
173
- if (value === void 0) {
174
- const cacheId = options.cacheId ?? "default";
175
- const coalesceKey = `${cacheId}::${cacheKey}`;
176
- value = await coalesceAsync(coalesceKey, async () => {
177
- try {
178
- const result = await function_(...arguments_);
179
- await cache.set(cacheKey, result, ttl);
180
- return result;
181
- } catch (error) {
182
- cache.emit("error", error);
183
- if (options.cacheErrors) {
184
- await cache.set(cacheKey, error, ttl);
185
- }
186
- }
187
- });
188
- }
189
- return value;
211
+ return cache.getOrSet(cacheKey, async () => function_(...arguments_), options);
190
212
  };
191
213
  }
192
214
  function createWrapKey(function_, arguments_, keyPrefix) {
@@ -270,19 +292,14 @@ var DoublyLinkedList = class {
270
292
  };
271
293
 
272
294
  // src/memory.ts
295
+ var defaultStoreHashSize = 16;
296
+ var maximumMapSize = 16777216;
273
297
  var CacheableMemory = class extends Hookified {
274
298
  _lru = new DoublyLinkedList();
275
- _hashCache = /* @__PURE__ */ new Map();
276
- _hash0 = /* @__PURE__ */ new Map();
277
- _hash1 = /* @__PURE__ */ new Map();
278
- _hash2 = /* @__PURE__ */ new Map();
279
- _hash3 = /* @__PURE__ */ new Map();
280
- _hash4 = /* @__PURE__ */ new Map();
281
- _hash5 = /* @__PURE__ */ new Map();
282
- _hash6 = /* @__PURE__ */ new Map();
283
- _hash7 = /* @__PURE__ */ new Map();
284
- _hash8 = /* @__PURE__ */ new Map();
285
- _hash9 = /* @__PURE__ */ new Map();
299
+ _storeHashSize = defaultStoreHashSize;
300
+ _storeHashAlgorithm = "djb2Hash" /* djb2Hash */;
301
+ // Default is djb2Hash
302
+ _store = Array.from({ length: this._storeHashSize }, () => /* @__PURE__ */ new Map());
286
303
  _ttl;
287
304
  // Turned off by default
288
305
  _useClone = true;
@@ -305,12 +322,23 @@ var CacheableMemory = class extends Hookified {
305
322
  if (options?.useClone !== void 0) {
306
323
  this._useClone = options.useClone;
307
324
  }
325
+ if (options?.storeHashSize && options.storeHashSize > 0) {
326
+ this._storeHashSize = options.storeHashSize;
327
+ }
308
328
  if (options?.lruSize) {
309
- this._lruSize = options.lruSize;
329
+ if (options.lruSize > maximumMapSize) {
330
+ this.emit("error", new Error(`LRU size cannot be larger than ${maximumMapSize} due to Map limitations.`));
331
+ } else {
332
+ this._lruSize = options.lruSize;
333
+ }
310
334
  }
311
335
  if (options?.checkInterval) {
312
336
  this._checkInterval = options.checkInterval;
313
337
  }
338
+ if (options?.storeHashAlgorithm) {
339
+ this._storeHashAlgorithm = options.storeHashAlgorithm;
340
+ }
341
+ this._store = Array.from({ length: this._storeHashSize }, () => /* @__PURE__ */ new Map());
314
342
  this.startIntervalCheck();
315
343
  }
316
344
  /**
@@ -343,17 +371,25 @@ var CacheableMemory = class extends Hookified {
343
371
  }
344
372
  /**
345
373
  * Gets the size of the LRU cache
346
- * @returns {number} - The size of the LRU cache. If set to 0, it will not use LRU cache. Default is 0.
374
+ * @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.
347
375
  */
348
376
  get lruSize() {
349
377
  return this._lruSize;
350
378
  }
351
379
  /**
352
380
  * Sets the size of the LRU cache
353
- * @param {number} value - The size of the LRU cache. If set to 0, it will not use LRU cache. Default is 0.
381
+ * @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.
354
382
  */
355
383
  set lruSize(value) {
384
+ if (value > maximumMapSize) {
385
+ this.emit("error", new Error(`LRU size cannot be larger than ${maximumMapSize} due to Map limitations.`));
386
+ return;
387
+ }
356
388
  this._lruSize = value;
389
+ if (this._lruSize === 0) {
390
+ this._lru = new DoublyLinkedList();
391
+ return;
392
+ }
357
393
  this.lruResize();
358
394
  }
359
395
  /**
@@ -375,21 +411,85 @@ var CacheableMemory = class extends Hookified {
375
411
  * @returns {number} - The size of the cache
376
412
  */
377
413
  get size() {
378
- return this._hash0.size + this._hash1.size + this._hash2.size + this._hash3.size + this._hash4.size + this._hash5.size + this._hash6.size + this._hash7.size + this._hash8.size + this._hash9.size;
414
+ let size = 0;
415
+ for (const store of this._store) {
416
+ size += store.size;
417
+ }
418
+ return size;
419
+ }
420
+ /**
421
+ * Gets the number of hash stores
422
+ * @returns {number} - The number of hash stores
423
+ */
424
+ get storeHashSize() {
425
+ return this._storeHashSize;
426
+ }
427
+ /**
428
+ * Sets the number of hash stores. This will recreate the store and all data will be cleared
429
+ * @param {number} value - The number of hash stores
430
+ */
431
+ set storeHashSize(value) {
432
+ if (value === this._storeHashSize) {
433
+ return;
434
+ }
435
+ this._storeHashSize = value;
436
+ this._store = Array.from({ length: this._storeHashSize }, () => /* @__PURE__ */ new Map());
437
+ }
438
+ /**
439
+ * Gets the store hash algorithm
440
+ * @returns {StoreHashAlgorithm | StoreHashAlgorithmFunction} - The store hash algorithm
441
+ */
442
+ get storeHashAlgorithm() {
443
+ return this._storeHashAlgorithm;
444
+ }
445
+ /**
446
+ * Sets the store hash algorithm. This will recreate the store and all data will be cleared
447
+ * @param {StoreHashAlgorithm | StoreHashAlgorithmFunction} value - The store hash algorithm
448
+ */
449
+ set storeHashAlgorithm(value) {
450
+ this._storeHashAlgorithm = value;
379
451
  }
380
452
  /**
381
453
  * Gets the keys
382
454
  * @returns {IterableIterator<string>} - The keys
383
455
  */
384
456
  get keys() {
385
- return this.concatStores().keys();
457
+ const keys = new Array();
458
+ for (const store of this._store) {
459
+ for (const key of store.keys()) {
460
+ const item = store.get(key);
461
+ if (item && this.hasExpired(item)) {
462
+ store.delete(key);
463
+ continue;
464
+ }
465
+ keys.push(key);
466
+ }
467
+ }
468
+ return keys.values();
386
469
  }
387
470
  /**
388
471
  * Gets the items
389
472
  * @returns {IterableIterator<CacheableStoreItem>} - The items
390
473
  */
391
474
  get items() {
392
- return this.concatStores().values();
475
+ const items = new Array();
476
+ for (const store of this._store) {
477
+ for (const item of store.values()) {
478
+ if (this.hasExpired(item)) {
479
+ store.delete(item.key);
480
+ continue;
481
+ }
482
+ items.push(item);
483
+ }
484
+ }
485
+ return items.values();
486
+ }
487
+ /**
488
+ * Gets the store
489
+ * @returns {Array<Map<string, CacheableStoreItem>>} - The store
490
+ */
491
+ get store() {
492
+ return this._store;
393
493
  }
394
494
  /**
395
495
  * Gets the value of the key
@@ -402,7 +502,7 @@ var CacheableMemory = class extends Hookified {
402
502
  if (!item) {
403
503
  return void 0;
404
504
  }
405
- if (item.expires && item.expires && Date.now() > item.expires) {
505
+ if (item.expires && Date.now() > item.expires) {
406
506
  store.delete(key);
407
507
  return void 0;
408
508
  }
@@ -585,17 +685,7 @@ var CacheableMemory = class extends Hookified {
585
685
  * @returns {void}
586
686
  */
587
687
  clear() {
588
- this._hash0.clear();
589
- this._hash1.clear();
590
- this._hash2.clear();
591
- this._hash3.clear();
592
- this._hash4.clear();
593
- this._hash5.clear();
594
- this._hash6.clear();
595
- this._hash7.clear();
596
- this._hash8.clear();
597
- this._hash9.clear();
598
- this._hashCache.clear();
688
+ this._store = Array.from({ length: this._storeHashSize }, () => /* @__PURE__ */ new Map());
599
689
  this._lru = new DoublyLinkedList();
600
690
  }
601
691
  /**
@@ -604,66 +694,27 @@ var CacheableMemory = class extends Hookified {
604
694
  * @returns {CacheableHashStore} - The store
605
695
  */
606
696
  getStore(key) {
607
- const hash2 = this.hashKey(key);
608
- return this.getStoreFromHash(hash2);
697
+ const hash2 = this.getKeyStoreHash(key);
698
+ this._store[hash2] ||= /* @__PURE__ */ new Map();
699
+ return this._store[hash2];
609
700
  }
610
701
  /**
611
- * Get the store based on the hash (internal use)
612
- * @param {number} hash
613
- * @returns {Map<string, CacheableStoreItem>}
702
+ * Hash the key for which store to go to (internal use)
703
+ * @param {string} key - The key to hash
704
+ * Available algorithms are: SHA256, SHA1, MD5, and djb2Hash.
705
+ * @returns {number} - The hashed key as a number
614
706
  */
615
- getStoreFromHash(hash2) {
616
- switch (hash2) {
617
- case 1: {
618
- return this._hash1;
619
- }
620
- case 2: {
621
- return this._hash2;
622
- }
623
- case 3: {
624
- return this._hash3;
625
- }
626
- case 4: {
627
- return this._hash4;
628
- }
629
- case 5: {
630
- return this._hash5;
631
- }
632
- case 6: {
633
- return this._hash6;
634
- }
635
- case 7: {
636
- return this._hash7;
637
- }
638
- case 8: {
639
- return this._hash8;
640
- }
641
- case 9: {
642
- return this._hash9;
643
- }
644
- default: {
645
- return this._hash0;
646
- }
707
+ getKeyStoreHash(key) {
708
+ if (this._store.length === 1) {
709
+ return 0;
647
710
  }
648
- }
649
- /**
650
- * Hash the key (internal use)
651
- * @param key
652
- * @returns {number} from 0 to 9
653
- */
654
- hashKey(key) {
655
- const cacheHashNumber = this._hashCache.get(key);
656
- if (cacheHashNumber) {
657
- return cacheHashNumber;
711
+ if (this._storeHashAlgorithm === "djb2Hash" /* djb2Hash */) {
712
+ return djb2Hash(key, 0, this._storeHashSize);
658
713
  }
659
- let hash2 = 0;
660
- const primeMultiplier = 31;
661
- for (let i = 0; i < key.length; i++) {
662
- hash2 = hash2 * primeMultiplier + key.charCodeAt(i);
714
+ if (typeof this._storeHashAlgorithm === "function") {
715
+ return this._storeHashAlgorithm(key, this._storeHashSize);
663
716
  }
664
- const result = Math.abs(hash2) % 10;
665
- this._hashCache.set(key, result);
666
- return result;
717
+ return hashToNumber(key, 0, this._storeHashSize, this._storeHashAlgorithm);
667
718
  }
668
719
  /**
669
720
  * Clone the value. This is for internal use
@@ -699,13 +750,10 @@ var CacheableMemory = class extends Hookified {
699
750
  this._lru.moveToFront(key);
700
751
  }
701
752
  /**
702
- * Resize the LRU cache. This is for internal use
753
+ * Resize the LRU cache. This is for internal use.
703
754
  * @returns {void}
704
755
  */
705
756
  lruResize() {
706
- if (this._lruSize === 0) {
707
- return;
708
- }
709
757
  while (this._lru.size > this._lruSize) {
710
758
  const oldestKey = this._lru.getOldest();
711
759
  if (oldestKey) {
@@ -719,10 +767,11 @@ var CacheableMemory = class extends Hookified {
719
767
  * @returns {void}
720
768
  */
721
769
  checkExpiration() {
722
- const stores = this.concatStores();
723
- for (const item of stores.values()) {
724
- if (item.expires && Date.now() > item.expires) {
725
- this.delete(item.key);
770
+ for (const store of this._store) {
771
+ for (const item of store.values()) {
772
+ if (item.expires && Date.now() > item.expires) {
773
+ store.delete(item.key);
774
+ }
726
775
  }
727
776
  }
728
777
  }
@@ -751,15 +800,6 @@ var CacheableMemory = class extends Hookified {
751
800
  this._interval = 0;
752
801
  this._checkInterval = 0;
753
802
  }
754
- /**
755
- * Hash the object. This is for internal use
756
- * @param {any} object - The object to hash
757
- * @param {string} [algorithm='sha256'] - The algorithm to hash
758
- * @returns {string} - The hashed string
759
- */
760
- hash(object, algorithm = "sha256") {
761
- return hash(object, algorithm);
762
- }
763
803
  /**
764
804
  * Wrap the function for caching
765
805
  * @param {Function} function_ - The function to wrap
@@ -784,9 +824,6 @@ var CacheableMemory = class extends Hookified {
784
824
  }
785
825
  return result;
786
826
  }
787
- concatStores() {
788
- return new Map([...this._hash0, ...this._hash1, ...this._hash2, ...this._hash3, ...this._hash4, ...this._hash5, ...this._hash6, ...this._hash7, ...this._hash8, ...this._hash9]);
789
- }
790
827
  setTtl(ttl) {
791
828
  if (typeof ttl === "string" || ttl === void 0) {
792
829
  this._ttl = ttl;
@@ -796,6 +833,12 @@ var CacheableMemory = class extends Hookified {
796
833
  this._ttl = void 0;
797
834
  }
798
835
  }
836
+ hasExpired(item) {
837
+ if (item.expires && Date.now() > item.expires) {
838
+ return true;
839
+ }
840
+ return false;
841
+ }
799
842
  };
800
843
 
801
844
  // src/keyv-memory.ts
@@ -1103,6 +1146,35 @@ var CacheableStats = class {
1103
1146
  }
1104
1147
  };
1105
1148
 
1149
+ // src/ttl.ts
1150
+ function getTtlFromExpires(expires) {
1151
+ if (expires === void 0 || expires === null) {
1152
+ return void 0;
1153
+ }
1154
+ const now = Date.now();
1155
+ if (expires < now) {
1156
+ return void 0;
1157
+ }
1158
+ return expires - now;
1159
+ }
1160
+ function getCascadingTtl(cacheableTtl, primaryTtl, secondaryTtl) {
1161
+ return secondaryTtl ?? primaryTtl ?? shorthandToMilliseconds(cacheableTtl);
1162
+ }
1163
+ function calculateTtlFromExpiration(ttl, expires) {
1164
+ const ttlFromExpires = getTtlFromExpires(expires);
1165
+ const expiresFromTtl = ttl ? Date.now() + ttl : void 0;
1166
+ if (ttlFromExpires === void 0) {
1167
+ return ttl;
1168
+ }
1169
+ if (expiresFromTtl === void 0) {
1170
+ return ttlFromExpires;
1171
+ }
1172
+ if (expires > expiresFromTtl) {
1173
+ return ttl;
1174
+ }
1175
+ return ttlFromExpires;
1176
+ }
1177
+
1106
1178
  // src/index.ts
1107
1179
  import {
1108
1180
  KeyvHooks,
@@ -1117,6 +1189,7 @@ var CacheableHooks = /* @__PURE__ */ ((CacheableHooks2) => {
1117
1189
  CacheableHooks2["AFTER_GET"] = "AFTER_GET";
1118
1190
  CacheableHooks2["BEFORE_GET_MANY"] = "BEFORE_GET_MANY";
1119
1191
  CacheableHooks2["AFTER_GET_MANY"] = "AFTER_GET_MANY";
1192
+ CacheableHooks2["BEFORE_SECONDARY_SETS_PRIMARY"] = "BEFORE_SECONDARY_SETS_PRIMARY";
1120
1193
  return CacheableHooks2;
1121
1194
  })(CacheableHooks || {});
1122
1195
  var CacheableEvents = /* @__PURE__ */ ((CacheableEvents2) => {
@@ -1325,37 +1398,26 @@ var Cacheable = class extends Hookified2 {
1325
1398
  }
1326
1399
  return this._namespace;
1327
1400
  }
1328
- /**
1329
- * Gets the value of the key. If the key does not exist in the primary store then it will check the secondary store.
1330
- * @param {string} key The key to get the value of
1331
- * @returns {Promise<T | undefined>} The value of the key or undefined if the key does not exist
1332
- */
1333
- async get(key) {
1401
+ async get(key, options = {}) {
1334
1402
  let result;
1403
+ const { raw = false } = options;
1335
1404
  try {
1336
1405
  await this.hook("BEFORE_GET" /* BEFORE_GET */, key);
1337
- result = await this._primary.get(key);
1406
+ result = await this._primary.get(key, { raw: true });
1407
+ let ttl;
1338
1408
  if (!result && this._secondary) {
1339
- const rawResult = await this._secondary.get(key, { raw: true });
1340
- if (rawResult) {
1341
- result = rawResult.value;
1342
- let finalTtl;
1343
- let expired = false;
1344
- if (rawResult.expires) {
1345
- const now = Date.now();
1346
- finalTtl = rawResult.expires - now;
1347
- if (finalTtl <= 0) {
1348
- expired = true;
1349
- }
1350
- }
1351
- if (expired) {
1352
- result = void 0;
1353
- } else {
1354
- await this._primary.set(key, result, finalTtl);
1355
- }
1409
+ const secondaryResult = await this.getSecondaryRawResults(key);
1410
+ if (secondaryResult?.value) {
1411
+ result = secondaryResult;
1412
+ const cascadeTtl = getCascadingTtl(this._ttl, this._primary.ttl);
1413
+ const expires = secondaryResult.expires ?? void 0;
1414
+ ttl = calculateTtlFromExpiration(cascadeTtl, expires);
1415
+ const setItem = { key, value: result.value, ttl };
1416
+ await this.hook("BEFORE_SECONDARY_SETS_PRIMARY" /* BEFORE_SECONDARY_SETS_PRIMARY */, setItem);
1417
+ await this._primary.set(setItem.key, setItem.value, setItem.ttl);
1356
1418
  }
1357
1419
  }
1358
- await this.hook("AFTER_GET" /* AFTER_GET */, { key, result });
1420
+ await this.hook("AFTER_GET" /* AFTER_GET */, { key, result, ttl });
1359
1421
  } catch (error) {
1360
1422
  this.emit("error" /* ERROR */, error);
1361
1423
  }
@@ -1367,18 +1429,14 @@ var Cacheable = class extends Hookified2 {
1367
1429
  }
1368
1430
  this.stats.incrementGets();
1369
1431
  }
1370
- return result;
1432
+ return raw ? result : result?.value;
1371
1433
  }
1372
- /**
1373
- * Gets the values of the keys. If the key does not exist in the primary store then it will check the secondary store.
1374
- * @param {string[]} keys The keys to get the values of
1375
- * @returns {Promise<Array<T | undefined>>} The values of the keys or undefined if the key does not exist
1376
- */
1377
- async getMany(keys) {
1434
+ async getMany(keys, options = {}) {
1378
1435
  let result = [];
1436
+ const { raw = false } = options;
1379
1437
  try {
1380
1438
  await this.hook("BEFORE_GET_MANY" /* BEFORE_GET_MANY */, keys);
1381
- result = await this._primary.get(keys);
1439
+ result = await this._primary.get(keys, { raw: true });
1382
1440
  if (this._secondary) {
1383
1441
  const missingKeys = [];
1384
1442
  for (const [i, key] of keys.entries()) {
@@ -1386,12 +1444,16 @@ var Cacheable = class extends Hookified2 {
1386
1444
  missingKeys.push(key);
1387
1445
  }
1388
1446
  }
1389
- const secondaryResult = await this._secondary.get(missingKeys);
1390
- for (const [i, key] of keys.entries()) {
1391
- if (!result[i] && secondaryResult[i]) {
1392
- result[i] = secondaryResult[i];
1393
- const finalTtl = shorthandToMilliseconds(this._ttl);
1394
- await this._primary.set(key, secondaryResult[i], finalTtl);
1447
+ const secondaryResults = await this.getManySecondaryRawResults(missingKeys);
1448
+ for await (const [i, key] of keys.entries()) {
1449
+ if (!result[i] && secondaryResults[i]) {
1450
+ result[i] = secondaryResults[i];
1451
+ const cascadeTtl = getCascadingTtl(this._ttl, this._primary.ttl);
1452
+ const expires = secondaryResults[i].expires;
1453
+ const ttl = calculateTtlFromExpiration(cascadeTtl, expires);
1454
+ const setItem = { key, value: result[i].value, ttl };
1455
+ await this.hook("BEFORE_SECONDARY_SETS_PRIMARY" /* BEFORE_SECONDARY_SETS_PRIMARY */, setItem);
1456
+ await this._primary.set(setItem.key, setItem.value, setItem.ttl);
1395
1457
  }
1396
1458
  }
1397
1459
  }
@@ -1409,7 +1471,7 @@ var Cacheable = class extends Hookified2 {
1409
1471
  }
1410
1472
  this.stats.incrementGets();
1411
1473
  }
1412
- return result;
1474
+ return raw ? result : result.map((item) => item?.value);
1413
1475
  }
1414
1476
  /**
1415
1477
  * Sets the value of the key. If the secondary store is set then it will also set the value in the secondary store.
@@ -1639,6 +1701,24 @@ var Cacheable = class extends Hookified2 {
1639
1701
  };
1640
1702
  return wrap(function_, wrapOptions);
1641
1703
  }
1704
+ /**
1705
+ * Retrieves the value associated with the given key from the cache. If the key is not found,
1706
+ * invokes the provided function to calculate the value, stores it in the cache, and then returns it.
1707
+ *
1708
+ * @param {string} key - The key to retrieve or set in the cache.
1709
+ * @param {() => Promise<T>} function_ - The asynchronous function that computes the value to be cached if the key does not exist.
1710
+ * @param {WrapFunctionOptions} [options] - Optional settings for caching, such as the time to live (TTL) or whether to cache errors.
1711
+ * @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.
1712
+ */
1713
+ async getOrSet(key, function_, options) {
1714
+ const getOrSetOptions = {
1715
+ cache: this,
1716
+ cacheId: this._cacheId,
1717
+ ttl: options?.ttl ?? this._ttl,
1718
+ cacheErrors: options?.cacheErrors
1719
+ };
1720
+ return getOrSet(key, function_, getOrSetOptions);
1721
+ }
1642
1722
  /**
1643
1723
  * Will hash an object using the specified algorithm. The default algorithm is 'sha256'.
1644
1724
  * @param {any} object the object to hash
@@ -1648,6 +1728,20 @@ var Cacheable = class extends Hookified2 {
1648
1728
  hash(object, algorithm = "sha256") {
1649
1729
  return hash(object, algorithm);
1650
1730
  }
1731
+ async getSecondaryRawResults(key) {
1732
+ let result;
1733
+ if (this._secondary) {
1734
+ result = await this._secondary.get(key, { raw: true });
1735
+ }
1736
+ return result;
1737
+ }
1738
+ async getManySecondaryRawResults(keys) {
1739
+ let result = new Array();
1740
+ if (this._secondary) {
1741
+ result = await this._secondary.get(keys, { raw: true });
1742
+ }
1743
+ return result;
1744
+ }
1651
1745
  async deleteManyKeyv(keyv, keys) {
1652
1746
  const promises = [];
1653
1747
  for (const key of keys) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cacheable",
3
- "version": "1.8.10",
3
+ "version": "1.10.0",
4
4
  "description": "High Performance Layer 1 / Layer 2 Caching with Keyv Storage",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -21,20 +21,20 @@
21
21
  "license": "MIT",
22
22
  "private": false,
23
23
  "devDependencies": {
24
- "@faker-js/faker": "^9.6.0",
25
- "@keyv/redis": "^4.3.2",
26
- "@types/node": "^22.14.0",
27
- "@vitest/coverage-v8": "^3.1.1",
24
+ "@faker-js/faker": "^9.7.0",
25
+ "@keyv/redis": "^4.4.0",
26
+ "@types/node": "^22.15.3",
27
+ "@vitest/coverage-v8": "^3.1.3",
28
28
  "lru-cache": "^11.1.0",
29
29
  "rimraf": "^6.0.1",
30
30
  "tsup": "^8.4.0",
31
- "typescript": "^5.8.2",
32
- "vitest": "^3.1.1",
31
+ "typescript": "^5.8.3",
32
+ "vitest": "^3.1.3",
33
33
  "xo": "^0.60.0"
34
34
  },
35
35
  "dependencies": {
36
- "hookified": "^1.8.1",
37
- "keyv": "^5.3.2"
36
+ "hookified": "^1.8.2",
37
+ "keyv": "^5.3.3"
38
38
  },
39
39
  "keywords": [
40
40
  "cacheable",