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/README.md +257 -20
- package/dist/index.cjs +257 -163
- package/dist/index.d.cts +107 -46
- package/dist/index.d.ts +107 -46
- package/dist/index.js +258 -164
- package/package.json +9 -9
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 "
|
|
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 {
|
|
208
|
+
const { keyPrefix, cache } = options;
|
|
169
209
|
return async function(...arguments_) {
|
|
170
|
-
let value;
|
|
171
210
|
const cacheKey = createWrapKey(function_, arguments_, keyPrefix);
|
|
172
|
-
|
|
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
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 &&
|
|
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.
|
|
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.
|
|
608
|
-
|
|
697
|
+
const hash2 = this.getKeyStoreHash(key);
|
|
698
|
+
this._store[hash2] ||= /* @__PURE__ */ new Map();
|
|
699
|
+
return this._store[hash2];
|
|
609
700
|
}
|
|
610
701
|
/**
|
|
611
|
-
*
|
|
612
|
-
* @param {
|
|
613
|
-
*
|
|
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
|
-
|
|
616
|
-
|
|
617
|
-
|
|
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
|
-
|
|
660
|
-
|
|
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
|
-
|
|
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
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
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
|
|
1340
|
-
if (
|
|
1341
|
-
result =
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
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
|
|
1390
|
-
for (const [i, key] of keys.entries()) {
|
|
1391
|
-
if (!result[i] &&
|
|
1392
|
-
result[i] =
|
|
1393
|
-
const
|
|
1394
|
-
|
|
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.
|
|
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.
|
|
25
|
-
"@keyv/redis": "^4.
|
|
26
|
-
"@types/node": "^22.
|
|
27
|
-
"@vitest/coverage-v8": "^3.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.
|
|
32
|
-
"vitest": "^3.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.
|
|
37
|
-
"keyv": "^5.3.
|
|
36
|
+
"hookified": "^1.8.2",
|
|
37
|
+
"keyv": "^5.3.3"
|
|
38
38
|
},
|
|
39
39
|
"keywords": [
|
|
40
40
|
"cacheable",
|