keyv 6.0.0-beta.1 → 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 +0 -10
- package/README.md +203 -136
- package/dist/index.cjs +261 -180
- package/dist/index.d.cts +158 -61
- package/dist/index.d.mts +158 -61
- package/dist/index.mjs +261 -181
- package/package.json +12 -9
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
|
[](https://github.com/jaredwray/keyv/actions/workflows/tests.yaml)
|
|
6
6
|
[](https://github.com/jaredwray/keyv/actions/workflows/bun-test.yaml)
|
|
7
7
|
[](https://github.com/jaredwray/keyv/actions/workflows/browser-compat.yaml)
|
|
8
|
-
[](https://www.jsdelivr.com/package/npm/keyv)
|
|
9
|
+
[](https://codecov.io/gh/jaredwray/keyv)
|
|
9
10
|
[](https://www.npmjs.com/package/keyv)
|
|
10
11
|
[](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
|
-
- [.
|
|
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
|
|
58
|
-
- [.getMany(keys
|
|
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
|
|
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(
|
|
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
|
|
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
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
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
|
-
|
|
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 `
|
|
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
|
-
//
|
|
255
|
+
// AFTER_GET hook - fires on both hits and misses
|
|
251
256
|
const keyv = new Keyv();
|
|
252
|
-
keyv.
|
|
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
|
-
//
|
|
270
|
+
// AFTER_GET_RAW hook - same behavior as AFTER_GET
|
|
266
271
|
const keyv = new Keyv();
|
|
267
|
-
keyv.
|
|
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
|
-
//
|
|
282
|
+
// BEFORE_SET hook
|
|
278
283
|
const keyv = new Keyv();
|
|
279
|
-
keyv.
|
|
284
|
+
keyv.onHook(KeyvHooks.BEFORE_SET, (data) => console.log(`Setting key ${data.key} to ${data.value}`));
|
|
280
285
|
|
|
281
|
-
//
|
|
286
|
+
// AFTER_SET hook
|
|
282
287
|
const keyv = new Keyv();
|
|
283
|
-
keyv.
|
|
288
|
+
keyv.onHook(KeyvHooks.AFTER_SET, ({ key, value }) => console.log(`Set key ${key} to ${value}`));
|
|
284
289
|
```
|
|
285
290
|
|
|
286
|
-
In
|
|
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.
|
|
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
|
|
302
|
+
Now this key will have `prefix-` added to it before it is set.
|
|
298
303
|
|
|
299
304
|
## Delete Hooks
|
|
300
305
|
|
|
301
|
-
In `
|
|
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
|
|
360
|
+
When serialization, compression, and/or encryption are configured, Keyv applies them in this order:
|
|
356
361
|
|
|
357
|
-
**On set:** serialize (optional) →
|
|
362
|
+
**On set:** serialize → compress (optional) → encrypt (optional) → store
|
|
358
363
|
|
|
359
|
-
**On get:** store →
|
|
364
|
+
**On get:** store → decrypt (optional) → decompress (optional) → parse → value
|
|
360
365
|
|
|
361
|
-
|
|
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
|
|
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
|
|
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`
|
|
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
|
-
//
|
|
553
|
-
//
|
|
554
|
-
//
|
|
555
|
-
//
|
|
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
|
-
//
|
|
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`.
|
|
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
|
-
|
|
566
|
-
- **`mapLike`** —
|
|
567
|
-
- **`
|
|
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
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
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.
|
|
586
|
-
adapterResult.
|
|
587
|
-
adapterResult.
|
|
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`.
|
|
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
|
-
//
|
|
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`.
|
|
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
|
-
//
|
|
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`.
|
|
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
|
-
//
|
|
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
|
|
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
|
|
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**:
|
|
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
|
-
## .
|
|
993
|
+
## .encryption
|
|
919
994
|
|
|
920
|
-
Type: `
|
|
921
|
-
Default: `
|
|
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
|
-
|
|
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
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
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
|
-
## .
|
|
1010
|
+
## .checkExpired
|
|
949
1011
|
|
|
950
1012
|
Type: `Boolean`<br />
|
|
951
|
-
Default: `
|
|
1013
|
+
Default: `false`
|
|
952
1014
|
|
|
953
|
-
|
|
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({
|
|
957
|
-
console.log(keyv.
|
|
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: `
|
|
1063
|
-
Default:
|
|
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
|
-
|
|
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
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
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.
|