cacheable 0.8.0 → 1.2.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 +38 -8
- package/dist/index.cjs +399 -21
- package/dist/index.d.cts +53 -2
- package/dist/index.d.ts +53 -2
- package/dist/index.js +396 -20
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -15,10 +15,12 @@
|
|
|
15
15
|
* Not bloated with additional modules
|
|
16
16
|
* Extendable to your own caching engine
|
|
17
17
|
* Scalable and trusted storage engine by Keyv
|
|
18
|
+
* Memory Caching with LRU and Expiration `CacheableMemory`
|
|
18
19
|
* Resilient to failures with try/catch and offline
|
|
19
20
|
* Hooks and Events to extend functionality
|
|
20
21
|
* Comprehensive testing and code coverage
|
|
21
22
|
* Distributed Caching Sync via Pub/Sub (coming soon)
|
|
23
|
+
* ESM and CommonJS support with TypeScript
|
|
22
24
|
* Maintained and supported regularly
|
|
23
25
|
|
|
24
26
|
## Getting Started
|
|
@@ -129,11 +131,11 @@ The following options are available for you to configure `cacheable`:
|
|
|
129
131
|
* `primary`: The primary store for the cache (layer 1) defaults to in-memory by Keyv.
|
|
130
132
|
* `secondary`: The secondary store for the cache (layer 2) usually a persistent cache by Keyv.
|
|
131
133
|
* `nonBlocking`: If the secondary store is non-blocking. Default is `false`.
|
|
132
|
-
* `
|
|
134
|
+
* `stats`: To enable statistics for this instance. Default is `false`.
|
|
133
135
|
|
|
134
136
|
## Cacheable Statistics (Instance Only)
|
|
135
137
|
|
|
136
|
-
If you want to enable statistics for your instance you can set the `
|
|
138
|
+
If you want to enable statistics for your instance you can set the `.stats.enabled` property to `true` in the options. This will enable statistics for your instance and you can get the statistics by calling the `stats` property. Here are the following property statistics:
|
|
137
139
|
|
|
138
140
|
* `hits`: The number of hits in the cache.
|
|
139
141
|
* `misses`: The number of misses in the cache.
|
|
@@ -145,7 +147,7 @@ If you want to enable statistics for your instance you can set the `enableStats`
|
|
|
145
147
|
* `vsize`: The estimated byte size of the values in the cache.
|
|
146
148
|
* `ksize`: The estimated byte size of the keys in the cache.
|
|
147
149
|
|
|
148
|
-
You can clear the stats by calling the
|
|
150
|
+
You can clear / reset the stats by calling the `.stats.reset()` method.
|
|
149
151
|
|
|
150
152
|
_This does not enable statistics for your layer 2 cache as that is a distributed cache_.
|
|
151
153
|
|
|
@@ -157,13 +159,11 @@ _This does not enable statistics for your layer 2 cache as that is a distributed
|
|
|
157
159
|
* `getMany([keys])`: Gets multiple values from the cache.
|
|
158
160
|
* `has(key | [key])`: Checks if a value exists in the cache.
|
|
159
161
|
* `hasMany([keys])`: Checks if multiple values exist in the cache.
|
|
160
|
-
* `take(key)`: Takes a value from the cache and deletes it.
|
|
161
|
-
* `takeMany([keys])`: Takes multiple values from the cache and deletes them.
|
|
162
|
+
* `take(key)`: Takes a value from the cache and deletes it.
|
|
163
|
+
* `takeMany([keys])`: Takes multiple values from the cache and deletes them.
|
|
162
164
|
* `delete(key | [key])`: Deletes a value from the cache.
|
|
163
165
|
* `deleteMany([keys])`: Deletes multiple values from the cache.
|
|
164
166
|
* `clear()`: Clears the cache stores. Be careful with this as it will clear both layer 1 and layer 2.
|
|
165
|
-
* `clearPrimary()`: Clears the primary store. (coming soon)
|
|
166
|
-
* `clearSecondary()`: Clears the secondary store. (coming soon)
|
|
167
167
|
* `wrap(function, options)`: Wraps a function in a cache. (coming soon)
|
|
168
168
|
* `disconnect()`: Disconnects from the cache stores.
|
|
169
169
|
* `onHook(hook, callback)`: Sets a hook.
|
|
@@ -174,7 +174,37 @@ _This does not enable statistics for your layer 2 cache as that is a distributed
|
|
|
174
174
|
* `secondary`: The secondary store for the cache (layer 2) usually a persistent cache by Keyv.
|
|
175
175
|
* `nonBlocking`: If the secondary store is non-blocking. Default is `false`.
|
|
176
176
|
* `stats`: The statistics for this instance which includes `hits`, `misses`, `sets`, `deletes`, `clears`, `errors`, `count`, `vsize`, `ksize`.
|
|
177
|
-
|
|
177
|
+
|
|
178
|
+
## CacheableMemory - In-Memory Cache
|
|
179
|
+
|
|
180
|
+
`cacheable` comes with a built-in in-memory cache called `CacheableMemory`. This is a simple in-memory cache that is used as the primary store for `cacheable`. You can use this as a standalone cache or as a primary store for `cacheable`. Here is an example of how to use `CacheableMemory`:
|
|
181
|
+
|
|
182
|
+
```javascript
|
|
183
|
+
import { CacheableMemory } from 'cacheable';
|
|
184
|
+
const options = {
|
|
185
|
+
ttl: 60 * 60 * 1000, // 1 hour
|
|
186
|
+
useClones: true, // use clones for the values (default is true)
|
|
187
|
+
lruSize: 1000, // the size of the LRU cache (default is 0 which is unlimited)
|
|
188
|
+
}
|
|
189
|
+
const cache = new CacheableMemory(options);
|
|
190
|
+
await cache.set('key', 'value');
|
|
191
|
+
const value = await cache.get('key'); // value
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
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
|
+
|
|
196
|
+
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
|
+
|
|
198
|
+
### CacheableMemory API
|
|
199
|
+
|
|
200
|
+
* `set(key, value, ttl?)`: Sets a value in the cache.
|
|
201
|
+
* `get(key)`: Gets a value from the cache.
|
|
202
|
+
* `has(key)`: Checks if a value exists in the cache.
|
|
203
|
+
* `delete(key)`: Deletes a value from the cache.
|
|
204
|
+
* `clear()`: Clears the cache.
|
|
205
|
+
* `size()`: The number of keys in the cache.
|
|
206
|
+
* `keys()`: The keys in the cache.
|
|
207
|
+
|
|
178
208
|
|
|
179
209
|
## How to Contribute
|
|
180
210
|
|
package/dist/index.cjs
CHANGED
|
@@ -22,7 +22,9 @@ var src_exports = {};
|
|
|
22
22
|
__export(src_exports, {
|
|
23
23
|
Cacheable: () => Cacheable,
|
|
24
24
|
CacheableEvents: () => CacheableEvents,
|
|
25
|
-
CacheableHooks: () => CacheableHooks
|
|
25
|
+
CacheableHooks: () => CacheableHooks,
|
|
26
|
+
CacheableMemory: () => CacheableMemory,
|
|
27
|
+
CacheableStats: () => CacheableStats
|
|
26
28
|
});
|
|
27
29
|
module.exports = __toCommonJS(src_exports);
|
|
28
30
|
var import_keyv = require("keyv");
|
|
@@ -148,7 +150,7 @@ var CacheableStats = class {
|
|
|
148
150
|
}
|
|
149
151
|
this._count++;
|
|
150
152
|
}
|
|
151
|
-
|
|
153
|
+
decreaseCount() {
|
|
152
154
|
if (!this._enabled) {
|
|
153
155
|
return;
|
|
154
156
|
}
|
|
@@ -196,6 +198,304 @@ var CacheableStats = class {
|
|
|
196
198
|
this._ksize = 0;
|
|
197
199
|
this._count = 0;
|
|
198
200
|
}
|
|
201
|
+
resetStoreValues() {
|
|
202
|
+
this._vsize = 0;
|
|
203
|
+
this._ksize = 0;
|
|
204
|
+
this._count = 0;
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
// src/memory-lru.ts
|
|
209
|
+
var ListNode = class {
|
|
210
|
+
// eslint-disable-next-line @typescript-eslint/parameter-properties
|
|
211
|
+
value;
|
|
212
|
+
prev = void 0;
|
|
213
|
+
next = void 0;
|
|
214
|
+
constructor(value) {
|
|
215
|
+
this.value = value;
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
var DoublyLinkedList = class {
|
|
219
|
+
head = void 0;
|
|
220
|
+
tail = void 0;
|
|
221
|
+
nodesMap = /* @__PURE__ */ new Map();
|
|
222
|
+
// Add a new node to the front (most recently used)
|
|
223
|
+
addToFront(value) {
|
|
224
|
+
const newNode = new ListNode(value);
|
|
225
|
+
if (this.head) {
|
|
226
|
+
newNode.next = this.head;
|
|
227
|
+
this.head.prev = newNode;
|
|
228
|
+
this.head = newNode;
|
|
229
|
+
} else {
|
|
230
|
+
this.head = this.tail = newNode;
|
|
231
|
+
}
|
|
232
|
+
this.nodesMap.set(value, newNode);
|
|
233
|
+
}
|
|
234
|
+
// Move an existing node to the front (most recently used)
|
|
235
|
+
moveToFront(value) {
|
|
236
|
+
const node = this.nodesMap.get(value);
|
|
237
|
+
if (!node || this.head === node) {
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
if (node.prev) {
|
|
241
|
+
node.prev.next = node.next;
|
|
242
|
+
}
|
|
243
|
+
if (node.next) {
|
|
244
|
+
node.next.prev = node.prev;
|
|
245
|
+
}
|
|
246
|
+
if (node === this.tail) {
|
|
247
|
+
this.tail = node.prev;
|
|
248
|
+
}
|
|
249
|
+
node.prev = void 0;
|
|
250
|
+
node.next = this.head;
|
|
251
|
+
if (this.head) {
|
|
252
|
+
this.head.prev = node;
|
|
253
|
+
}
|
|
254
|
+
this.head = node;
|
|
255
|
+
this.tail ||= node;
|
|
256
|
+
}
|
|
257
|
+
// Get the oldest node (tail)
|
|
258
|
+
getOldest() {
|
|
259
|
+
return this.tail ? this.tail.value : void 0;
|
|
260
|
+
}
|
|
261
|
+
// Remove the oldest node (tail)
|
|
262
|
+
removeOldest() {
|
|
263
|
+
if (!this.tail) {
|
|
264
|
+
return void 0;
|
|
265
|
+
}
|
|
266
|
+
const oldValue = this.tail.value;
|
|
267
|
+
if (this.tail.prev) {
|
|
268
|
+
this.tail = this.tail.prev;
|
|
269
|
+
this.tail.next = void 0;
|
|
270
|
+
} else {
|
|
271
|
+
this.head = this.tail = void 0;
|
|
272
|
+
}
|
|
273
|
+
this.nodesMap.delete(oldValue);
|
|
274
|
+
return oldValue;
|
|
275
|
+
}
|
|
276
|
+
get size() {
|
|
277
|
+
return this.nodesMap.size;
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
// src/memory.ts
|
|
282
|
+
var CacheableMemory = class {
|
|
283
|
+
_hashCache = /* @__PURE__ */ new Map();
|
|
284
|
+
_hash0 = /* @__PURE__ */ new Map();
|
|
285
|
+
_hash1 = /* @__PURE__ */ new Map();
|
|
286
|
+
_hash2 = /* @__PURE__ */ new Map();
|
|
287
|
+
_hash3 = /* @__PURE__ */ new Map();
|
|
288
|
+
_hash4 = /* @__PURE__ */ new Map();
|
|
289
|
+
_hash5 = /* @__PURE__ */ new Map();
|
|
290
|
+
_hash6 = /* @__PURE__ */ new Map();
|
|
291
|
+
_hash7 = /* @__PURE__ */ new Map();
|
|
292
|
+
_hash8 = /* @__PURE__ */ new Map();
|
|
293
|
+
_hash9 = /* @__PURE__ */ new Map();
|
|
294
|
+
_lru = new DoublyLinkedList();
|
|
295
|
+
_ttl = 0;
|
|
296
|
+
_useClone = true;
|
|
297
|
+
_lruSize = 0;
|
|
298
|
+
constructor(options) {
|
|
299
|
+
if (options?.ttl) {
|
|
300
|
+
this._ttl = options.ttl;
|
|
301
|
+
}
|
|
302
|
+
if (options?.useClone !== void 0) {
|
|
303
|
+
this._useClone = options.useClone;
|
|
304
|
+
}
|
|
305
|
+
if (options?.lruSize) {
|
|
306
|
+
this._lruSize = options.lruSize;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
get ttl() {
|
|
310
|
+
return this._ttl;
|
|
311
|
+
}
|
|
312
|
+
set ttl(value) {
|
|
313
|
+
this._ttl = value;
|
|
314
|
+
}
|
|
315
|
+
get useClone() {
|
|
316
|
+
return this._useClone;
|
|
317
|
+
}
|
|
318
|
+
set useClone(value) {
|
|
319
|
+
this._useClone = value;
|
|
320
|
+
}
|
|
321
|
+
get lruSize() {
|
|
322
|
+
return this._lruSize;
|
|
323
|
+
}
|
|
324
|
+
set lruSize(value) {
|
|
325
|
+
this._lruSize = value;
|
|
326
|
+
this.lruResize();
|
|
327
|
+
}
|
|
328
|
+
get size() {
|
|
329
|
+
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
|
+
}
|
|
331
|
+
get keys() {
|
|
332
|
+
return this.concatStores().keys();
|
|
333
|
+
}
|
|
334
|
+
get(key) {
|
|
335
|
+
const store = this.getStore(key);
|
|
336
|
+
const item = store.get(key);
|
|
337
|
+
if (!item) {
|
|
338
|
+
return void 0;
|
|
339
|
+
}
|
|
340
|
+
if (item.expires && item.expires && Date.now() > item.expires) {
|
|
341
|
+
store.delete(key);
|
|
342
|
+
return void 0;
|
|
343
|
+
}
|
|
344
|
+
this.lruMoveToFront(key);
|
|
345
|
+
if (!this._useClone) {
|
|
346
|
+
return item.value;
|
|
347
|
+
}
|
|
348
|
+
return this.clone(item.value);
|
|
349
|
+
}
|
|
350
|
+
set(key, value, ttl) {
|
|
351
|
+
const store = this.getStore(key);
|
|
352
|
+
let expires;
|
|
353
|
+
if (ttl !== void 0 || this._ttl !== 0) {
|
|
354
|
+
expires = Date.now() + (ttl ?? this._ttl);
|
|
355
|
+
}
|
|
356
|
+
if (this._lruSize > 0) {
|
|
357
|
+
if (store.has(key)) {
|
|
358
|
+
this.lruMoveToFront(key);
|
|
359
|
+
} else {
|
|
360
|
+
this.lruAddToFront(key);
|
|
361
|
+
if (this._lru.size > this._lruSize) {
|
|
362
|
+
const oldestKey = this._lru.getOldest();
|
|
363
|
+
if (oldestKey) {
|
|
364
|
+
this._lru.removeOldest();
|
|
365
|
+
this.delete(oldestKey);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
store.set(key, {
|
|
371
|
+
key,
|
|
372
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
373
|
+
value,
|
|
374
|
+
expires
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
has(key) {
|
|
378
|
+
const item = this.get(key);
|
|
379
|
+
return Boolean(item);
|
|
380
|
+
}
|
|
381
|
+
take(key) {
|
|
382
|
+
const item = this.get(key);
|
|
383
|
+
if (!item) {
|
|
384
|
+
return void 0;
|
|
385
|
+
}
|
|
386
|
+
this.delete(key);
|
|
387
|
+
return item;
|
|
388
|
+
}
|
|
389
|
+
delete(key) {
|
|
390
|
+
const store = this.getStore(key);
|
|
391
|
+
store.delete(key);
|
|
392
|
+
}
|
|
393
|
+
clear() {
|
|
394
|
+
this._hash0.clear();
|
|
395
|
+
this._hash1.clear();
|
|
396
|
+
this._hash2.clear();
|
|
397
|
+
this._hash3.clear();
|
|
398
|
+
this._hash4.clear();
|
|
399
|
+
this._hash5.clear();
|
|
400
|
+
this._hash6.clear();
|
|
401
|
+
this._hash7.clear();
|
|
402
|
+
this._hash8.clear();
|
|
403
|
+
this._hash9.clear();
|
|
404
|
+
this._hashCache.clear();
|
|
405
|
+
}
|
|
406
|
+
hashKey(key) {
|
|
407
|
+
const cacheHashNumber = this._hashCache.get(key);
|
|
408
|
+
if (cacheHashNumber) {
|
|
409
|
+
return cacheHashNumber;
|
|
410
|
+
}
|
|
411
|
+
let hash = 0;
|
|
412
|
+
const primeMultiplier = 31;
|
|
413
|
+
for (let i = 0; i < key.length; i++) {
|
|
414
|
+
hash = hash * primeMultiplier + key.charCodeAt(i);
|
|
415
|
+
}
|
|
416
|
+
const result = Math.abs(hash) % 10;
|
|
417
|
+
this._hashCache.set(key, result);
|
|
418
|
+
return result;
|
|
419
|
+
}
|
|
420
|
+
getStore(key) {
|
|
421
|
+
const hashKey = this.hashKey(key);
|
|
422
|
+
switch (hashKey) {
|
|
423
|
+
case 1: {
|
|
424
|
+
return this._hash1;
|
|
425
|
+
}
|
|
426
|
+
case 2: {
|
|
427
|
+
return this._hash2;
|
|
428
|
+
}
|
|
429
|
+
case 3: {
|
|
430
|
+
return this._hash3;
|
|
431
|
+
}
|
|
432
|
+
case 4: {
|
|
433
|
+
return this._hash4;
|
|
434
|
+
}
|
|
435
|
+
case 5: {
|
|
436
|
+
return this._hash5;
|
|
437
|
+
}
|
|
438
|
+
case 6: {
|
|
439
|
+
return this._hash6;
|
|
440
|
+
}
|
|
441
|
+
case 7: {
|
|
442
|
+
return this._hash7;
|
|
443
|
+
}
|
|
444
|
+
case 8: {
|
|
445
|
+
return this._hash8;
|
|
446
|
+
}
|
|
447
|
+
case 9: {
|
|
448
|
+
return this._hash9;
|
|
449
|
+
}
|
|
450
|
+
default: {
|
|
451
|
+
return this._hash0;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
clone(value) {
|
|
456
|
+
if (this.isPrimitive(value)) {
|
|
457
|
+
return value;
|
|
458
|
+
}
|
|
459
|
+
return structuredClone(value);
|
|
460
|
+
}
|
|
461
|
+
lruAddToFront(key) {
|
|
462
|
+
if (this._lruSize === 0) {
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
this._lru.addToFront(key);
|
|
466
|
+
}
|
|
467
|
+
lruMoveToFront(key) {
|
|
468
|
+
if (this._lruSize === 0) {
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
this._lru.moveToFront(key);
|
|
472
|
+
}
|
|
473
|
+
lruResize() {
|
|
474
|
+
if (this._lruSize === 0) {
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
while (this._lru.size > this._lruSize) {
|
|
478
|
+
const oldestKey = this._lru.getOldest();
|
|
479
|
+
if (oldestKey) {
|
|
480
|
+
this._lru.removeOldest();
|
|
481
|
+
this.delete(oldestKey);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
isPrimitive(value) {
|
|
486
|
+
const result = false;
|
|
487
|
+
if (value === null || value === void 0) {
|
|
488
|
+
return true;
|
|
489
|
+
}
|
|
490
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
491
|
+
return true;
|
|
492
|
+
}
|
|
493
|
+
return result;
|
|
494
|
+
}
|
|
495
|
+
concatStores() {
|
|
496
|
+
const result = new Map([...this._hash0, ...this._hash1, ...this._hash2, ...this._hash3, ...this._hash4, ...this._hash5, ...this._hash6, ...this._hash7, ...this._hash8, ...this._hash9]);
|
|
497
|
+
return result;
|
|
498
|
+
}
|
|
199
499
|
};
|
|
200
500
|
|
|
201
501
|
// src/index.ts
|
|
@@ -215,9 +515,10 @@ var CacheableEvents = /* @__PURE__ */ ((CacheableEvents2) => {
|
|
|
215
515
|
return CacheableEvents2;
|
|
216
516
|
})(CacheableEvents || {});
|
|
217
517
|
var Cacheable = class extends import_hookified.Hookified {
|
|
218
|
-
_primary = new import_keyv.Keyv();
|
|
518
|
+
_primary = new import_keyv.Keyv({ store: new CacheableMemory() });
|
|
219
519
|
_secondary;
|
|
220
520
|
_nonBlocking = false;
|
|
521
|
+
_ttl;
|
|
221
522
|
_stats = new CacheableStats({ enabled: false });
|
|
222
523
|
constructor(options) {
|
|
223
524
|
super();
|
|
@@ -233,6 +534,9 @@ var Cacheable = class extends import_hookified.Hookified {
|
|
|
233
534
|
if (options?.stats) {
|
|
234
535
|
this._stats.enabled = options.stats;
|
|
235
536
|
}
|
|
537
|
+
if (options?.ttl) {
|
|
538
|
+
this._ttl = options.ttl;
|
|
539
|
+
}
|
|
236
540
|
}
|
|
237
541
|
get stats() {
|
|
238
542
|
return this._stats;
|
|
@@ -255,6 +559,12 @@ var Cacheable = class extends import_hookified.Hookified {
|
|
|
255
559
|
set nonBlocking(nonBlocking) {
|
|
256
560
|
this._nonBlocking = nonBlocking;
|
|
257
561
|
}
|
|
562
|
+
get ttl() {
|
|
563
|
+
return this._ttl;
|
|
564
|
+
}
|
|
565
|
+
set ttl(ttl) {
|
|
566
|
+
this._ttl = ttl;
|
|
567
|
+
}
|
|
258
568
|
setPrimary(primary) {
|
|
259
569
|
this._primary = primary instanceof import_keyv.Keyv ? primary : new import_keyv.Keyv(primary);
|
|
260
570
|
}
|
|
@@ -269,13 +579,21 @@ var Cacheable = class extends import_hookified.Hookified {
|
|
|
269
579
|
if (!result && this._secondary) {
|
|
270
580
|
result = await this._secondary.get(key);
|
|
271
581
|
if (result) {
|
|
272
|
-
await this._primary.set(key, result);
|
|
582
|
+
await this._primary.set(key, result, this._ttl);
|
|
273
583
|
}
|
|
274
584
|
}
|
|
275
585
|
await this.hook("AFTER_GET" /* AFTER_GET */, { key, result });
|
|
276
586
|
} catch (error) {
|
|
277
587
|
await this.emit("error" /* ERROR */, error);
|
|
278
588
|
}
|
|
589
|
+
if (this.stats.enabled) {
|
|
590
|
+
if (result) {
|
|
591
|
+
this._stats.incrementHits();
|
|
592
|
+
} else {
|
|
593
|
+
this._stats.incrementMisses();
|
|
594
|
+
}
|
|
595
|
+
this.stats.incrementGets();
|
|
596
|
+
}
|
|
279
597
|
return result;
|
|
280
598
|
}
|
|
281
599
|
async getMany(keys) {
|
|
@@ -294,7 +612,7 @@ var Cacheable = class extends import_hookified.Hookified {
|
|
|
294
612
|
for (const [i, key] of keys.entries()) {
|
|
295
613
|
if (!result[i] && secondaryResult[i]) {
|
|
296
614
|
result[i] = secondaryResult[i];
|
|
297
|
-
await this._primary.set(key, secondaryResult[i]);
|
|
615
|
+
await this._primary.set(key, secondaryResult[i], this._ttl);
|
|
298
616
|
}
|
|
299
617
|
}
|
|
300
618
|
}
|
|
@@ -302,16 +620,27 @@ var Cacheable = class extends import_hookified.Hookified {
|
|
|
302
620
|
} catch (error) {
|
|
303
621
|
await this.emit("error" /* ERROR */, error);
|
|
304
622
|
}
|
|
623
|
+
if (this.stats.enabled) {
|
|
624
|
+
for (const item of result) {
|
|
625
|
+
if (item) {
|
|
626
|
+
this._stats.incrementHits();
|
|
627
|
+
} else {
|
|
628
|
+
this._stats.incrementMisses();
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
this.stats.incrementGets();
|
|
632
|
+
}
|
|
305
633
|
return result;
|
|
306
634
|
}
|
|
307
635
|
async set(key, value, ttl) {
|
|
308
636
|
let result = false;
|
|
637
|
+
const finalTtl = ttl ?? this._ttl;
|
|
309
638
|
try {
|
|
310
|
-
await this.hook("BEFORE_SET" /* BEFORE_SET */, { key, value,
|
|
639
|
+
await this.hook("BEFORE_SET" /* BEFORE_SET */, { key, value, finalTtl });
|
|
311
640
|
const promises = [];
|
|
312
|
-
promises.push(this._primary.set(key, value,
|
|
641
|
+
promises.push(this._primary.set(key, value, finalTtl));
|
|
313
642
|
if (this._secondary) {
|
|
314
|
-
promises.push(this._secondary.set(key, value,
|
|
643
|
+
promises.push(this._secondary.set(key, value, finalTtl));
|
|
315
644
|
}
|
|
316
645
|
if (this._nonBlocking) {
|
|
317
646
|
result = await Promise.race(promises);
|
|
@@ -319,10 +648,16 @@ var Cacheable = class extends import_hookified.Hookified {
|
|
|
319
648
|
const results = await Promise.all(promises);
|
|
320
649
|
result = results[0];
|
|
321
650
|
}
|
|
322
|
-
await this.hook("AFTER_SET" /* AFTER_SET */, { key, value,
|
|
651
|
+
await this.hook("AFTER_SET" /* AFTER_SET */, { key, value, finalTtl });
|
|
323
652
|
} catch (error) {
|
|
324
653
|
await this.emit("error" /* ERROR */, error);
|
|
325
654
|
}
|
|
655
|
+
if (this.stats.enabled) {
|
|
656
|
+
this.stats.incrementKSize(key);
|
|
657
|
+
this.stats.incrementCount();
|
|
658
|
+
this.stats.incrementVSize(value);
|
|
659
|
+
this.stats.incrementSets();
|
|
660
|
+
}
|
|
326
661
|
return result;
|
|
327
662
|
}
|
|
328
663
|
async setMany(items) {
|
|
@@ -341,6 +676,13 @@ var Cacheable = class extends import_hookified.Hookified {
|
|
|
341
676
|
} catch (error) {
|
|
342
677
|
await this.emit("error" /* ERROR */, error);
|
|
343
678
|
}
|
|
679
|
+
if (this.stats.enabled) {
|
|
680
|
+
for (const item of items) {
|
|
681
|
+
this.stats.incrementKSize(item.key);
|
|
682
|
+
this.stats.incrementCount();
|
|
683
|
+
this.stats.incrementVSize(item.value);
|
|
684
|
+
}
|
|
685
|
+
}
|
|
344
686
|
return result;
|
|
345
687
|
}
|
|
346
688
|
async take(key) {
|
|
@@ -354,11 +696,18 @@ var Cacheable = class extends import_hookified.Hookified {
|
|
|
354
696
|
return result;
|
|
355
697
|
}
|
|
356
698
|
async has(key) {
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
699
|
+
const promises = [];
|
|
700
|
+
promises.push(this._primary.has(key));
|
|
701
|
+
if (this._secondary) {
|
|
702
|
+
promises.push(this._secondary.has(key));
|
|
360
703
|
}
|
|
361
|
-
|
|
704
|
+
const resultAll = await Promise.all(promises);
|
|
705
|
+
for (const result of resultAll) {
|
|
706
|
+
if (result) {
|
|
707
|
+
return true;
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
return false;
|
|
362
711
|
}
|
|
363
712
|
async hasMany(keys) {
|
|
364
713
|
const result = await this.hasManyKeyv(this._primary, keys);
|
|
@@ -379,17 +728,39 @@ var Cacheable = class extends import_hookified.Hookified {
|
|
|
379
728
|
return result;
|
|
380
729
|
}
|
|
381
730
|
async delete(key) {
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
731
|
+
let result = false;
|
|
732
|
+
const promises = [];
|
|
733
|
+
if (this.stats.enabled) {
|
|
734
|
+
const statResult = await this._primary.get(key);
|
|
735
|
+
if (statResult) {
|
|
736
|
+
this.stats.decreaseKSize(key);
|
|
737
|
+
this.stats.decreaseVSize(statResult);
|
|
738
|
+
this.stats.decreaseCount();
|
|
739
|
+
this.stats.incrementDeletes();
|
|
388
740
|
}
|
|
389
741
|
}
|
|
742
|
+
promises.push(this._primary.delete(key));
|
|
743
|
+
if (this._secondary) {
|
|
744
|
+
promises.push(this._secondary.delete(key));
|
|
745
|
+
}
|
|
746
|
+
if (this.nonBlocking) {
|
|
747
|
+
result = await Promise.race(promises);
|
|
748
|
+
} else {
|
|
749
|
+
const resultAll = await Promise.all(promises);
|
|
750
|
+
result = resultAll[0];
|
|
751
|
+
}
|
|
390
752
|
return result;
|
|
391
753
|
}
|
|
392
754
|
async deleteMany(keys) {
|
|
755
|
+
if (this.stats.enabled) {
|
|
756
|
+
const statResult = await this._primary.get(keys);
|
|
757
|
+
for (const key of keys) {
|
|
758
|
+
this.stats.decreaseKSize(key);
|
|
759
|
+
this.stats.decreaseVSize(statResult);
|
|
760
|
+
this.stats.decreaseCount();
|
|
761
|
+
this.stats.incrementDeletes();
|
|
762
|
+
}
|
|
763
|
+
}
|
|
393
764
|
const result = await this.deleteManyKeyv(this._primary, keys);
|
|
394
765
|
if (this._secondary) {
|
|
395
766
|
if (this._nonBlocking) {
|
|
@@ -407,6 +778,10 @@ var Cacheable = class extends import_hookified.Hookified {
|
|
|
407
778
|
promises.push(this._secondary.clear());
|
|
408
779
|
}
|
|
409
780
|
await (this._nonBlocking ? Promise.race(promises) : Promise.all(promises));
|
|
781
|
+
if (this.stats.enabled) {
|
|
782
|
+
this._stats.resetStoreValues();
|
|
783
|
+
this._stats.incrementClears();
|
|
784
|
+
}
|
|
410
785
|
}
|
|
411
786
|
async disconnect() {
|
|
412
787
|
const promises = [];
|
|
@@ -427,7 +802,8 @@ var Cacheable = class extends import_hookified.Hookified {
|
|
|
427
802
|
async setManyKeyv(keyv, items) {
|
|
428
803
|
const promises = [];
|
|
429
804
|
for (const item of items) {
|
|
430
|
-
|
|
805
|
+
const finalTtl = item.ttl ?? this._ttl;
|
|
806
|
+
promises.push(keyv.set(item.key, item.value, finalTtl));
|
|
431
807
|
}
|
|
432
808
|
await Promise.all(promises);
|
|
433
809
|
return true;
|
|
@@ -444,5 +820,7 @@ var Cacheable = class extends import_hookified.Hookified {
|
|
|
444
820
|
0 && (module.exports = {
|
|
445
821
|
Cacheable,
|
|
446
822
|
CacheableEvents,
|
|
447
|
-
CacheableHooks
|
|
823
|
+
CacheableHooks,
|
|
824
|
+
CacheableMemory,
|
|
825
|
+
CacheableStats
|
|
448
826
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -38,11 +38,58 @@ declare class CacheableStats {
|
|
|
38
38
|
incrementKSize(key: string): void;
|
|
39
39
|
decreaseKSize(key: string): void;
|
|
40
40
|
incrementCount(): void;
|
|
41
|
-
|
|
41
|
+
decreaseCount(): void;
|
|
42
42
|
setCount(count: number): void;
|
|
43
43
|
roughSizeOfString(value: string): number;
|
|
44
44
|
roughSizeOfObject(object: any): number;
|
|
45
45
|
reset(): void;
|
|
46
|
+
resetStoreValues(): void;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
type CacheableMemoryOptions = {
|
|
50
|
+
ttl?: number;
|
|
51
|
+
useClone?: boolean;
|
|
52
|
+
lruSize?: number;
|
|
53
|
+
};
|
|
54
|
+
declare class CacheableMemory {
|
|
55
|
+
private readonly _hashCache;
|
|
56
|
+
private readonly _hash0;
|
|
57
|
+
private readonly _hash1;
|
|
58
|
+
private readonly _hash2;
|
|
59
|
+
private readonly _hash3;
|
|
60
|
+
private readonly _hash4;
|
|
61
|
+
private readonly _hash5;
|
|
62
|
+
private readonly _hash6;
|
|
63
|
+
private readonly _hash7;
|
|
64
|
+
private readonly _hash8;
|
|
65
|
+
private readonly _hash9;
|
|
66
|
+
private readonly _lru;
|
|
67
|
+
private _ttl;
|
|
68
|
+
private _useClone;
|
|
69
|
+
private _lruSize;
|
|
70
|
+
constructor(options?: CacheableMemoryOptions);
|
|
71
|
+
get ttl(): number;
|
|
72
|
+
set ttl(value: number);
|
|
73
|
+
get useClone(): boolean;
|
|
74
|
+
set useClone(value: boolean);
|
|
75
|
+
get lruSize(): number;
|
|
76
|
+
set lruSize(value: number);
|
|
77
|
+
get size(): number;
|
|
78
|
+
get keys(): IterableIterator<string>;
|
|
79
|
+
get<T>(key: string): any;
|
|
80
|
+
set(key: string, value: any, ttl?: number): void;
|
|
81
|
+
has(key: string): boolean;
|
|
82
|
+
take<T>(key: string): any;
|
|
83
|
+
delete(key: string): void;
|
|
84
|
+
clear(): void;
|
|
85
|
+
hashKey(key: string): number;
|
|
86
|
+
getStore(key: string): Map<string, any>;
|
|
87
|
+
clone(value: any): any;
|
|
88
|
+
lruAddToFront(key: string): void;
|
|
89
|
+
lruMoveToFront(key: string): void;
|
|
90
|
+
lruResize(): void;
|
|
91
|
+
private isPrimitive;
|
|
92
|
+
private concatStores;
|
|
46
93
|
}
|
|
47
94
|
|
|
48
95
|
declare enum CacheableHooks {
|
|
@@ -68,11 +115,13 @@ type CacheableOptions = {
|
|
|
68
115
|
secondary?: Keyv | KeyvStoreAdapter;
|
|
69
116
|
stats?: boolean;
|
|
70
117
|
nonBlocking?: boolean;
|
|
118
|
+
ttl?: number;
|
|
71
119
|
};
|
|
72
120
|
declare class Cacheable extends Hookified {
|
|
73
121
|
private _primary;
|
|
74
122
|
private _secondary;
|
|
75
123
|
private _nonBlocking;
|
|
124
|
+
private _ttl?;
|
|
76
125
|
private readonly _stats;
|
|
77
126
|
constructor(options?: CacheableOptions);
|
|
78
127
|
get stats(): CacheableStats;
|
|
@@ -82,6 +131,8 @@ declare class Cacheable extends Hookified {
|
|
|
82
131
|
set secondary(secondary: Keyv | undefined);
|
|
83
132
|
get nonBlocking(): boolean;
|
|
84
133
|
set nonBlocking(nonBlocking: boolean);
|
|
134
|
+
get ttl(): number | undefined;
|
|
135
|
+
set ttl(ttl: number | undefined);
|
|
85
136
|
setPrimary(primary: Keyv | KeyvStoreAdapter): void;
|
|
86
137
|
setSecondary(secondary: Keyv | KeyvStoreAdapter): void;
|
|
87
138
|
get<T>(key: string): Promise<T | undefined>;
|
|
@@ -101,4 +152,4 @@ declare class Cacheable extends Hookified {
|
|
|
101
152
|
private hasManyKeyv;
|
|
102
153
|
}
|
|
103
154
|
|
|
104
|
-
export { Cacheable, CacheableEvents, CacheableHooks, type CacheableItem, type CacheableOptions };
|
|
155
|
+
export { Cacheable, CacheableEvents, CacheableHooks, type CacheableItem, CacheableMemory, type CacheableOptions, CacheableStats };
|
package/dist/index.d.ts
CHANGED
|
@@ -38,11 +38,58 @@ declare class CacheableStats {
|
|
|
38
38
|
incrementKSize(key: string): void;
|
|
39
39
|
decreaseKSize(key: string): void;
|
|
40
40
|
incrementCount(): void;
|
|
41
|
-
|
|
41
|
+
decreaseCount(): void;
|
|
42
42
|
setCount(count: number): void;
|
|
43
43
|
roughSizeOfString(value: string): number;
|
|
44
44
|
roughSizeOfObject(object: any): number;
|
|
45
45
|
reset(): void;
|
|
46
|
+
resetStoreValues(): void;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
type CacheableMemoryOptions = {
|
|
50
|
+
ttl?: number;
|
|
51
|
+
useClone?: boolean;
|
|
52
|
+
lruSize?: number;
|
|
53
|
+
};
|
|
54
|
+
declare class CacheableMemory {
|
|
55
|
+
private readonly _hashCache;
|
|
56
|
+
private readonly _hash0;
|
|
57
|
+
private readonly _hash1;
|
|
58
|
+
private readonly _hash2;
|
|
59
|
+
private readonly _hash3;
|
|
60
|
+
private readonly _hash4;
|
|
61
|
+
private readonly _hash5;
|
|
62
|
+
private readonly _hash6;
|
|
63
|
+
private readonly _hash7;
|
|
64
|
+
private readonly _hash8;
|
|
65
|
+
private readonly _hash9;
|
|
66
|
+
private readonly _lru;
|
|
67
|
+
private _ttl;
|
|
68
|
+
private _useClone;
|
|
69
|
+
private _lruSize;
|
|
70
|
+
constructor(options?: CacheableMemoryOptions);
|
|
71
|
+
get ttl(): number;
|
|
72
|
+
set ttl(value: number);
|
|
73
|
+
get useClone(): boolean;
|
|
74
|
+
set useClone(value: boolean);
|
|
75
|
+
get lruSize(): number;
|
|
76
|
+
set lruSize(value: number);
|
|
77
|
+
get size(): number;
|
|
78
|
+
get keys(): IterableIterator<string>;
|
|
79
|
+
get<T>(key: string): any;
|
|
80
|
+
set(key: string, value: any, ttl?: number): void;
|
|
81
|
+
has(key: string): boolean;
|
|
82
|
+
take<T>(key: string): any;
|
|
83
|
+
delete(key: string): void;
|
|
84
|
+
clear(): void;
|
|
85
|
+
hashKey(key: string): number;
|
|
86
|
+
getStore(key: string): Map<string, any>;
|
|
87
|
+
clone(value: any): any;
|
|
88
|
+
lruAddToFront(key: string): void;
|
|
89
|
+
lruMoveToFront(key: string): void;
|
|
90
|
+
lruResize(): void;
|
|
91
|
+
private isPrimitive;
|
|
92
|
+
private concatStores;
|
|
46
93
|
}
|
|
47
94
|
|
|
48
95
|
declare enum CacheableHooks {
|
|
@@ -68,11 +115,13 @@ type CacheableOptions = {
|
|
|
68
115
|
secondary?: Keyv | KeyvStoreAdapter;
|
|
69
116
|
stats?: boolean;
|
|
70
117
|
nonBlocking?: boolean;
|
|
118
|
+
ttl?: number;
|
|
71
119
|
};
|
|
72
120
|
declare class Cacheable extends Hookified {
|
|
73
121
|
private _primary;
|
|
74
122
|
private _secondary;
|
|
75
123
|
private _nonBlocking;
|
|
124
|
+
private _ttl?;
|
|
76
125
|
private readonly _stats;
|
|
77
126
|
constructor(options?: CacheableOptions);
|
|
78
127
|
get stats(): CacheableStats;
|
|
@@ -82,6 +131,8 @@ declare class Cacheable extends Hookified {
|
|
|
82
131
|
set secondary(secondary: Keyv | undefined);
|
|
83
132
|
get nonBlocking(): boolean;
|
|
84
133
|
set nonBlocking(nonBlocking: boolean);
|
|
134
|
+
get ttl(): number | undefined;
|
|
135
|
+
set ttl(ttl: number | undefined);
|
|
85
136
|
setPrimary(primary: Keyv | KeyvStoreAdapter): void;
|
|
86
137
|
setSecondary(secondary: Keyv | KeyvStoreAdapter): void;
|
|
87
138
|
get<T>(key: string): Promise<T | undefined>;
|
|
@@ -101,4 +152,4 @@ declare class Cacheable extends Hookified {
|
|
|
101
152
|
private hasManyKeyv;
|
|
102
153
|
}
|
|
103
154
|
|
|
104
|
-
export { Cacheable, CacheableEvents, CacheableHooks, type CacheableItem, type CacheableOptions };
|
|
155
|
+
export { Cacheable, CacheableEvents, CacheableHooks, type CacheableItem, CacheableMemory, type CacheableOptions, CacheableStats };
|
package/dist/index.js
CHANGED
|
@@ -122,7 +122,7 @@ var CacheableStats = class {
|
|
|
122
122
|
}
|
|
123
123
|
this._count++;
|
|
124
124
|
}
|
|
125
|
-
|
|
125
|
+
decreaseCount() {
|
|
126
126
|
if (!this._enabled) {
|
|
127
127
|
return;
|
|
128
128
|
}
|
|
@@ -170,6 +170,304 @@ var CacheableStats = class {
|
|
|
170
170
|
this._ksize = 0;
|
|
171
171
|
this._count = 0;
|
|
172
172
|
}
|
|
173
|
+
resetStoreValues() {
|
|
174
|
+
this._vsize = 0;
|
|
175
|
+
this._ksize = 0;
|
|
176
|
+
this._count = 0;
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
// src/memory-lru.ts
|
|
181
|
+
var ListNode = class {
|
|
182
|
+
// eslint-disable-next-line @typescript-eslint/parameter-properties
|
|
183
|
+
value;
|
|
184
|
+
prev = void 0;
|
|
185
|
+
next = void 0;
|
|
186
|
+
constructor(value) {
|
|
187
|
+
this.value = value;
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
var DoublyLinkedList = class {
|
|
191
|
+
head = void 0;
|
|
192
|
+
tail = void 0;
|
|
193
|
+
nodesMap = /* @__PURE__ */ new Map();
|
|
194
|
+
// Add a new node to the front (most recently used)
|
|
195
|
+
addToFront(value) {
|
|
196
|
+
const newNode = new ListNode(value);
|
|
197
|
+
if (this.head) {
|
|
198
|
+
newNode.next = this.head;
|
|
199
|
+
this.head.prev = newNode;
|
|
200
|
+
this.head = newNode;
|
|
201
|
+
} else {
|
|
202
|
+
this.head = this.tail = newNode;
|
|
203
|
+
}
|
|
204
|
+
this.nodesMap.set(value, newNode);
|
|
205
|
+
}
|
|
206
|
+
// Move an existing node to the front (most recently used)
|
|
207
|
+
moveToFront(value) {
|
|
208
|
+
const node = this.nodesMap.get(value);
|
|
209
|
+
if (!node || this.head === node) {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
if (node.prev) {
|
|
213
|
+
node.prev.next = node.next;
|
|
214
|
+
}
|
|
215
|
+
if (node.next) {
|
|
216
|
+
node.next.prev = node.prev;
|
|
217
|
+
}
|
|
218
|
+
if (node === this.tail) {
|
|
219
|
+
this.tail = node.prev;
|
|
220
|
+
}
|
|
221
|
+
node.prev = void 0;
|
|
222
|
+
node.next = this.head;
|
|
223
|
+
if (this.head) {
|
|
224
|
+
this.head.prev = node;
|
|
225
|
+
}
|
|
226
|
+
this.head = node;
|
|
227
|
+
this.tail ||= node;
|
|
228
|
+
}
|
|
229
|
+
// Get the oldest node (tail)
|
|
230
|
+
getOldest() {
|
|
231
|
+
return this.tail ? this.tail.value : void 0;
|
|
232
|
+
}
|
|
233
|
+
// Remove the oldest node (tail)
|
|
234
|
+
removeOldest() {
|
|
235
|
+
if (!this.tail) {
|
|
236
|
+
return void 0;
|
|
237
|
+
}
|
|
238
|
+
const oldValue = this.tail.value;
|
|
239
|
+
if (this.tail.prev) {
|
|
240
|
+
this.tail = this.tail.prev;
|
|
241
|
+
this.tail.next = void 0;
|
|
242
|
+
} else {
|
|
243
|
+
this.head = this.tail = void 0;
|
|
244
|
+
}
|
|
245
|
+
this.nodesMap.delete(oldValue);
|
|
246
|
+
return oldValue;
|
|
247
|
+
}
|
|
248
|
+
get size() {
|
|
249
|
+
return this.nodesMap.size;
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
// src/memory.ts
|
|
254
|
+
var CacheableMemory = class {
|
|
255
|
+
_hashCache = /* @__PURE__ */ new Map();
|
|
256
|
+
_hash0 = /* @__PURE__ */ new Map();
|
|
257
|
+
_hash1 = /* @__PURE__ */ new Map();
|
|
258
|
+
_hash2 = /* @__PURE__ */ new Map();
|
|
259
|
+
_hash3 = /* @__PURE__ */ new Map();
|
|
260
|
+
_hash4 = /* @__PURE__ */ new Map();
|
|
261
|
+
_hash5 = /* @__PURE__ */ new Map();
|
|
262
|
+
_hash6 = /* @__PURE__ */ new Map();
|
|
263
|
+
_hash7 = /* @__PURE__ */ new Map();
|
|
264
|
+
_hash8 = /* @__PURE__ */ new Map();
|
|
265
|
+
_hash9 = /* @__PURE__ */ new Map();
|
|
266
|
+
_lru = new DoublyLinkedList();
|
|
267
|
+
_ttl = 0;
|
|
268
|
+
_useClone = true;
|
|
269
|
+
_lruSize = 0;
|
|
270
|
+
constructor(options) {
|
|
271
|
+
if (options?.ttl) {
|
|
272
|
+
this._ttl = options.ttl;
|
|
273
|
+
}
|
|
274
|
+
if (options?.useClone !== void 0) {
|
|
275
|
+
this._useClone = options.useClone;
|
|
276
|
+
}
|
|
277
|
+
if (options?.lruSize) {
|
|
278
|
+
this._lruSize = options.lruSize;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
get ttl() {
|
|
282
|
+
return this._ttl;
|
|
283
|
+
}
|
|
284
|
+
set ttl(value) {
|
|
285
|
+
this._ttl = value;
|
|
286
|
+
}
|
|
287
|
+
get useClone() {
|
|
288
|
+
return this._useClone;
|
|
289
|
+
}
|
|
290
|
+
set useClone(value) {
|
|
291
|
+
this._useClone = value;
|
|
292
|
+
}
|
|
293
|
+
get lruSize() {
|
|
294
|
+
return this._lruSize;
|
|
295
|
+
}
|
|
296
|
+
set lruSize(value) {
|
|
297
|
+
this._lruSize = value;
|
|
298
|
+
this.lruResize();
|
|
299
|
+
}
|
|
300
|
+
get size() {
|
|
301
|
+
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
|
+
}
|
|
303
|
+
get keys() {
|
|
304
|
+
return this.concatStores().keys();
|
|
305
|
+
}
|
|
306
|
+
get(key) {
|
|
307
|
+
const store = this.getStore(key);
|
|
308
|
+
const item = store.get(key);
|
|
309
|
+
if (!item) {
|
|
310
|
+
return void 0;
|
|
311
|
+
}
|
|
312
|
+
if (item.expires && item.expires && Date.now() > item.expires) {
|
|
313
|
+
store.delete(key);
|
|
314
|
+
return void 0;
|
|
315
|
+
}
|
|
316
|
+
this.lruMoveToFront(key);
|
|
317
|
+
if (!this._useClone) {
|
|
318
|
+
return item.value;
|
|
319
|
+
}
|
|
320
|
+
return this.clone(item.value);
|
|
321
|
+
}
|
|
322
|
+
set(key, value, ttl) {
|
|
323
|
+
const store = this.getStore(key);
|
|
324
|
+
let expires;
|
|
325
|
+
if (ttl !== void 0 || this._ttl !== 0) {
|
|
326
|
+
expires = Date.now() + (ttl ?? this._ttl);
|
|
327
|
+
}
|
|
328
|
+
if (this._lruSize > 0) {
|
|
329
|
+
if (store.has(key)) {
|
|
330
|
+
this.lruMoveToFront(key);
|
|
331
|
+
} else {
|
|
332
|
+
this.lruAddToFront(key);
|
|
333
|
+
if (this._lru.size > this._lruSize) {
|
|
334
|
+
const oldestKey = this._lru.getOldest();
|
|
335
|
+
if (oldestKey) {
|
|
336
|
+
this._lru.removeOldest();
|
|
337
|
+
this.delete(oldestKey);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
store.set(key, {
|
|
343
|
+
key,
|
|
344
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
345
|
+
value,
|
|
346
|
+
expires
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
has(key) {
|
|
350
|
+
const item = this.get(key);
|
|
351
|
+
return Boolean(item);
|
|
352
|
+
}
|
|
353
|
+
take(key) {
|
|
354
|
+
const item = this.get(key);
|
|
355
|
+
if (!item) {
|
|
356
|
+
return void 0;
|
|
357
|
+
}
|
|
358
|
+
this.delete(key);
|
|
359
|
+
return item;
|
|
360
|
+
}
|
|
361
|
+
delete(key) {
|
|
362
|
+
const store = this.getStore(key);
|
|
363
|
+
store.delete(key);
|
|
364
|
+
}
|
|
365
|
+
clear() {
|
|
366
|
+
this._hash0.clear();
|
|
367
|
+
this._hash1.clear();
|
|
368
|
+
this._hash2.clear();
|
|
369
|
+
this._hash3.clear();
|
|
370
|
+
this._hash4.clear();
|
|
371
|
+
this._hash5.clear();
|
|
372
|
+
this._hash6.clear();
|
|
373
|
+
this._hash7.clear();
|
|
374
|
+
this._hash8.clear();
|
|
375
|
+
this._hash9.clear();
|
|
376
|
+
this._hashCache.clear();
|
|
377
|
+
}
|
|
378
|
+
hashKey(key) {
|
|
379
|
+
const cacheHashNumber = this._hashCache.get(key);
|
|
380
|
+
if (cacheHashNumber) {
|
|
381
|
+
return cacheHashNumber;
|
|
382
|
+
}
|
|
383
|
+
let hash = 0;
|
|
384
|
+
const primeMultiplier = 31;
|
|
385
|
+
for (let i = 0; i < key.length; i++) {
|
|
386
|
+
hash = hash * primeMultiplier + key.charCodeAt(i);
|
|
387
|
+
}
|
|
388
|
+
const result = Math.abs(hash) % 10;
|
|
389
|
+
this._hashCache.set(key, result);
|
|
390
|
+
return result;
|
|
391
|
+
}
|
|
392
|
+
getStore(key) {
|
|
393
|
+
const hashKey = this.hashKey(key);
|
|
394
|
+
switch (hashKey) {
|
|
395
|
+
case 1: {
|
|
396
|
+
return this._hash1;
|
|
397
|
+
}
|
|
398
|
+
case 2: {
|
|
399
|
+
return this._hash2;
|
|
400
|
+
}
|
|
401
|
+
case 3: {
|
|
402
|
+
return this._hash3;
|
|
403
|
+
}
|
|
404
|
+
case 4: {
|
|
405
|
+
return this._hash4;
|
|
406
|
+
}
|
|
407
|
+
case 5: {
|
|
408
|
+
return this._hash5;
|
|
409
|
+
}
|
|
410
|
+
case 6: {
|
|
411
|
+
return this._hash6;
|
|
412
|
+
}
|
|
413
|
+
case 7: {
|
|
414
|
+
return this._hash7;
|
|
415
|
+
}
|
|
416
|
+
case 8: {
|
|
417
|
+
return this._hash8;
|
|
418
|
+
}
|
|
419
|
+
case 9: {
|
|
420
|
+
return this._hash9;
|
|
421
|
+
}
|
|
422
|
+
default: {
|
|
423
|
+
return this._hash0;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
clone(value) {
|
|
428
|
+
if (this.isPrimitive(value)) {
|
|
429
|
+
return value;
|
|
430
|
+
}
|
|
431
|
+
return structuredClone(value);
|
|
432
|
+
}
|
|
433
|
+
lruAddToFront(key) {
|
|
434
|
+
if (this._lruSize === 0) {
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
this._lru.addToFront(key);
|
|
438
|
+
}
|
|
439
|
+
lruMoveToFront(key) {
|
|
440
|
+
if (this._lruSize === 0) {
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
this._lru.moveToFront(key);
|
|
444
|
+
}
|
|
445
|
+
lruResize() {
|
|
446
|
+
if (this._lruSize === 0) {
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
while (this._lru.size > this._lruSize) {
|
|
450
|
+
const oldestKey = this._lru.getOldest();
|
|
451
|
+
if (oldestKey) {
|
|
452
|
+
this._lru.removeOldest();
|
|
453
|
+
this.delete(oldestKey);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
isPrimitive(value) {
|
|
458
|
+
const result = false;
|
|
459
|
+
if (value === null || value === void 0) {
|
|
460
|
+
return true;
|
|
461
|
+
}
|
|
462
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
463
|
+
return true;
|
|
464
|
+
}
|
|
465
|
+
return result;
|
|
466
|
+
}
|
|
467
|
+
concatStores() {
|
|
468
|
+
const result = new Map([...this._hash0, ...this._hash1, ...this._hash2, ...this._hash3, ...this._hash4, ...this._hash5, ...this._hash6, ...this._hash7, ...this._hash8, ...this._hash9]);
|
|
469
|
+
return result;
|
|
470
|
+
}
|
|
173
471
|
};
|
|
174
472
|
|
|
175
473
|
// src/index.ts
|
|
@@ -189,9 +487,10 @@ var CacheableEvents = /* @__PURE__ */ ((CacheableEvents2) => {
|
|
|
189
487
|
return CacheableEvents2;
|
|
190
488
|
})(CacheableEvents || {});
|
|
191
489
|
var Cacheable = class extends Hookified {
|
|
192
|
-
_primary = new Keyv();
|
|
490
|
+
_primary = new Keyv({ store: new CacheableMemory() });
|
|
193
491
|
_secondary;
|
|
194
492
|
_nonBlocking = false;
|
|
493
|
+
_ttl;
|
|
195
494
|
_stats = new CacheableStats({ enabled: false });
|
|
196
495
|
constructor(options) {
|
|
197
496
|
super();
|
|
@@ -207,6 +506,9 @@ var Cacheable = class extends Hookified {
|
|
|
207
506
|
if (options?.stats) {
|
|
208
507
|
this._stats.enabled = options.stats;
|
|
209
508
|
}
|
|
509
|
+
if (options?.ttl) {
|
|
510
|
+
this._ttl = options.ttl;
|
|
511
|
+
}
|
|
210
512
|
}
|
|
211
513
|
get stats() {
|
|
212
514
|
return this._stats;
|
|
@@ -229,6 +531,12 @@ var Cacheable = class extends Hookified {
|
|
|
229
531
|
set nonBlocking(nonBlocking) {
|
|
230
532
|
this._nonBlocking = nonBlocking;
|
|
231
533
|
}
|
|
534
|
+
get ttl() {
|
|
535
|
+
return this._ttl;
|
|
536
|
+
}
|
|
537
|
+
set ttl(ttl) {
|
|
538
|
+
this._ttl = ttl;
|
|
539
|
+
}
|
|
232
540
|
setPrimary(primary) {
|
|
233
541
|
this._primary = primary instanceof Keyv ? primary : new Keyv(primary);
|
|
234
542
|
}
|
|
@@ -243,13 +551,21 @@ var Cacheable = class extends Hookified {
|
|
|
243
551
|
if (!result && this._secondary) {
|
|
244
552
|
result = await this._secondary.get(key);
|
|
245
553
|
if (result) {
|
|
246
|
-
await this._primary.set(key, result);
|
|
554
|
+
await this._primary.set(key, result, this._ttl);
|
|
247
555
|
}
|
|
248
556
|
}
|
|
249
557
|
await this.hook("AFTER_GET" /* AFTER_GET */, { key, result });
|
|
250
558
|
} catch (error) {
|
|
251
559
|
await this.emit("error" /* ERROR */, error);
|
|
252
560
|
}
|
|
561
|
+
if (this.stats.enabled) {
|
|
562
|
+
if (result) {
|
|
563
|
+
this._stats.incrementHits();
|
|
564
|
+
} else {
|
|
565
|
+
this._stats.incrementMisses();
|
|
566
|
+
}
|
|
567
|
+
this.stats.incrementGets();
|
|
568
|
+
}
|
|
253
569
|
return result;
|
|
254
570
|
}
|
|
255
571
|
async getMany(keys) {
|
|
@@ -268,7 +584,7 @@ var Cacheable = class extends Hookified {
|
|
|
268
584
|
for (const [i, key] of keys.entries()) {
|
|
269
585
|
if (!result[i] && secondaryResult[i]) {
|
|
270
586
|
result[i] = secondaryResult[i];
|
|
271
|
-
await this._primary.set(key, secondaryResult[i]);
|
|
587
|
+
await this._primary.set(key, secondaryResult[i], this._ttl);
|
|
272
588
|
}
|
|
273
589
|
}
|
|
274
590
|
}
|
|
@@ -276,16 +592,27 @@ var Cacheable = class extends Hookified {
|
|
|
276
592
|
} catch (error) {
|
|
277
593
|
await this.emit("error" /* ERROR */, error);
|
|
278
594
|
}
|
|
595
|
+
if (this.stats.enabled) {
|
|
596
|
+
for (const item of result) {
|
|
597
|
+
if (item) {
|
|
598
|
+
this._stats.incrementHits();
|
|
599
|
+
} else {
|
|
600
|
+
this._stats.incrementMisses();
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
this.stats.incrementGets();
|
|
604
|
+
}
|
|
279
605
|
return result;
|
|
280
606
|
}
|
|
281
607
|
async set(key, value, ttl) {
|
|
282
608
|
let result = false;
|
|
609
|
+
const finalTtl = ttl ?? this._ttl;
|
|
283
610
|
try {
|
|
284
|
-
await this.hook("BEFORE_SET" /* BEFORE_SET */, { key, value,
|
|
611
|
+
await this.hook("BEFORE_SET" /* BEFORE_SET */, { key, value, finalTtl });
|
|
285
612
|
const promises = [];
|
|
286
|
-
promises.push(this._primary.set(key, value,
|
|
613
|
+
promises.push(this._primary.set(key, value, finalTtl));
|
|
287
614
|
if (this._secondary) {
|
|
288
|
-
promises.push(this._secondary.set(key, value,
|
|
615
|
+
promises.push(this._secondary.set(key, value, finalTtl));
|
|
289
616
|
}
|
|
290
617
|
if (this._nonBlocking) {
|
|
291
618
|
result = await Promise.race(promises);
|
|
@@ -293,10 +620,16 @@ var Cacheable = class extends Hookified {
|
|
|
293
620
|
const results = await Promise.all(promises);
|
|
294
621
|
result = results[0];
|
|
295
622
|
}
|
|
296
|
-
await this.hook("AFTER_SET" /* AFTER_SET */, { key, value,
|
|
623
|
+
await this.hook("AFTER_SET" /* AFTER_SET */, { key, value, finalTtl });
|
|
297
624
|
} catch (error) {
|
|
298
625
|
await this.emit("error" /* ERROR */, error);
|
|
299
626
|
}
|
|
627
|
+
if (this.stats.enabled) {
|
|
628
|
+
this.stats.incrementKSize(key);
|
|
629
|
+
this.stats.incrementCount();
|
|
630
|
+
this.stats.incrementVSize(value);
|
|
631
|
+
this.stats.incrementSets();
|
|
632
|
+
}
|
|
300
633
|
return result;
|
|
301
634
|
}
|
|
302
635
|
async setMany(items) {
|
|
@@ -315,6 +648,13 @@ var Cacheable = class extends Hookified {
|
|
|
315
648
|
} catch (error) {
|
|
316
649
|
await this.emit("error" /* ERROR */, error);
|
|
317
650
|
}
|
|
651
|
+
if (this.stats.enabled) {
|
|
652
|
+
for (const item of items) {
|
|
653
|
+
this.stats.incrementKSize(item.key);
|
|
654
|
+
this.stats.incrementCount();
|
|
655
|
+
this.stats.incrementVSize(item.value);
|
|
656
|
+
}
|
|
657
|
+
}
|
|
318
658
|
return result;
|
|
319
659
|
}
|
|
320
660
|
async take(key) {
|
|
@@ -328,11 +668,18 @@ var Cacheable = class extends Hookified {
|
|
|
328
668
|
return result;
|
|
329
669
|
}
|
|
330
670
|
async has(key) {
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
671
|
+
const promises = [];
|
|
672
|
+
promises.push(this._primary.has(key));
|
|
673
|
+
if (this._secondary) {
|
|
674
|
+
promises.push(this._secondary.has(key));
|
|
334
675
|
}
|
|
335
|
-
|
|
676
|
+
const resultAll = await Promise.all(promises);
|
|
677
|
+
for (const result of resultAll) {
|
|
678
|
+
if (result) {
|
|
679
|
+
return true;
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
return false;
|
|
336
683
|
}
|
|
337
684
|
async hasMany(keys) {
|
|
338
685
|
const result = await this.hasManyKeyv(this._primary, keys);
|
|
@@ -353,17 +700,39 @@ var Cacheable = class extends Hookified {
|
|
|
353
700
|
return result;
|
|
354
701
|
}
|
|
355
702
|
async delete(key) {
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
703
|
+
let result = false;
|
|
704
|
+
const promises = [];
|
|
705
|
+
if (this.stats.enabled) {
|
|
706
|
+
const statResult = await this._primary.get(key);
|
|
707
|
+
if (statResult) {
|
|
708
|
+
this.stats.decreaseKSize(key);
|
|
709
|
+
this.stats.decreaseVSize(statResult);
|
|
710
|
+
this.stats.decreaseCount();
|
|
711
|
+
this.stats.incrementDeletes();
|
|
362
712
|
}
|
|
363
713
|
}
|
|
714
|
+
promises.push(this._primary.delete(key));
|
|
715
|
+
if (this._secondary) {
|
|
716
|
+
promises.push(this._secondary.delete(key));
|
|
717
|
+
}
|
|
718
|
+
if (this.nonBlocking) {
|
|
719
|
+
result = await Promise.race(promises);
|
|
720
|
+
} else {
|
|
721
|
+
const resultAll = await Promise.all(promises);
|
|
722
|
+
result = resultAll[0];
|
|
723
|
+
}
|
|
364
724
|
return result;
|
|
365
725
|
}
|
|
366
726
|
async deleteMany(keys) {
|
|
727
|
+
if (this.stats.enabled) {
|
|
728
|
+
const statResult = await this._primary.get(keys);
|
|
729
|
+
for (const key of keys) {
|
|
730
|
+
this.stats.decreaseKSize(key);
|
|
731
|
+
this.stats.decreaseVSize(statResult);
|
|
732
|
+
this.stats.decreaseCount();
|
|
733
|
+
this.stats.incrementDeletes();
|
|
734
|
+
}
|
|
735
|
+
}
|
|
367
736
|
const result = await this.deleteManyKeyv(this._primary, keys);
|
|
368
737
|
if (this._secondary) {
|
|
369
738
|
if (this._nonBlocking) {
|
|
@@ -381,6 +750,10 @@ var Cacheable = class extends Hookified {
|
|
|
381
750
|
promises.push(this._secondary.clear());
|
|
382
751
|
}
|
|
383
752
|
await (this._nonBlocking ? Promise.race(promises) : Promise.all(promises));
|
|
753
|
+
if (this.stats.enabled) {
|
|
754
|
+
this._stats.resetStoreValues();
|
|
755
|
+
this._stats.incrementClears();
|
|
756
|
+
}
|
|
384
757
|
}
|
|
385
758
|
async disconnect() {
|
|
386
759
|
const promises = [];
|
|
@@ -401,7 +774,8 @@ var Cacheable = class extends Hookified {
|
|
|
401
774
|
async setManyKeyv(keyv, items) {
|
|
402
775
|
const promises = [];
|
|
403
776
|
for (const item of items) {
|
|
404
|
-
|
|
777
|
+
const finalTtl = item.ttl ?? this._ttl;
|
|
778
|
+
promises.push(keyv.set(item.key, item.value, finalTtl));
|
|
405
779
|
}
|
|
406
780
|
await Promise.all(promises);
|
|
407
781
|
return true;
|
|
@@ -417,5 +791,7 @@ var Cacheable = class extends Hookified {
|
|
|
417
791
|
export {
|
|
418
792
|
Cacheable,
|
|
419
793
|
CacheableEvents,
|
|
420
|
-
CacheableHooks
|
|
794
|
+
CacheableHooks,
|
|
795
|
+
CacheableMemory,
|
|
796
|
+
CacheableStats
|
|
421
797
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cacheable",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Simple Caching Engine using Keyv",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -19,11 +19,11 @@
|
|
|
19
19
|
"devDependencies": {
|
|
20
20
|
"@keyv/redis": "^3.0.1",
|
|
21
21
|
"@vitest/coverage-v8": "^2.1.1",
|
|
22
|
-
"lru-cache": "^
|
|
22
|
+
"lru-cache": "^11.0.1",
|
|
23
23
|
"rimraf": "^6.0.1",
|
|
24
24
|
"ts-node": "^10.9.2",
|
|
25
|
-
"tsup": "^8.
|
|
26
|
-
"typescript": "^5.
|
|
25
|
+
"tsup": "^8.3.0",
|
|
26
|
+
"typescript": "^5.6.2",
|
|
27
27
|
"vitest": "^2.1.1",
|
|
28
28
|
"xo": "^0.59.3"
|
|
29
29
|
},
|