keyv 6.0.0-beta.3 → 6.0.0-beta.4

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
@@ -3,16 +3,6 @@ MIT License
3
3
  Copyright (c) 2017-2021 Luke Childs
4
4
  Copyright (c) 2021-2026 Jared Wray
5
5
 
6
- Permission is hereby granted, free of charge, to any person obtaining a copy
7
- of this software and associated documentation files (the "Software"), to deal
8
- in the Software without restriction, including without limitation the rights
9
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
- copies of the Software, and to permit persons to whom the Software is
11
- furnished to do so, subject to the following conditions:
12
-
13
- The above copyright notice and this permission notice shall be included in all
14
- copies or substantial portions of the Software.
15
-
16
6
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
7
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
8
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
package/README.md CHANGED
@@ -5,10 +5,11 @@
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
7
  [![browser](https://github.com/jaredwray/keyv/actions/workflows/browser-compat.yaml/badge.svg)](https://github.com/jaredwray/keyv/actions/workflows/browser-compat.yaml)
8
- [![codecov](https://codecov.io/gh/jaredwray/keyv/branch/main/graph/badge.svg?token=bRzR3RyOXZ)](https://codecov.io/gh/jaredwray/keyv)
8
+ [![](https://data.jsdelivr.com/v1/package/npm/keyv/badge)](https://www.jsdelivr.com/package/npm/keyv)
9
+ [![codecov](https://codecov.io/gh/jaredwray/keyv/graph/badge.svg?token=bRzR3RyOXZ)](https://codecov.io/gh/jaredwray/keyv)
9
10
  [![npm](https://img.shields.io/npm/dm/keyv.svg)](https://www.npmjs.com/package/keyv)
10
11
  [![npm](https://img.shields.io/npm/v/keyv.svg)](https://www.npmjs.com/package/keyv)
11
-
12
+
12
13
  Keyv provides a consistent interface for key-value storage across multiple backends via storage adapters. It supports TTL based expiry, making it suitable as a cache or a persistent key-value store.
13
14
 
14
15
  # Features
@@ -45,17 +46,17 @@ There are a few existing modules similar to Keyv, however Keyv is different beca
45
46
  - [.ttl](#ttl)
46
47
  - [.store](#store)
47
48
  - [.serialization](#serialization-1)
48
- - [.compression](#compression)
49
- - [.useKeyPrefix](#usekeyprefix)
50
- - [.emitErrors](#emiterrors)
49
+ - [.compression](#compression-1)
50
+ - [.encryption](#encryption-1)
51
51
  - [.throwOnErrors](#throwonerrors)
52
+ - [.checkExpired](#checkexpired)
52
53
  - [.stats](#stats)
53
54
  - [.sanitize](#sanitize)
54
55
  - [Keyv Instance](#keyv-instance)
55
56
  - [.set(key, value, [ttl])](#setkey-value-ttl)
56
57
  - [.setMany(entries)](#setmanyentries)
57
- - [.get(key, [options])](#getkey-options)
58
- - [.getMany(keys, [options])](#getmanykeys-options)
58
+ - [.get(key)](#getkey)
59
+ - [.getMany(keys)](#getmanykeys)
59
60
  - [.getRaw(key)](#getrawkey)
60
61
  - [.getManyRaw(keys)](#getmanyrawkeys)
61
62
  - [.setRaw(key, value)](#setrawkey-value)
@@ -192,12 +193,11 @@ await cache.get('foo'); // 'cache'
192
193
 
193
194
  # Events
194
195
 
195
- Keyv is a custom `EventEmitter` and will emit an `'error'` event if there is an error.
196
- If there is no listener for the `'error'` event, an uncaught exception will be thrown.
197
- To disable the `'error'` event, pass `emitErrors: false` in the constructor options.
196
+ Keyv is an `EventEmitter` (built on [hookified](https://github.com/jaredwray/hookified)) and will emit an `'error'` event if there is an error. By default an error is only thrown if there are no listeners attached to the `'error'` event. To always throw on errors regardless of listeners, enable the [`throwOnErrors`](#throwonerrors) option.
198
197
 
199
198
  ```js
200
- const keyv = new Keyv({ emitErrors: false });
199
+ const keyv = new Keyv();
200
+ keyv.on('error', err => console.log('Connection Error', err));
201
201
  ```
202
202
 
203
203
  In addition it will emit `clear` and `disconnect` events when the corresponding methods are called.
@@ -215,41 +215,46 @@ keyv.on('disconnect', handleDisconnect);
215
215
 
216
216
  # Hooks
217
217
 
218
- Keyv supports hooks for `get`, `set`, and `delete` methods. Hooks are useful for logging, debugging, and other custom functionality. Here is a list of all the hooks:
218
+ Keyv supports hooks for all of its operations. Hooks are useful for logging, debugging, and other custom functionality. Each operation fires a `BEFORE_*` hook before it runs and an `AFTER_*` hook after it completes. Here is the list of all the hooks:
219
219
 
220
220
  ```
221
- PRE_GET
222
- POST_GET
223
- PRE_GET_RAW
224
- POST_GET_RAW
225
- PRE_GET_MANY
226
- POST_GET_MANY
227
- PRE_GET_MANY_RAW
228
- POST_GET_MANY_RAW
229
- PRE_SET
230
- POST_SET
231
- PRE_SET_RAW
232
- POST_SET_RAW
233
- PRE_SET_MANY_RAW
234
- POST_SET_MANY_RAW
235
- PRE_DELETE
236
- POST_DELETE
221
+ BEFORE_GET / AFTER_GET
222
+ BEFORE_GET_MANY / AFTER_GET_MANY
223
+ BEFORE_GET_RAW / AFTER_GET_RAW
224
+ BEFORE_GET_MANY_RAW / AFTER_GET_MANY_RAW
225
+ BEFORE_SET / AFTER_SET
226
+ BEFORE_SET_RAW / AFTER_SET_RAW
227
+ BEFORE_SET_MANY / AFTER_SET_MANY
228
+ BEFORE_SET_MANY_RAW / AFTER_SET_MANY_RAW
229
+ BEFORE_DELETE / AFTER_DELETE
230
+ BEFORE_DELETE_MANY / AFTER_DELETE_MANY
231
+ BEFORE_HAS / AFTER_HAS
232
+ BEFORE_HAS_MANY / AFTER_HAS_MANY
233
+ BEFORE_CLEAR / AFTER_CLEAR
234
+ BEFORE_DISCONNECT / AFTER_DISCONNECT
237
235
  ```
238
236
 
239
- You can access this by importing `KeyvHooks` from the main Keyv package.
237
+ > The older `PRE_*` / `POST_*` hook names (e.g. `PRE_GET`, `POST_SET`) are deprecated aliases that still fire for backward compatibility. Prefer the `BEFORE_*` / `AFTER_*` names going forward.
238
+
239
+ You can access these by importing `KeyvHooks` from the main Keyv package and registering a handler with `onHook()`:
240
240
 
241
241
  ```js
242
242
  import Keyv, { KeyvHooks } from 'keyv';
243
+
244
+ const keyv = new Keyv();
245
+ keyv.onHook(KeyvHooks.BEFORE_SET, (data) => {
246
+ console.log(`Setting key ${data.key} to ${data.value}`);
247
+ });
243
248
  ```
244
249
 
245
250
  ## Get Hooks
246
251
 
247
- The `POST_GET` and `POST_GET_RAW` hooks fire on both cache hits and misses. When a cache miss occurs (key doesn't exist or is expired), the hooks receive `undefined` as the value.
252
+ The `AFTER_GET` and `AFTER_GET_RAW` hooks fire on both cache hits and misses. When a cache miss occurs (key doesn't exist or is expired), the hook receives `undefined` as the value.
248
253
 
249
254
  ```js
250
- // POST_GET hook - fires on both hits and misses
255
+ // AFTER_GET hook - fires on both hits and misses
251
256
  const keyv = new Keyv();
252
- keyv.hooks.addHandler(KeyvHooks.POST_GET, (data) => {
257
+ keyv.onHook(KeyvHooks.AFTER_GET, (data) => {
253
258
  if (data.value === undefined) {
254
259
  console.log(`Cache miss for key: ${data.key}`);
255
260
  } else {
@@ -262,9 +267,9 @@ await keyv.get('missing-key'); // Logs cache miss with undefined
262
267
  ```
263
268
 
264
269
  ```js
265
- // POST_GET_RAW hook - same behavior as POST_GET
270
+ // AFTER_GET_RAW hook - same behavior as AFTER_GET
266
271
  const keyv = new Keyv();
267
- keyv.hooks.addHandler(KeyvHooks.POST_GET_RAW, (data) => {
272
+ keyv.onHook(KeyvHooks.AFTER_GET_RAW, (data) => {
268
273
  console.log(`Key: ${data.key}, Value:`, data.value);
269
274
  });
270
275
 
@@ -274,31 +279,31 @@ await keyv.getRaw('foo'); // Logs with value or undefined
274
279
  ## Set Hooks
275
280
 
276
281
  ```js
277
- //PRE_SET hook
282
+ // BEFORE_SET hook
278
283
  const keyv = new Keyv();
279
- keyv.hooks.addHandler(KeyvHooks.PRE_SET, (data) => console.log(`Setting key ${data.key} to ${data.value}`));
284
+ keyv.onHook(KeyvHooks.BEFORE_SET, (data) => console.log(`Setting key ${data.key} to ${data.value}`));
280
285
 
281
- //POST_SET hook
286
+ // AFTER_SET hook
282
287
  const keyv = new Keyv();
283
- keyv.hooks.addHandler(KeyvHooks.POST_SET, ({key, value}) => console.log(`Set key ${key} to ${value}`));
288
+ keyv.onHook(KeyvHooks.AFTER_SET, ({ key, value }) => console.log(`Set key ${key} to ${value}`));
284
289
  ```
285
290
 
286
- In these examples you can also manipulate the value before it is set. For example, you could add a prefix to all keys.
291
+ In the `BEFORE_SET` hook you can also manipulate the value before it is set. For example, you could add a prefix to all keys.
287
292
 
288
293
  ```js
289
294
  const keyv = new Keyv();
290
- keyv.hooks.addHandler(KeyvHooks.PRE_SET, (data) => {
295
+ keyv.onHook(KeyvHooks.BEFORE_SET, (data) => {
291
296
  console.log(`Manipulating key ${data.key} and ${data.value}`);
292
297
  data.key = `prefix-${data.key}`;
293
298
  data.value = `prefix-${data.value}`;
294
299
  });
295
300
  ```
296
301
 
297
- Now this key will have prefix- added to it before it is set.
302
+ Now this key will have `prefix-` added to it before it is set.
298
303
 
299
304
  ## Delete Hooks
300
305
 
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.
306
+ In the `BEFORE_DELETE` and `AFTER_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.
302
307
 
303
308
 
304
309
  # Serialization
@@ -352,13 +357,13 @@ const keyv = new Keyv({ serialization: false });
352
357
 
353
358
  ## Pipeline
354
359
 
355
- When serialization and/or compression are configured, Keyv applies them in this order:
360
+ When serialization, compression, and/or encryption are configured, Keyv applies them in this order:
356
361
 
357
- **On set:** serialize (optional) → compress (optional) → store
362
+ **On set:** serialize → compress (optional) → encrypt (optional) → store
358
363
 
359
- **On get:** store → decompress (optional) → parse (optional) → value
364
+ **On get:** store → decrypt (optional) → decompress (optional) → parse → value
360
365
 
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.
366
+ Compression and encryption operate on the serialized string, so they only run when a serializer is configured. The built-in `KeyvJsonSerializer` is enabled by default, so this works out of the box. If you disable serialization with `serialization: false`, values are passed through to the store as-is and compression/encryption are skipped.
362
367
 
363
368
  # Official Storage Adapters
364
369
 
@@ -407,6 +412,34 @@ const keyv = new Keyv({ store: lru });
407
412
 
408
413
  View the complete list of third-party storage adapters and learn how to build your own at https://keyv.org/docs/third-party-storage-adapters/
409
414
 
415
+ ## Storage Adapter Contract (v6)
416
+
417
+ > The public API above is unchanged — `keyv.set(key, value, ttl)` still takes a relative TTL in milliseconds. The change below only affects authors of custom **storage adapters**.
418
+
419
+ As of v6, Keyv passes an **absolute `expires`** timestamp (Unix ms since epoch) to a storage adapter's write methods instead of a relative TTL. Keyv computes `expires` once, so adapters never need to derive or parse it:
420
+
421
+ ```ts
422
+ import { keyvStorageCapability } from 'keyv';
423
+
424
+ type KeyvStorageEntry<Value> = { key: string; value: Value; expires?: number };
425
+
426
+ class MyAdapter {
427
+ // Declare support for the absolute-`expires` contract:
428
+ get capabilities() {
429
+ return keyvStorageCapability(this); // -> { ...detected, expires: true }
430
+ }
431
+
432
+ // `expires` is absolute Unix ms; `undefined` means no expiry; `<= Date.now()` means already expired.
433
+ async set(key: string, value: unknown, expires?: number): Promise<boolean> { /* ... */ }
434
+ async setMany<Value>(entries: KeyvStorageEntry<Value>[]): Promise<boolean[] | undefined> { /* ... */ }
435
+ // ...get, delete, clear, has, getMany, deleteMany, hasMany, etc.
436
+ }
437
+ ```
438
+
439
+ A v6 adapter declares `capabilities.expires === true` (the `keyvStorageCapability(this)` helper sets it for you). Keyv then passes the absolute `expires` to it directly — this takes precedence over structural detection, so an adapter whose methods aren't written with `async` is still used directly rather than bridged. Any **legacy** storage adapter that does *not* declare `capabilities.expires` is treated as a relative-TTL adapter and transparently wrapped by [`KeyvBridgeAdapter`](#third-party-storage-adapters), which converts the absolute `expires` back to a relative TTL before delegating (and deletes outright when the deadline has already elapsed) — so existing third-party adapters keep working unchanged. Stores that expose absolute-expiry primitives (e.g. Redis `PXAT`) use `expires` directly. Map-like stores wrapped via `new Keyv({ store: new Map() })` are unaffected.
440
+
441
+ > **Adapters are the expiry authority.** Declaring `capabilities.expires === true` is a two-way contract: because Keyv core does not filter expired reads by default (`checkExpired` is off), a v6 adapter must enforce expiry itself — `get`/`getMany`/`has` must not return a key past its deadline, whether via a native mechanism (key expiry, TTL index, lease) or a client-side check. Run `@keyv/test-suite`'s `storageTtlTests` against your adapter to verify it.
442
+
410
443
  # Using BigMap to Scale
411
444
 
412
445
  ## Understanding JavaScript Map Limitations
@@ -517,7 +550,7 @@ keyvCompressionTests(test, new KeyvGzip());
517
550
 
518
551
  # Encryption
519
552
 
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.
553
+ Keyv supports pluggable encryption of stored values via the `KeyvEncryptionAdapter` interface. Pass an adapter with `encrypt` and `decrypt` methods using the `encryption` option (or set the [`.encryption`](#encryption-1) property). Encryption runs on the serialized (and optionally compressed) value, so it requires a serializer — the built-in `KeyvJsonSerializer` is enabled by default.
521
554
 
522
555
  ```typescript
523
556
  interface KeyvEncryptionAdapter {
@@ -526,9 +559,22 @@ interface KeyvEncryptionAdapter {
526
559
  }
527
560
  ```
528
561
 
562
+ ```js
563
+ import Keyv from 'keyv';
564
+
565
+ const encryption = {
566
+ encrypt: async (data) => Buffer.from(data).toString('base64'),
567
+ decrypt: async (data) => Buffer.from(data, 'base64').toString('utf8'),
568
+ };
569
+
570
+ const keyv = new Keyv({ encryption });
571
+ await keyv.set('foo', 'bar'); // value is encrypted at rest
572
+ await keyv.get('foo'); // 'bar'
573
+ ```
574
+
529
575
  # Capability Detection
530
576
 
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.
577
+ 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 a top-level `compatible` boolean (whether the object fully satisfies the interface) plus a `methods` record describing every method it looked for.
532
578
 
533
579
  ```ts
534
580
  import {
@@ -537,43 +583,47 @@ import {
537
583
  detectKeyvCompression,
538
584
  detectKeyvSerialization,
539
585
  detectKeyvEncryption,
540
- detectCapabilities,
541
586
  } from 'keyv';
542
587
  ```
543
588
 
589
+ Every entry in the `methods` record has the shape `{ exists: boolean, methodType: "sync" | "async" | "none" }`.
590
+
544
591
  ## detectKeyv(obj)
545
592
 
546
- Returns a `KeyvCapability` with a boolean for each Keyv method/property. The `keyv` flag is `true` only when **all** capabilities are present.
593
+ Returns a `KeyvCapability`: `{ compatible, methods, properties }`. `compatible` is `true` only when **all** Keyv methods and properties are present.
547
594
 
548
595
  ```ts
549
596
  import Keyv, { detectKeyv } from 'keyv';
550
597
 
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 }
598
+ const result = detectKeyv(new Keyv());
599
+ result.compatible; // true all capabilities present
600
+ result.methods.get.exists; // true
601
+ result.methods.get.methodType; // "async"
602
+ result.properties.hooks; // true
603
+ result.properties.stats; // true
556
604
 
557
- detectKeyv(new Map());
558
- // { keyv: false, get: true, set: true, ... }
605
+ const partial = detectKeyv(new Map());
606
+ partial.compatible; // false missing getMany, setMany, hooks, stats, etc.
607
+ partial.methods.get.exists; // true
559
608
  ```
560
609
 
561
610
  ## detectKeyvStorage(obj)
562
611
 
563
- Returns a `KeyvStorageCapability`. The `keyvStorage` flag is `true` when the object has `get`, `set`, `delete`, `clear`, `has`, `setMany`, `deleteMany`, and `hasMany`.
612
+ Returns a `KeyvStorageCapability`: `{ compatible, store, methods }`. `compatible` is `true` when the object is a usable storage adapter, and `store` reports the detected kind:
564
613
 
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)
614
+ - **`"keyvStorage"`** implements the full async storage adapter interface (`get`, `set`, `delete`, `clear`, `has`, `setMany`, `deleteMany`, `hasMany`, all async)
615
+ - **`"mapLike"`** — has synchronous `get`, `set`, `delete`, and `has` (i.e. it behaves like a `Map`)
616
+ - **`"asyncMap"`** — has at least async `get`, `set`, `delete`, and `clear`
617
+ - **`"none"`** — not a usable store
568
618
 
569
619
  ```ts
570
620
  import { detectKeyvStorage } from 'keyv';
571
621
 
572
622
  // Map-like object
573
- const result = detectKeyvStorage(new Map());
574
- result.mapLike; // true
575
- result.methodTypes.get; // "sync"
576
- result.methodTypes.set; // "sync"
623
+ const map = detectKeyvStorage(new Map());
624
+ map.compatible; // true
625
+ map.store; // "mapLike"
626
+ map.methods.get.methodType; // "sync"
577
627
 
578
628
  // Async storage adapter
579
629
  const adapter = {
@@ -582,58 +632,48 @@ const adapter = {
582
632
  deleteMany: async () => {}, hasMany: async () => {},
583
633
  };
584
634
  const adapterResult = detectKeyvStorage(adapter);
585
- adapterResult.keyvStorage; // true
586
- adapterResult.mapLike; // false
587
- adapterResult.methodTypes.get; // "async"
635
+ adapterResult.compatible; // true
636
+ adapterResult.store; // "keyvStorage"
637
+ adapterResult.methods.get.methodType; // "async"
588
638
  ```
589
639
 
590
640
  ## detectKeyvCompression(obj)
591
641
 
592
- Returns a `KeyvCompressionCapability`. The `keyvCompression` flag is `true` when both `compress` and `decompress` methods are present.
642
+ Returns a `KeyvCompressionCapability`: `{ compatible, methods }`. `compatible` is `true` when both `compress` and `decompress` methods are present.
593
643
 
594
644
  ```ts
595
645
  import { detectKeyvCompression } from 'keyv';
596
646
 
597
- detectKeyvCompression({ compress: (d) => d, decompress: (d) => d });
598
- // { keyvCompression: true, compress: true, decompress: true }
647
+ const result = detectKeyvCompression({ compress: (d) => d, decompress: (d) => d });
648
+ result.compatible; // true
649
+ result.methods.compress.exists; // true
650
+ result.methods.decompress.exists; // true
599
651
  ```
600
652
 
601
653
  ## detectKeyvSerialization(obj)
602
654
 
603
- Returns a `KeyvSerializationCapability`. The `keyvSerialization` flag is `true` when both `stringify` and `parse` methods are present.
655
+ Returns a `KeyvSerializationCapability`: `{ compatible, methods }`. `compatible` is `true` when both `stringify` and `parse` methods are present.
604
656
 
605
657
  ```ts
606
658
  import { detectKeyvSerialization } from 'keyv';
607
659
 
608
- detectKeyvSerialization(JSON);
609
- // { keyvSerialization: true, stringify: true, parse: true }
660
+ const result = detectKeyvSerialization(JSON);
661
+ result.compatible; // true
662
+ result.methods.stringify.exists; // true
663
+ result.methods.parse.exists; // true
610
664
  ```
611
665
 
612
666
  ## detectKeyvEncryption(obj)
613
667
 
614
- Returns a `KeyvEncryptionCapability`. The `keyvEncryption` flag is `true` when both `encrypt` and `decrypt` methods are present.
668
+ Returns a `KeyvEncryptionCapability`: `{ compatible, methods }`. `compatible` is `true` when both `encrypt` and `decrypt` methods are present.
615
669
 
616
670
  ```ts
617
671
  import { detectKeyvEncryption } from 'keyv';
618
672
 
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 }
673
+ const result = detectKeyvEncryption({ encrypt: (d) => d, decrypt: (d) => d });
674
+ result.compatible; // true
675
+ result.methods.encrypt.exists; // true
676
+ result.methods.decrypt.exists; // true
637
677
  ```
638
678
 
639
679
  # API
@@ -699,6 +739,41 @@ Default: `new Map()`
699
739
 
700
740
  The storage adapter instance to be used by Keyv.
701
741
 
742
+ ## options.stats
743
+
744
+ Type: `Boolean`<br />
745
+ Default: `false`
746
+
747
+ Enable statistics tracking (hits, misses, sets, deletes, errors). See [.stats](#stats) for details.
748
+
749
+ ## options.throwOnErrors
750
+
751
+ Type: `Boolean`<br />
752
+ Default: `false`
753
+
754
+ Throw on all errors instead of only when there are no `'error'` listeners. See [.throwOnErrors](#throwonerrors) for details.
755
+
756
+ ## options.sanitize
757
+
758
+ Type: `KeyvSanitizeOptions`<br />
759
+ Default: `undefined`
760
+
761
+ Enable sanitization of keys and namespaces by stripping dangerous patterns. See [.sanitize](#sanitize) for details.
762
+
763
+ ## options.encryption
764
+
765
+ Type: `KeyvEncryptionAdapter`<br />
766
+ Default: `undefined`
767
+
768
+ Encryption adapter used to encrypt and decrypt stored values. See [Encryption](#encryption) for details.
769
+
770
+ ## options.checkExpired
771
+
772
+ Type: `Boolean`<br />
773
+ Default: `false`
774
+
775
+ When `true`, Keyv checks expiry at its own layer on `get`/`getMany`/`has`/`hasMany` instead of trusting the storage adapter. See [.checkExpired](#checkexpired) for details.
776
+
702
777
  # Keyv Instance
703
778
 
704
779
  Keys must always be strings. Values can be of any type.
@@ -715,13 +790,13 @@ Returns a promise which resolves to `true`.
715
790
 
716
791
  Set multiple values using `KeyvEntry<Value>` objects (`{ key: string, value: Value, ttl?: number }`). The `Value` type is inferred from the entries provided.
717
792
 
718
- ## .get(key, [options])
793
+ ## .get(key)
719
794
 
720
- Returns a promise which resolves to the retrieved value.
795
+ Returns a promise which resolves to the retrieved value, or `undefined` if the key does not exist or is expired. If an array of keys is passed it delegates to `.getMany()` and resolves to an array of values.
721
796
 
722
- ## .getMany(keys, [options])
797
+ ## .getMany(keys)
723
798
 
724
- Returns a promise which resolves to an array of retrieved values.
799
+ Returns a promise which resolves to an array of retrieved values, with `undefined` for keys that do not exist or are expired.
725
800
 
726
801
  ## .getRaw(key)
727
802
 
@@ -832,7 +907,7 @@ for await (const [key, value] of keyv.iterator()) {
832
907
  The iterator works with any storage backend:
833
908
  - **Map stores**: iterates using the built-in `Symbol.iterator`
834
909
  - **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
910
+ - **Unsupported stores**: yields nothing if the store does not support iteration
836
911
 
837
912
  # API - Properties
838
913
 
@@ -915,48 +990,33 @@ keyv.compression = new KeyvGzip();
915
990
  console.log(keyv.compression); // KeyvGzip
916
991
  ```
917
992
 
918
- ## .useKeyPrefix
993
+ ## .encryption
919
994
 
920
- Type: `Boolean`<br />
921
- Default: `true`
922
-
923
- If set to `true` Keyv will prefix all keys with the namespace. This is useful if you want to avoid collisions with other data in your storage.
924
-
925
- ```js
926
- const keyv = new Keyv({ useKeyPrefix: false });
927
- console.log(keyv.useKeyPrefix); // false
928
- keyv.useKeyPrefix = true;
929
- console.log(keyv.useKeyPrefix); // true
930
- ```
995
+ Type: `KeyvEncryptionAdapter`<br />
996
+ Default: `undefined`
931
997
 
932
- With many of the storage adapters you will also need to set the `namespace` option to `undefined` to have it work correctly. This is because in `v5` we started the transition to having the storage adapter handle the namespacing and `Keyv` will no longer handle it internally via KeyPrefixing. Here is an example of doing ith with `KeyvSqlite`:
998
+ The encryption adapter used to encrypt and decrypt stored values. If `undefined` (default) values are not encrypted. See [Encryption](#encryption) for more details.
933
999
 
934
1000
  ```js
935
- import Keyv from 'keyv';
936
- import KeyvSqlite from '@keyv/sqlite';
937
-
938
- const store = new KeyvSqlite('sqlite://path/to/database.sqlite');
939
- const keyv = new Keyv({ store });
940
- keyv.useKeyPrefix = false; // disable key prefixing
941
- store.namespace = undefined; // disable namespacing in the storage adapter
942
-
943
- await keyv.set('foo', 'bar'); // true
944
- await keyv.get('foo'); // 'bar'
945
- await keyv.clear();
1001
+ const keyv = new Keyv();
1002
+ console.log(keyv.encryption); // undefined
1003
+ keyv.encryption = {
1004
+ encrypt: async (data) => Buffer.from(data).toString('base64'),
1005
+ decrypt: async (data) => Buffer.from(data, 'base64').toString('utf8'),
1006
+ };
1007
+ console.log(keyv.encryption); // the encryption adapter
946
1008
  ```
947
1009
 
948
- ## .emitErrors
1010
+ ## .checkExpired
949
1011
 
950
1012
  Type: `Boolean`<br />
951
- Default: `true`
1013
+ Default: `false`
952
1014
 
953
- If set to `true`, Keyv will emit an `'error'` event when an error occurs. Set to `false` to suppress error events.
1015
+ A read-only property (configured via the `checkExpired` constructor option). When `true`, Keyv checks expiry at its own layer on `get`, `getMany`, `has`, and `hasMany`, deleting any expired entries it encounters. When `false` (default) it trusts the storage adapter to handle expiry.
954
1016
 
955
1017
  ```js
956
- const keyv = new Keyv({ emitErrors: false });
957
- console.log(keyv.emitErrors); // false
958
- keyv.emitErrors = true;
959
- console.log(keyv.emitErrors); // true
1018
+ const keyv = new Keyv({ checkExpired: true });
1019
+ console.log(keyv.checkExpired); // true
960
1020
  ```
961
1021
 
962
1022
  ## .throwOnErrors
@@ -1059,10 +1119,12 @@ const stats = new KeyvStats({ enabled: true, maxEntries: 500, emitter: keyv });
1059
1119
  ```
1060
1120
 
1061
1121
  ## .sanitize
1062
- Type: `boolean | KeyvSanitizeOptions`<br />
1063
- Default: `false`
1122
+ Type: `KeyvSanitize` (configured via the `sanitize` option: `KeyvSanitizeOptions`)<br />
1123
+ Default: disabled
1124
+
1125
+ The `.sanitize` property is a `KeyvSanitize` adapter. It is configured through the `sanitize` constructor option (`true`, or a `KeyvSanitizeOptions` object) and disabled by default.
1064
1126
 
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.
1127
+ It 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
1128
 
1067
1129
  Results are cached in an LRU cache (10,000 entries) for fast repeated lookups.
1068
1130
 
@@ -1117,11 +1179,16 @@ const keyv = new Keyv({
1117
1179
  });
1118
1180
  ```
1119
1181
 
1120
- Change at runtime:
1182
+ Change at runtime by updating the options on the existing adapter, or by replacing it:
1121
1183
  ```js
1122
- keyv.sanitize = false; // disable
1123
- keyv.sanitize = true; // enable all
1124
- keyv.sanitize = { keys: { sql: true, mongo: false } }; // granular
1184
+ import { KeyvSanitize } from 'keyv';
1185
+
1186
+ // Update options on the existing adapter
1187
+ keyv.sanitize.updateOptions({ keys: true, namespace: true }); // enable all
1188
+ keyv.sanitize.updateOptions({ keys: { sql: true, mongo: false } }); // granular
1189
+
1190
+ // Or replace the adapter entirely
1191
+ keyv.sanitize = new KeyvSanitize({ keys: true, namespace: true });
1125
1192
  ```
1126
1193
 
1127
1194
  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.