cacheable 2.1.1 → 2.3.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 +153 -8
- package/dist/index.cjs +92 -35
- package/dist/index.d.cts +45 -8
- package/dist/index.d.ts +45 -8
- package/dist/index.js +95 -39
- package/package.json +24 -19
package/README.md
CHANGED
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
* [Storage Tiering and Caching](#storage-tiering-and-caching)
|
|
33
33
|
* [TTL Propagation and Storage Tiering](#ttl-propagation-and-storage-tiering)
|
|
34
34
|
* [Shorthand for Time to Live (ttl)](#shorthand-for-time-to-live-ttl)
|
|
35
|
+
* [Iteration on Primary and Secondary Stores](#iteration-on-primary-and-secondary-stores)
|
|
35
36
|
* [Non-Blocking Operations](#non-blocking-operations)
|
|
36
37
|
* [Non-Blocking with @keyv/redis](#non-blocking-with-keyvredis)
|
|
37
38
|
* [CacheableSync - Distributed Updates](#cacheablesync---distributed-updates)
|
|
@@ -279,6 +280,110 @@ raws.forEach((entry, idx) => {
|
|
|
279
280
|
});
|
|
280
281
|
```
|
|
281
282
|
|
|
283
|
+
## Checking multiple keys with hasMany
|
|
284
|
+
|
|
285
|
+
The `hasMany` method allows you to efficiently check if multiple keys exist in the cache. It leverages Keyv's native `hasMany` support for optimal performance:
|
|
286
|
+
|
|
287
|
+
```typescript
|
|
288
|
+
import { Cacheable } from 'cacheable';
|
|
289
|
+
|
|
290
|
+
const cache = new Cacheable();
|
|
291
|
+
|
|
292
|
+
// set some values
|
|
293
|
+
await cache.set('user:1', { name: 'Alice' });
|
|
294
|
+
await cache.set('user:2', { name: 'Bob' });
|
|
295
|
+
|
|
296
|
+
// check if multiple keys exist
|
|
297
|
+
const exists = await cache.hasMany(['user:1', 'user:2', 'user:3']);
|
|
298
|
+
console.log(exists); // [true, true, false]
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
The `hasMany` method returns an array of booleans in the same order as the input keys. This is particularly useful when you need to verify the existence of multiple cache entries before performing batch operations.
|
|
302
|
+
|
|
303
|
+
# Iteration on Primary and Secondary Stores
|
|
304
|
+
|
|
305
|
+
The `Cacheable` class exposes both `primary` and `secondary` Keyv instances, which support iteration over their stored entries using the `iterator()` method. This allows you to access and process all keys and values in either storage layer.
|
|
306
|
+
|
|
307
|
+
**Important Notes:**
|
|
308
|
+
- Not all storage adapters support iteration. Always check if `iterator` exists before using it.
|
|
309
|
+
- The iterator automatically filters by namespace, skips expired entries (and deletes them), and deserializes values.
|
|
310
|
+
- **Performance Warning:** Be careful when using `iterator()` as it can cause performance issues with large datasets.
|
|
311
|
+
|
|
312
|
+
## Basic Iteration Example
|
|
313
|
+
|
|
314
|
+
```typescript
|
|
315
|
+
import { Cacheable } from 'cacheable';
|
|
316
|
+
import KeyvRedis from '@keyv/redis';
|
|
317
|
+
|
|
318
|
+
// Create cache with primary (in-memory) and secondary (Redis) stores
|
|
319
|
+
const secondary = new KeyvRedis('redis://user:pass@localhost:6379');
|
|
320
|
+
const cache = new Cacheable({ secondary });
|
|
321
|
+
|
|
322
|
+
// Add some data
|
|
323
|
+
await cache.set('user:1', { name: 'Alice', role: 'admin' });
|
|
324
|
+
await cache.set('user:2', { name: 'Bob', role: 'user' });
|
|
325
|
+
await cache.set('session:abc', { userId: '1', active: true });
|
|
326
|
+
|
|
327
|
+
// Iterate over primary store (in-memory)
|
|
328
|
+
console.log('Primary store contents:');
|
|
329
|
+
if (cache.primary.iterator) {
|
|
330
|
+
for await (const [key, value] of cache.primary.iterator()) {
|
|
331
|
+
console.log(` ${key}:`, JSON.stringify(value));
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// Iterate over secondary store (Redis)
|
|
336
|
+
console.log('\nSecondary store contents:');
|
|
337
|
+
if (cache.secondary?.iterator) {
|
|
338
|
+
for await (const [key, value] of cache.secondary.iterator()) {
|
|
339
|
+
console.log(` ${key}:`, JSON.stringify(value));
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
## Safe Iteration Helper
|
|
345
|
+
|
|
346
|
+
Here's a recommended helper function for safe iteration that checks for store availability and iterator support:
|
|
347
|
+
|
|
348
|
+
```typescript
|
|
349
|
+
import { Cacheable } from 'cacheable';
|
|
350
|
+
import type { Keyv } from 'keyv';
|
|
351
|
+
|
|
352
|
+
async function iterateStore(store: Keyv | undefined, storeName: string) {
|
|
353
|
+
if (!store) {
|
|
354
|
+
console.log(`${storeName} store not configured`);
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
if (!store.iterator) {
|
|
359
|
+
console.log(`${storeName} store does not support iteration`);
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
console.log(`${storeName} store entries:`);
|
|
364
|
+
for await (const [key, value] of store.iterator()) {
|
|
365
|
+
console.log(` ${key}:`, value);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// Usage
|
|
370
|
+
const cache = new Cacheable({ /* options */ });
|
|
371
|
+
await iterateStore(cache.primary, 'Primary');
|
|
372
|
+
await iterateStore(cache.secondary, 'Secondary');
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
## Storage Adapter Support
|
|
376
|
+
|
|
377
|
+
The `iterator()` method is available when:
|
|
378
|
+
- The store is a Map instance (has Symbol.iterator)
|
|
379
|
+
- The store implements an `iterator()` method (e.g., Redis, Valkey, etc.)
|
|
380
|
+
- The store is a supported iterable adapter
|
|
381
|
+
|
|
382
|
+
Common stores that support iteration:
|
|
383
|
+
- In-memory (Map-based stores)
|
|
384
|
+
- @keyv/redis
|
|
385
|
+
- @keyv/valkey
|
|
386
|
+
- Other Keyv adapters that implement the iterator interface
|
|
282
387
|
|
|
283
388
|
# Non-Blocking Operations
|
|
284
389
|
|
|
@@ -442,6 +547,45 @@ const cache = new Cacheable({
|
|
|
442
547
|
});
|
|
443
548
|
```
|
|
444
549
|
|
|
550
|
+
## Namespace Isolation with Sync
|
|
551
|
+
|
|
552
|
+
When multiple services share the same Redis instance (or other message provider), you can use namespaces to isolate cache synchronization events between services. This prevents one service's cache updates from affecting another service's cache.
|
|
553
|
+
|
|
554
|
+
```javascript
|
|
555
|
+
import { Cacheable } from 'cacheable';
|
|
556
|
+
import { RedisMessageProvider } from '@qified/redis';
|
|
557
|
+
|
|
558
|
+
const provider = new RedisMessageProvider({
|
|
559
|
+
connection: { host: 'localhost', port: 6379 }
|
|
560
|
+
});
|
|
561
|
+
|
|
562
|
+
// Service 1 with namespace
|
|
563
|
+
const serviceA = new Cacheable({
|
|
564
|
+
namespace: 'service-a',
|
|
565
|
+
sync: { qified: provider }
|
|
566
|
+
});
|
|
567
|
+
|
|
568
|
+
// Service 2 with different namespace
|
|
569
|
+
const serviceB = new Cacheable({
|
|
570
|
+
namespace: 'service-b',
|
|
571
|
+
sync: { qified: provider }
|
|
572
|
+
});
|
|
573
|
+
|
|
574
|
+
// Set value in service A
|
|
575
|
+
await serviceA.set('config', { timeout: 5000 });
|
|
576
|
+
|
|
577
|
+
// Service B won't receive this update because it has a different namespace
|
|
578
|
+
const value = await serviceB.get('config'); // undefined
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
**How Namespace Isolation Works:**
|
|
582
|
+
- Without namespaces, sync events use channel names like `cache:set` and `cache:delete`
|
|
583
|
+
- With namespaces, events are prefixed: `service-a::cache:set`, `service-b::cache:set`
|
|
584
|
+
- Services only subscribe to events matching their namespace, ensuring complete isolation
|
|
585
|
+
- Namespaces can be static strings or functions that return strings
|
|
586
|
+
|
|
587
|
+
**Note:** The namespace is automatically passed from Cacheable to CacheableSync, so you only need to set it once in the Cacheable options.
|
|
588
|
+
|
|
445
589
|
## How Sync Works
|
|
446
590
|
|
|
447
591
|
1. **SET Operations**: When you call `cache.set()` or `cache.setMany()`, the cache:
|
|
@@ -517,7 +661,8 @@ _This does not enable statistics for your layer 2 cache as that is a distributed
|
|
|
517
661
|
* `removeHook(hook)`: Removes a hook.
|
|
518
662
|
* `on(event, callback)`: Listens for an event.
|
|
519
663
|
* `removeListener(event, callback)`: Removes a listener.
|
|
520
|
-
* `hash(object: any, algorithm = '
|
|
664
|
+
* `hash(object: any, algorithm = 'SHA-256'): Promise<string>`: Asynchronously hashes an object with a cryptographic algorithm (SHA-256, SHA-384, SHA-512). Default is `SHA-256`.
|
|
665
|
+
* `hashSync(object: any, algorithm = 'djb2'): string`: Synchronously hashes an object with a non-cryptographic algorithm (djb2, fnv1, murmer, crc32). Default is `djb2`.
|
|
521
666
|
* `primary`: The primary store for the cache (layer 1) defaults to in-memory by Keyv.
|
|
522
667
|
* `secondary`: The secondary store for the cache (layer 2) usually a persistent cache by Keyv.
|
|
523
668
|
* `namespace`: The namespace for the cache. Default is `undefined`. This will set the namespace for the primary and secondary stores.
|
|
@@ -544,7 +689,7 @@ To learn more go to [@cacheable/memory](https://cacheable.org/docs/memory/)
|
|
|
544
689
|
|
|
545
690
|
# Wrap / Memoization for Sync and Async Functions
|
|
546
691
|
|
|
547
|
-
`Cacheable` and `CacheableMemory` has a feature called `wrap` that comes from [@cacheable/
|
|
692
|
+
`Cacheable` and `CacheableMemory` has a feature called `wrap` that comes from [@cacheable/utils](https://cacheable.org/docs/utils/) and allows you to wrap a function in a cache. This is useful for memoization and caching the results of a function. You can wrap a `sync` or `async` function in a cache. Here is an example of how to use the `wrap` function:
|
|
548
693
|
|
|
549
694
|
```javascript
|
|
550
695
|
import { Cacheable } from 'cacheable';
|
|
@@ -637,11 +782,11 @@ If you would like to generate your own key for the wrapped function you can set
|
|
|
637
782
|
|
|
638
783
|
We will pass in the `function` that is being wrapped, the `arguments` passed to the function, and the `options` used to wrap the function. You can then use these to generate a custom key for the cache.
|
|
639
784
|
|
|
640
|
-
To learn more visit [@cacheable/
|
|
785
|
+
To learn more visit [@cacheable/utils](https://cacheable.org/docs/utils/)
|
|
641
786
|
|
|
642
787
|
# Get Or Set Memoization Function
|
|
643
788
|
|
|
644
|
-
The `getOrSet` method that comes from [@cacheable/
|
|
789
|
+
The `getOrSet` method that comes from [@cacheable/utils](https://cacheable.org/docs/utils/) provides a convenient way to implement the cache-aside pattern. It attempts to retrieve a value from cache, and if not found, calls the provided function to compute the value and store it in cache before returning it. Here are the options:
|
|
645
790
|
|
|
646
791
|
```typescript
|
|
647
792
|
export type GetOrSetFunctionOptions = {
|
|
@@ -677,17 +822,17 @@ const function_ = async () => Math.random() * 100;
|
|
|
677
822
|
const value = await cache.getOrSet(generateKey(), function_, { ttl: '1h' });
|
|
678
823
|
```
|
|
679
824
|
|
|
680
|
-
To learn more go to [@cacheable/
|
|
825
|
+
To learn more go to [@cacheable/utils](https://cacheable.org/docs/utils/)
|
|
681
826
|
|
|
682
827
|
# v1 to v2 Changes
|
|
683
828
|
|
|
684
|
-
`cacheable` is now using `@cacheable/utils
|
|
829
|
+
`cacheable` is now using `@cacheable/utils` and `@cacheable/memory` for its core functionality as we are moving to this modular architecture and plan to eventually have these modules across `cache-manager` and `flat-cache`. In addition there are some breaking changes:
|
|
685
830
|
|
|
686
831
|
* `get()` and `getMany()` no longer have the `raw` option but instead we have built out `getRaw()` and `getManyRaw()` to use.
|
|
687
832
|
* All `get` related functions now support `nonBlocking` which means if `nonBlocking: true` the primary store will return what it has and then in the background will work to sync from secondary storage for any misses. You can disable this by setting at the `get` function level the option `nonBlocking: false` which will look for any missing keys in the secondary.
|
|
688
|
-
* `Keyv` v5.5+ is now the recommended supported version as we are using its native `getMany
|
|
833
|
+
* `Keyv` v5.5+ is now the recommended supported version as we are using its native `getMany*`, `getRaw*`, and `hasMany` methods for improved performance
|
|
689
834
|
* `Wrap` and `getOrSet` have been updated with more robust options including the ability to use your own `serialize` function for creating the key in `wrap`.
|
|
690
|
-
* `hash` has
|
|
835
|
+
* `hash` has been split into async (`hash()` and `hashToNumber()`) and sync (`hashSync()` and `hashToNumberSync()`) methods. MD5 support has been removed. Now uses Hashery library with support for additional algorithms (SHA-384, FNV1, MURMER, CRC32).
|
|
691
836
|
|
|
692
837
|
# How to Contribute
|
|
693
838
|
|
package/dist/index.cjs
CHANGED
|
@@ -34,15 +34,14 @@ __export(index_exports, {
|
|
|
34
34
|
calculateTtlFromExpiration: () => import_utils2.calculateTtlFromExpiration,
|
|
35
35
|
createKeyv: () => import_memory2.createKeyv,
|
|
36
36
|
getCascadingTtl: () => import_utils2.getCascadingTtl,
|
|
37
|
-
getOrSet: () =>
|
|
37
|
+
getOrSet: () => import_utils2.getOrSet,
|
|
38
38
|
hash: () => import_utils2.hash,
|
|
39
39
|
shorthandToMilliseconds: () => import_utils2.shorthandToMilliseconds,
|
|
40
40
|
shorthandToTime: () => import_utils2.shorthandToTime,
|
|
41
|
-
wrap: () =>
|
|
42
|
-
wrapSync: () =>
|
|
41
|
+
wrap: () => import_utils2.wrap,
|
|
42
|
+
wrapSync: () => import_utils2.wrapSync
|
|
43
43
|
});
|
|
44
44
|
module.exports = __toCommonJS(index_exports);
|
|
45
|
-
var import_memoize = require("@cacheable/memoize");
|
|
46
45
|
var import_memory = require("@cacheable/memory");
|
|
47
46
|
var import_utils = require("@cacheable/utils");
|
|
48
47
|
var import_hookified2 = require("hookified");
|
|
@@ -79,12 +78,16 @@ var CacheableSyncEvents = /* @__PURE__ */ ((CacheableSyncEvents2) => {
|
|
|
79
78
|
})(CacheableSyncEvents || {});
|
|
80
79
|
var CacheableSync = class extends import_hookified.Hookified {
|
|
81
80
|
_qified = new import_qified.Qified();
|
|
81
|
+
_namespace;
|
|
82
|
+
_storage;
|
|
83
|
+
_cacheId;
|
|
82
84
|
/**
|
|
83
85
|
* Creates an instance of CacheableSync
|
|
84
86
|
* @param options - Configuration options for CacheableSync
|
|
85
87
|
*/
|
|
86
88
|
constructor(options) {
|
|
87
89
|
super(options);
|
|
90
|
+
this._namespace = options.namespace;
|
|
88
91
|
this._qified = this.createQified(options.qified);
|
|
89
92
|
}
|
|
90
93
|
/**
|
|
@@ -101,12 +104,36 @@ var CacheableSync = class extends import_hookified.Hookified {
|
|
|
101
104
|
set qified(value) {
|
|
102
105
|
this._qified = this.createQified(value);
|
|
103
106
|
}
|
|
107
|
+
/**
|
|
108
|
+
* Gets the namespace for sync events
|
|
109
|
+
* @returns The namespace or undefined if not set
|
|
110
|
+
*/
|
|
111
|
+
get namespace() {
|
|
112
|
+
return this._namespace;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Sets the namespace for sync events and resubscribes if needed
|
|
116
|
+
* @param namespace - The namespace string or function
|
|
117
|
+
*/
|
|
118
|
+
set namespace(namespace) {
|
|
119
|
+
if (this._storage && this._cacheId) {
|
|
120
|
+
const oldSetEvent = this.getPrefixedEvent("cache:set" /* SET */);
|
|
121
|
+
const oldDeleteEvent = this.getPrefixedEvent("cache:delete" /* DELETE */);
|
|
122
|
+
void this._qified.unsubscribe(oldSetEvent);
|
|
123
|
+
void this._qified.unsubscribe(oldDeleteEvent);
|
|
124
|
+
}
|
|
125
|
+
this._namespace = namespace;
|
|
126
|
+
if (this._storage && this._cacheId) {
|
|
127
|
+
this.subscribe(this._storage, this._cacheId);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
104
130
|
/**
|
|
105
131
|
* Publishes a cache event to all the cache instances
|
|
106
132
|
* @param data - The cache item data containing cacheId, key, value, and optional ttl
|
|
107
133
|
*/
|
|
108
134
|
async publish(event, data) {
|
|
109
|
-
|
|
135
|
+
const eventName = this.getPrefixedEvent(event);
|
|
136
|
+
await this._qified.publish(eventName, {
|
|
110
137
|
id: crypto.randomUUID(),
|
|
111
138
|
data
|
|
112
139
|
});
|
|
@@ -117,7 +144,11 @@ var CacheableSync = class extends import_hookified.Hookified {
|
|
|
117
144
|
* @param cacheId - The cache ID to identify this instance
|
|
118
145
|
*/
|
|
119
146
|
subscribe(storage, cacheId) {
|
|
120
|
-
this.
|
|
147
|
+
this._storage = storage;
|
|
148
|
+
this._cacheId = cacheId;
|
|
149
|
+
const setEvent = this.getPrefixedEvent("cache:set" /* SET */);
|
|
150
|
+
const deleteEvent = this.getPrefixedEvent("cache:delete" /* DELETE */);
|
|
151
|
+
this._qified.subscribe(setEvent, {
|
|
121
152
|
handler: async (message) => {
|
|
122
153
|
const data = message.data;
|
|
123
154
|
if (data.cacheId !== cacheId) {
|
|
@@ -125,7 +156,7 @@ var CacheableSync = class extends import_hookified.Hookified {
|
|
|
125
156
|
}
|
|
126
157
|
}
|
|
127
158
|
});
|
|
128
|
-
this._qified.subscribe(
|
|
159
|
+
this._qified.subscribe(deleteEvent, {
|
|
129
160
|
handler: async (message) => {
|
|
130
161
|
const data = message.data;
|
|
131
162
|
if (data.cacheId !== cacheId) {
|
|
@@ -146,10 +177,28 @@ var CacheableSync = class extends import_hookified.Hookified {
|
|
|
146
177
|
const providers = Array.isArray(value) ? value : [value];
|
|
147
178
|
return new import_qified.Qified({ messageProviders: providers });
|
|
148
179
|
}
|
|
180
|
+
/**
|
|
181
|
+
* Gets the namespace prefix to use for event names
|
|
182
|
+
* @returns The resolved namespace string or undefined
|
|
183
|
+
*/
|
|
184
|
+
getNamespace() {
|
|
185
|
+
if (typeof this._namespace === "function") {
|
|
186
|
+
return this._namespace();
|
|
187
|
+
}
|
|
188
|
+
return this._namespace;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Prefixes an event name with the namespace if one is set
|
|
192
|
+
* @param event - The event to prefix
|
|
193
|
+
* @returns The prefixed event name or the original event
|
|
194
|
+
*/
|
|
195
|
+
getPrefixedEvent(event) {
|
|
196
|
+
const ns = this.getNamespace();
|
|
197
|
+
return ns ? `${ns}::${event}` : event;
|
|
198
|
+
}
|
|
149
199
|
};
|
|
150
200
|
|
|
151
201
|
// src/index.ts
|
|
152
|
-
var import_memoize2 = require("@cacheable/memoize");
|
|
153
202
|
var import_memory2 = require("@cacheable/memory");
|
|
154
203
|
var import_utils2 = require("@cacheable/utils");
|
|
155
204
|
var import_keyv2 = require("keyv");
|
|
@@ -194,7 +243,10 @@ var Cacheable = class extends import_hookified2.Hookified {
|
|
|
194
243
|
}
|
|
195
244
|
}
|
|
196
245
|
if (options?.sync) {
|
|
197
|
-
this._sync = options.sync instanceof CacheableSync ? options.sync : new CacheableSync(
|
|
246
|
+
this._sync = options.sync instanceof CacheableSync ? options.sync : new CacheableSync({
|
|
247
|
+
...options.sync,
|
|
248
|
+
namespace: options.namespace
|
|
249
|
+
});
|
|
198
250
|
this._sync.subscribe(this._primary, this._cacheId);
|
|
199
251
|
}
|
|
200
252
|
}
|
|
@@ -216,6 +268,9 @@ var Cacheable = class extends import_hookified2.Hookified {
|
|
|
216
268
|
if (this._secondary) {
|
|
217
269
|
this._secondary.namespace = this.getNameSpace();
|
|
218
270
|
}
|
|
271
|
+
if (this._sync) {
|
|
272
|
+
this._sync.namespace = namespace;
|
|
273
|
+
}
|
|
219
274
|
}
|
|
220
275
|
/**
|
|
221
276
|
* The statistics for the cacheable instance
|
|
@@ -677,7 +732,7 @@ var Cacheable = class extends import_hookified2.Hookified {
|
|
|
677
732
|
* @returns {Promise<boolean[]>} Whether the keys exist
|
|
678
733
|
*/
|
|
679
734
|
async hasMany(keys) {
|
|
680
|
-
const result = await this.
|
|
735
|
+
const result = await this._primary.hasMany(keys);
|
|
681
736
|
const missingKeys = [];
|
|
682
737
|
for (const [i, key] of keys.entries()) {
|
|
683
738
|
if (!result[i] && this._secondary) {
|
|
@@ -685,7 +740,7 @@ var Cacheable = class extends import_hookified2.Hookified {
|
|
|
685
740
|
}
|
|
686
741
|
}
|
|
687
742
|
if (missingKeys.length > 0 && this._secondary) {
|
|
688
|
-
const secondary = await this.
|
|
743
|
+
const secondary = await this._secondary.hasMany(keys);
|
|
689
744
|
for (const [i, _key] of keys.entries()) {
|
|
690
745
|
if (!result[i] && secondary[i]) {
|
|
691
746
|
result[i] = secondary[i];
|
|
@@ -795,6 +850,7 @@ var Cacheable = class extends import_hookified2.Hookified {
|
|
|
795
850
|
if (this._secondary) {
|
|
796
851
|
promises.push(this._secondary.disconnect());
|
|
797
852
|
}
|
|
853
|
+
promises.push(this._sync?.qified.disconnect());
|
|
798
854
|
await (this._nonBlocking ? Promise.race(promises) : Promise.all(promises));
|
|
799
855
|
}
|
|
800
856
|
/**
|
|
@@ -809,18 +865,16 @@ var Cacheable = class extends import_hookified2.Hookified {
|
|
|
809
865
|
wrap(function_, options) {
|
|
810
866
|
const cacheAdapter = {
|
|
811
867
|
get: async (key) => this.get(key),
|
|
868
|
+
/* v8 ignore next -- @preserve */
|
|
812
869
|
has: async (key) => this.has(key),
|
|
813
|
-
// biome-ignore lint/suspicious/noExplicitAny: CacheInstance requires any type
|
|
814
870
|
set: async (key, value, ttl) => {
|
|
815
871
|
await this.set(key, value, ttl);
|
|
816
872
|
},
|
|
817
|
-
/*
|
|
818
|
-
// biome-ignore lint/suspicious/noExplicitAny: CacheInstance interface
|
|
873
|
+
/* v8 ignore next -- @preserve */
|
|
819
874
|
on: (event, listener) => {
|
|
820
875
|
this.on(event, listener);
|
|
821
876
|
},
|
|
822
|
-
/*
|
|
823
|
-
// biome-ignore lint/suspicious/noExplicitAny: CacheInstance requires any type
|
|
877
|
+
/* v8 ignore next -- @preserve */
|
|
824
878
|
emit: (event, ...args) => this.emit(event, ...args)
|
|
825
879
|
};
|
|
826
880
|
const wrapOptions = {
|
|
@@ -832,7 +886,7 @@ var Cacheable = class extends import_hookified2.Hookified {
|
|
|
832
886
|
cacheId: this._cacheId,
|
|
833
887
|
serialize: options?.serialize
|
|
834
888
|
};
|
|
835
|
-
return (0,
|
|
889
|
+
return (0, import_utils.wrap)(function_, wrapOptions);
|
|
836
890
|
}
|
|
837
891
|
/**
|
|
838
892
|
* Retrieves the value associated with the given key from the cache. If the key is not found,
|
|
@@ -847,18 +901,15 @@ var Cacheable = class extends import_hookified2.Hookified {
|
|
|
847
901
|
async getOrSet(key, function_, options) {
|
|
848
902
|
const cacheAdapter = {
|
|
849
903
|
get: async (key2) => this.get(key2),
|
|
904
|
+
/* v8 ignore next -- @preserve */
|
|
850
905
|
has: async (key2) => this.has(key2),
|
|
851
|
-
// biome-ignore lint/suspicious/noExplicitAny: CacheInstance requires any type
|
|
852
906
|
set: async (key2, value, ttl) => {
|
|
853
907
|
await this.set(key2, value, ttl);
|
|
854
908
|
},
|
|
855
|
-
/*
|
|
856
|
-
// biome-ignore lint/suspicious/noExplicitAny: CacheInstance interface
|
|
909
|
+
/* v8 ignore next -- @preserve */
|
|
857
910
|
on: (event, listener) => {
|
|
858
911
|
this.on(event, listener);
|
|
859
912
|
},
|
|
860
|
-
/* c8 ignore stop */
|
|
861
|
-
// biome-ignore lint/suspicious/noExplicitAny: CacheInstance requires any type
|
|
862
913
|
emit: (event, ...args) => this.emit(event, ...args)
|
|
863
914
|
};
|
|
864
915
|
const getOrSetOptions = {
|
|
@@ -868,17 +919,29 @@ var Cacheable = class extends import_hookified2.Hookified {
|
|
|
868
919
|
cacheErrors: options?.cacheErrors,
|
|
869
920
|
throwErrors: options?.throwErrors
|
|
870
921
|
};
|
|
871
|
-
return (0,
|
|
922
|
+
return (0, import_utils.getOrSet)(key, function_, getOrSetOptions);
|
|
923
|
+
}
|
|
924
|
+
/**
|
|
925
|
+
* Will hash an object asynchronously using the specified cryptographic algorithm.
|
|
926
|
+
* Use this for cryptographic algorithms (SHA-256, SHA-384, SHA-512).
|
|
927
|
+
* For non-cryptographic algorithms, use hashSync() for better performance.
|
|
928
|
+
* @param {any} object the object to hash
|
|
929
|
+
* @param {string} algorithm the hash algorithm to use. The default is 'SHA-256'
|
|
930
|
+
* @returns {Promise<string>} the hash of the object
|
|
931
|
+
*/
|
|
932
|
+
async hash(object, algorithm = import_utils.HashAlgorithm.SHA256) {
|
|
933
|
+
return (0, import_utils.hash)(object, { algorithm });
|
|
872
934
|
}
|
|
873
935
|
/**
|
|
874
|
-
* Will hash an object using the specified
|
|
936
|
+
* Will hash an object synchronously using the specified non-cryptographic algorithm.
|
|
937
|
+
* Use this for non-cryptographic algorithms (DJB2, FNV1, MURMER, CRC32).
|
|
938
|
+
* For cryptographic algorithms, use hash() instead.
|
|
875
939
|
* @param {any} object the object to hash
|
|
876
|
-
* @param {string} algorithm the hash algorithm to use. The default is '
|
|
940
|
+
* @param {string} algorithm the hash algorithm to use. The default is 'djb2'
|
|
877
941
|
* @returns {string} the hash of the object
|
|
878
942
|
*/
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
return (0, import_utils.hash)(object, { algorithm: validAlgorithm });
|
|
943
|
+
hashSync(object, algorithm = import_utils.HashAlgorithm.DJB2) {
|
|
944
|
+
return (0, import_utils.hashSync)(object, { algorithm });
|
|
882
945
|
}
|
|
883
946
|
async setManyKeyv(keyv, items) {
|
|
884
947
|
const entries = [];
|
|
@@ -889,13 +952,6 @@ var Cacheable = class extends import_hookified2.Hookified {
|
|
|
889
952
|
await keyv.setMany(entries);
|
|
890
953
|
return true;
|
|
891
954
|
}
|
|
892
|
-
async hasManyKeyv(keyv, keys) {
|
|
893
|
-
const promises = [];
|
|
894
|
-
for (const key of keys) {
|
|
895
|
-
promises.push(keyv.has(key));
|
|
896
|
-
}
|
|
897
|
-
return Promise.all(promises);
|
|
898
|
-
}
|
|
899
955
|
/**
|
|
900
956
|
* Processes a single key from secondary store for getRaw operation
|
|
901
957
|
* @param primary - the primary store to use
|
|
@@ -1085,3 +1141,4 @@ var Cacheable = class extends import_hookified2.Hookified {
|
|
|
1085
1141
|
wrap,
|
|
1086
1142
|
wrapSync
|
|
1087
1143
|
});
|
|
1144
|
+
/* v8 ignore next -- @preserve */
|
package/dist/index.d.cts
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import { WrapFunctionOptions, GetOrSetKey, GetOrSetFunctionOptions } from '@cacheable/
|
|
2
|
-
export { GetOrSetFunctionOptions, GetOrSetKey, GetOrSetOptions, WrapOptions, WrapSyncOptions, getOrSet, wrap, wrapSync } from '@cacheable/
|
|
3
|
-
import { Stats, CacheableItem, HashAlgorithm } from '@cacheable/utils';
|
|
4
|
-
export { CacheableItem, Stats as CacheableStats, HashAlgorithm, calculateTtlFromExpiration, getCascadingTtl, hash, shorthandToMilliseconds, shorthandToTime } from '@cacheable/utils';
|
|
1
|
+
import { Stats, CacheableItem, WrapFunctionOptions, GetOrSetKey, GetOrSetFunctionOptions, HashAlgorithm } from '@cacheable/utils';
|
|
2
|
+
export { CacheableItem, Stats as CacheableStats, GetOrSetFunctionOptions, GetOrSetKey, GetOrSetOptions, HashAlgorithm, WrapOptions, WrapSyncOptions, calculateTtlFromExpiration, getCascadingTtl, getOrSet, hash, shorthandToMilliseconds, shorthandToTime, wrap, wrapSync } from '@cacheable/utils';
|
|
5
3
|
import { Hookified, HookifiedOptions } from 'hookified';
|
|
6
4
|
import { Keyv, KeyvStoreAdapter, StoredDataRaw } from 'keyv';
|
|
7
5
|
export { Keyv, KeyvHooks, KeyvOptions, KeyvStoreAdapter } from 'keyv';
|
|
@@ -24,6 +22,11 @@ type CacheableSyncOptions = {
|
|
|
24
22
|
* Qified instance or message provider(s) for synchronization
|
|
25
23
|
*/
|
|
26
24
|
qified: Qified | MessageProvider | MessageProvider[];
|
|
25
|
+
/**
|
|
26
|
+
* The namespace for sync events. It can be a string or a function that returns a string.
|
|
27
|
+
* When set, event names will be prefixed with the namespace (e.g., "my-namespace::cache:set")
|
|
28
|
+
*/
|
|
29
|
+
namespace?: string | (() => string);
|
|
27
30
|
} & HookifiedOptions;
|
|
28
31
|
type CacheableSyncItem = {
|
|
29
32
|
cacheId: string;
|
|
@@ -37,6 +40,9 @@ type CacheableSyncItem = {
|
|
|
37
40
|
*/
|
|
38
41
|
declare class CacheableSync extends Hookified {
|
|
39
42
|
private _qified;
|
|
43
|
+
private _namespace?;
|
|
44
|
+
private _storage?;
|
|
45
|
+
private _cacheId?;
|
|
40
46
|
/**
|
|
41
47
|
* Creates an instance of CacheableSync
|
|
42
48
|
* @param options - Configuration options for CacheableSync
|
|
@@ -52,6 +58,16 @@ declare class CacheableSync extends Hookified {
|
|
|
52
58
|
* @param value - Either an existing Qified instance or MessageProvider(s)
|
|
53
59
|
*/
|
|
54
60
|
set qified(value: Qified | MessageProvider | MessageProvider[]);
|
|
61
|
+
/**
|
|
62
|
+
* Gets the namespace for sync events
|
|
63
|
+
* @returns The namespace or undefined if not set
|
|
64
|
+
*/
|
|
65
|
+
get namespace(): string | (() => string) | undefined;
|
|
66
|
+
/**
|
|
67
|
+
* Sets the namespace for sync events and resubscribes if needed
|
|
68
|
+
* @param namespace - The namespace string or function
|
|
69
|
+
*/
|
|
70
|
+
set namespace(namespace: string | (() => string) | undefined);
|
|
55
71
|
/**
|
|
56
72
|
* Publishes a cache event to all the cache instances
|
|
57
73
|
* @param data - The cache item data containing cacheId, key, value, and optional ttl
|
|
@@ -69,6 +85,17 @@ declare class CacheableSync extends Hookified {
|
|
|
69
85
|
* @returns A Qified instance configured with the provided message provider(s)
|
|
70
86
|
*/
|
|
71
87
|
createQified(value: Qified | MessageProvider | MessageProvider[]): Qified;
|
|
88
|
+
/**
|
|
89
|
+
* Gets the namespace prefix to use for event names
|
|
90
|
+
* @returns The resolved namespace string or undefined
|
|
91
|
+
*/
|
|
92
|
+
private getNamespace;
|
|
93
|
+
/**
|
|
94
|
+
* Prefixes an event name with the namespace if one is set
|
|
95
|
+
* @param event - The event to prefix
|
|
96
|
+
* @returns The prefixed event name or the original event
|
|
97
|
+
*/
|
|
98
|
+
private getPrefixedEvent;
|
|
72
99
|
}
|
|
73
100
|
|
|
74
101
|
type CacheableOptions = {
|
|
@@ -408,14 +435,24 @@ declare class Cacheable extends Hookified {
|
|
|
408
435
|
*/
|
|
409
436
|
getOrSet<T>(key: GetOrSetKey, function_: () => Promise<T>, options?: GetOrSetFunctionOptions): Promise<T | undefined>;
|
|
410
437
|
/**
|
|
411
|
-
* Will hash an object using the specified
|
|
438
|
+
* Will hash an object asynchronously using the specified cryptographic algorithm.
|
|
439
|
+
* Use this for cryptographic algorithms (SHA-256, SHA-384, SHA-512).
|
|
440
|
+
* For non-cryptographic algorithms, use hashSync() for better performance.
|
|
441
|
+
* @param {any} object the object to hash
|
|
442
|
+
* @param {string} algorithm the hash algorithm to use. The default is 'SHA-256'
|
|
443
|
+
* @returns {Promise<string>} the hash of the object
|
|
444
|
+
*/
|
|
445
|
+
hash(object: any, algorithm?: HashAlgorithm): Promise<string>;
|
|
446
|
+
/**
|
|
447
|
+
* Will hash an object synchronously using the specified non-cryptographic algorithm.
|
|
448
|
+
* Use this for non-cryptographic algorithms (DJB2, FNV1, MURMER, CRC32).
|
|
449
|
+
* For cryptographic algorithms, use hash() instead.
|
|
412
450
|
* @param {any} object the object to hash
|
|
413
|
-
* @param {string} algorithm the hash algorithm to use. The default is '
|
|
451
|
+
* @param {string} algorithm the hash algorithm to use. The default is 'djb2'
|
|
414
452
|
* @returns {string} the hash of the object
|
|
415
453
|
*/
|
|
416
|
-
|
|
454
|
+
hashSync(object: any, algorithm?: HashAlgorithm): string;
|
|
417
455
|
private setManyKeyv;
|
|
418
|
-
private hasManyKeyv;
|
|
419
456
|
/**
|
|
420
457
|
* Processes a single key from secondary store for getRaw operation
|
|
421
458
|
* @param primary - the primary store to use
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import { WrapFunctionOptions, GetOrSetKey, GetOrSetFunctionOptions } from '@cacheable/
|
|
2
|
-
export { GetOrSetFunctionOptions, GetOrSetKey, GetOrSetOptions, WrapOptions, WrapSyncOptions, getOrSet, wrap, wrapSync } from '@cacheable/
|
|
3
|
-
import { Stats, CacheableItem, HashAlgorithm } from '@cacheable/utils';
|
|
4
|
-
export { CacheableItem, Stats as CacheableStats, HashAlgorithm, calculateTtlFromExpiration, getCascadingTtl, hash, shorthandToMilliseconds, shorthandToTime } from '@cacheable/utils';
|
|
1
|
+
import { Stats, CacheableItem, WrapFunctionOptions, GetOrSetKey, GetOrSetFunctionOptions, HashAlgorithm } from '@cacheable/utils';
|
|
2
|
+
export { CacheableItem, Stats as CacheableStats, GetOrSetFunctionOptions, GetOrSetKey, GetOrSetOptions, HashAlgorithm, WrapOptions, WrapSyncOptions, calculateTtlFromExpiration, getCascadingTtl, getOrSet, hash, shorthandToMilliseconds, shorthandToTime, wrap, wrapSync } from '@cacheable/utils';
|
|
5
3
|
import { Hookified, HookifiedOptions } from 'hookified';
|
|
6
4
|
import { Keyv, KeyvStoreAdapter, StoredDataRaw } from 'keyv';
|
|
7
5
|
export { Keyv, KeyvHooks, KeyvOptions, KeyvStoreAdapter } from 'keyv';
|
|
@@ -24,6 +22,11 @@ type CacheableSyncOptions = {
|
|
|
24
22
|
* Qified instance or message provider(s) for synchronization
|
|
25
23
|
*/
|
|
26
24
|
qified: Qified | MessageProvider | MessageProvider[];
|
|
25
|
+
/**
|
|
26
|
+
* The namespace for sync events. It can be a string or a function that returns a string.
|
|
27
|
+
* When set, event names will be prefixed with the namespace (e.g., "my-namespace::cache:set")
|
|
28
|
+
*/
|
|
29
|
+
namespace?: string | (() => string);
|
|
27
30
|
} & HookifiedOptions;
|
|
28
31
|
type CacheableSyncItem = {
|
|
29
32
|
cacheId: string;
|
|
@@ -37,6 +40,9 @@ type CacheableSyncItem = {
|
|
|
37
40
|
*/
|
|
38
41
|
declare class CacheableSync extends Hookified {
|
|
39
42
|
private _qified;
|
|
43
|
+
private _namespace?;
|
|
44
|
+
private _storage?;
|
|
45
|
+
private _cacheId?;
|
|
40
46
|
/**
|
|
41
47
|
* Creates an instance of CacheableSync
|
|
42
48
|
* @param options - Configuration options for CacheableSync
|
|
@@ -52,6 +58,16 @@ declare class CacheableSync extends Hookified {
|
|
|
52
58
|
* @param value - Either an existing Qified instance or MessageProvider(s)
|
|
53
59
|
*/
|
|
54
60
|
set qified(value: Qified | MessageProvider | MessageProvider[]);
|
|
61
|
+
/**
|
|
62
|
+
* Gets the namespace for sync events
|
|
63
|
+
* @returns The namespace or undefined if not set
|
|
64
|
+
*/
|
|
65
|
+
get namespace(): string | (() => string) | undefined;
|
|
66
|
+
/**
|
|
67
|
+
* Sets the namespace for sync events and resubscribes if needed
|
|
68
|
+
* @param namespace - The namespace string or function
|
|
69
|
+
*/
|
|
70
|
+
set namespace(namespace: string | (() => string) | undefined);
|
|
55
71
|
/**
|
|
56
72
|
* Publishes a cache event to all the cache instances
|
|
57
73
|
* @param data - The cache item data containing cacheId, key, value, and optional ttl
|
|
@@ -69,6 +85,17 @@ declare class CacheableSync extends Hookified {
|
|
|
69
85
|
* @returns A Qified instance configured with the provided message provider(s)
|
|
70
86
|
*/
|
|
71
87
|
createQified(value: Qified | MessageProvider | MessageProvider[]): Qified;
|
|
88
|
+
/**
|
|
89
|
+
* Gets the namespace prefix to use for event names
|
|
90
|
+
* @returns The resolved namespace string or undefined
|
|
91
|
+
*/
|
|
92
|
+
private getNamespace;
|
|
93
|
+
/**
|
|
94
|
+
* Prefixes an event name with the namespace if one is set
|
|
95
|
+
* @param event - The event to prefix
|
|
96
|
+
* @returns The prefixed event name or the original event
|
|
97
|
+
*/
|
|
98
|
+
private getPrefixedEvent;
|
|
72
99
|
}
|
|
73
100
|
|
|
74
101
|
type CacheableOptions = {
|
|
@@ -408,14 +435,24 @@ declare class Cacheable extends Hookified {
|
|
|
408
435
|
*/
|
|
409
436
|
getOrSet<T>(key: GetOrSetKey, function_: () => Promise<T>, options?: GetOrSetFunctionOptions): Promise<T | undefined>;
|
|
410
437
|
/**
|
|
411
|
-
* Will hash an object using the specified
|
|
438
|
+
* Will hash an object asynchronously using the specified cryptographic algorithm.
|
|
439
|
+
* Use this for cryptographic algorithms (SHA-256, SHA-384, SHA-512).
|
|
440
|
+
* For non-cryptographic algorithms, use hashSync() for better performance.
|
|
441
|
+
* @param {any} object the object to hash
|
|
442
|
+
* @param {string} algorithm the hash algorithm to use. The default is 'SHA-256'
|
|
443
|
+
* @returns {Promise<string>} the hash of the object
|
|
444
|
+
*/
|
|
445
|
+
hash(object: any, algorithm?: HashAlgorithm): Promise<string>;
|
|
446
|
+
/**
|
|
447
|
+
* Will hash an object synchronously using the specified non-cryptographic algorithm.
|
|
448
|
+
* Use this for non-cryptographic algorithms (DJB2, FNV1, MURMER, CRC32).
|
|
449
|
+
* For cryptographic algorithms, use hash() instead.
|
|
412
450
|
* @param {any} object the object to hash
|
|
413
|
-
* @param {string} algorithm the hash algorithm to use. The default is '
|
|
451
|
+
* @param {string} algorithm the hash algorithm to use. The default is 'djb2'
|
|
414
452
|
* @returns {string} the hash of the object
|
|
415
453
|
*/
|
|
416
|
-
|
|
454
|
+
hashSync(object: any, algorithm?: HashAlgorithm): string;
|
|
417
455
|
private setManyKeyv;
|
|
418
|
-
private hasManyKeyv;
|
|
419
456
|
/**
|
|
420
457
|
* Processes a single key from secondary store for getRaw operation
|
|
421
458
|
* @param primary - the primary store to use
|
package/dist/index.js
CHANGED
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
-
import {
|
|
3
|
-
getOrSet,
|
|
4
|
-
wrap
|
|
5
|
-
} from "@cacheable/memoize";
|
|
6
2
|
import { createKeyv } from "@cacheable/memory";
|
|
7
3
|
import {
|
|
8
4
|
Stats as CacheableStats,
|
|
9
5
|
calculateTtlFromExpiration,
|
|
10
6
|
getCascadingTtl,
|
|
7
|
+
getOrSet,
|
|
11
8
|
HashAlgorithm,
|
|
12
9
|
hash,
|
|
10
|
+
hashSync,
|
|
13
11
|
isKeyvInstance,
|
|
14
|
-
shorthandToMilliseconds
|
|
12
|
+
shorthandToMilliseconds,
|
|
13
|
+
wrap
|
|
15
14
|
} from "@cacheable/utils";
|
|
16
15
|
import { Hookified as Hookified2 } from "hookified";
|
|
17
16
|
import {
|
|
@@ -49,12 +48,16 @@ var CacheableSyncEvents = /* @__PURE__ */ ((CacheableSyncEvents2) => {
|
|
|
49
48
|
})(CacheableSyncEvents || {});
|
|
50
49
|
var CacheableSync = class extends Hookified {
|
|
51
50
|
_qified = new Qified();
|
|
51
|
+
_namespace;
|
|
52
|
+
_storage;
|
|
53
|
+
_cacheId;
|
|
52
54
|
/**
|
|
53
55
|
* Creates an instance of CacheableSync
|
|
54
56
|
* @param options - Configuration options for CacheableSync
|
|
55
57
|
*/
|
|
56
58
|
constructor(options) {
|
|
57
59
|
super(options);
|
|
60
|
+
this._namespace = options.namespace;
|
|
58
61
|
this._qified = this.createQified(options.qified);
|
|
59
62
|
}
|
|
60
63
|
/**
|
|
@@ -71,12 +74,36 @@ var CacheableSync = class extends Hookified {
|
|
|
71
74
|
set qified(value) {
|
|
72
75
|
this._qified = this.createQified(value);
|
|
73
76
|
}
|
|
77
|
+
/**
|
|
78
|
+
* Gets the namespace for sync events
|
|
79
|
+
* @returns The namespace or undefined if not set
|
|
80
|
+
*/
|
|
81
|
+
get namespace() {
|
|
82
|
+
return this._namespace;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Sets the namespace for sync events and resubscribes if needed
|
|
86
|
+
* @param namespace - The namespace string or function
|
|
87
|
+
*/
|
|
88
|
+
set namespace(namespace) {
|
|
89
|
+
if (this._storage && this._cacheId) {
|
|
90
|
+
const oldSetEvent = this.getPrefixedEvent("cache:set" /* SET */);
|
|
91
|
+
const oldDeleteEvent = this.getPrefixedEvent("cache:delete" /* DELETE */);
|
|
92
|
+
void this._qified.unsubscribe(oldSetEvent);
|
|
93
|
+
void this._qified.unsubscribe(oldDeleteEvent);
|
|
94
|
+
}
|
|
95
|
+
this._namespace = namespace;
|
|
96
|
+
if (this._storage && this._cacheId) {
|
|
97
|
+
this.subscribe(this._storage, this._cacheId);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
74
100
|
/**
|
|
75
101
|
* Publishes a cache event to all the cache instances
|
|
76
102
|
* @param data - The cache item data containing cacheId, key, value, and optional ttl
|
|
77
103
|
*/
|
|
78
104
|
async publish(event, data) {
|
|
79
|
-
|
|
105
|
+
const eventName = this.getPrefixedEvent(event);
|
|
106
|
+
await this._qified.publish(eventName, {
|
|
80
107
|
id: crypto.randomUUID(),
|
|
81
108
|
data
|
|
82
109
|
});
|
|
@@ -87,7 +114,11 @@ var CacheableSync = class extends Hookified {
|
|
|
87
114
|
* @param cacheId - The cache ID to identify this instance
|
|
88
115
|
*/
|
|
89
116
|
subscribe(storage, cacheId) {
|
|
90
|
-
this.
|
|
117
|
+
this._storage = storage;
|
|
118
|
+
this._cacheId = cacheId;
|
|
119
|
+
const setEvent = this.getPrefixedEvent("cache:set" /* SET */);
|
|
120
|
+
const deleteEvent = this.getPrefixedEvent("cache:delete" /* DELETE */);
|
|
121
|
+
this._qified.subscribe(setEvent, {
|
|
91
122
|
handler: async (message) => {
|
|
92
123
|
const data = message.data;
|
|
93
124
|
if (data.cacheId !== cacheId) {
|
|
@@ -95,7 +126,7 @@ var CacheableSync = class extends Hookified {
|
|
|
95
126
|
}
|
|
96
127
|
}
|
|
97
128
|
});
|
|
98
|
-
this._qified.subscribe(
|
|
129
|
+
this._qified.subscribe(deleteEvent, {
|
|
99
130
|
handler: async (message) => {
|
|
100
131
|
const data = message.data;
|
|
101
132
|
if (data.cacheId !== cacheId) {
|
|
@@ -116,14 +147,28 @@ var CacheableSync = class extends Hookified {
|
|
|
116
147
|
const providers = Array.isArray(value) ? value : [value];
|
|
117
148
|
return new Qified({ messageProviders: providers });
|
|
118
149
|
}
|
|
150
|
+
/**
|
|
151
|
+
* Gets the namespace prefix to use for event names
|
|
152
|
+
* @returns The resolved namespace string or undefined
|
|
153
|
+
*/
|
|
154
|
+
getNamespace() {
|
|
155
|
+
if (typeof this._namespace === "function") {
|
|
156
|
+
return this._namespace();
|
|
157
|
+
}
|
|
158
|
+
return this._namespace;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Prefixes an event name with the namespace if one is set
|
|
162
|
+
* @param event - The event to prefix
|
|
163
|
+
* @returns The prefixed event name or the original event
|
|
164
|
+
*/
|
|
165
|
+
getPrefixedEvent(event) {
|
|
166
|
+
const ns = this.getNamespace();
|
|
167
|
+
return ns ? `${ns}::${event}` : event;
|
|
168
|
+
}
|
|
119
169
|
};
|
|
120
170
|
|
|
121
171
|
// src/index.ts
|
|
122
|
-
import {
|
|
123
|
-
getOrSet as getOrSet2,
|
|
124
|
-
wrap as wrap2,
|
|
125
|
-
wrapSync
|
|
126
|
-
} from "@cacheable/memoize";
|
|
127
172
|
import {
|
|
128
173
|
CacheableMemory,
|
|
129
174
|
createKeyv as createKeyv2,
|
|
@@ -132,11 +177,14 @@ import {
|
|
|
132
177
|
import {
|
|
133
178
|
calculateTtlFromExpiration as calculateTtlFromExpiration2,
|
|
134
179
|
getCascadingTtl as getCascadingTtl2,
|
|
180
|
+
getOrSet as getOrSet2,
|
|
135
181
|
HashAlgorithm as HashAlgorithm2,
|
|
136
182
|
hash as hash2,
|
|
137
183
|
Stats,
|
|
138
184
|
shorthandToMilliseconds as shorthandToMilliseconds2,
|
|
139
|
-
shorthandToTime
|
|
185
|
+
shorthandToTime,
|
|
186
|
+
wrap as wrap2,
|
|
187
|
+
wrapSync
|
|
140
188
|
} from "@cacheable/utils";
|
|
141
189
|
import { Keyv as Keyv2, KeyvHooks } from "keyv";
|
|
142
190
|
var Cacheable = class extends Hookified2 {
|
|
@@ -180,7 +228,10 @@ var Cacheable = class extends Hookified2 {
|
|
|
180
228
|
}
|
|
181
229
|
}
|
|
182
230
|
if (options?.sync) {
|
|
183
|
-
this._sync = options.sync instanceof CacheableSync ? options.sync : new CacheableSync(
|
|
231
|
+
this._sync = options.sync instanceof CacheableSync ? options.sync : new CacheableSync({
|
|
232
|
+
...options.sync,
|
|
233
|
+
namespace: options.namespace
|
|
234
|
+
});
|
|
184
235
|
this._sync.subscribe(this._primary, this._cacheId);
|
|
185
236
|
}
|
|
186
237
|
}
|
|
@@ -202,6 +253,9 @@ var Cacheable = class extends Hookified2 {
|
|
|
202
253
|
if (this._secondary) {
|
|
203
254
|
this._secondary.namespace = this.getNameSpace();
|
|
204
255
|
}
|
|
256
|
+
if (this._sync) {
|
|
257
|
+
this._sync.namespace = namespace;
|
|
258
|
+
}
|
|
205
259
|
}
|
|
206
260
|
/**
|
|
207
261
|
* The statistics for the cacheable instance
|
|
@@ -663,7 +717,7 @@ var Cacheable = class extends Hookified2 {
|
|
|
663
717
|
* @returns {Promise<boolean[]>} Whether the keys exist
|
|
664
718
|
*/
|
|
665
719
|
async hasMany(keys) {
|
|
666
|
-
const result = await this.
|
|
720
|
+
const result = await this._primary.hasMany(keys);
|
|
667
721
|
const missingKeys = [];
|
|
668
722
|
for (const [i, key] of keys.entries()) {
|
|
669
723
|
if (!result[i] && this._secondary) {
|
|
@@ -671,7 +725,7 @@ var Cacheable = class extends Hookified2 {
|
|
|
671
725
|
}
|
|
672
726
|
}
|
|
673
727
|
if (missingKeys.length > 0 && this._secondary) {
|
|
674
|
-
const secondary = await this.
|
|
728
|
+
const secondary = await this._secondary.hasMany(keys);
|
|
675
729
|
for (const [i, _key] of keys.entries()) {
|
|
676
730
|
if (!result[i] && secondary[i]) {
|
|
677
731
|
result[i] = secondary[i];
|
|
@@ -781,6 +835,7 @@ var Cacheable = class extends Hookified2 {
|
|
|
781
835
|
if (this._secondary) {
|
|
782
836
|
promises.push(this._secondary.disconnect());
|
|
783
837
|
}
|
|
838
|
+
promises.push(this._sync?.qified.disconnect());
|
|
784
839
|
await (this._nonBlocking ? Promise.race(promises) : Promise.all(promises));
|
|
785
840
|
}
|
|
786
841
|
/**
|
|
@@ -795,18 +850,16 @@ var Cacheable = class extends Hookified2 {
|
|
|
795
850
|
wrap(function_, options) {
|
|
796
851
|
const cacheAdapter = {
|
|
797
852
|
get: async (key) => this.get(key),
|
|
853
|
+
/* v8 ignore next -- @preserve */
|
|
798
854
|
has: async (key) => this.has(key),
|
|
799
|
-
// biome-ignore lint/suspicious/noExplicitAny: CacheInstance requires any type
|
|
800
855
|
set: async (key, value, ttl) => {
|
|
801
856
|
await this.set(key, value, ttl);
|
|
802
857
|
},
|
|
803
|
-
/*
|
|
804
|
-
// biome-ignore lint/suspicious/noExplicitAny: CacheInstance interface
|
|
858
|
+
/* v8 ignore next -- @preserve */
|
|
805
859
|
on: (event, listener) => {
|
|
806
860
|
this.on(event, listener);
|
|
807
861
|
},
|
|
808
|
-
/*
|
|
809
|
-
// biome-ignore lint/suspicious/noExplicitAny: CacheInstance requires any type
|
|
862
|
+
/* v8 ignore next -- @preserve */
|
|
810
863
|
emit: (event, ...args) => this.emit(event, ...args)
|
|
811
864
|
};
|
|
812
865
|
const wrapOptions = {
|
|
@@ -833,18 +886,15 @@ var Cacheable = class extends Hookified2 {
|
|
|
833
886
|
async getOrSet(key, function_, options) {
|
|
834
887
|
const cacheAdapter = {
|
|
835
888
|
get: async (key2) => this.get(key2),
|
|
889
|
+
/* v8 ignore next -- @preserve */
|
|
836
890
|
has: async (key2) => this.has(key2),
|
|
837
|
-
// biome-ignore lint/suspicious/noExplicitAny: CacheInstance requires any type
|
|
838
891
|
set: async (key2, value, ttl) => {
|
|
839
892
|
await this.set(key2, value, ttl);
|
|
840
893
|
},
|
|
841
|
-
/*
|
|
842
|
-
// biome-ignore lint/suspicious/noExplicitAny: CacheInstance interface
|
|
894
|
+
/* v8 ignore next -- @preserve */
|
|
843
895
|
on: (event, listener) => {
|
|
844
896
|
this.on(event, listener);
|
|
845
897
|
},
|
|
846
|
-
/* c8 ignore stop */
|
|
847
|
-
// biome-ignore lint/suspicious/noExplicitAny: CacheInstance requires any type
|
|
848
898
|
emit: (event, ...args) => this.emit(event, ...args)
|
|
849
899
|
};
|
|
850
900
|
const getOrSetOptions = {
|
|
@@ -857,14 +907,26 @@ var Cacheable = class extends Hookified2 {
|
|
|
857
907
|
return getOrSet(key, function_, getOrSetOptions);
|
|
858
908
|
}
|
|
859
909
|
/**
|
|
860
|
-
* Will hash an object using the specified
|
|
910
|
+
* Will hash an object asynchronously using the specified cryptographic algorithm.
|
|
911
|
+
* Use this for cryptographic algorithms (SHA-256, SHA-384, SHA-512).
|
|
912
|
+
* For non-cryptographic algorithms, use hashSync() for better performance.
|
|
861
913
|
* @param {any} object the object to hash
|
|
862
|
-
* @param {string} algorithm the hash algorithm to use. The default is '
|
|
914
|
+
* @param {string} algorithm the hash algorithm to use. The default is 'SHA-256'
|
|
915
|
+
* @returns {Promise<string>} the hash of the object
|
|
916
|
+
*/
|
|
917
|
+
async hash(object, algorithm = HashAlgorithm.SHA256) {
|
|
918
|
+
return hash(object, { algorithm });
|
|
919
|
+
}
|
|
920
|
+
/**
|
|
921
|
+
* Will hash an object synchronously using the specified non-cryptographic algorithm.
|
|
922
|
+
* Use this for non-cryptographic algorithms (DJB2, FNV1, MURMER, CRC32).
|
|
923
|
+
* For cryptographic algorithms, use hash() instead.
|
|
924
|
+
* @param {any} object the object to hash
|
|
925
|
+
* @param {string} algorithm the hash algorithm to use. The default is 'djb2'
|
|
863
926
|
* @returns {string} the hash of the object
|
|
864
927
|
*/
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
return hash(object, { algorithm: validAlgorithm });
|
|
928
|
+
hashSync(object, algorithm = HashAlgorithm.DJB2) {
|
|
929
|
+
return hashSync(object, { algorithm });
|
|
868
930
|
}
|
|
869
931
|
async setManyKeyv(keyv, items) {
|
|
870
932
|
const entries = [];
|
|
@@ -875,13 +937,6 @@ var Cacheable = class extends Hookified2 {
|
|
|
875
937
|
await keyv.setMany(entries);
|
|
876
938
|
return true;
|
|
877
939
|
}
|
|
878
|
-
async hasManyKeyv(keyv, keys) {
|
|
879
|
-
const promises = [];
|
|
880
|
-
for (const key of keys) {
|
|
881
|
-
promises.push(keyv.has(key));
|
|
882
|
-
}
|
|
883
|
-
return Promise.all(promises);
|
|
884
|
-
}
|
|
885
940
|
/**
|
|
886
941
|
* Processes a single key from secondary store for getRaw operation
|
|
887
942
|
* @param primary - the primary store to use
|
|
@@ -1070,3 +1125,4 @@ export {
|
|
|
1070
1125
|
wrap2 as wrap,
|
|
1071
1126
|
wrapSync
|
|
1072
1127
|
};
|
|
1128
|
+
/* v8 ignore next -- @preserve */
|
package/package.json
CHANGED
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cacheable",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"description": "High Performance Layer 1 / Layer 2 Caching with Keyv Storage",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"main": "./dist/index.
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
7
|
"module": "./dist/index.js",
|
|
8
8
|
"types": "./dist/index.d.ts",
|
|
9
9
|
"exports": {
|
|
10
10
|
".": {
|
|
11
|
-
"
|
|
12
|
-
|
|
11
|
+
"import": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"default": "./dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"require": {
|
|
16
|
+
"types": "./dist/index.d.cts",
|
|
17
|
+
"default": "./dist/index.cjs"
|
|
18
|
+
}
|
|
13
19
|
}
|
|
14
20
|
},
|
|
15
21
|
"repository": {
|
|
@@ -21,26 +27,25 @@
|
|
|
21
27
|
"license": "MIT",
|
|
22
28
|
"private": false,
|
|
23
29
|
"devDependencies": {
|
|
24
|
-
"@biomejs/biome": "^2.
|
|
30
|
+
"@biomejs/biome": "^2.3.5",
|
|
25
31
|
"@faker-js/faker": "^10.1.0",
|
|
26
|
-
"@keyv/redis": "^5.1.
|
|
27
|
-
"@keyv/valkey": "^1.0.
|
|
28
|
-
"@qified/redis": "^0.5.
|
|
29
|
-
"@types/node": "^24.
|
|
30
|
-
"@vitest/coverage-v8": "^
|
|
32
|
+
"@keyv/redis": "^5.1.4",
|
|
33
|
+
"@keyv/valkey": "^1.0.11",
|
|
34
|
+
"@qified/redis": "^0.5.2",
|
|
35
|
+
"@types/node": "^24.10.1",
|
|
36
|
+
"@vitest/coverage-v8": "^4.0.9",
|
|
31
37
|
"lru-cache": "^11.2.2",
|
|
32
|
-
"rimraf": "^6.0
|
|
33
|
-
"tsup": "^8.5.
|
|
38
|
+
"rimraf": "^6.1.0",
|
|
39
|
+
"tsup": "^8.5.1",
|
|
34
40
|
"typescript": "^5.9.3",
|
|
35
|
-
"vitest": "^
|
|
41
|
+
"vitest": "^4.0.9"
|
|
36
42
|
},
|
|
37
43
|
"dependencies": {
|
|
38
|
-
"hookified": "^1.
|
|
39
|
-
"keyv": "^5.5.
|
|
40
|
-
"qified": "^0.5.
|
|
41
|
-
"@cacheable/
|
|
42
|
-
"@cacheable/
|
|
43
|
-
"@cacheable/utils": "^2.1.0"
|
|
44
|
+
"hookified": "^1.13.0",
|
|
45
|
+
"keyv": "^5.5.4",
|
|
46
|
+
"qified": "^0.5.2",
|
|
47
|
+
"@cacheable/memory": "^2.0.6",
|
|
48
|
+
"@cacheable/utils": "^2.3.2"
|
|
44
49
|
},
|
|
45
50
|
"keywords": [
|
|
46
51
|
"cacheable",
|