keyv 6.0.0-alpha.3 → 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/LICENSE CHANGED
@@ -1,7 +1,7 @@
1
1
  MIT License
2
2
 
3
3
  Copyright (c) 2017-2021 Luke Childs
4
- Copyright (c) 2021-2022 Jared Wray
4
+ Copyright (c) 2021-2026 Jared Wray
5
5
 
6
6
  Permission is hereby granted, free of charge, to any person obtaining a copy
7
7
  of this software and associated documentation files (the "Software"), to deal
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)
@@ -37,6 +38,7 @@ There are a few existing modules similar to Keyv, however Keyv is different beca
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)
@@ -48,6 +50,7 @@ There are a few existing modules similar to Keyv, however Keyv is different beca
48
50
  - [.emitErrors](#emiterrors)
49
51
  - [.throwOnErrors](#throwonerrors)
50
52
  - [.stats](#stats)
53
+ - [.sanitize](#sanitize)
51
54
  - [Keyv Instance](#keyv-instance)
52
55
  - [.set(key, value, [ttl])](#setkey-value-ttl)
53
56
  - [.setMany(entries)](#setmanyentries)
@@ -55,7 +58,7 @@ There are a few existing modules similar to Keyv, however Keyv is different beca
55
58
  - [.getMany(keys, [options])](#getmanykeys-options)
56
59
  - [.getRaw(key)](#getrawkey)
57
60
  - [.getManyRaw(keys)](#getmanyrawkeys)
58
- - [.setRaw(key, value, [ttl])](#setrawkey-value-ttl)
61
+ - [.setRaw(key, value)](#setrawkey-value)
59
62
  - [.setManyRaw(entries)](#setmanyrawentries)
60
63
  - [.delete(key)](#deletekey)
61
64
  - [.deleteMany(keys)](#deletemanykeys)
@@ -302,37 +305,42 @@ In `PRE_DELETE` and `POST_DELETE` hooks, the value could be a single item or an
302
305
 
303
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.
304
307
 
305
- ## Custom Serializers
308
+ ## Official Serializers
306
309
 
307
- You can provide your own serializer by implementing the `KeyvSerializationAdapter` interface:
310
+ In addition to the built-in serializer, Keyv offers two official serialization packages:
308
311
 
309
- ```typescript
310
- interface KeyvSerializationAdapter {
311
- stringify: (object: unknown) => string | Promise<string>;
312
- parse: <T>(data: string) => T | Promise<T>;
313
- }
314
- ```
312
+ ### SuperJSON
315
313
 
316
- For example, using the built-in `JSON` object:
314
+ [`@keyv/serialize-superjson`](https://github.com/jaredwray/keyv/tree/main/serialization/superjson) supports `Date`, `RegExp`, `Map`, `Set`, `BigInt`, `undefined`, `Error`, and `URL` types.
317
315
 
318
316
  ```js
319
- const keyv = new Keyv({
320
- serialization: { stringify: JSON.stringify, parse: JSON.parse },
321
- });
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 });
322
321
  ```
323
322
 
324
- Or a custom async serializer:
323
+ ### MessagePack (msgpackr)
324
+
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.
325
326
 
326
327
  ```js
327
- const keyv = new Keyv({
328
- serialization: {
329
- stringify: async (value) => JSON.stringify(value),
330
- parse: async (data) => JSON.parse(data),
331
- },
332
- });
328
+ import Keyv from 'keyv';
329
+ import { KeyvMsgpackrSerializer } from '@keyv/serialize-msgpackr';
330
+
331
+ const keyv = new Keyv({ serialization: new KeyvMsgpackrSerializer() });
333
332
  ```
334
333
 
335
- **Warning:** Using custom serializers means you lose any guarantee of data consistency. You should do extensive testing with your serialization functions and chosen storage engine.
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
+ ```
336
344
 
337
345
  ## Disabling Serialization
338
346
 
@@ -358,15 +366,15 @@ The official storage adapters are covered by [over 150 integration tests](https:
358
366
 
359
367
  Database | Adapter | Native TTL
360
368
  ---|---|---
361
- Redis | [@keyv/redis](https://github.com/jaredwray/keyv/tree/master/storage/redis) | Yes
362
- Valkey | [@keyv/valkey](https://github.com/jaredwray/keyv/tree/master/storage/valkey) | Yes
363
- MongoDB | [@keyv/mongo](https://github.com/jaredwray/keyv/tree/master/storage/mongo) | Yes
364
- SQLite | [@keyv/sqlite](https://github.com/jaredwray/keyv/tree/master/storage/sqlite) | No
365
- PostgreSQL | [@keyv/postgres](https://github.com/jaredwray/keyv/tree/master/storage/postgres) | No
366
- MySQL | [@keyv/mysql](https://github.com/jaredwray/keyv/tree/master/storage/mysql) | No
367
- Etcd | [@keyv/etcd](https://github.com/jaredwray/keyv/tree/master/storage/etcd) | Yes
368
- Memcache | [@keyv/memcache](https://github.com/jaredwray/keyv/tree/master/storage/memcache) | Yes
369
- 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
370
378
 
371
379
  # Third-party Storage Adapters
372
380
 
@@ -501,10 +509,10 @@ interface KeyvCompressionAdapter {
501
509
  In addition to the interface, you can test it with our compression test suite using @keyv/test-suite:
502
510
 
503
511
  ```js
504
- import { keyvCompresstionTests } from '@keyv/test-suite';
512
+ import { keyvCompressionTests } from '@keyv/test-suite';
505
513
  import KeyvGzip from '@keyv/compress-gzip';
506
514
 
507
- keyvCompresstionTests(test, new KeyvGzip());
515
+ keyvCompressionTests(test, new KeyvGzip());
508
516
  ```
509
517
 
510
518
  # Encryption
@@ -518,6 +526,116 @@ interface KeyvEncryptionAdapter {
518
526
  }
519
527
  ```
520
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 }
637
+ ```
638
+
521
639
  # API
522
640
 
523
641
  ## new Keyv([storage-adapter], [options]) or new Keyv([options])
@@ -536,9 +654,9 @@ The storage adapter instance to be used by Keyv.
536
654
  ## .namespace
537
655
 
538
656
  Type: `String`
539
- Default: `'keyv'`
657
+ Default: `undefined`
540
658
 
541
- 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.
542
660
 
543
661
  ## options
544
662
 
@@ -549,7 +667,7 @@ The options object is also passed through to the storage adapter. Check your sto
549
667
  ## options.namespace
550
668
 
551
669
  Type: `String`<br />
552
- Default: `'keyv'`
670
+ Default: `undefined`
553
671
 
554
672
  Namespace for the current instance.
555
673
 
@@ -595,7 +713,7 @@ Returns a promise which resolves to `true`.
595
713
 
596
714
  ## .setMany(entries)
597
715
 
598
- 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.
599
717
 
600
718
  ## .get(key, [options])
601
719
 
@@ -613,30 +731,32 @@ Returns a promise which resolves to the raw stored data for the key or `undefine
613
731
 
614
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.
615
733
 
616
- ## .setRaw(key, value, [ttl])
734
+ ## .setRaw(key, value)
617
735
 
618
- Sets a raw value in the store without wrapping. This is the write-side counterpart to `.getRaw()`. The caller provides the `DeserializedData` 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 `expires` is not set in the value and `ttl` is provided, `expires` will be computed from `ttl`.
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`.
619
737
 
620
738
  Returns a promise which resolves to `true`.
621
739
 
622
740
  ```js
623
741
  const keyv = new Keyv();
624
742
 
625
- // Set a raw value directly
743
+ // Set a raw value with expiration
626
744
  await keyv.setRaw('foo', { value: 'bar', expires: Date.now() + 60000 });
627
745
 
746
+ // Set a raw value without expiration
747
+ await keyv.setRaw('foo', { value: 'bar' });
748
+
628
749
  // Round-trip: get raw, modify, set raw
629
750
  const raw = await keyv.getRaw('foo');
630
- raw.value = 'updated';
631
- await keyv.setRaw('foo', raw);
632
-
633
- // TTL computes expires automatically if not set
634
- await keyv.setRaw('foo', { value: 'bar' }, 60000);
751
+ if (raw) {
752
+ raw.value = 'updated';
753
+ await keyv.setRaw('foo', raw);
754
+ }
635
755
  ```
636
756
 
637
757
  ## .setManyRaw(entries)
638
758
 
639
- Sets many raw values in the store without wrapping. Each entry should have a `key`, a `value` (`DeserializedData` envelope), and an optional `ttl`. Like `setRaw()`, the envelopes are serialized before storing.
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`.
640
760
 
641
761
  Returns a promise which resolves to an array of booleans.
642
762
 
@@ -699,17 +819,21 @@ await keyv.disconnect();
699
819
 
700
820
  ## .iterator()
701
821
 
702
- 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.
703
823
 
704
- 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`:
705
825
 
706
826
  ```js
707
- // please note that the "await" keyword should be used here
708
- for await (const [key, value] of this.keyv.iterator()) {
827
+ for await (const [key, value] of keyv.iterator()) {
709
828
  console.log(key, value);
710
- };
829
+ }
711
830
  ```
712
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
+
713
837
  # API - Properties
714
838
 
715
839
  ## .namespace
@@ -727,7 +851,7 @@ here is an example of setting the namespace to `undefined`:
727
851
 
728
852
  ```js
729
853
  const keyv = new Keyv();
730
- console.log(keyv.namespace); // 'keyv' which is default
854
+ console.log(keyv.namespace); // undefined which is default
731
855
  keyv.namespace = undefined;
732
856
  console.log(keyv.namespace); // undefined
733
857
  ```
@@ -864,10 +988,10 @@ const keyv = new Keyv({ store: keyvRedis, throwOnErrors: true });
864
988
  What this does is it only throw on connection errors with the Redis client.
865
989
 
866
990
  ## .stats
867
- Type: `StatsManager`<br />
868
- Default: `StatsManager` instance with `enabled: false`
991
+ Type: `KeyvStats`<br />
992
+ Default: `KeyvStats` instance with `enabled: false`
869
993
 
870
- 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.
871
995
 
872
996
  ### Enabling Stats:
873
997
  ```js
@@ -876,12 +1000,21 @@ console.log(keyv.stats.enabled); // true
876
1000
  ```
877
1001
 
878
1002
  ### Available Statistics:
1003
+
1004
+ **Aggregate counters:**
879
1005
  - `hits`: Number of successful cache retrievals
880
1006
  - `misses`: Number of failed cache retrievals
881
1007
  - `sets`: Number of set operations
882
1008
  - `deletes`: Number of delete operations
883
1009
  - `errors`: Number of errors encountered
884
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
+
885
1018
  ### Accessing Stats:
886
1019
  ```js
887
1020
  const keyv = new Keyv({ stats: true });
@@ -895,23 +1028,104 @@ console.log(keyv.stats.hits); // 1
895
1028
  console.log(keyv.stats.misses); // 1
896
1029
  console.log(keyv.stats.sets); // 1
897
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
898
1035
  ```
899
1036
 
900
1037
  ### Resetting Stats:
901
1038
  ```js
902
1039
  keyv.stats.reset();
903
1040
  console.log(keyv.stats.hits); // 0
1041
+ console.log(keyv.stats.hitKeys.size); // 0
904
1042
  ```
905
1043
 
906
1044
  ### Manual Control:
907
- 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:
908
1046
  ```js
909
1047
  const keyv = new Keyv({ stats: false });
910
1048
  keyv.stats.enabled = true; // Enable stats tracking
911
1049
  // ... perform operations ...
912
- keyv.stats.enabled = false; // Disable stats tracking
1050
+ keyv.stats.enabled = false; // Disable stats tracking and unsubscribe
913
1051
  ```
914
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
+
915
1129
  # Bun Support
916
1130
 
917
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).