cacheable 1.3.0 → 1.6.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 CHANGED
@@ -9,7 +9,7 @@
9
9
  [![npm](https://img.shields.io/npm/dm/cacheable.svg)](https://www.npmjs.com/package/cacheable)
10
10
  [![npm](https://img.shields.io/npm/v/cacheable)](https://www.npmjs.com/package/cacheable)
11
11
 
12
- `cacheable` is a high performance layer 1 / layer 2 caching engine that is focused on distributed caching with enterprise features such as `CacheSync`. It is built on top of the robust storage engine [Keyv](https://keyv.org) and provides a simple API to cache and retrieve data.
12
+ `cacheable` is a high performance layer 1 / layer 2 caching engine that is focused on distributed caching with enterprise features such as `CacheSync` (coming soon). It is built on top of the robust storage engine [Keyv](https://keyv.org) and provides a simple API to cache and retrieve data.
13
13
 
14
14
  * Simple to use with robust API
15
15
  * Not bloated with additional modules
@@ -25,7 +25,7 @@
25
25
 
26
26
  ## Getting Started
27
27
 
28
- `cacheable` is primarily used as an extension to you caching engine with a robust storage backend [Keyv](https://keyv.org), Memonization, Hooks, Events, and Statistics.
28
+ `cacheable` is primarily used as an extension to you caching engine with a robust storage backend [Keyv](https://keyv.org), Memonization (Wrap), Hooks, Events, and Statistics.
29
29
 
30
30
  ```bash
31
31
  npm install cacheable
@@ -115,7 +115,6 @@ const cache = new Cacheable({secondary, nonBlocking: true});
115
115
 
116
116
  `cacheable` has a feature called `CacheSync` that is coming soon. This feature will allow you to have distributed caching with Pub/Sub. This will allow you to have multiple instances of `cacheable` running and when a value is set, deleted, or cleared it will update all instances of `cacheable` with the same value. Current plan is to support the following:
117
117
 
118
- * [Google Pub/Sub](https://cloud.google.com/pubsub)
119
118
  * [AWS SQS](https://aws.amazon.com/sqs)
120
119
  * [RabbitMQ](https://www.rabbitmq.com)
121
120
  * [Nats](https://nats.io)
@@ -153,15 +152,15 @@ _This does not enable statistics for your layer 2 cache as that is a distributed
153
152
 
154
153
  ## API
155
154
 
156
- * `set(key, value, ttl? | [{string, string, ttl?}])`: Sets a value in the cache.
155
+ * `set(key, value, ttl?)`: Sets a value in the cache.
157
156
  * `setMany([{key, value, ttl?}])`: Sets multiple values in the cache.
158
- * `get(key | [keys])`: Gets a value from the cache.
157
+ * `get(key)`: Gets a value from the cache.
159
158
  * `getMany([keys])`: Gets multiple values from the cache.
160
- * `has(key | [key])`: Checks if a value exists in the cache.
159
+ * `has(key)`: Checks if a value exists in the cache.
161
160
  * `hasMany([keys])`: Checks if multiple values exist in the cache.
162
161
  * `take(key)`: Takes a value from the cache and deletes it.
163
162
  * `takeMany([keys])`: Takes multiple values from the cache and deletes them.
164
- * `delete(key | [key])`: Deletes a value from the cache.
163
+ * `delete(key)`: Deletes a value from the cache.
165
164
  * `deleteMany([keys])`: Deletes multiple values from the cache.
166
165
  * `clear()`: Clears the cache stores. Be careful with this as it will clear both layer 1 and layer 2.
167
166
  * `wrap(function, options)`: Wraps a function in a cache. (coming soon)
@@ -187,14 +186,23 @@ const options = {
187
186
  lruSize: 1000, // the size of the LRU cache (default is 0 which is unlimited)
188
187
  }
189
188
  const cache = new CacheableMemory(options);
190
- await cache.set('key', 'value');
191
- const value = await cache.get('key'); // value
189
+ cache.set('key', 'value');
190
+ const value = cache.get('key'); // value
192
191
  ```
193
192
 
194
193
  You can use `CacheableMemory` as a standalone cache or as a primary store for `cacheable`. You can also set the `useClones` property to `false` if you want to use the same reference for the values. This is useful if you are using large objects and want to save memory. The `lruSize` property is the size of the LRU cache and is set to `0` by default which is unlimited. When setting the `lruSize` property it will limit the number of keys in the cache.
195
194
 
196
195
  This simple in-memory cache uses multiple Map objects and a with `expiration` and `lru` policies if set to manage the in memory cache at scale.
197
196
 
197
+ By default we use lazy expiration deletion which means on `get` and `getMany` type functions we look if it is expired and then delete it. If you want to have a more aggressive expiration policy you can set the `checkInterval` property to a value greater than `0` which will check for expired keys at the interval you set.
198
+
199
+ ### CacheableMemory Options
200
+
201
+ * `ttl`: The time to live for the cache in milliseconds.
202
+ * `useClones`: If the cache should use clones for the values. Default is `true`.
203
+ * `lruSize`: The size of the LRU cache. Default is `0` which is unlimited.
204
+ * `checkInterval`: The interval to check for expired keys in milliseconds. Default is `0` which is disabled.
205
+
198
206
  ### CacheableMemory API
199
207
 
200
208
  * `set(key, value, ttl?)`: Sets a value in the cache.
@@ -204,6 +212,9 @@ This simple in-memory cache uses multiple Map objects and a with `expiration` an
204
212
  * `clear()`: Clears the cache.
205
213
  * `size()`: The number of keys in the cache.
206
214
  * `keys()`: The keys in the cache.
215
+ * `checkExpired()`: Checks for expired keys in the cache. This is used by the `checkInterval` property.
216
+ * `startIntervalCheck()`: Starts the interval check for expired keys if `checkInterval` is above 0 ms.
217
+ * `stopIntervalCheck()`: Stops the interval check for expired keys.
207
218
 
208
219
 
209
220
  ## How to Contribute
package/dist/index.cjs CHANGED
@@ -24,187 +24,13 @@ __export(src_exports, {
24
24
  CacheableEvents: () => CacheableEvents,
25
25
  CacheableHooks: () => CacheableHooks,
26
26
  CacheableMemory: () => CacheableMemory,
27
- CacheableStats: () => CacheableStats
27
+ CacheableStats: () => CacheableStats,
28
+ KeyvCacheableMemory: () => KeyvCacheableMemory
28
29
  });
29
30
  module.exports = __toCommonJS(src_exports);
30
31
  var import_keyv = require("keyv");
31
32
  var import_hookified = require("hookified");
32
33
 
33
- // src/stats.ts
34
- var CacheableStats = class {
35
- _hits = 0;
36
- _misses = 0;
37
- _gets = 0;
38
- _sets = 0;
39
- _deletes = 0;
40
- _clears = 0;
41
- _vsize = 0;
42
- _ksize = 0;
43
- _count = 0;
44
- _enabled = false;
45
- constructor(options) {
46
- if (options?.enabled) {
47
- this._enabled = options.enabled;
48
- }
49
- }
50
- get enabled() {
51
- return this._enabled;
52
- }
53
- set enabled(enabled) {
54
- this._enabled = enabled;
55
- }
56
- get hits() {
57
- return this._hits;
58
- }
59
- get misses() {
60
- return this._misses;
61
- }
62
- get gets() {
63
- return this._gets;
64
- }
65
- get sets() {
66
- return this._sets;
67
- }
68
- get deletes() {
69
- return this._deletes;
70
- }
71
- get clears() {
72
- return this._clears;
73
- }
74
- get vsize() {
75
- return this._vsize;
76
- }
77
- get ksize() {
78
- return this._ksize;
79
- }
80
- get count() {
81
- return this._count;
82
- }
83
- incrementHits() {
84
- if (!this._enabled) {
85
- return;
86
- }
87
- this._hits++;
88
- }
89
- incrementMisses() {
90
- if (!this._enabled) {
91
- return;
92
- }
93
- this._misses++;
94
- }
95
- incrementGets() {
96
- if (!this._enabled) {
97
- return;
98
- }
99
- this._gets++;
100
- }
101
- incrementSets() {
102
- if (!this._enabled) {
103
- return;
104
- }
105
- this._sets++;
106
- }
107
- incrementDeletes() {
108
- if (!this._enabled) {
109
- return;
110
- }
111
- this._deletes++;
112
- }
113
- incrementClears() {
114
- if (!this._enabled) {
115
- return;
116
- }
117
- this._clears++;
118
- }
119
- // eslint-disable-next-line @typescript-eslint/naming-convention
120
- incrementVSize(value) {
121
- if (!this._enabled) {
122
- return;
123
- }
124
- this._vsize += this.roughSizeOfObject(value);
125
- }
126
- // eslint-disable-next-line @typescript-eslint/naming-convention
127
- decreaseVSize(value) {
128
- if (!this._enabled) {
129
- return;
130
- }
131
- this._vsize -= this.roughSizeOfObject(value);
132
- }
133
- // eslint-disable-next-line @typescript-eslint/naming-convention
134
- incrementKSize(key) {
135
- if (!this._enabled) {
136
- return;
137
- }
138
- this._ksize += this.roughSizeOfString(key);
139
- }
140
- // eslint-disable-next-line @typescript-eslint/naming-convention
141
- decreaseKSize(key) {
142
- if (!this._enabled) {
143
- return;
144
- }
145
- this._ksize -= this.roughSizeOfString(key);
146
- }
147
- incrementCount() {
148
- if (!this._enabled) {
149
- return;
150
- }
151
- this._count++;
152
- }
153
- decreaseCount() {
154
- if (!this._enabled) {
155
- return;
156
- }
157
- this._count--;
158
- }
159
- setCount(count) {
160
- if (!this._enabled) {
161
- return;
162
- }
163
- this._count = count;
164
- }
165
- roughSizeOfString(value) {
166
- return value.length * 2;
167
- }
168
- roughSizeOfObject(object) {
169
- const objectList = [];
170
- const stack = [object];
171
- let bytes = 0;
172
- while (stack.length > 0) {
173
- const value = stack.pop();
174
- if (typeof value === "boolean") {
175
- bytes += 4;
176
- } else if (typeof value === "string") {
177
- bytes += value.length * 2;
178
- } else if (typeof value === "number") {
179
- bytes += 8;
180
- } else if (typeof value === "object" && value !== null && !objectList.includes(value)) {
181
- objectList.push(value);
182
- for (const key in value) {
183
- bytes += key.length * 2;
184
- stack.push(value[key]);
185
- }
186
- }
187
- }
188
- return bytes;
189
- }
190
- reset() {
191
- this._hits = 0;
192
- this._misses = 0;
193
- this._gets = 0;
194
- this._sets = 0;
195
- this._deletes = 0;
196
- this._clears = 0;
197
- this._vsize = 0;
198
- this._ksize = 0;
199
- this._count = 0;
200
- }
201
- resetStoreValues() {
202
- this._vsize = 0;
203
- this._ksize = 0;
204
- this._count = 0;
205
- }
206
- };
207
-
208
34
  // src/memory-lru.ts
209
35
  var ListNode = class {
210
36
  // eslint-disable-next-line @typescript-eslint/parameter-properties
@@ -293,8 +119,15 @@ var CacheableMemory = class {
293
119
  _hash9 = /* @__PURE__ */ new Map();
294
120
  _lru = new DoublyLinkedList();
295
121
  _ttl = 0;
122
+ // Turned off by default
296
123
  _useClone = true;
124
+ // Turned on by default
297
125
  _lruSize = 0;
126
+ // Turned off by default
127
+ _checkInterval = 0;
128
+ // Turned off by default
129
+ _interval = 0;
130
+ // Turned off by default
298
131
  constructor(options) {
299
132
  if (options?.ttl) {
300
133
  this._ttl = options.ttl;
@@ -305,6 +138,10 @@ var CacheableMemory = class {
305
138
  if (options?.lruSize) {
306
139
  this._lruSize = options.lruSize;
307
140
  }
141
+ if (options?.checkInterval) {
142
+ this._checkInterval = options.checkInterval;
143
+ }
144
+ this.startIntervalCheck();
308
145
  }
309
146
  get ttl() {
310
147
  return this._ttl;
@@ -325,6 +162,12 @@ var CacheableMemory = class {
325
162
  this._lruSize = value;
326
163
  this.lruResize();
327
164
  }
165
+ get checkInterval() {
166
+ return this._checkInterval;
167
+ }
168
+ set checkInterval(value) {
169
+ this._checkInterval = value;
170
+ }
328
171
  get size() {
329
172
  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;
330
173
  }
@@ -482,6 +325,28 @@ var CacheableMemory = class {
482
325
  }
483
326
  }
484
327
  }
328
+ checkExpiration() {
329
+ const stores = this.concatStores();
330
+ for (const item of stores.values()) {
331
+ if (item.expires && Date.now() > item.expires) {
332
+ this.delete(item.key);
333
+ }
334
+ }
335
+ }
336
+ startIntervalCheck() {
337
+ if (this._checkInterval > 0) {
338
+ this._interval = setInterval(() => {
339
+ this.checkExpiration();
340
+ }, this._checkInterval);
341
+ }
342
+ }
343
+ stopIntervalCheck() {
344
+ if (this._interval) {
345
+ clearInterval(this._interval);
346
+ }
347
+ this._interval = 0;
348
+ this._checkInterval = 0;
349
+ }
485
350
  isPrimitive(value) {
486
351
  const result = false;
487
352
  if (value === null || value === void 0) {
@@ -498,6 +363,236 @@ var CacheableMemory = class {
498
363
  }
499
364
  };
500
365
 
366
+ // src/keyv-memory.ts
367
+ var KeyvCacheableMemory = class {
368
+ opts = {
369
+ ttl: 0,
370
+ useClone: true,
371
+ lruSize: 0,
372
+ checkInterval: 0
373
+ };
374
+ namespace;
375
+ _cache = new CacheableMemory();
376
+ constructor(options) {
377
+ if (options) {
378
+ this.opts = options;
379
+ this._cache = new CacheableMemory(options);
380
+ }
381
+ }
382
+ async get(key) {
383
+ const result = this._cache.get(key);
384
+ console.log("result", result);
385
+ if (result) {
386
+ return result;
387
+ }
388
+ return void 0;
389
+ }
390
+ set(key, value, ttl) {
391
+ this._cache.set(key, value, ttl);
392
+ }
393
+ async delete(key) {
394
+ this._cache.delete(key);
395
+ return true;
396
+ }
397
+ async clear() {
398
+ this._cache.clear();
399
+ }
400
+ async has(key) {
401
+ return this._cache.has(key);
402
+ }
403
+ async getMany(keys) {
404
+ const result = [];
405
+ for (const key of keys) {
406
+ result.push(this._cache.get(key));
407
+ }
408
+ return result;
409
+ }
410
+ async deleteMany(key) {
411
+ for (const k of key) {
412
+ this._cache.delete(k);
413
+ }
414
+ return true;
415
+ }
416
+ on(event, listener) {
417
+ return this;
418
+ }
419
+ };
420
+
421
+ // src/stats.ts
422
+ var CacheableStats = class {
423
+ _hits = 0;
424
+ _misses = 0;
425
+ _gets = 0;
426
+ _sets = 0;
427
+ _deletes = 0;
428
+ _clears = 0;
429
+ _vsize = 0;
430
+ _ksize = 0;
431
+ _count = 0;
432
+ _enabled = false;
433
+ constructor(options) {
434
+ if (options?.enabled) {
435
+ this._enabled = options.enabled;
436
+ }
437
+ }
438
+ get enabled() {
439
+ return this._enabled;
440
+ }
441
+ set enabled(enabled) {
442
+ this._enabled = enabled;
443
+ }
444
+ get hits() {
445
+ return this._hits;
446
+ }
447
+ get misses() {
448
+ return this._misses;
449
+ }
450
+ get gets() {
451
+ return this._gets;
452
+ }
453
+ get sets() {
454
+ return this._sets;
455
+ }
456
+ get deletes() {
457
+ return this._deletes;
458
+ }
459
+ get clears() {
460
+ return this._clears;
461
+ }
462
+ get vsize() {
463
+ return this._vsize;
464
+ }
465
+ get ksize() {
466
+ return this._ksize;
467
+ }
468
+ get count() {
469
+ return this._count;
470
+ }
471
+ incrementHits() {
472
+ if (!this._enabled) {
473
+ return;
474
+ }
475
+ this._hits++;
476
+ }
477
+ incrementMisses() {
478
+ if (!this._enabled) {
479
+ return;
480
+ }
481
+ this._misses++;
482
+ }
483
+ incrementGets() {
484
+ if (!this._enabled) {
485
+ return;
486
+ }
487
+ this._gets++;
488
+ }
489
+ incrementSets() {
490
+ if (!this._enabled) {
491
+ return;
492
+ }
493
+ this._sets++;
494
+ }
495
+ incrementDeletes() {
496
+ if (!this._enabled) {
497
+ return;
498
+ }
499
+ this._deletes++;
500
+ }
501
+ incrementClears() {
502
+ if (!this._enabled) {
503
+ return;
504
+ }
505
+ this._clears++;
506
+ }
507
+ // eslint-disable-next-line @typescript-eslint/naming-convention
508
+ incrementVSize(value) {
509
+ if (!this._enabled) {
510
+ return;
511
+ }
512
+ this._vsize += this.roughSizeOfObject(value);
513
+ }
514
+ // eslint-disable-next-line @typescript-eslint/naming-convention
515
+ decreaseVSize(value) {
516
+ if (!this._enabled) {
517
+ return;
518
+ }
519
+ this._vsize -= this.roughSizeOfObject(value);
520
+ }
521
+ // eslint-disable-next-line @typescript-eslint/naming-convention
522
+ incrementKSize(key) {
523
+ if (!this._enabled) {
524
+ return;
525
+ }
526
+ this._ksize += this.roughSizeOfString(key);
527
+ }
528
+ // eslint-disable-next-line @typescript-eslint/naming-convention
529
+ decreaseKSize(key) {
530
+ if (!this._enabled) {
531
+ return;
532
+ }
533
+ this._ksize -= this.roughSizeOfString(key);
534
+ }
535
+ incrementCount() {
536
+ if (!this._enabled) {
537
+ return;
538
+ }
539
+ this._count++;
540
+ }
541
+ decreaseCount() {
542
+ if (!this._enabled) {
543
+ return;
544
+ }
545
+ this._count--;
546
+ }
547
+ setCount(count) {
548
+ if (!this._enabled) {
549
+ return;
550
+ }
551
+ this._count = count;
552
+ }
553
+ roughSizeOfString(value) {
554
+ return value.length * 2;
555
+ }
556
+ roughSizeOfObject(object) {
557
+ const objectList = [];
558
+ const stack = [object];
559
+ let bytes = 0;
560
+ while (stack.length > 0) {
561
+ const value = stack.pop();
562
+ if (typeof value === "boolean") {
563
+ bytes += 4;
564
+ } else if (typeof value === "string") {
565
+ bytes += value.length * 2;
566
+ } else if (typeof value === "number") {
567
+ bytes += 8;
568
+ } else if (typeof value === "object" && value !== null && !objectList.includes(value)) {
569
+ objectList.push(value);
570
+ for (const key in value) {
571
+ bytes += key.length * 2;
572
+ stack.push(value[key]);
573
+ }
574
+ }
575
+ }
576
+ return bytes;
577
+ }
578
+ reset() {
579
+ this._hits = 0;
580
+ this._misses = 0;
581
+ this._gets = 0;
582
+ this._sets = 0;
583
+ this._deletes = 0;
584
+ this._clears = 0;
585
+ this._vsize = 0;
586
+ this._ksize = 0;
587
+ this._count = 0;
588
+ }
589
+ resetStoreValues() {
590
+ this._vsize = 0;
591
+ this._ksize = 0;
592
+ this._count = 0;
593
+ }
594
+ };
595
+
501
596
  // src/index.ts
502
597
  var CacheableHooks = /* @__PURE__ */ ((CacheableHooks2) => {
503
598
  CacheableHooks2["BEFORE_SET"] = "BEFORE_SET";
@@ -515,7 +610,7 @@ var CacheableEvents = /* @__PURE__ */ ((CacheableEvents2) => {
515
610
  return CacheableEvents2;
516
611
  })(CacheableEvents || {});
517
612
  var Cacheable = class extends import_hookified.Hookified {
518
- _primary = new import_keyv.Keyv({ store: new CacheableMemory() });
613
+ _primary = new import_keyv.Keyv({ store: new KeyvCacheableMemory() });
519
614
  _secondary;
520
615
  _nonBlocking = false;
521
616
  _ttl;
@@ -823,5 +918,6 @@ var Cacheable = class extends import_hookified.Hookified {
823
918
  CacheableEvents,
824
919
  CacheableHooks,
825
920
  CacheableMemory,
826
- CacheableStats
921
+ CacheableStats,
922
+ KeyvCacheableMemory
827
923
  });
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { Keyv, KeyvStoreAdapter } from 'keyv';
1
+ import { KeyvStoreAdapter, StoredData, Keyv } from 'keyv';
2
2
  import { Hookified } from 'hookified';
3
3
 
4
4
  type CacheableOptions$1 = {
@@ -50,6 +50,7 @@ type CacheableMemoryOptions = {
50
50
  ttl?: number;
51
51
  useClone?: boolean;
52
52
  lruSize?: number;
53
+ checkInterval?: number;
53
54
  };
54
55
  declare class CacheableMemory {
55
56
  private readonly _hashCache;
@@ -67,6 +68,8 @@ declare class CacheableMemory {
67
68
  private _ttl;
68
69
  private _useClone;
69
70
  private _lruSize;
71
+ private _checkInterval;
72
+ private _interval;
70
73
  constructor(options?: CacheableMemoryOptions);
71
74
  get ttl(): number;
72
75
  set ttl(value: number);
@@ -74,6 +77,8 @@ declare class CacheableMemory {
74
77
  set useClone(value: boolean);
75
78
  get lruSize(): number;
76
79
  set lruSize(value: number);
80
+ get checkInterval(): number;
81
+ set checkInterval(value: number);
77
82
  get size(): number;
78
83
  get keys(): IterableIterator<string>;
79
84
  get<T>(key: string): any;
@@ -88,10 +93,28 @@ declare class CacheableMemory {
88
93
  lruAddToFront(key: string): void;
89
94
  lruMoveToFront(key: string): void;
90
95
  lruResize(): void;
96
+ checkExpiration(): void;
97
+ startIntervalCheck(): void;
98
+ stopIntervalCheck(): void;
91
99
  private isPrimitive;
92
100
  private concatStores;
93
101
  }
94
102
 
103
+ declare class KeyvCacheableMemory implements KeyvStoreAdapter {
104
+ opts: CacheableMemoryOptions;
105
+ namespace?: string | undefined;
106
+ private readonly _cache;
107
+ constructor(options?: CacheableMemoryOptions);
108
+ get<Value>(key: string): Promise<StoredData<Value> | undefined>;
109
+ set(key: string, value: any, ttl?: number): void;
110
+ delete(key: string): Promise<boolean>;
111
+ clear(): Promise<void>;
112
+ has?(key: string): Promise<boolean>;
113
+ getMany?<Value>(keys: string[]): Promise<Array<StoredData<Value | undefined>>>;
114
+ deleteMany?(key: string[]): Promise<boolean>;
115
+ on(event: string, listener: (...arguments_: any[]) => void): this;
116
+ }
117
+
95
118
  declare enum CacheableHooks {
96
119
  BEFORE_SET = "BEFORE_SET",
97
120
  AFTER_SET = "AFTER_SET",
@@ -152,4 +175,4 @@ declare class Cacheable extends Hookified {
152
175
  private hasManyKeyv;
153
176
  }
154
177
 
155
- export { Cacheable, CacheableEvents, CacheableHooks, type CacheableItem, CacheableMemory, type CacheableOptions, CacheableStats };
178
+ export { Cacheable, CacheableEvents, CacheableHooks, type CacheableItem, CacheableMemory, type CacheableOptions, CacheableStats, KeyvCacheableMemory };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Keyv, KeyvStoreAdapter } from 'keyv';
1
+ import { KeyvStoreAdapter, StoredData, Keyv } from 'keyv';
2
2
  import { Hookified } from 'hookified';
3
3
 
4
4
  type CacheableOptions$1 = {
@@ -50,6 +50,7 @@ type CacheableMemoryOptions = {
50
50
  ttl?: number;
51
51
  useClone?: boolean;
52
52
  lruSize?: number;
53
+ checkInterval?: number;
53
54
  };
54
55
  declare class CacheableMemory {
55
56
  private readonly _hashCache;
@@ -67,6 +68,8 @@ declare class CacheableMemory {
67
68
  private _ttl;
68
69
  private _useClone;
69
70
  private _lruSize;
71
+ private _checkInterval;
72
+ private _interval;
70
73
  constructor(options?: CacheableMemoryOptions);
71
74
  get ttl(): number;
72
75
  set ttl(value: number);
@@ -74,6 +77,8 @@ declare class CacheableMemory {
74
77
  set useClone(value: boolean);
75
78
  get lruSize(): number;
76
79
  set lruSize(value: number);
80
+ get checkInterval(): number;
81
+ set checkInterval(value: number);
77
82
  get size(): number;
78
83
  get keys(): IterableIterator<string>;
79
84
  get<T>(key: string): any;
@@ -88,10 +93,28 @@ declare class CacheableMemory {
88
93
  lruAddToFront(key: string): void;
89
94
  lruMoveToFront(key: string): void;
90
95
  lruResize(): void;
96
+ checkExpiration(): void;
97
+ startIntervalCheck(): void;
98
+ stopIntervalCheck(): void;
91
99
  private isPrimitive;
92
100
  private concatStores;
93
101
  }
94
102
 
103
+ declare class KeyvCacheableMemory implements KeyvStoreAdapter {
104
+ opts: CacheableMemoryOptions;
105
+ namespace?: string | undefined;
106
+ private readonly _cache;
107
+ constructor(options?: CacheableMemoryOptions);
108
+ get<Value>(key: string): Promise<StoredData<Value> | undefined>;
109
+ set(key: string, value: any, ttl?: number): void;
110
+ delete(key: string): Promise<boolean>;
111
+ clear(): Promise<void>;
112
+ has?(key: string): Promise<boolean>;
113
+ getMany?<Value>(keys: string[]): Promise<Array<StoredData<Value | undefined>>>;
114
+ deleteMany?(key: string[]): Promise<boolean>;
115
+ on(event: string, listener: (...arguments_: any[]) => void): this;
116
+ }
117
+
95
118
  declare enum CacheableHooks {
96
119
  BEFORE_SET = "BEFORE_SET",
97
120
  AFTER_SET = "AFTER_SET",
@@ -152,4 +175,4 @@ declare class Cacheable extends Hookified {
152
175
  private hasManyKeyv;
153
176
  }
154
177
 
155
- export { Cacheable, CacheableEvents, CacheableHooks, type CacheableItem, CacheableMemory, type CacheableOptions, CacheableStats };
178
+ export { Cacheable, CacheableEvents, CacheableHooks, type CacheableItem, CacheableMemory, type CacheableOptions, CacheableStats, KeyvCacheableMemory };
package/dist/index.js CHANGED
@@ -2,181 +2,6 @@
2
2
  import { Keyv } from "keyv";
3
3
  import { Hookified } from "hookified";
4
4
 
5
- // src/stats.ts
6
- var CacheableStats = class {
7
- _hits = 0;
8
- _misses = 0;
9
- _gets = 0;
10
- _sets = 0;
11
- _deletes = 0;
12
- _clears = 0;
13
- _vsize = 0;
14
- _ksize = 0;
15
- _count = 0;
16
- _enabled = false;
17
- constructor(options) {
18
- if (options?.enabled) {
19
- this._enabled = options.enabled;
20
- }
21
- }
22
- get enabled() {
23
- return this._enabled;
24
- }
25
- set enabled(enabled) {
26
- this._enabled = enabled;
27
- }
28
- get hits() {
29
- return this._hits;
30
- }
31
- get misses() {
32
- return this._misses;
33
- }
34
- get gets() {
35
- return this._gets;
36
- }
37
- get sets() {
38
- return this._sets;
39
- }
40
- get deletes() {
41
- return this._deletes;
42
- }
43
- get clears() {
44
- return this._clears;
45
- }
46
- get vsize() {
47
- return this._vsize;
48
- }
49
- get ksize() {
50
- return this._ksize;
51
- }
52
- get count() {
53
- return this._count;
54
- }
55
- incrementHits() {
56
- if (!this._enabled) {
57
- return;
58
- }
59
- this._hits++;
60
- }
61
- incrementMisses() {
62
- if (!this._enabled) {
63
- return;
64
- }
65
- this._misses++;
66
- }
67
- incrementGets() {
68
- if (!this._enabled) {
69
- return;
70
- }
71
- this._gets++;
72
- }
73
- incrementSets() {
74
- if (!this._enabled) {
75
- return;
76
- }
77
- this._sets++;
78
- }
79
- incrementDeletes() {
80
- if (!this._enabled) {
81
- return;
82
- }
83
- this._deletes++;
84
- }
85
- incrementClears() {
86
- if (!this._enabled) {
87
- return;
88
- }
89
- this._clears++;
90
- }
91
- // eslint-disable-next-line @typescript-eslint/naming-convention
92
- incrementVSize(value) {
93
- if (!this._enabled) {
94
- return;
95
- }
96
- this._vsize += this.roughSizeOfObject(value);
97
- }
98
- // eslint-disable-next-line @typescript-eslint/naming-convention
99
- decreaseVSize(value) {
100
- if (!this._enabled) {
101
- return;
102
- }
103
- this._vsize -= this.roughSizeOfObject(value);
104
- }
105
- // eslint-disable-next-line @typescript-eslint/naming-convention
106
- incrementKSize(key) {
107
- if (!this._enabled) {
108
- return;
109
- }
110
- this._ksize += this.roughSizeOfString(key);
111
- }
112
- // eslint-disable-next-line @typescript-eslint/naming-convention
113
- decreaseKSize(key) {
114
- if (!this._enabled) {
115
- return;
116
- }
117
- this._ksize -= this.roughSizeOfString(key);
118
- }
119
- incrementCount() {
120
- if (!this._enabled) {
121
- return;
122
- }
123
- this._count++;
124
- }
125
- decreaseCount() {
126
- if (!this._enabled) {
127
- return;
128
- }
129
- this._count--;
130
- }
131
- setCount(count) {
132
- if (!this._enabled) {
133
- return;
134
- }
135
- this._count = count;
136
- }
137
- roughSizeOfString(value) {
138
- return value.length * 2;
139
- }
140
- roughSizeOfObject(object) {
141
- const objectList = [];
142
- const stack = [object];
143
- let bytes = 0;
144
- while (stack.length > 0) {
145
- const value = stack.pop();
146
- if (typeof value === "boolean") {
147
- bytes += 4;
148
- } else if (typeof value === "string") {
149
- bytes += value.length * 2;
150
- } else if (typeof value === "number") {
151
- bytes += 8;
152
- } else if (typeof value === "object" && value !== null && !objectList.includes(value)) {
153
- objectList.push(value);
154
- for (const key in value) {
155
- bytes += key.length * 2;
156
- stack.push(value[key]);
157
- }
158
- }
159
- }
160
- return bytes;
161
- }
162
- reset() {
163
- this._hits = 0;
164
- this._misses = 0;
165
- this._gets = 0;
166
- this._sets = 0;
167
- this._deletes = 0;
168
- this._clears = 0;
169
- this._vsize = 0;
170
- this._ksize = 0;
171
- this._count = 0;
172
- }
173
- resetStoreValues() {
174
- this._vsize = 0;
175
- this._ksize = 0;
176
- this._count = 0;
177
- }
178
- };
179
-
180
5
  // src/memory-lru.ts
181
6
  var ListNode = class {
182
7
  // eslint-disable-next-line @typescript-eslint/parameter-properties
@@ -265,8 +90,15 @@ var CacheableMemory = class {
265
90
  _hash9 = /* @__PURE__ */ new Map();
266
91
  _lru = new DoublyLinkedList();
267
92
  _ttl = 0;
93
+ // Turned off by default
268
94
  _useClone = true;
95
+ // Turned on by default
269
96
  _lruSize = 0;
97
+ // Turned off by default
98
+ _checkInterval = 0;
99
+ // Turned off by default
100
+ _interval = 0;
101
+ // Turned off by default
270
102
  constructor(options) {
271
103
  if (options?.ttl) {
272
104
  this._ttl = options.ttl;
@@ -277,6 +109,10 @@ var CacheableMemory = class {
277
109
  if (options?.lruSize) {
278
110
  this._lruSize = options.lruSize;
279
111
  }
112
+ if (options?.checkInterval) {
113
+ this._checkInterval = options.checkInterval;
114
+ }
115
+ this.startIntervalCheck();
280
116
  }
281
117
  get ttl() {
282
118
  return this._ttl;
@@ -297,6 +133,12 @@ var CacheableMemory = class {
297
133
  this._lruSize = value;
298
134
  this.lruResize();
299
135
  }
136
+ get checkInterval() {
137
+ return this._checkInterval;
138
+ }
139
+ set checkInterval(value) {
140
+ this._checkInterval = value;
141
+ }
300
142
  get size() {
301
143
  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;
302
144
  }
@@ -454,6 +296,28 @@ var CacheableMemory = class {
454
296
  }
455
297
  }
456
298
  }
299
+ checkExpiration() {
300
+ const stores = this.concatStores();
301
+ for (const item of stores.values()) {
302
+ if (item.expires && Date.now() > item.expires) {
303
+ this.delete(item.key);
304
+ }
305
+ }
306
+ }
307
+ startIntervalCheck() {
308
+ if (this._checkInterval > 0) {
309
+ this._interval = setInterval(() => {
310
+ this.checkExpiration();
311
+ }, this._checkInterval);
312
+ }
313
+ }
314
+ stopIntervalCheck() {
315
+ if (this._interval) {
316
+ clearInterval(this._interval);
317
+ }
318
+ this._interval = 0;
319
+ this._checkInterval = 0;
320
+ }
457
321
  isPrimitive(value) {
458
322
  const result = false;
459
323
  if (value === null || value === void 0) {
@@ -470,6 +334,236 @@ var CacheableMemory = class {
470
334
  }
471
335
  };
472
336
 
337
+ // src/keyv-memory.ts
338
+ var KeyvCacheableMemory = class {
339
+ opts = {
340
+ ttl: 0,
341
+ useClone: true,
342
+ lruSize: 0,
343
+ checkInterval: 0
344
+ };
345
+ namespace;
346
+ _cache = new CacheableMemory();
347
+ constructor(options) {
348
+ if (options) {
349
+ this.opts = options;
350
+ this._cache = new CacheableMemory(options);
351
+ }
352
+ }
353
+ async get(key) {
354
+ const result = this._cache.get(key);
355
+ console.log("result", result);
356
+ if (result) {
357
+ return result;
358
+ }
359
+ return void 0;
360
+ }
361
+ set(key, value, ttl) {
362
+ this._cache.set(key, value, ttl);
363
+ }
364
+ async delete(key) {
365
+ this._cache.delete(key);
366
+ return true;
367
+ }
368
+ async clear() {
369
+ this._cache.clear();
370
+ }
371
+ async has(key) {
372
+ return this._cache.has(key);
373
+ }
374
+ async getMany(keys) {
375
+ const result = [];
376
+ for (const key of keys) {
377
+ result.push(this._cache.get(key));
378
+ }
379
+ return result;
380
+ }
381
+ async deleteMany(key) {
382
+ for (const k of key) {
383
+ this._cache.delete(k);
384
+ }
385
+ return true;
386
+ }
387
+ on(event, listener) {
388
+ return this;
389
+ }
390
+ };
391
+
392
+ // src/stats.ts
393
+ var CacheableStats = class {
394
+ _hits = 0;
395
+ _misses = 0;
396
+ _gets = 0;
397
+ _sets = 0;
398
+ _deletes = 0;
399
+ _clears = 0;
400
+ _vsize = 0;
401
+ _ksize = 0;
402
+ _count = 0;
403
+ _enabled = false;
404
+ constructor(options) {
405
+ if (options?.enabled) {
406
+ this._enabled = options.enabled;
407
+ }
408
+ }
409
+ get enabled() {
410
+ return this._enabled;
411
+ }
412
+ set enabled(enabled) {
413
+ this._enabled = enabled;
414
+ }
415
+ get hits() {
416
+ return this._hits;
417
+ }
418
+ get misses() {
419
+ return this._misses;
420
+ }
421
+ get gets() {
422
+ return this._gets;
423
+ }
424
+ get sets() {
425
+ return this._sets;
426
+ }
427
+ get deletes() {
428
+ return this._deletes;
429
+ }
430
+ get clears() {
431
+ return this._clears;
432
+ }
433
+ get vsize() {
434
+ return this._vsize;
435
+ }
436
+ get ksize() {
437
+ return this._ksize;
438
+ }
439
+ get count() {
440
+ return this._count;
441
+ }
442
+ incrementHits() {
443
+ if (!this._enabled) {
444
+ return;
445
+ }
446
+ this._hits++;
447
+ }
448
+ incrementMisses() {
449
+ if (!this._enabled) {
450
+ return;
451
+ }
452
+ this._misses++;
453
+ }
454
+ incrementGets() {
455
+ if (!this._enabled) {
456
+ return;
457
+ }
458
+ this._gets++;
459
+ }
460
+ incrementSets() {
461
+ if (!this._enabled) {
462
+ return;
463
+ }
464
+ this._sets++;
465
+ }
466
+ incrementDeletes() {
467
+ if (!this._enabled) {
468
+ return;
469
+ }
470
+ this._deletes++;
471
+ }
472
+ incrementClears() {
473
+ if (!this._enabled) {
474
+ return;
475
+ }
476
+ this._clears++;
477
+ }
478
+ // eslint-disable-next-line @typescript-eslint/naming-convention
479
+ incrementVSize(value) {
480
+ if (!this._enabled) {
481
+ return;
482
+ }
483
+ this._vsize += this.roughSizeOfObject(value);
484
+ }
485
+ // eslint-disable-next-line @typescript-eslint/naming-convention
486
+ decreaseVSize(value) {
487
+ if (!this._enabled) {
488
+ return;
489
+ }
490
+ this._vsize -= this.roughSizeOfObject(value);
491
+ }
492
+ // eslint-disable-next-line @typescript-eslint/naming-convention
493
+ incrementKSize(key) {
494
+ if (!this._enabled) {
495
+ return;
496
+ }
497
+ this._ksize += this.roughSizeOfString(key);
498
+ }
499
+ // eslint-disable-next-line @typescript-eslint/naming-convention
500
+ decreaseKSize(key) {
501
+ if (!this._enabled) {
502
+ return;
503
+ }
504
+ this._ksize -= this.roughSizeOfString(key);
505
+ }
506
+ incrementCount() {
507
+ if (!this._enabled) {
508
+ return;
509
+ }
510
+ this._count++;
511
+ }
512
+ decreaseCount() {
513
+ if (!this._enabled) {
514
+ return;
515
+ }
516
+ this._count--;
517
+ }
518
+ setCount(count) {
519
+ if (!this._enabled) {
520
+ return;
521
+ }
522
+ this._count = count;
523
+ }
524
+ roughSizeOfString(value) {
525
+ return value.length * 2;
526
+ }
527
+ roughSizeOfObject(object) {
528
+ const objectList = [];
529
+ const stack = [object];
530
+ let bytes = 0;
531
+ while (stack.length > 0) {
532
+ const value = stack.pop();
533
+ if (typeof value === "boolean") {
534
+ bytes += 4;
535
+ } else if (typeof value === "string") {
536
+ bytes += value.length * 2;
537
+ } else if (typeof value === "number") {
538
+ bytes += 8;
539
+ } else if (typeof value === "object" && value !== null && !objectList.includes(value)) {
540
+ objectList.push(value);
541
+ for (const key in value) {
542
+ bytes += key.length * 2;
543
+ stack.push(value[key]);
544
+ }
545
+ }
546
+ }
547
+ return bytes;
548
+ }
549
+ reset() {
550
+ this._hits = 0;
551
+ this._misses = 0;
552
+ this._gets = 0;
553
+ this._sets = 0;
554
+ this._deletes = 0;
555
+ this._clears = 0;
556
+ this._vsize = 0;
557
+ this._ksize = 0;
558
+ this._count = 0;
559
+ }
560
+ resetStoreValues() {
561
+ this._vsize = 0;
562
+ this._ksize = 0;
563
+ this._count = 0;
564
+ }
565
+ };
566
+
473
567
  // src/index.ts
474
568
  var CacheableHooks = /* @__PURE__ */ ((CacheableHooks2) => {
475
569
  CacheableHooks2["BEFORE_SET"] = "BEFORE_SET";
@@ -487,7 +581,7 @@ var CacheableEvents = /* @__PURE__ */ ((CacheableEvents2) => {
487
581
  return CacheableEvents2;
488
582
  })(CacheableEvents || {});
489
583
  var Cacheable = class extends Hookified {
490
- _primary = new Keyv({ store: new CacheableMemory() });
584
+ _primary = new Keyv({ store: new KeyvCacheableMemory() });
491
585
  _secondary;
492
586
  _nonBlocking = false;
493
587
  _ttl;
@@ -794,5 +888,6 @@ export {
794
888
  CacheableEvents,
795
889
  CacheableHooks,
796
890
  CacheableMemory,
797
- CacheableStats
891
+ CacheableStats,
892
+ KeyvCacheableMemory
798
893
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cacheable",
3
- "version": "1.3.0",
3
+ "version": "1.6.0",
4
4
  "description": "Simple Caching Engine using Keyv",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -18,6 +18,7 @@
18
18
  "private": false,
19
19
  "devDependencies": {
20
20
  "@keyv/redis": "^3.0.1",
21
+ "@types/node": "^22.6.1",
21
22
  "@vitest/coverage-v8": "^2.1.1",
22
23
  "lru-cache": "^11.0.1",
23
24
  "rimraf": "^6.0.1",