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 +1 -1
- package/README.md +270 -56
- package/dist/index.cjs +2269 -1100
- package/dist/index.d.cts +1329 -367
- package/dist/index.d.mts +1351 -0
- package/dist/index.mjs +2267 -0
- package/package.json +14 -8
- package/dist/index.d.ts +0 -389
- package/dist/index.js +0 -1087
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-
|
|
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
|
[](https://github.com/jaredwray/keyv/actions/workflows/tests.yaml)
|
|
6
6
|
[](https://github.com/jaredwray/keyv/actions/workflows/bun-test.yaml)
|
|
7
|
+
[](https://github.com/jaredwray/keyv/actions/workflows/browser-compat.yaml)
|
|
7
8
|
[](https://codecov.io/gh/jaredwray/keyv)
|
|
8
9
|
[](https://www.npmjs.com/package/keyv)
|
|
9
10
|
[](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
|
|
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
|
-
##
|
|
308
|
+
## Official Serializers
|
|
306
309
|
|
|
307
|
-
|
|
310
|
+
In addition to the built-in serializer, Keyv offers two official serialization packages:
|
|
308
311
|
|
|
309
|
-
|
|
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
|
-
|
|
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
|
-
|
|
320
|
-
|
|
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
|
-
|
|
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
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
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
|
-
|
|
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/
|
|
362
|
-
Valkey | [@keyv/valkey](https://github.com/jaredwray/keyv/tree/
|
|
363
|
-
MongoDB | [@keyv/mongo](https://github.com/jaredwray/keyv/tree/
|
|
364
|
-
SQLite | [@keyv/sqlite](https://github.com/jaredwray/keyv/tree/
|
|
365
|
-
PostgreSQL | [@keyv/postgres](https://github.com/jaredwray/keyv/tree/
|
|
366
|
-
MySQL | [@keyv/mysql](https://github.com/jaredwray/keyv/tree/
|
|
367
|
-
Etcd | [@keyv/etcd](https://github.com/jaredwray/keyv/tree/
|
|
368
|
-
Memcache | [@keyv/memcache](https://github.com/jaredwray/keyv/tree/
|
|
369
|
-
DynamoDB | [@keyv/dynamo](https://github.com/jaredwray/keyv/tree/
|
|
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 {
|
|
512
|
+
import { keyvCompressionTests } from '@keyv/test-suite';
|
|
505
513
|
import KeyvGzip from '@keyv/compress-gzip';
|
|
506
514
|
|
|
507
|
-
|
|
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: `
|
|
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.
|
|
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: `
|
|
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
|
|
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
|
|
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 `
|
|
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
|
|
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
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
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
|
|
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
|
|
822
|
+
Iterate over all key-value pairs in the store. Automatically deserializes values, filters out expired entries, and deletes them.
|
|
703
823
|
|
|
704
|
-
Returns
|
|
824
|
+
Returns an async generator that yields `[key, value]` pairs. Use with `for await...of`:
|
|
705
825
|
|
|
706
826
|
```js
|
|
707
|
-
|
|
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); //
|
|
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: `
|
|
868
|
-
Default: `
|
|
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).
|