cacheable 2.1.0 → 2.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 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
 
@@ -517,7 +622,8 @@ _This does not enable statistics for your layer 2 cache as that is a distributed
517
622
  * `removeHook(hook)`: Removes a hook.
518
623
  * `on(event, callback)`: Listens for an event.
519
624
  * `removeListener(event, callback)`: Removes a listener.
520
- * `hash(object: any, algorithm = 'sha256'): string`: Hashes an object with the algorithm. Default is `sha256`.
625
+ * `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`.
626
+ * `hashSync(object: any, algorithm = 'djb2'): string`: Synchronously hashes an object with a non-cryptographic algorithm (djb2, fnv1, murmer, crc32). Default is `djb2`.
521
627
  * `primary`: The primary store for the cache (layer 1) defaults to in-memory by Keyv.
522
628
  * `secondary`: The secondary store for the cache (layer 2) usually a persistent cache by Keyv.
523
629
  * `namespace`: The namespace for the cache. Default is `undefined`. This will set the namespace for the primary and secondary stores.
@@ -544,7 +650,7 @@ To learn more go to [@cacheable/memory](https://cacheable.org/docs/memory/)
544
650
 
545
651
  # Wrap / Memoization for Sync and Async Functions
546
652
 
547
- `Cacheable` and `CacheableMemory` has a feature called `wrap` that comes from [@cacheable/memoize](https://cacheable.org/docs/memoize/) 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:
653
+ `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
654
 
549
655
  ```javascript
550
656
  import { Cacheable } from 'cacheable';
@@ -637,11 +743,11 @@ If you would like to generate your own key for the wrapped function you can set
637
743
 
638
744
  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
745
 
640
- To learn more visit [@cacheable/memoize](https://cacheable.org/docs/memoize/)
746
+ To learn more visit [@cacheable/utils](https://cacheable.org/docs/utils/)
641
747
 
642
748
  # Get Or Set Memoization Function
643
749
 
644
- The `getOrSet` method that comes from [@cacheable/memoize](https://cacheable.org/docs/memoize/) 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:
750
+ 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
751
 
646
752
  ```typescript
647
753
  export type GetOrSetFunctionOptions = {
@@ -677,17 +783,17 @@ const function_ = async () => Math.random() * 100;
677
783
  const value = await cache.getOrSet(generateKey(), function_, { ttl: '1h' });
678
784
  ```
679
785
 
680
- To learn more go to [@cacheable/memoize](https://cacheable.org/docs/memoize/)
786
+ To learn more go to [@cacheable/utils](https://cacheable.org/docs/utils/)
681
787
 
682
788
  # v1 to v2 Changes
683
789
 
684
- `cacheable` is now using `@cacheable/utils`, `@cacheable/memoize`, 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:
790
+ `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
791
 
686
792
  * `get()` and `getMany()` no longer have the `raw` option but instead we have built out `getRaw()` and `getManyRaw()` to use.
687
793
  * 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*` and `getRaw*`
794
+ * `Keyv` v5.5+ is now the recommended supported version as we are using its native `getMany*`, `getRaw*`, and `hasMany` methods for improved performance
689
795
  * `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 now been updated with robust options and also an enum for setting the algorithm.
796
+ * `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
797
 
692
798
  # How to Contribute
693
799