keyv 6.0.0-alpha.2 → 6.0.0-beta.1

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
@@ -4,6 +4,7 @@
4
4
 
5
5
  [![build](https://github.com/jaredwray/keyv/actions/workflows/tests.yaml/badge.svg)](https://github.com/jaredwray/keyv/actions/workflows/tests.yaml)
6
6
  [![bun](https://github.com/jaredwray/keyv/actions/workflows/bun-test.yaml/badge.svg)](https://github.com/jaredwray/keyv/actions/workflows/bun-test.yaml)
7
+ [![browser](https://github.com/jaredwray/keyv/actions/workflows/browser-compat.yaml/badge.svg)](https://github.com/jaredwray/keyv/actions/workflows/browser-compat.yaml)
7
8
  [![codecov](https://codecov.io/gh/jaredwray/keyv/branch/main/graph/badge.svg?token=bRzR3RyOXZ)](https://codecov.io/gh/jaredwray/keyv)
8
9
  [![npm](https://img.shields.io/npm/dm/keyv.svg)](https://www.npmjs.com/package/keyv)
9
10
  [![npm](https://img.shields.io/npm/v/keyv.svg)](https://www.npmjs.com/package/keyv)
@@ -19,7 +20,7 @@ There are a few existing modules similar to Keyv, however Keyv is different beca
19
20
  - Suitable as a TTL based cache or persistent key-value store
20
21
  - [Easily embeddable](#add-cache-support-to-your-module) inside another module
21
22
  - Works with any storage that implements the [`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) API
22
- - Handles all JSON types plus `Buffer`
23
+ - Handles all JSON types plus `Buffer` and `BigInt` via the built-in `KeyvJsonSerializer`
23
24
  - Supports namespaces
24
25
  - Wide range of [**efficient, well tested**](#official-storage-adapters) storage adapters
25
26
  - Connection errors are passed through (db failures won't kill your app)
@@ -32,21 +33,24 @@ There are a few existing modules similar to Keyv, however Keyv is different beca
32
33
  - [Namespaces](#namespaces)
33
34
  - [Events](#events)
34
35
  - [Hooks](#hooks)
35
- - [Custom Serializers](#custom-serializers)
36
+ - [Serialization](#serialization)
36
37
  - [Official Storage Adapters](#official-storage-adapters)
37
38
  - [Third-party Storage Adapters](#third-party-storage-adapters)
38
39
  - [Using BigMap to Scale](#using-bigmap-to-scale)
39
40
  - [Compression](#compression)
41
+ - [Capability Detection](#capability-detection)
40
42
  - [API](#api)
41
43
  - [new Keyv([storage-adapter], [options]) or new Keyv([options])](#new-keyvstorage-adapter-options-or-new-keyvoptions)
42
44
  - [.namespace](#namespace)
43
45
  - [.ttl](#ttl)
44
46
  - [.store](#store)
45
- - [.serialize](#serialize)
46
- - [.deserialize](#deserialize)
47
+ - [.serialization](#serialization-1)
47
48
  - [.compression](#compression)
48
49
  - [.useKeyPrefix](#usekeyprefix)
50
+ - [.emitErrors](#emiterrors)
51
+ - [.throwOnErrors](#throwonerrors)
49
52
  - [.stats](#stats)
53
+ - [.sanitize](#sanitize)
50
54
  - [Keyv Instance](#keyv-instance)
51
55
  - [.set(key, value, [ttl])](#setkey-value-ttl)
52
56
  - [.setMany(entries)](#setmanyentries)
@@ -54,9 +58,14 @@ There are a few existing modules similar to Keyv, however Keyv is different beca
54
58
  - [.getMany(keys, [options])](#getmanykeys-options)
55
59
  - [.getRaw(key)](#getrawkey)
56
60
  - [.getManyRaw(keys)](#getmanyrawkeys)
61
+ - [.setRaw(key, value)](#setrawkey-value)
62
+ - [.setManyRaw(entries)](#setmanyrawentries)
57
63
  - [.delete(key)](#deletekey)
58
64
  - [.deleteMany(keys)](#deletemanykeys)
59
65
  - [.clear()](#clear)
66
+ - [.has(key)](#haskey)
67
+ - [.hasMany(keys)](#hasmanykeys)
68
+ - [.disconnect()](#disconnect)
60
69
  - [.iterator()](#iterator)
61
70
  - [Bun Support](#bun-support)
62
71
  - [How to Contribute](#how-to-contribute)
@@ -219,6 +228,10 @@ PRE_GET_MANY_RAW
219
228
  POST_GET_MANY_RAW
220
229
  PRE_SET
221
230
  POST_SET
231
+ PRE_SET_RAW
232
+ POST_SET_RAW
233
+ PRE_SET_MANY_RAW
234
+ POST_SET_MANY_RAW
222
235
  PRE_DELETE
223
236
  POST_DELETE
224
237
  ```
@@ -288,41 +301,80 @@ Now this key will have prefix- added to it before it is set.
288
301
  In `PRE_DELETE` and `POST_DELETE` hooks, the value could be a single item or an `Array`. This is based on the fact that `delete` can accept a single key or an `Array` of keys.
289
302
 
290
303
 
291
- # Custom Serializers
304
+ # Serialization
292
305
 
293
- Keyv uses [`buffer`](https://nodejs.org/api/buffer.html) for data serialization to ensure consistency across different backends.
306
+ By default, Keyv uses its built-in `KeyvJsonSerializer` — a JSON-based serializer with support for `Buffer` and `BigInt` types. This works out of the box with all storage adapters.
294
307
 
295
- You can optionally provide your own serialization functions to support extra data types or to serialize to something other than JSON.
308
+ ## Official Serializers
309
+
310
+ In addition to the built-in serializer, Keyv offers two official serialization packages:
311
+
312
+ ### SuperJSON
313
+
314
+ [`@keyv/serialize-superjson`](https://github.com/jaredwray/keyv/tree/main/serialization/superjson) supports `Date`, `RegExp`, `Map`, `Set`, `BigInt`, `undefined`, `Error`, and `URL` types.
296
315
 
297
316
  ```js
298
- const keyv = new Keyv({ serialize: JSON.stringify, deserialize: JSON.parse });
317
+ import Keyv from 'keyv';
318
+ import { superJsonSerializer } from '@keyv/serialize-superjson'; // using the helper function that does new KeyvSuperJsonSerializer()
319
+
320
+ const keyv = new Keyv({ serialization: superJsonSerializer });
299
321
  ```
300
322
 
301
- **Warning:** Using custom serializers means you lose any guarantee of data consistency. You should do extensive testing with your serialisation functions and chosen storage engine.
323
+ ### MessagePack (msgpackr)
302
324
 
303
- If you do not want to use serialization you can set the `serialize` and `deserialize` functions to `undefined`. This will also turn off compression.
325
+ [`@keyv/serialize-msgpackr`](https://github.com/jaredwray/keyv/tree/main/serialization/msgpackr) is a binary serializer that supports `Date`, `RegExp`, `Map`, `Set`, `Error`, `undefined`, `NaN`, and `Infinity` types.
304
326
 
305
327
  ```js
306
- const keyv = new Keyv();
307
- keyv.serialize = undefined;
308
- keyv.deserialize = undefined;
328
+ import Keyv from 'keyv';
329
+ import { KeyvMsgpackrSerializer } from '@keyv/serialize-msgpackr';
330
+
331
+ const keyv = new Keyv({ serialization: new KeyvMsgpackrSerializer() });
309
332
  ```
310
333
 
334
+ ## Custom Serializers
335
+
336
+ You can provide your own serializer by implementing the `KeyvSerializationAdapter` interface with `stringify` and `parse` methods:
337
+
338
+ ```typescript
339
+ interface KeyvSerializationAdapter {
340
+ stringify: (object: unknown) => string | Promise<string>;
341
+ parse: <T>(data: string) => T | Promise<T>;
342
+ }
343
+ ```
344
+
345
+ ## Disabling Serialization
346
+
347
+ You can disable serialization entirely by passing `false`. This stores data as raw objects, which works for in-memory `Map` storage where string conversion is not needed:
348
+
349
+ ```js
350
+ const keyv = new Keyv({ serialization: false });
351
+ ```
352
+
353
+ ## Pipeline
354
+
355
+ When serialization and/or compression are configured, Keyv applies them in this order:
356
+
357
+ **On set:** serialize (optional) → compress (optional) → store
358
+
359
+ **On get:** store → decompress (optional) → parse (optional) → value
360
+
361
+ If compression is configured without a serializer, Keyv will use `JSON.stringify`/`JSON.parse` as a minimum fallback since compression adapters require string input.
362
+
311
363
  # Official Storage Adapters
312
364
 
313
365
  The official storage adapters are covered by [over 150 integration tests](https://github.com/jaredwray/keyv/actions/workflows/tests.yaml) to guarantee consistent behaviour. They are lightweight, efficient wrappers over the DB clients making use of indexes and native TTLs where available.
314
366
 
315
367
  Database | Adapter | Native TTL
316
368
  ---|---|---
317
- Redis | [@keyv/redis](https://github.com/jaredwray/keyv/tree/master/storage/redis) | Yes
318
- Valkey | [@keyv/valkey](https://github.com/jaredwray/keyv/tree/master/storage/valkey) | Yes
319
- MongoDB | [@keyv/mongo](https://github.com/jaredwray/keyv/tree/master/storage/mongo) | Yes
320
- SQLite | [@keyv/sqlite](https://github.com/jaredwray/keyv/tree/master/storage/sqlite) | No
321
- PostgreSQL | [@keyv/postgres](https://github.com/jaredwray/keyv/tree/master/storage/postgres) | No
322
- MySQL | [@keyv/mysql](https://github.com/jaredwray/keyv/tree/master/storage/mysql) | No
323
- Etcd | [@keyv/etcd](https://github.com/jaredwray/keyv/tree/master/storage/etcd) | Yes
324
- Memcache | [@keyv/memcache](https://github.com/jaredwray/keyv/tree/master/storage/memcache) | Yes
325
- DynamoDB | [@keyv/dynamo](https://github.com/jaredwray/keyv/tree/master/storage/dynamo) | Yes
369
+ Redis | [@keyv/redis](https://github.com/jaredwray/keyv/tree/main/storage/redis) | Yes
370
+ Valkey | [@keyv/valkey](https://github.com/jaredwray/keyv/tree/main/storage/valkey) | Yes
371
+ MongoDB | [@keyv/mongo](https://github.com/jaredwray/keyv/tree/main/storage/mongo) | Yes
372
+ SQLite | [@keyv/sqlite](https://github.com/jaredwray/keyv/tree/main/storage/sqlite) | No
373
+ PostgreSQL | [@keyv/postgres](https://github.com/jaredwray/keyv/tree/main/storage/postgres) | No
374
+ MySQL | [@keyv/mysql](https://github.com/jaredwray/keyv/tree/main/storage/mysql) | No
375
+ Etcd | [@keyv/etcd](https://github.com/jaredwray/keyv/tree/main/storage/etcd) | Yes
376
+ Memcache | [@keyv/memcache](https://github.com/jaredwray/keyv/tree/main/storage/memcache) | Yes
377
+ DynamoDB | [@keyv/dynamo](https://github.com/jaredwray/keyv/tree/main/storage/dynamo) | Yes
326
378
 
327
379
  # Third-party Storage Adapters
328
380
 
@@ -411,7 +463,7 @@ await keyv.delete('user:1');
411
463
  await keyv.clear();
412
464
  ```
413
465
 
414
- For more details about BigMap, see the [@keyv/bigmap documentation](https://github.com/jaredwray/keyv/tree/main/storage/bigmap).
466
+ For more details about BigMap, see the [@keyv/bigmap documentation](https://github.com/jaredwray/keyv/tree/main/core/bigmap).
415
467
 
416
468
  # Compression
417
469
 
@@ -443,26 +495,145 @@ const keyv = new Keyv({ compression: keyvLz4 });
443
495
 
444
496
  You can also pass a custom compression function to the `compression` option. Following the pattern of the official compression adapters.
445
497
 
446
- ## Want to build your own CompressionAdapter?
498
+ ## Want to build your own KeyvCompressionAdapter?
447
499
 
448
500
  Great! Keyv is designed to be easily extended. You can build your own compression adapter by following the pattern of the official compression adapters based on this interface:
449
501
 
450
502
  ```typescript
451
- interface CompressionAdapter {
452
- async compress(value: any, options?: any);
453
- async decompress(value: any, options?: any);
454
- async serialize(value: any);
455
- async deserialize(value: any);
503
+ interface KeyvCompressionAdapter {
504
+ compress(value: any, options?: any): Promise<any>;
505
+ decompress(value: any, options?: any): Promise<any>;
456
506
  }
457
507
  ```
458
508
 
459
509
  In addition to the interface, you can test it with our compression test suite using @keyv/test-suite:
460
510
 
461
511
  ```js
462
- import { keyvCompresstionTests } from '@keyv/test-suite';
512
+ import { keyvCompressionTests } from '@keyv/test-suite';
463
513
  import KeyvGzip from '@keyv/compress-gzip';
464
514
 
465
- keyvCompresstionTests(test, new KeyvGzip());
515
+ keyvCompressionTests(test, new KeyvGzip());
516
+ ```
517
+
518
+ # Encryption
519
+
520
+ Keyv provides a `KeyvEncryptionAdapter` interface for encryption support. This interface is available for custom implementations but is not yet wired into the core pipeline.
521
+
522
+ ```typescript
523
+ interface KeyvEncryptionAdapter {
524
+ encrypt: (data: string) => string | Promise<string>;
525
+ decrypt: (data: string) => string | Promise<string>;
526
+ }
527
+ ```
528
+
529
+ # Capability Detection
530
+
531
+ Keyv exports helper functions to check whether an object implements the expected interface for a Keyv instance, storage adapter, compression adapter, serialization adapter, or encryption adapter. Each function returns an object with boolean flags for every capability, plus a top-level boolean indicating whether the object fully satisfies the interface.
532
+
533
+ ```ts
534
+ import {
535
+ detectKeyv,
536
+ detectKeyvStorage,
537
+ detectKeyvCompression,
538
+ detectKeyvSerialization,
539
+ detectKeyvEncryption,
540
+ detectCapabilities,
541
+ } from 'keyv';
542
+ ```
543
+
544
+ ## detectKeyv(obj)
545
+
546
+ Returns a `KeyvCapability` with a boolean for each Keyv method/property. The `keyv` flag is `true` only when **all** capabilities are present.
547
+
548
+ ```ts
549
+ import Keyv, { detectKeyv } from 'keyv';
550
+
551
+ detectKeyv(new Keyv());
552
+ // { keyv: true, get: true, set: true, delete: true, clear: true, has: true,
553
+ // getMany: true, setMany: true, deleteMany: true, hasMany: true,
554
+ // disconnect: true, getRaw: true, getManyRaw: true, setRaw: true,
555
+ // setManyRaw: true, hooks: true, stats: true, iterator: true }
556
+
557
+ detectKeyv(new Map());
558
+ // { keyv: false, get: true, set: true, ... }
559
+ ```
560
+
561
+ ## detectKeyvStorage(obj)
562
+
563
+ Returns a `KeyvStorageCapability`. The `keyvStorage` flag is `true` when the object has `get`, `set`, `delete`, `clear`, `has`, `setMany`, `deleteMany`, and `hasMany`.
564
+
565
+ The result also includes:
566
+ - **`mapLike`** — `true` when the object has synchronous `get`, `set`, `delete`, `has`, `entries`, and `keys` methods (i.e. it behaves like a `Map`)
567
+ - **`methodTypes`** — a record mapping each method name to `"sync"`, `"async"`, or `"none"` (not present)
568
+
569
+ ```ts
570
+ import { detectKeyvStorage } from 'keyv';
571
+
572
+ // Map-like object
573
+ const result = detectKeyvStorage(new Map());
574
+ result.mapLike; // true
575
+ result.methodTypes.get; // "sync"
576
+ result.methodTypes.set; // "sync"
577
+
578
+ // Async storage adapter
579
+ const adapter = {
580
+ get: async () => {}, set: async () => {}, delete: async () => {},
581
+ clear: async () => {}, has: async () => {}, setMany: async () => {},
582
+ deleteMany: async () => {}, hasMany: async () => {},
583
+ };
584
+ const adapterResult = detectKeyvStorage(adapter);
585
+ adapterResult.keyvStorage; // true
586
+ adapterResult.mapLike; // false
587
+ adapterResult.methodTypes.get; // "async"
588
+ ```
589
+
590
+ ## detectKeyvCompression(obj)
591
+
592
+ Returns a `KeyvCompressionCapability`. The `keyvCompression` flag is `true` when both `compress` and `decompress` methods are present.
593
+
594
+ ```ts
595
+ import { detectKeyvCompression } from 'keyv';
596
+
597
+ detectKeyvCompression({ compress: (d) => d, decompress: (d) => d });
598
+ // { keyvCompression: true, compress: true, decompress: true }
599
+ ```
600
+
601
+ ## detectKeyvSerialization(obj)
602
+
603
+ Returns a `KeyvSerializationCapability`. The `keyvSerialization` flag is `true` when both `stringify` and `parse` methods are present.
604
+
605
+ ```ts
606
+ import { detectKeyvSerialization } from 'keyv';
607
+
608
+ detectKeyvSerialization(JSON);
609
+ // { keyvSerialization: true, stringify: true, parse: true }
610
+ ```
611
+
612
+ ## detectKeyvEncryption(obj)
613
+
614
+ Returns a `KeyvEncryptionCapability`. The `keyvEncryption` flag is `true` when both `encrypt` and `decrypt` methods are present.
615
+
616
+ ```ts
617
+ import { detectKeyvEncryption } from 'keyv';
618
+
619
+ detectKeyvEncryption({ encrypt: (d) => d, decrypt: (d) => d });
620
+ // { keyvEncryption: true, encrypt: true, decrypt: true }
621
+ ```
622
+
623
+ ## detectCapabilities(obj, spec)
624
+
625
+ A generic helper for building your own capability checks. Accepts a `CapabilitySpec` describing which methods and properties to look for, which are required, and the name of the composite boolean key.
626
+
627
+ ```ts
628
+ import { detectCapabilities } from 'keyv';
629
+
630
+ const result = detectCapabilities(myObject, {
631
+ methods: ['read', 'write'],
632
+ properties: ['name'],
633
+ requiredKeys: ['read', 'write', 'name'],
634
+ compositeKey: 'isValid',
635
+ });
636
+ // { isValid: true/false, read: true/false, write: true/false, name: true/false }
466
637
  ```
467
638
 
468
639
  # API
@@ -478,16 +649,14 @@ The Keyv instance is also an `EventEmitter` that will emit an `'error'` event if
478
649
  Type: `KeyvStorageAdapter`<br />
479
650
  Default: `undefined`
480
651
 
481
- The connection string URI.
482
-
483
- Merged into the options object as options.uri.
652
+ The storage adapter instance to be used by Keyv.
484
653
 
485
654
  ## .namespace
486
655
 
487
656
  Type: `String`
488
- Default: `'keyv'`
657
+ Default: `undefined`
489
658
 
490
- This is the namespace for the current instance. When you set it it will set it also on the storage adapter. This is the preferred way to set the namespace over `.opts.namespace`.
659
+ This is the namespace for the current instance. When you set it it will set it also on the storage adapter.
491
660
 
492
661
  ## options
493
662
 
@@ -498,7 +667,7 @@ The options object is also passed through to the storage adapter. Check your sto
498
667
  ## options.namespace
499
668
 
500
669
  Type: `String`<br />
501
- Default: `'keyv'`
670
+ Default: `undefined`
502
671
 
503
672
  Namespace for the current instance.
504
673
 
@@ -511,24 +680,17 @@ Default TTL. Can be overridden by specififying a TTL on `.set()`.
511
680
 
512
681
  ## options.compression
513
682
 
514
- Type: `@keyv/compress-<compression_package_name>`<br />
683
+ Type: `KeyvCompressionAdapter`<br />
515
684
  Default: `undefined`
516
685
 
517
686
  Compression package to use. See [Compression](#compression) for more details.
518
687
 
519
- ## options.serialize
520
-
521
- Type: `Function`<br />
522
- Default: `JSON.stringify`
688
+ ## options.serialization
523
689
 
524
- A custom serialization function.
690
+ Type: `KeyvSerializationAdapter | false`<br />
691
+ Default: `KeyvJsonSerializer` (built-in)
525
692
 
526
- ## options.deserialize
527
-
528
- Type: `Function`<br />
529
- Default: `JSON.parse`
530
-
531
- A custom deserialization function.
693
+ A serialization object with `stringify` and `parse` methods. Set to `false` to disable serialization and store raw objects. See [Serialization](#serialization) for more details.
532
694
 
533
695
  ## options.store
534
696
 
@@ -551,7 +713,7 @@ Returns a promise which resolves to `true`.
551
713
 
552
714
  ## .setMany(entries)
553
715
 
554
- Set multiple values using KeyvEntrys `{ key: string, value: any, ttl?: number }`.
716
+ Set multiple values using `KeyvEntry<Value>` objects (`{ key: string, value: Value, ttl?: number }`). The `Value` type is inferred from the entries provided.
555
717
 
556
718
  ## .get(key, [options])
557
719
 
@@ -561,24 +723,50 @@ Returns a promise which resolves to the retrieved value.
561
723
 
562
724
  Returns a promise which resolves to an array of retrieved values.
563
725
 
564
- ### options.raw - (Will be deprecated in v6)
726
+ ## .getRaw(key)
565
727
 
566
- Type: `Boolean`<br />
567
- Default: `false`
728
+ Returns a promise which resolves to the raw stored data for the key or `undefined` if the key does not exist or is expired.
568
729
 
569
- If set to true the raw DB object Keyv stores internally will be returned instead of just the value.
730
+ ## .getManyRaw(keys)
570
731
 
571
- This contains the TTL timestamp.
732
+ Returns a promise which resolves to an array of raw stored data for the keys or `undefined` if the key does not exist or is expired.
572
733
 
573
- NOTE: This option will be deprecated in v6 and replaced with `.getRaw()` and `.getManyRaw()` methods.
734
+ ## .setRaw(key, value)
574
735
 
575
- ## .getRaw(key)
736
+ Sets a raw value in the store without wrapping. This is the write-side counterpart to `.getRaw()`. The caller provides the `KeyvValue` envelope directly (`{ value, expires? }`) instead of having Keyv wrap it. The envelope is still serialized before storing so that all read paths (`get()`, `getRaw()`, `has()`, `getManyRaw()`) work consistently. If you need TTL-based expiration, set `expires` on the value directly (e.g. `{ value: 'bar', expires: Date.now() + 60000 }`). The store-level TTL is derived automatically from `value.expires`.
576
737
 
577
- Returns a promise which resolves to the raw stored data for the key or `undefined` if the key does not exist or is expired.
738
+ Returns a promise which resolves to `true`.
578
739
 
579
- ## .getManyRaw(keys)
740
+ ```js
741
+ const keyv = new Keyv();
580
742
 
581
- Returns a promise which resolves to an array of raw stored data for the keys or `undefined` if the key does not exist or is expired.
743
+ // Set a raw value with expiration
744
+ await keyv.setRaw('foo', { value: 'bar', expires: Date.now() + 60000 });
745
+
746
+ // Set a raw value without expiration
747
+ await keyv.setRaw('foo', { value: 'bar' });
748
+
749
+ // Round-trip: get raw, modify, set raw
750
+ const raw = await keyv.getRaw('foo');
751
+ if (raw) {
752
+ raw.value = 'updated';
753
+ await keyv.setRaw('foo', raw);
754
+ }
755
+ ```
756
+
757
+ ## .setManyRaw(entries)
758
+
759
+ Sets many raw values in the store without wrapping. Each entry should have a `key` and a `value` (`KeyvValue` envelope). Like `setRaw()`, the envelopes are serialized before storing and the store-level TTL is derived from each entry's `value.expires`.
760
+
761
+ Returns a promise which resolves to an array of booleans.
762
+
763
+ ```js
764
+ const keyv = new Keyv();
765
+ await keyv.setManyRaw([
766
+ { key: 'foo', value: { value: 'bar' } },
767
+ { key: 'baz', value: { value: 'qux', expires: Date.now() + 60000 } },
768
+ ]);
769
+ ```
582
770
 
583
771
  ## .delete(key)
584
772
 
@@ -588,7 +776,7 @@ Returns a promise which resolves to `true` if the key existed, `false` if not.
588
776
 
589
777
  ## .deleteMany(keys)
590
778
  Deletes multiple entries.
591
- Returns a promise which resolves to an array of booleans indicating if the key existed or not.
779
+ Returns a promise which resolves to `true` if all keys were deleted successfully, `false` otherwise.
592
780
 
593
781
  ## .clear()
594
782
 
@@ -596,19 +784,56 @@ Delete all entries in the current namespace.
596
784
 
597
785
  Returns a promise which is resolved when the entries have been cleared.
598
786
 
787
+ ## .has(key)
788
+
789
+ Check if a key exists in the store.
790
+
791
+ Returns a promise which resolves to `true` if the key exists, `false` if not.
792
+
793
+ ```js
794
+ await keyv.set('foo', 'bar');
795
+ await keyv.has('foo'); // true
796
+ await keyv.has('unknown'); // false
797
+ ```
798
+
799
+ ## .hasMany(keys)
800
+
801
+ Check if multiple keys exist in the store.
802
+
803
+ Returns a promise which resolves to an array of booleans indicating if each key exists.
804
+
805
+ ```js
806
+ await keyv.set('foo', 'bar');
807
+ await keyv.hasMany(['foo', 'unknown']); // [true, false]
808
+ ```
809
+
810
+ ## .disconnect()
811
+
812
+ Disconnect from the storage adapter. Emits a `'disconnect'` event.
813
+
814
+ Returns a promise which is resolved when the connection has been closed.
815
+
816
+ ```js
817
+ await keyv.disconnect();
818
+ ```
819
+
599
820
  ## .iterator()
600
821
 
601
- Iterate over all entries of the current namespace.
822
+ Iterate over all key-value pairs in the store. Automatically deserializes values, filters out expired entries, and deletes them.
602
823
 
603
- Returns a iterable that can be iterated by for-of loops. For example:
824
+ Returns an async generator that yields `[key, value]` pairs. Use with `for await...of`:
604
825
 
605
826
  ```js
606
- // please note that the "await" keyword should be used here
607
- for await (const [key, value] of this.keyv.iterator()) {
827
+ for await (const [key, value] of keyv.iterator()) {
608
828
  console.log(key, value);
609
- };
829
+ }
610
830
  ```
611
831
 
832
+ The iterator works with any storage backend:
833
+ - **Map stores**: iterates using the built-in `Symbol.iterator`
834
+ - **Storage adapters**: delegates to the adapter's `iterator()` method (e.g., Redis SCAN, SQL cursor)
835
+ - **Unsupported stores**: emits an `error` event if the store does not support iteration
836
+
612
837
  # API - Properties
613
838
 
614
839
  ## .namespace
@@ -626,7 +851,7 @@ here is an example of setting the namespace to `undefined`:
626
851
 
627
852
  ```js
628
853
  const keyv = new Keyv();
629
- console.log(keyv.namespace); // 'keyv' which is default
854
+ console.log(keyv.namespace); // undefined which is default
630
855
  keyv.namespace = undefined;
631
856
  console.log(keyv.namespace); // undefined
632
857
  ```
@@ -660,40 +885,26 @@ keyv.store = new KeyvSqlite('sqlite://path/to/database.sqlite');
660
885
  console.log(keyv.store instanceof KeyvSqlite); // true
661
886
  ```
662
887
 
663
- ## .serialize
664
-
665
- Type: `Function`<br />
666
- Default: `JSON.stringify`
667
-
668
- A custom serialization function used for any value.
669
-
670
- ```js
671
- const keyv = new Keyv();
672
- console.log(keyv.serialize); // JSON.stringify
673
- keyv.serialize = value => value.toString();
674
- console.log(keyv.serialize); // value => value.toString()
675
- ```
676
-
677
- ## .deserialize
888
+ ## .serialization
678
889
 
679
- Type: `Function`<br />
680
- Default: `JSON.parse`
890
+ Type: `KeyvSerializationAdapter | false | undefined`<br />
891
+ Default: `KeyvJsonSerializer` (built-in)
681
892
 
682
- A custom deserialization function used for any value.
893
+ The serialization object used for storing and retrieving values. Set to `false` or `undefined` to disable serialization and use raw object pass-through. See [Serialization](#serialization) for more details.
683
894
 
684
895
  ```js
685
896
  const keyv = new Keyv();
686
- console.log(keyv.deserialize); // JSON.parse
687
- keyv.deserialize = value => parseInt(value);
688
- console.log(keyv.deserialize); // value => parseInt(value)
897
+ console.log(keyv.serialization); // KeyvJsonSerializer (default)
898
+ keyv.serialization = false; // disable serialization
899
+ console.log(keyv.serialization); // undefined
689
900
  ```
690
901
 
691
902
  ## .compression
692
903
 
693
- Type: `CompressionAdapter`<br />
904
+ Type: `KeyvCompressionAdapter`<br />
694
905
  Default: `undefined`
695
906
 
696
- this is the compression package to use. See [Compression](#compression) for more details. If it is undefined it will not compress (default).
907
+ This is the compression package to use. See [Compression](#compression) for more details. If it is undefined it will not compress (default).
697
908
 
698
909
  ```js
699
910
  import KeyvGzip from '@keyv/compress-gzip';
@@ -734,6 +945,20 @@ await keyv.get('foo'); // 'bar'
734
945
  await keyv.clear();
735
946
  ```
736
947
 
948
+ ## .emitErrors
949
+
950
+ Type: `Boolean`<br />
951
+ Default: `true`
952
+
953
+ If set to `true`, Keyv will emit an `'error'` event when an error occurs. Set to `false` to suppress error events.
954
+
955
+ ```js
956
+ const keyv = new Keyv({ emitErrors: false });
957
+ console.log(keyv.emitErrors); // false
958
+ keyv.emitErrors = true;
959
+ console.log(keyv.emitErrors); // true
960
+ ```
961
+
737
962
  ## .throwOnErrors
738
963
 
739
964
  Type: `Boolean`<br />
@@ -763,10 +988,10 @@ const keyv = new Keyv({ store: keyvRedis, throwOnErrors: true });
763
988
  What this does is it only throw on connection errors with the Redis client.
764
989
 
765
990
  ## .stats
766
- Type: `StatsManager`<br />
767
- Default: `StatsManager` instance with `enabled: false`
991
+ Type: `KeyvStats`<br />
992
+ Default: `KeyvStats` instance with `enabled: false`
768
993
 
769
- The stats property provides access to statistics tracking for cache operations. When enabled via the `stats` option during initialization, it tracks hits, misses, sets, deletes, and errors.
994
+ The stats property provides access to statistics tracking for cache operations. When enabled via the `stats` option during initialization, it tracks hits, misses, sets, deletes, and errors. It also maintains LRU-bounded per-key frequency maps for each event type, allowing you to see which keys are accessed most.
770
995
 
771
996
  ### Enabling Stats:
772
997
  ```js
@@ -775,12 +1000,21 @@ console.log(keyv.stats.enabled); // true
775
1000
  ```
776
1001
 
777
1002
  ### Available Statistics:
1003
+
1004
+ **Aggregate counters:**
778
1005
  - `hits`: Number of successful cache retrievals
779
1006
  - `misses`: Number of failed cache retrievals
780
1007
  - `sets`: Number of set operations
781
1008
  - `deletes`: Number of delete operations
782
1009
  - `errors`: Number of errors encountered
783
1010
 
1011
+ **Per-key LRU frequency maps** (each capped at `maxEntries`, default 1000):
1012
+ - `hitKeys`: `Map<string, number>` — key to hit count
1013
+ - `missKeys`: `Map<string, number>` — key to miss count
1014
+ - `setKeys`: `Map<string, number>` — key to set count
1015
+ - `deleteKeys`: `Map<string, number>` — key to delete count
1016
+ - `errorKeys`: `Map<string, number>` — key to error count
1017
+
784
1018
  ### Accessing Stats:
785
1019
  ```js
786
1020
  const keyv = new Keyv({ stats: true });
@@ -794,23 +1028,104 @@ console.log(keyv.stats.hits); // 1
794
1028
  console.log(keyv.stats.misses); // 1
795
1029
  console.log(keyv.stats.sets); // 1
796
1030
  console.log(keyv.stats.deletes); // 1
1031
+
1032
+ // Per-key frequency maps
1033
+ console.log(keyv.stats.hitKeys.get('foo')); // 1
1034
+ console.log(keyv.stats.missKeys.get('nonexistent')); // 1
797
1035
  ```
798
1036
 
799
1037
  ### Resetting Stats:
800
1038
  ```js
801
1039
  keyv.stats.reset();
802
1040
  console.log(keyv.stats.hits); // 0
1041
+ console.log(keyv.stats.hitKeys.size); // 0
803
1042
  ```
804
1043
 
805
1044
  ### Manual Control:
806
- You can also manually enable/disable stats tracking at runtime:
1045
+ You can also manually enable/disable stats tracking at runtime. Disabling stats will automatically unsubscribe from events:
807
1046
  ```js
808
1047
  const keyv = new Keyv({ stats: false });
809
1048
  keyv.stats.enabled = true; // Enable stats tracking
810
1049
  // ... perform operations ...
811
- keyv.stats.enabled = false; // Disable stats tracking
1050
+ keyv.stats.enabled = false; // Disable stats tracking and unsubscribe
812
1051
  ```
813
1052
 
1053
+ ### Standalone Usage:
1054
+ You can create a `KeyvStats` instance independently and subscribe it to a Keyv instance:
1055
+ ```js
1056
+ import { KeyvStats } from 'keyv';
1057
+
1058
+ const stats = new KeyvStats({ enabled: true, maxEntries: 500, emitter: keyv });
1059
+ ```
1060
+
1061
+ ## .sanitize
1062
+ Type: `boolean | KeyvSanitizeOptions`<br />
1063
+ Default: `false`
1064
+
1065
+ Detects and strips dangerous patterns from keys and namespaces to protect against SQL injection, MongoDB operator injection, path traversal, and control character attacks. Harmless characters like quotes, slashes, and dollar signs pass through unchanged — only dangerous *patterns* are stripped.
1066
+
1067
+ Results are cached in an LRU cache (10,000 entries) for fast repeated lookups.
1068
+
1069
+ ### Pattern Categories
1070
+
1071
+ | Category | Patterns Stripped | Purpose |
1072
+ |----------|------------------|---------|
1073
+ | `sql` | `;` `--` `/*` | Prevents SQL injection |
1074
+ | `mongo` | leading `$`, `{$` sequences | Prevents MongoDB operator injection |
1075
+ | `escape` | `\0` `\r` `\n` | Strips null bytes, CRLF injection |
1076
+ | `path` | `../` `..\` | Prevents path traversal |
1077
+
1078
+ ### Targets
1079
+
1080
+ | Target | Default | Description |
1081
+ |--------|---------|-------------|
1082
+ | `keys` | `true` (when enabled) | Sanitize keys on all operations |
1083
+ | `namespace` | `true` (when enabled) | Sanitize namespace on construction and setter |
1084
+
1085
+ ### Usage
1086
+
1087
+ Enable all sanitization:
1088
+ ```js
1089
+ const keyv = new Keyv({ sanitize: true });
1090
+ await keyv.set("test; DROP TABLE", "value");
1091
+ // Key is stored as "test DROP TABLE"
1092
+
1093
+ // Harmless characters pass through
1094
+ await keyv.set("user's-data", "value");
1095
+ // Key is stored as "user's-data" (unchanged)
1096
+ ```
1097
+
1098
+ Disable all sanitization (default):
1099
+ ```js
1100
+ const keyv = new Keyv({ sanitize: false });
1101
+ ```
1102
+
1103
+ Granular control per target and category:
1104
+ ```js
1105
+ const keyv = new Keyv({
1106
+ sanitize: {
1107
+ keys: { sql: true, mongo: false }, // only SQL patterns on keys
1108
+ namespace: { path: true, sql: false }, // only path patterns on namespace
1109
+ }
1110
+ });
1111
+ ```
1112
+
1113
+ Disable namespace sanitization only:
1114
+ ```js
1115
+ const keyv = new Keyv({
1116
+ sanitize: { keys: true, namespace: false }
1117
+ });
1118
+ ```
1119
+
1120
+ Change at runtime:
1121
+ ```js
1122
+ keyv.sanitize = false; // disable
1123
+ keyv.sanitize = true; // enable all
1124
+ keyv.sanitize = { keys: { sql: true, mongo: false } }; // granular
1125
+ ```
1126
+
1127
+ Sanitization is applied to all key-accepting methods: `get`, `set`, `delete`, `has`, `getMany`, `setMany`, `deleteMany`, `hasMany`, `getRaw`, `getManyRaw`, `setRaw`, and `setManyRaw`. Namespace sanitization is applied at construction and when the `namespace` setter is used.
1128
+
814
1129
  # Bun Support
815
1130
 
816
1131
  We make a best effort to support [Bun](https://bun.sh/) as a runtime. Our default and primary target is Node.js, but we run tests against Bun to ensure compatibility. If you encounter any issues while using Keyv with Bun, please report them at our [GitHub issues](https://github.com/jaredwray/keyv/issues).