@cacheable/memory 1.0.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.cjs ADDED
@@ -0,0 +1,665 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ CacheableMemory: () => CacheableMemory,
24
+ HashAlgorithm: () => import_utils2.HashAlgorithm,
25
+ defaultStoreHashSize: () => defaultStoreHashSize,
26
+ hash: () => import_utils2.hash,
27
+ hashToNumber: () => import_utils2.hashToNumber,
28
+ maximumMapSize: () => maximumMapSize
29
+ });
30
+ module.exports = __toCommonJS(index_exports);
31
+ var import_hookified = require("hookified");
32
+ var import_memoize = require("@cacheable/memoize");
33
+ var import_utils = require("@cacheable/utils");
34
+
35
+ // src/memory-lru.ts
36
+ var ListNode = class {
37
+ // eslint-disable-next-line @typescript-eslint/parameter-properties
38
+ value;
39
+ prev = void 0;
40
+ next = void 0;
41
+ constructor(value) {
42
+ this.value = value;
43
+ }
44
+ };
45
+ var DoublyLinkedList = class {
46
+ head = void 0;
47
+ tail = void 0;
48
+ nodesMap = /* @__PURE__ */ new Map();
49
+ // Add a new node to the front (most recently used)
50
+ addToFront(value) {
51
+ const newNode = new ListNode(value);
52
+ if (this.head) {
53
+ newNode.next = this.head;
54
+ this.head.prev = newNode;
55
+ this.head = newNode;
56
+ } else {
57
+ this.head = this.tail = newNode;
58
+ }
59
+ this.nodesMap.set(value, newNode);
60
+ }
61
+ // Move an existing node to the front (most recently used)
62
+ moveToFront(value) {
63
+ const node = this.nodesMap.get(value);
64
+ if (!node || this.head === node) {
65
+ return;
66
+ }
67
+ if (node.prev) {
68
+ node.prev.next = node.next;
69
+ }
70
+ if (node.next) {
71
+ node.next.prev = node.prev;
72
+ }
73
+ if (node === this.tail) {
74
+ this.tail = node.prev;
75
+ }
76
+ node.prev = void 0;
77
+ node.next = this.head;
78
+ if (this.head) {
79
+ this.head.prev = node;
80
+ }
81
+ this.head = node;
82
+ this.tail ??= node;
83
+ }
84
+ // Get the oldest node (tail)
85
+ getOldest() {
86
+ return this.tail ? this.tail.value : void 0;
87
+ }
88
+ // Remove the oldest node (tail)
89
+ removeOldest() {
90
+ if (!this.tail) {
91
+ return void 0;
92
+ }
93
+ const oldValue = this.tail.value;
94
+ if (this.tail.prev) {
95
+ this.tail = this.tail.prev;
96
+ this.tail.next = void 0;
97
+ } else {
98
+ this.head = this.tail = void 0;
99
+ }
100
+ this.nodesMap.delete(oldValue);
101
+ return oldValue;
102
+ }
103
+ get size() {
104
+ return this.nodesMap.size;
105
+ }
106
+ };
107
+
108
+ // src/index.ts
109
+ var import_utils2 = require("@cacheable/utils");
110
+ var defaultStoreHashSize = 16;
111
+ var maximumMapSize = 16777216;
112
+ var CacheableMemory = class extends import_hookified.Hookified {
113
+ _lru = new DoublyLinkedList();
114
+ _storeHashSize = defaultStoreHashSize;
115
+ _storeHashAlgorithm = import_utils.HashAlgorithm.DJB2;
116
+ // Default is djb2Hash
117
+ _store = Array.from({ length: this._storeHashSize }, () => /* @__PURE__ */ new Map());
118
+ _ttl;
119
+ // Turned off by default
120
+ _useClone = true;
121
+ // Turned on by default
122
+ _lruSize = 0;
123
+ // Turned off by default
124
+ _checkInterval = 0;
125
+ // Turned off by default
126
+ _interval = 0;
127
+ // Turned off by default
128
+ /**
129
+ * @constructor
130
+ * @param {CacheableMemoryOptions} [options] - The options for the CacheableMemory
131
+ */
132
+ constructor(options) {
133
+ super();
134
+ if (options?.ttl) {
135
+ this.setTtl(options.ttl);
136
+ }
137
+ if (options?.useClone !== void 0) {
138
+ this._useClone = options.useClone;
139
+ }
140
+ if (options?.storeHashSize && options.storeHashSize > 0) {
141
+ this._storeHashSize = options.storeHashSize;
142
+ }
143
+ if (options?.lruSize) {
144
+ if (options.lruSize > maximumMapSize) {
145
+ this.emit("error", new Error(`LRU size cannot be larger than ${maximumMapSize} due to Map limitations.`));
146
+ } else {
147
+ this._lruSize = options.lruSize;
148
+ }
149
+ }
150
+ if (options?.checkInterval) {
151
+ this._checkInterval = options.checkInterval;
152
+ }
153
+ if (options?.storeHashAlgorithm) {
154
+ this._storeHashAlgorithm = options.storeHashAlgorithm;
155
+ }
156
+ this._store = Array.from({ length: this._storeHashSize }, () => /* @__PURE__ */ new Map());
157
+ this.startIntervalCheck();
158
+ }
159
+ /**
160
+ * Gets the time-to-live
161
+ * @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.
162
+ */
163
+ get ttl() {
164
+ return this._ttl;
165
+ }
166
+ /**
167
+ * Sets the time-to-live
168
+ * @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.
169
+ */
170
+ set ttl(value) {
171
+ this.setTtl(value);
172
+ }
173
+ /**
174
+ * Gets whether to use clone
175
+ * @returns {boolean} - If true, it will clone the value before returning it. If false, it will return the value directly. Default is true.
176
+ */
177
+ get useClone() {
178
+ return this._useClone;
179
+ }
180
+ /**
181
+ * Sets whether to use clone
182
+ * @param {boolean} value - If true, it will clone the value before returning it. If false, it will return the value directly. Default is true.
183
+ */
184
+ set useClone(value) {
185
+ this._useClone = value;
186
+ }
187
+ /**
188
+ * Gets the size of the LRU cache
189
+ * @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.
190
+ */
191
+ get lruSize() {
192
+ return this._lruSize;
193
+ }
194
+ /**
195
+ * Sets the size of the LRU cache
196
+ * @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.
197
+ */
198
+ set lruSize(value) {
199
+ if (value > maximumMapSize) {
200
+ this.emit("error", new Error(`LRU size cannot be larger than ${maximumMapSize} due to Map limitations.`));
201
+ return;
202
+ }
203
+ this._lruSize = value;
204
+ if (this._lruSize === 0) {
205
+ this._lru = new DoublyLinkedList();
206
+ return;
207
+ }
208
+ this.lruResize();
209
+ }
210
+ /**
211
+ * Gets the check interval
212
+ * @returns {number} - The interval to check for expired items. If set to 0, it will not check for expired items. Default is 0.
213
+ */
214
+ get checkInterval() {
215
+ return this._checkInterval;
216
+ }
217
+ /**
218
+ * Sets the check interval
219
+ * @param {number} value - The interval to check for expired items. If set to 0, it will not check for expired items. Default is 0.
220
+ */
221
+ set checkInterval(value) {
222
+ this._checkInterval = value;
223
+ }
224
+ /**
225
+ * Gets the size of the cache
226
+ * @returns {number} - The size of the cache
227
+ */
228
+ get size() {
229
+ let size = 0;
230
+ for (const store of this._store) {
231
+ size += store.size;
232
+ }
233
+ return size;
234
+ }
235
+ /**
236
+ * Gets the number of hash stores
237
+ * @returns {number} - The number of hash stores
238
+ */
239
+ get storeHashSize() {
240
+ return this._storeHashSize;
241
+ }
242
+ /**
243
+ * Sets the number of hash stores. This will recreate the store and all data will be cleared
244
+ * @param {number} value - The number of hash stores
245
+ */
246
+ set storeHashSize(value) {
247
+ if (value === this._storeHashSize) {
248
+ return;
249
+ }
250
+ this._storeHashSize = value;
251
+ this._store = Array.from({ length: this._storeHashSize }, () => /* @__PURE__ */ new Map());
252
+ }
253
+ /**
254
+ * Gets the store hash algorithm
255
+ * @returns {HashAlgorithm | StoreHashAlgorithmFunction} - The store hash algorithm
256
+ */
257
+ get storeHashAlgorithm() {
258
+ return this._storeHashAlgorithm;
259
+ }
260
+ /**
261
+ * Sets the store hash algorithm. This will recreate the store and all data will be cleared
262
+ * @param {HashAlgorithm | HashAlgorithmFunction} value - The store hash algorithm
263
+ */
264
+ set storeHashAlgorithm(value) {
265
+ this._storeHashAlgorithm = value;
266
+ }
267
+ /**
268
+ * Gets the keys
269
+ * @returns {IterableIterator<string>} - The keys
270
+ */
271
+ get keys() {
272
+ const keys = new Array();
273
+ for (const store of this._store) {
274
+ for (const key of store.keys()) {
275
+ const item = store.get(key);
276
+ if (item && this.hasExpired(item)) {
277
+ store.delete(key);
278
+ continue;
279
+ }
280
+ keys.push(key);
281
+ }
282
+ }
283
+ return keys.values();
284
+ }
285
+ /**
286
+ * Gets the items
287
+ * @returns {IterableIterator<CacheableStoreItem>} - The items
288
+ */
289
+ get items() {
290
+ const items = new Array();
291
+ for (const store of this._store) {
292
+ for (const item of store.values()) {
293
+ if (this.hasExpired(item)) {
294
+ store.delete(item.key);
295
+ continue;
296
+ }
297
+ items.push(item);
298
+ }
299
+ }
300
+ return items.values();
301
+ }
302
+ /**
303
+ * Gets the store
304
+ * @returns {Array<Map<string, CacheableStoreItem>>} - The store
305
+ */
306
+ get store() {
307
+ return this._store;
308
+ }
309
+ /**
310
+ * Gets the value of the key
311
+ * @param {string} key - The key to get the value
312
+ * @returns {T | undefined} - The value of the key
313
+ */
314
+ get(key) {
315
+ const store = this.getStore(key);
316
+ const item = store.get(key);
317
+ if (!item) {
318
+ return void 0;
319
+ }
320
+ if (item.expires && Date.now() > item.expires) {
321
+ store.delete(key);
322
+ return void 0;
323
+ }
324
+ this.lruMoveToFront(key);
325
+ if (!this._useClone) {
326
+ return item.value;
327
+ }
328
+ return this.clone(item.value);
329
+ }
330
+ /**
331
+ * Gets the values of the keys
332
+ * @param {string[]} keys - The keys to get the values
333
+ * @returns {T[]} - The values of the keys
334
+ */
335
+ getMany(keys) {
336
+ const result = new Array();
337
+ for (const key of keys) {
338
+ result.push(this.get(key));
339
+ }
340
+ return result;
341
+ }
342
+ /**
343
+ * Gets the raw value of the key
344
+ * @param {string} key - The key to get the value
345
+ * @returns {CacheableStoreItem | undefined} - The raw value of the key
346
+ */
347
+ getRaw(key) {
348
+ const store = this.getStore(key);
349
+ const item = store.get(key);
350
+ if (!item) {
351
+ return void 0;
352
+ }
353
+ if (item.expires && item.expires && Date.now() > item.expires) {
354
+ store.delete(key);
355
+ return void 0;
356
+ }
357
+ this.lruMoveToFront(key);
358
+ return item;
359
+ }
360
+ /**
361
+ * Gets the raw values of the keys
362
+ * @param {string[]} keys - The keys to get the values
363
+ * @returns {CacheableStoreItem[]} - The raw values of the keys
364
+ */
365
+ getManyRaw(keys) {
366
+ const result = new Array();
367
+ for (const key of keys) {
368
+ result.push(this.getRaw(key));
369
+ }
370
+ return result;
371
+ }
372
+ /**
373
+ * Sets the value of the key
374
+ * @param {string} key - The key to set the value
375
+ * @param {any} value - The value to set
376
+ * @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.
377
+ * If you want to set expire directly you can do that by setting the expire property in the SetOptions.
378
+ * 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.
379
+ * @returns {void}
380
+ */
381
+ set(key, value, ttl) {
382
+ const store = this.getStore(key);
383
+ let expires;
384
+ if (ttl !== void 0 || this._ttl !== void 0) {
385
+ if (typeof ttl === "object") {
386
+ if (ttl.expire) {
387
+ expires = typeof ttl.expire === "number" ? ttl.expire : ttl.expire.getTime();
388
+ }
389
+ if (ttl.ttl) {
390
+ const finalTtl = (0, import_utils.shorthandToTime)(ttl.ttl);
391
+ if (finalTtl !== void 0) {
392
+ expires = finalTtl;
393
+ }
394
+ }
395
+ } else {
396
+ const finalTtl = (0, import_utils.shorthandToTime)(ttl ?? this._ttl);
397
+ if (finalTtl !== void 0) {
398
+ expires = finalTtl;
399
+ }
400
+ }
401
+ }
402
+ if (this._lruSize > 0) {
403
+ if (store.has(key)) {
404
+ this.lruMoveToFront(key);
405
+ } else {
406
+ this.lruAddToFront(key);
407
+ if (this._lru.size > this._lruSize) {
408
+ const oldestKey = this._lru.getOldest();
409
+ if (oldestKey) {
410
+ this._lru.removeOldest();
411
+ this.delete(oldestKey);
412
+ }
413
+ }
414
+ }
415
+ }
416
+ const item = { key, value, expires };
417
+ store.set(
418
+ key,
419
+ item
420
+ );
421
+ }
422
+ /**
423
+ * Sets the values of the keys
424
+ * @param {CacheableItem[]} items - The items to set
425
+ * @returns {void}
426
+ */
427
+ setMany(items) {
428
+ for (const item of items) {
429
+ this.set(item.key, item.value, item.ttl);
430
+ }
431
+ }
432
+ /**
433
+ * Checks if the key exists
434
+ * @param {string} key - The key to check
435
+ * @returns {boolean} - If true, the key exists. If false, the key does not exist.
436
+ */
437
+ has(key) {
438
+ const item = this.get(key);
439
+ return Boolean(item);
440
+ }
441
+ /**
442
+ * @function hasMany
443
+ * @param {string[]} keys - The keys to check
444
+ * @returns {boolean[]} - If true, the key exists. If false, the key does not exist.
445
+ */
446
+ hasMany(keys) {
447
+ const result = new Array();
448
+ for (const key of keys) {
449
+ const item = this.get(key);
450
+ result.push(Boolean(item));
451
+ }
452
+ return result;
453
+ }
454
+ /**
455
+ * Take will get the key and delete the entry from cache
456
+ * @param {string} key - The key to take
457
+ * @returns {T | undefined} - The value of the key
458
+ */
459
+ take(key) {
460
+ const item = this.get(key);
461
+ if (!item) {
462
+ return void 0;
463
+ }
464
+ this.delete(key);
465
+ return item;
466
+ }
467
+ /**
468
+ * TakeMany will get the keys and delete the entries from cache
469
+ * @param {string[]} keys - The keys to take
470
+ * @returns {T[]} - The values of the keys
471
+ */
472
+ takeMany(keys) {
473
+ const result = new Array();
474
+ for (const key of keys) {
475
+ result.push(this.take(key));
476
+ }
477
+ return result;
478
+ }
479
+ /**
480
+ * Delete the key
481
+ * @param {string} key - The key to delete
482
+ * @returns {void}
483
+ */
484
+ delete(key) {
485
+ const store = this.getStore(key);
486
+ store.delete(key);
487
+ }
488
+ /**
489
+ * Delete the keys
490
+ * @param {string[]} keys - The keys to delete
491
+ * @returns {void}
492
+ */
493
+ deleteMany(keys) {
494
+ for (const key of keys) {
495
+ this.delete(key);
496
+ }
497
+ }
498
+ /**
499
+ * Clear the cache
500
+ * @returns {void}
501
+ */
502
+ clear() {
503
+ this._store = Array.from({ length: this._storeHashSize }, () => /* @__PURE__ */ new Map());
504
+ this._lru = new DoublyLinkedList();
505
+ }
506
+ /**
507
+ * Get the store based on the key (internal use)
508
+ * @param {string} key - The key to get the store
509
+ * @returns {CacheableHashStore} - The store
510
+ */
511
+ getStore(key) {
512
+ const hash2 = this.getKeyStoreHash(key);
513
+ this._store[hash2] ||= /* @__PURE__ */ new Map();
514
+ return this._store[hash2];
515
+ }
516
+ /**
517
+ * Hash the key for which store to go to (internal use)
518
+ * @param {string} key - The key to hash
519
+ * Available algorithms are: SHA256, SHA1, MD5, and djb2Hash.
520
+ * @returns {number} - The hashed key as a number
521
+ */
522
+ getKeyStoreHash(key) {
523
+ if (this._store.length === 1) {
524
+ return 0;
525
+ }
526
+ if (typeof this._storeHashAlgorithm === "function") {
527
+ return this._storeHashAlgorithm(key, this._storeHashSize);
528
+ }
529
+ const storeHashSize = this._storeHashSize - 1;
530
+ const hash2 = (0, import_utils.hashToNumber)(key, 0, storeHashSize, this._storeHashAlgorithm);
531
+ return hash2;
532
+ }
533
+ /**
534
+ * Clone the value. This is for internal use
535
+ * @param {any} value - The value to clone
536
+ * @returns {any} - The cloned value
537
+ */
538
+ clone(value) {
539
+ if (this.isPrimitive(value)) {
540
+ return value;
541
+ }
542
+ return structuredClone(value);
543
+ }
544
+ /**
545
+ * Add to the front of the LRU cache. This is for internal use
546
+ * @param {string} key - The key to add to the front
547
+ * @returns {void}
548
+ */
549
+ lruAddToFront(key) {
550
+ if (this._lruSize === 0) {
551
+ return;
552
+ }
553
+ this._lru.addToFront(key);
554
+ }
555
+ /**
556
+ * Move to the front of the LRU cache. This is for internal use
557
+ * @param {string} key - The key to move to the front
558
+ * @returns {void}
559
+ */
560
+ lruMoveToFront(key) {
561
+ if (this._lruSize === 0) {
562
+ return;
563
+ }
564
+ this._lru.moveToFront(key);
565
+ }
566
+ /**
567
+ * Resize the LRU cache. This is for internal use.
568
+ * @returns {void}
569
+ */
570
+ lruResize() {
571
+ while (this._lru.size > this._lruSize) {
572
+ const oldestKey = this._lru.getOldest();
573
+ if (oldestKey) {
574
+ this._lru.removeOldest();
575
+ this.delete(oldestKey);
576
+ }
577
+ }
578
+ }
579
+ /**
580
+ * Check for expiration. This is for internal use
581
+ * @returns {void}
582
+ */
583
+ checkExpiration() {
584
+ for (const store of this._store) {
585
+ for (const item of store.values()) {
586
+ if (item.expires && Date.now() > item.expires) {
587
+ store.delete(item.key);
588
+ }
589
+ }
590
+ }
591
+ }
592
+ /**
593
+ * Start the interval check. This is for internal use
594
+ * @returns {void}
595
+ */
596
+ startIntervalCheck() {
597
+ if (this._checkInterval > 0) {
598
+ if (this._interval) {
599
+ clearInterval(this._interval);
600
+ }
601
+ this._interval = setInterval(() => {
602
+ this.checkExpiration();
603
+ }, this._checkInterval).unref();
604
+ }
605
+ }
606
+ /**
607
+ * Stop the interval check. This is for internal use
608
+ * @returns {void}
609
+ */
610
+ stopIntervalCheck() {
611
+ if (this._interval) {
612
+ clearInterval(this._interval);
613
+ }
614
+ this._interval = 0;
615
+ this._checkInterval = 0;
616
+ }
617
+ /**
618
+ * Wrap the function for caching
619
+ * @param {Function} function_ - The function to wrap
620
+ * @param {Object} [options] - The options to wrap
621
+ * @returns {Function} - The wrapped function
622
+ */
623
+ wrap(function_, options) {
624
+ const wrapOptions = {
625
+ ttl: options?.ttl ?? this._ttl,
626
+ keyPrefix: options?.keyPrefix,
627
+ cache: this
628
+ };
629
+ return (0, import_memoize.wrapSync)(function_, wrapOptions);
630
+ }
631
+ isPrimitive(value) {
632
+ const result = false;
633
+ if (value === null || value === void 0) {
634
+ return true;
635
+ }
636
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
637
+ return true;
638
+ }
639
+ return result;
640
+ }
641
+ setTtl(ttl) {
642
+ if (typeof ttl === "string" || ttl === void 0) {
643
+ this._ttl = ttl;
644
+ } else if (ttl > 0) {
645
+ this._ttl = ttl;
646
+ } else {
647
+ this._ttl = void 0;
648
+ }
649
+ }
650
+ hasExpired(item) {
651
+ if (item.expires && Date.now() > item.expires) {
652
+ return true;
653
+ }
654
+ return false;
655
+ }
656
+ };
657
+ // Annotate the CommonJS export names for ESM import in node:
658
+ 0 && (module.exports = {
659
+ CacheableMemory,
660
+ HashAlgorithm,
661
+ defaultStoreHashSize,
662
+ hash,
663
+ hashToNumber,
664
+ maximumMapSize
665
+ });