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/dist/index.mjs ADDED
@@ -0,0 +1,2267 @@
1
+ import { Hookified } from "hookified";
2
+ //#region src/capabilities.ts
3
+ const keyvMethodNames = [
4
+ "get",
5
+ "set",
6
+ "delete",
7
+ "clear",
8
+ "has",
9
+ "getMany",
10
+ "setMany",
11
+ "deleteMany",
12
+ "hasMany",
13
+ "disconnect",
14
+ "getRaw",
15
+ "getManyRaw",
16
+ "setRaw",
17
+ "setManyRaw",
18
+ "iterator"
19
+ ];
20
+ const keyvPropertyNames = ["hooks", "stats"];
21
+ const keyvStorageMethodNames = [
22
+ "get",
23
+ "getMany",
24
+ "has",
25
+ "hasMany",
26
+ "set",
27
+ "setMany",
28
+ "delete",
29
+ "deleteMany",
30
+ "clear",
31
+ "disconnect",
32
+ "iterator"
33
+ ];
34
+ const keyvCompressionMethodNames = ["compress", "decompress"];
35
+ const keyvSerializationMethodNames = ["stringify", "parse"];
36
+ const keyvEncryptionMethodNames = ["encrypt", "decrypt"];
37
+ function isMethod(obj, name) {
38
+ return name in obj && typeof obj[name] === "function";
39
+ }
40
+ function isProperty(obj, name) {
41
+ return name in obj;
42
+ }
43
+ function resolveMethodType(obj, name) {
44
+ if (!(name in obj)) return "none";
45
+ const value = obj[name];
46
+ if (typeof value !== "function") return "none";
47
+ return value.constructor.name === "AsyncFunction" ? "async" : "sync";
48
+ }
49
+ function resolveMethod(obj, name) {
50
+ return {
51
+ exists: isMethod(obj, name),
52
+ methodType: resolveMethodType(obj, name)
53
+ };
54
+ }
55
+ const noneMethod = {
56
+ exists: false,
57
+ methodType: "none"
58
+ };
59
+ function buildMethods(obj, names) {
60
+ const methods = {};
61
+ for (const name of names) methods[name] = obj ? resolveMethod(obj, name) : { ...noneMethod };
62
+ return methods;
63
+ }
64
+ /**
65
+ * Detect whether an object implements the full Keyv interface
66
+ * @param obj - The object to check
67
+ * @returns A {@link KeyvCapability} where `compatible` is `true` only when all required capabilities are present
68
+ * @example
69
+ * ```typescript
70
+ * import Keyv, { detectKeyv } from 'keyv';
71
+ *
72
+ * const result = detectKeyv(new Keyv());
73
+ * result.compatible; // true — all capabilities present
74
+ * result.methods.get.exists; // true
75
+ * result.methods.get.methodType; // "async"
76
+ *
77
+ * const partial = detectKeyv(new Map());
78
+ * partial.compatible; // false — missing getMany, setMany, hooks, stats, etc.
79
+ * partial.methods.get.exists; // true
80
+ * ```
81
+ */
82
+ function detectKeyv(obj) {
83
+ if (obj === null || obj === void 0 || typeof obj !== "object") return {
84
+ compatible: false,
85
+ methods: buildMethods(null, keyvMethodNames),
86
+ properties: {
87
+ hooks: false,
88
+ stats: false
89
+ }
90
+ };
91
+ const methods = buildMethods(obj, keyvMethodNames);
92
+ const properties = {
93
+ hooks: isProperty(obj, "hooks"),
94
+ stats: isProperty(obj, "stats")
95
+ };
96
+ return {
97
+ compatible: [...keyvMethodNames, ...keyvPropertyNames].every((k) => {
98
+ if (k === "hooks" || k === "stats") return properties[k];
99
+ return methods[k].exists;
100
+ }),
101
+ methods,
102
+ properties
103
+ };
104
+ }
105
+ /**
106
+ * Detect whether an object implements the Keyv storage adapter interface
107
+ * @param obj - The object to check
108
+ * @returns A {@link KeyvStorageCapability} where:
109
+ * - `compatible` is `true` when the object is a valid storage adapter (`"keyvStorage"`, `"mapLike"`, or `"asyncMap"`)
110
+ * - `store` indicates the detected store type: `"keyvStorage"`, `"mapLike"`, `"asyncMap"`, or `"none"`
111
+ * - `methods` maps each method name to `{ exists, methodType }`
112
+ * @example
113
+ * ```typescript
114
+ * import { detectKeyvStorage } from 'keyv';
115
+ *
116
+ * const map = detectKeyvStorage(new Map());
117
+ * map.compatible; // true
118
+ * map.store; // "mapLike"
119
+ * map.methods.get.exists; // true
120
+ * map.methods.get.methodType; // "sync"
121
+ *
122
+ * const adapter = detectKeyvStorage(asyncAdapter);
123
+ * adapter.compatible; // true
124
+ * adapter.store; // "keyvStorage"
125
+ * adapter.methods.get.methodType; // "async"
126
+ * ```
127
+ */
128
+ function detectKeyvStorage(obj) {
129
+ if (obj === null || obj === void 0 || typeof obj !== "object") return {
130
+ compatible: false,
131
+ store: "none",
132
+ methods: buildMethods(null, keyvStorageMethodNames)
133
+ };
134
+ const methods = buildMethods(obj, keyvStorageMethodNames);
135
+ if ([
136
+ "get",
137
+ "has",
138
+ "hasMany",
139
+ "set",
140
+ "setMany",
141
+ "delete",
142
+ "deleteMany",
143
+ "clear"
144
+ ].every((k) => methods[k].exists && methods[k].methodType === "async")) return {
145
+ compatible: true,
146
+ store: "keyvStorage",
147
+ methods
148
+ };
149
+ if ([
150
+ "get",
151
+ "set",
152
+ "delete",
153
+ "has"
154
+ ].every((m) => methods[m].exists && methods[m].methodType === "sync")) return {
155
+ compatible: true,
156
+ store: "mapLike",
157
+ methods
158
+ };
159
+ if ([
160
+ "get",
161
+ "set",
162
+ "delete",
163
+ "clear"
164
+ ].every((m) => methods[m].exists)) return {
165
+ compatible: true,
166
+ store: "asyncMap",
167
+ methods
168
+ };
169
+ return {
170
+ compatible: false,
171
+ store: "none",
172
+ methods
173
+ };
174
+ }
175
+ /**
176
+ * Detect whether an object implements the Keyv compression adapter interface
177
+ * @param obj - The object to check
178
+ * @returns A {@link KeyvCompressionCapability} where `compatible` is `true` when both `compress` and `decompress` methods are present
179
+ * @example
180
+ * ```typescript
181
+ * import { detectKeyvCompression } from 'keyv';
182
+ *
183
+ * detectKeyvCompression({ compress: (d) => d, decompress: (d) => d });
184
+ * // { compatible: true, methods: { compress: { exists: true, methodType: "sync" }, decompress: { exists: true, methodType: "sync" } } }
185
+ * ```
186
+ */
187
+ function detectKeyvCompression(obj) {
188
+ if (obj === null || obj === void 0 || typeof obj !== "object") return {
189
+ compatible: false,
190
+ methods: buildMethods(null, keyvCompressionMethodNames)
191
+ };
192
+ const methods = buildMethods(obj, keyvCompressionMethodNames);
193
+ return {
194
+ compatible: keyvCompressionMethodNames.every((k) => methods[k].exists),
195
+ methods
196
+ };
197
+ }
198
+ /**
199
+ * Detect whether an object implements the Keyv serialization adapter interface
200
+ * @param obj - The object to check
201
+ * @returns A {@link KeyvSerializationCapability} where `compatible` is `true` when both `stringify` and `parse` methods are present
202
+ * @example
203
+ * ```typescript
204
+ * import { detectKeyvSerialization } from 'keyv';
205
+ *
206
+ * detectKeyvSerialization(JSON);
207
+ * // { compatible: true, methods: { stringify: { exists: true, methodType: "sync" }, parse: { exists: true, methodType: "sync" } } }
208
+ * ```
209
+ */
210
+ function detectKeyvSerialization(obj) {
211
+ if (obj === null || obj === void 0 || typeof obj !== "object") return {
212
+ compatible: false,
213
+ methods: buildMethods(null, keyvSerializationMethodNames)
214
+ };
215
+ const methods = buildMethods(obj, keyvSerializationMethodNames);
216
+ return {
217
+ compatible: keyvSerializationMethodNames.every((k) => methods[k].exists),
218
+ methods
219
+ };
220
+ }
221
+ /**
222
+ * Detect whether an object implements the Keyv encryption adapter interface
223
+ * @param obj - The object to check
224
+ * @returns A {@link KeyvEncryptionCapability} where `compatible` is `true` when both `encrypt` and `decrypt` methods are present
225
+ * @example
226
+ * ```typescript
227
+ * import { detectKeyvEncryption } from 'keyv';
228
+ *
229
+ * detectKeyvEncryption({ encrypt: (d) => d, decrypt: (d) => d });
230
+ * // { compatible: true, methods: { encrypt: { exists: true, methodType: "sync" }, decrypt: { exists: true, methodType: "sync" } } }
231
+ * ```
232
+ */
233
+ function detectKeyvEncryption(obj) {
234
+ if (obj === null || obj === void 0 || typeof obj !== "object") return {
235
+ compatible: false,
236
+ methods: buildMethods(null, keyvEncryptionMethodNames)
237
+ };
238
+ const methods = buildMethods(obj, keyvEncryptionMethodNames);
239
+ return {
240
+ compatible: keyvEncryptionMethodNames.every((k) => methods[k].exists),
241
+ methods
242
+ };
243
+ }
244
+ //#endregion
245
+ //#region src/types/keyv.ts
246
+ /**
247
+ * Events emitted by Keyv for error handling and telemetry.
248
+ */
249
+ let KeyvEvents = /* @__PURE__ */ function(KeyvEvents) {
250
+ /** Emitted when an error occurs in a store operation. */
251
+ KeyvEvents["ERROR"] = "error";
252
+ /** Emitted for informational messages. */
253
+ KeyvEvents["INFO"] = "info";
254
+ /** Emitted for warning messages. */
255
+ KeyvEvents["WARN"] = "warn";
256
+ /** Telemetry: cache hit. */
257
+ KeyvEvents["STAT_HIT"] = "stat:hit";
258
+ /** Telemetry: cache miss. */
259
+ KeyvEvents["STAT_MISS"] = "stat:miss";
260
+ /** Telemetry: value set. */
261
+ KeyvEvents["STAT_SET"] = "stat:set";
262
+ /** Telemetry: value deleted. */
263
+ KeyvEvents["STAT_DELETE"] = "stat:delete";
264
+ /** Telemetry: operation error. */
265
+ KeyvEvents["STAT_ERROR"] = "stat:error";
266
+ return KeyvEvents;
267
+ }({});
268
+ /**
269
+ * Hook names for intercepting Keyv operations.
270
+ * Register hooks via `keyv.on(KeyvHooks.BEFORE_SET, callback)` to run logic before/after operations.
271
+ */
272
+ let KeyvHooks = /* @__PURE__ */ function(KeyvHooks) {
273
+ /** @deprecated Use BEFORE_SET instead */
274
+ KeyvHooks["PRE_SET"] = "preSet";
275
+ /** @deprecated Use AFTER_SET instead */
276
+ KeyvHooks["POST_SET"] = "postSet";
277
+ /** @deprecated Use BEFORE_GET instead */
278
+ KeyvHooks["PRE_GET"] = "preGet";
279
+ /** @deprecated Use AFTER_GET instead */
280
+ KeyvHooks["POST_GET"] = "postGet";
281
+ /** @deprecated Use BEFORE_GET_MANY instead */
282
+ KeyvHooks["PRE_GET_MANY"] = "preGetMany";
283
+ /** @deprecated Use AFTER_GET_MANY instead */
284
+ KeyvHooks["POST_GET_MANY"] = "postGetMany";
285
+ /** @deprecated Use BEFORE_GET_RAW instead */
286
+ KeyvHooks["PRE_GET_RAW"] = "preGetRaw";
287
+ /** @deprecated Use AFTER_GET_RAW instead */
288
+ KeyvHooks["POST_GET_RAW"] = "postGetRaw";
289
+ /** @deprecated Use BEFORE_GET_MANY_RAW instead */
290
+ KeyvHooks["PRE_GET_MANY_RAW"] = "preGetManyRaw";
291
+ /** @deprecated Use AFTER_GET_MANY_RAW instead */
292
+ KeyvHooks["POST_GET_MANY_RAW"] = "postGetManyRaw";
293
+ /** @deprecated Use BEFORE_SET_RAW instead */
294
+ KeyvHooks["PRE_SET_RAW"] = "preSetRaw";
295
+ /** @deprecated Use AFTER_SET_RAW instead */
296
+ KeyvHooks["POST_SET_RAW"] = "postSetRaw";
297
+ /** @deprecated Use BEFORE_SET_MANY_RAW instead */
298
+ KeyvHooks["PRE_SET_MANY_RAW"] = "preSetManyRaw";
299
+ /** @deprecated Use AFTER_SET_MANY_RAW instead */
300
+ KeyvHooks["POST_SET_MANY_RAW"] = "postSetManyRaw";
301
+ /** @deprecated Use BEFORE_SET_MANY instead */
302
+ KeyvHooks["PRE_SET_MANY"] = "preSetMany";
303
+ /** @deprecated Use AFTER_SET_MANY instead */
304
+ KeyvHooks["POST_SET_MANY"] = "postSetMany";
305
+ /** @deprecated Use BEFORE_DELETE instead */
306
+ KeyvHooks["PRE_DELETE"] = "preDelete";
307
+ /** @deprecated Use AFTER_DELETE instead */
308
+ KeyvHooks["POST_DELETE"] = "postDelete";
309
+ /** @deprecated Use BEFORE_DELETE_MANY instead */
310
+ KeyvHooks["PRE_DELETE_MANY"] = "preDeleteMany";
311
+ /** @deprecated Use AFTER_DELETE_MANY instead */
312
+ KeyvHooks["POST_DELETE_MANY"] = "postDeleteMany";
313
+ KeyvHooks["BEFORE_SET"] = "before:set";
314
+ KeyvHooks["AFTER_SET"] = "after:set";
315
+ KeyvHooks["BEFORE_GET"] = "before:get";
316
+ KeyvHooks["AFTER_GET"] = "after:get";
317
+ KeyvHooks["BEFORE_GET_MANY"] = "before:getMany";
318
+ KeyvHooks["AFTER_GET_MANY"] = "after:getMany";
319
+ KeyvHooks["BEFORE_GET_RAW"] = "before:getRaw";
320
+ KeyvHooks["AFTER_GET_RAW"] = "after:getRaw";
321
+ KeyvHooks["BEFORE_GET_MANY_RAW"] = "before:getManyRaw";
322
+ KeyvHooks["AFTER_GET_MANY_RAW"] = "after:getManyRaw";
323
+ KeyvHooks["BEFORE_SET_RAW"] = "before:setRaw";
324
+ KeyvHooks["AFTER_SET_RAW"] = "after:setRaw";
325
+ KeyvHooks["BEFORE_SET_MANY"] = "before:setMany";
326
+ KeyvHooks["AFTER_SET_MANY"] = "after:setMany";
327
+ KeyvHooks["BEFORE_SET_MANY_RAW"] = "before:setManyRaw";
328
+ KeyvHooks["AFTER_SET_MANY_RAW"] = "after:setManyRaw";
329
+ KeyvHooks["BEFORE_DELETE"] = "before:delete";
330
+ KeyvHooks["AFTER_DELETE"] = "after:delete";
331
+ KeyvHooks["BEFORE_DELETE_MANY"] = "before:deleteMany";
332
+ KeyvHooks["AFTER_DELETE_MANY"] = "after:deleteMany";
333
+ KeyvHooks["BEFORE_HAS"] = "before:has";
334
+ KeyvHooks["AFTER_HAS"] = "after:has";
335
+ KeyvHooks["BEFORE_HAS_MANY"] = "before:hasMany";
336
+ KeyvHooks["AFTER_HAS_MANY"] = "after:hasMany";
337
+ KeyvHooks["BEFORE_CLEAR"] = "before:clear";
338
+ KeyvHooks["AFTER_CLEAR"] = "after:clear";
339
+ KeyvHooks["BEFORE_DISCONNECT"] = "before:disconnect";
340
+ KeyvHooks["AFTER_DISCONNECT"] = "after:disconnect";
341
+ return KeyvHooks;
342
+ }({});
343
+ //#endregion
344
+ //#region src/utils.ts
345
+ /**
346
+ * Check whether a deserialized entry has expired based on its `expires` timestamp.
347
+ */
348
+ function isDataExpired(data) {
349
+ return typeof data.expires === "number" && Date.now() > data.expires;
350
+ }
351
+ /**
352
+ * Calculate an absolute expiry timestamp from a TTL value.
353
+ * Returns `undefined` when `ttl` is absent, zero, negative, or non-finite
354
+ * (meaning "no expiry").
355
+ *
356
+ * @param ttl - Time-to-live in milliseconds, or `undefined`
357
+ * @returns Absolute expiry timestamp (ms since epoch), or `undefined`
358
+ */
359
+ function calculateExpires(ttl) {
360
+ if (typeof ttl !== "number" || ttl <= 0 || !Number.isFinite(ttl)) return;
361
+ return Date.now() + ttl;
362
+ }
363
+ /**
364
+ * Resolve a TTL value by falling back to a default when none is given,
365
+ * then normalising zero, negative, or non-finite values to `undefined` (meaning "no expiry").
366
+ *
367
+ * @param ttl - Explicit TTL in milliseconds, or `undefined`
368
+ * @param defaultTtl - Fallback TTL (typically `Keyv._ttl`), or `undefined`
369
+ * @returns The resolved TTL in milliseconds, or `undefined` for no expiry
370
+ */
371
+ function resolveTtl(ttl, defaultTtl) {
372
+ const resolved = ttl ?? defaultTtl;
373
+ if (resolved === void 0 || resolved <= 0 || !Number.isFinite(resolved)) return;
374
+ return resolved;
375
+ }
376
+ /**
377
+ * Derive a store-level TTL from an absolute `expires` timestamp.
378
+ * Returns `undefined` when `expires` is absent, non-finite, or when the derived
379
+ * TTL is zero or negative (i.e. the entry has already expired).
380
+ *
381
+ * @param expires - Absolute expiry timestamp in milliseconds since epoch, or `undefined`
382
+ * @returns The remaining TTL in milliseconds, or `undefined`
383
+ */
384
+ function ttlFromExpires(expires) {
385
+ if (typeof expires !== "number" || !Number.isFinite(expires)) return;
386
+ const remaining = expires - Date.now();
387
+ return remaining > 0 ? remaining : void 0;
388
+ }
389
+ /**
390
+ * Scan parallel `keys` and `data` arrays, nullify any expired entries in
391
+ * `data`, and batch-delete the corresponding keys via `keyv.deleteMany()`.
392
+ */
393
+ async function deleteExpiredKeys(keys, data, keyv) {
394
+ const expiredKeys = [];
395
+ for (const [index, row] of data.entries()) if (row !== void 0 && row !== null && isDataExpired(row)) {
396
+ expiredKeys.push(keys[index]);
397
+ data[index] = void 0;
398
+ }
399
+ if (expiredKeys.length > 0) await keyv.deleteMany(expiredKeys);
400
+ }
401
+ /**
402
+ * Maps new hook names to their deprecated equivalents so both fire during migration.
403
+ */
404
+ const deprecatedHookAliases = new Map([
405
+ [KeyvHooks.BEFORE_SET, KeyvHooks.PRE_SET],
406
+ [KeyvHooks.AFTER_SET, KeyvHooks.POST_SET],
407
+ [KeyvHooks.BEFORE_GET, KeyvHooks.PRE_GET],
408
+ [KeyvHooks.AFTER_GET, KeyvHooks.POST_GET],
409
+ [KeyvHooks.BEFORE_GET_MANY, KeyvHooks.PRE_GET_MANY],
410
+ [KeyvHooks.AFTER_GET_MANY, KeyvHooks.POST_GET_MANY],
411
+ [KeyvHooks.BEFORE_GET_RAW, KeyvHooks.PRE_GET_RAW],
412
+ [KeyvHooks.AFTER_GET_RAW, KeyvHooks.POST_GET_RAW],
413
+ [KeyvHooks.BEFORE_GET_MANY_RAW, KeyvHooks.PRE_GET_MANY_RAW],
414
+ [KeyvHooks.AFTER_GET_MANY_RAW, KeyvHooks.POST_GET_MANY_RAW],
415
+ [KeyvHooks.BEFORE_SET_RAW, KeyvHooks.PRE_SET_RAW],
416
+ [KeyvHooks.AFTER_SET_RAW, KeyvHooks.POST_SET_RAW],
417
+ [KeyvHooks.BEFORE_SET_MANY, KeyvHooks.PRE_SET_MANY],
418
+ [KeyvHooks.AFTER_SET_MANY, KeyvHooks.POST_SET_MANY],
419
+ [KeyvHooks.BEFORE_SET_MANY_RAW, KeyvHooks.PRE_SET_MANY_RAW],
420
+ [KeyvHooks.AFTER_SET_MANY_RAW, KeyvHooks.POST_SET_MANY_RAW],
421
+ [KeyvHooks.BEFORE_DELETE, KeyvHooks.PRE_DELETE],
422
+ [KeyvHooks.AFTER_DELETE, KeyvHooks.POST_DELETE],
423
+ [KeyvHooks.BEFORE_DELETE_MANY, KeyvHooks.PRE_DELETE_MANY],
424
+ [KeyvHooks.AFTER_DELETE_MANY, KeyvHooks.POST_DELETE_MANY]
425
+ ]);
426
+ /**
427
+ * Build the deprecated-hooks map used by Hookified to warn when old PRE_/POST_ hook names are registered.
428
+ */
429
+ function buildDeprecatedHooks() {
430
+ return new Map([
431
+ ["preSet", "Use KeyvHooks.BEFORE_SET ('before:set') instead"],
432
+ ["postSet", "Use KeyvHooks.AFTER_SET ('after:set') instead"],
433
+ ["preGet", "Use KeyvHooks.BEFORE_GET ('before:get') instead"],
434
+ ["postGet", "Use KeyvHooks.AFTER_GET ('after:get') instead"],
435
+ ["preGetMany", "Use KeyvHooks.BEFORE_GET_MANY ('before:getMany') instead"],
436
+ ["postGetMany", "Use KeyvHooks.AFTER_GET_MANY ('after:getMany') instead"],
437
+ ["preGetRaw", "Use KeyvHooks.BEFORE_GET_RAW ('before:getRaw') instead"],
438
+ ["postGetRaw", "Use KeyvHooks.AFTER_GET_RAW ('after:getRaw') instead"],
439
+ ["preGetManyRaw", "Use KeyvHooks.BEFORE_GET_MANY_RAW ('before:getManyRaw') instead"],
440
+ ["postGetManyRaw", "Use KeyvHooks.AFTER_GET_MANY_RAW ('after:getManyRaw') instead"],
441
+ ["preSetRaw", "Use KeyvHooks.BEFORE_SET_RAW ('before:setRaw') instead"],
442
+ ["postSetRaw", "Use KeyvHooks.AFTER_SET_RAW ('after:setRaw') instead"],
443
+ ["preSetMany", "Use KeyvHooks.BEFORE_SET_MANY ('before:setMany') instead"],
444
+ ["postSetMany", "Use KeyvHooks.AFTER_SET_MANY ('after:setMany') instead"],
445
+ ["preSetManyRaw", "Use KeyvHooks.BEFORE_SET_MANY_RAW ('before:setManyRaw') instead"],
446
+ ["postSetManyRaw", "Use KeyvHooks.AFTER_SET_MANY_RAW ('after:setManyRaw') instead"],
447
+ ["preDelete", "Use KeyvHooks.BEFORE_DELETE ('before:delete') instead"],
448
+ ["postDelete", "Use KeyvHooks.AFTER_DELETE ('after:delete') instead"],
449
+ ["preDeleteMany", "Use KeyvHooks.BEFORE_DELETE_MANY ('before:deleteMany') instead"],
450
+ ["postDeleteMany", "Use KeyvHooks.AFTER_DELETE_MANY ('after:deleteMany') instead"]
451
+ ]);
452
+ }
453
+ //#endregion
454
+ //#region src/adapters/bridge.ts
455
+ /**
456
+ * A bridge storage adapter for Keyv that wraps any promise-based store.
457
+ *
458
+ * This class provides a unified interface for using various async stores
459
+ * with Keyv, handling namespace prefixing, TTL-based expiration, and batch operations.
460
+ * If the underlying store implements optional methods (has, hasMany, getMany, etc.),
461
+ * the bridge will delegate to them. Otherwise, it falls back to using primitives.
462
+ *
463
+ * @example
464
+ * ```typescript
465
+ * // Using with a promise-based store
466
+ * const bridge = new KeyvBridgeAdapter(myAsyncStore, { namespace: 'cache' });
467
+ *
468
+ * // Using with Keyv
469
+ * const keyv = new Keyv({ store: new KeyvBridgeAdapter(myAsyncStore) });
470
+ * ```
471
+ */
472
+ var KeyvBridgeAdapter = class extends Hookified {
473
+ _store;
474
+ _namespace;
475
+ _keySeparator = ":";
476
+ _capabilities;
477
+ /**
478
+ * Creates a new KeyvBridgeAdapter instance.
479
+ * @param store - The underlying promise-based store to bridge
480
+ * @param options - Configuration options for the adapter
481
+ */
482
+ constructor(store, options) {
483
+ super({ throwOnHookError: false });
484
+ this._store = store;
485
+ if (options?.keySeparator) this._keySeparator = options.keySeparator;
486
+ if (options?.namespace) this._namespace = options.namespace;
487
+ this._capabilities = detectKeyvStorage(store);
488
+ if (typeof store.on === "function") store.on(KeyvEvents.ERROR, (error) => this.emit(KeyvEvents.ERROR, error));
489
+ }
490
+ /**
491
+ * Gets the underlying store instance.
492
+ */
493
+ get store() {
494
+ return this._store;
495
+ }
496
+ /**
497
+ * Sets the underlying store instance.
498
+ */
499
+ set store(store) {
500
+ this._store = store;
501
+ }
502
+ /**
503
+ * Gets the detected capabilities of the underlying store.
504
+ */
505
+ get capabilities() {
506
+ return this._capabilities;
507
+ }
508
+ /**
509
+ * Gets the current key separator used between namespace and key.
510
+ */
511
+ get keySeparator() {
512
+ return this._keySeparator;
513
+ }
514
+ /**
515
+ * Sets the key separator used between namespace and key.
516
+ */
517
+ set keySeparator(separator) {
518
+ this._keySeparator = separator;
519
+ }
520
+ /**
521
+ * Gets the current namespace.
522
+ */
523
+ get namespace() {
524
+ return this._namespace;
525
+ }
526
+ /**
527
+ * Sets the namespace.
528
+ */
529
+ set namespace(namespace) {
530
+ this._namespace = namespace;
531
+ }
532
+ /**
533
+ * Creates a prefixed key by combining the namespace and key with the separator.
534
+ * @param key - The base key
535
+ * @param namespace - Optional namespace to prefix the key with
536
+ * @returns The prefixed key if namespace is provided, otherwise the original key
537
+ */
538
+ getKeyPrefix(key, namespace) {
539
+ if (namespace) return `${namespace}${this._keySeparator}${key}`;
540
+ return key;
541
+ }
542
+ /**
543
+ * Parses a prefixed key to extract the namespace and original key.
544
+ * @param key - The prefixed key to parse
545
+ * @returns An object containing the namespace (if present) and the original key
546
+ */
547
+ getKeyPrefixData(key) {
548
+ if (this._namespace && key.startsWith(`${this._namespace}${this._keySeparator}`)) return {
549
+ namespace: this._namespace,
550
+ key: key.slice(this._namespace.length + this._keySeparator.length)
551
+ };
552
+ return { key };
553
+ }
554
+ /**
555
+ * Retrieves a value from the store by key.
556
+ * Automatically handles namespace prefixing and TTL expiration.
557
+ * @param key - The key to retrieve
558
+ * @returns The stored data, or undefined if not found or expired
559
+ */
560
+ async get(key) {
561
+ const keyPrefix = this.getKeyPrefix(key, this._namespace);
562
+ const data = await this._store.get(keyPrefix);
563
+ if (data === void 0 || data === null) return;
564
+ if (isDataExpired(data)) {
565
+ await this._store.delete(keyPrefix);
566
+ return;
567
+ }
568
+ return data;
569
+ }
570
+ /**
571
+ * Retrieves multiple values from the store by their keys.
572
+ * Delegates to the store's native getMany if available.
573
+ * @param keys - Array of keys to retrieve
574
+ * @returns Array of stored data in the same order as the input keys
575
+ */
576
+ async getMany(keys) {
577
+ if (this._capabilities.methods.getMany.exists) {
578
+ const prefixedKeys = keys.map((key) => this.getKeyPrefix(key, this._namespace));
579
+ /* v8 ignore next -- @preserve */
580
+ const results = await this._store.getMany?.(prefixedKeys) ?? [];
581
+ const values = [];
582
+ for (const [index, data] of results.entries()) {
583
+ if (data === void 0 || data === null) {
584
+ values.push(void 0);
585
+ continue;
586
+ }
587
+ if (isDataExpired(data)) {
588
+ await this._store.delete(prefixedKeys[index]);
589
+ values.push(void 0);
590
+ continue;
591
+ }
592
+ values.push(data);
593
+ }
594
+ return values;
595
+ }
596
+ const values = [];
597
+ for (const key of keys) {
598
+ const data = await this.get(key);
599
+ values.push(data);
600
+ }
601
+ return values;
602
+ }
603
+ /**
604
+ * Stores a value in the store with an optional TTL.
605
+ * @param key - The key to store the value under
606
+ * @param value - The value to store
607
+ * @param ttl - Optional time-to-live in milliseconds
608
+ * @returns Always returns true indicating success
609
+ */
610
+ async set(key, value, ttl) {
611
+ const keyPrefix = this.getKeyPrefix(key, this._namespace);
612
+ const result = await this._store.set(keyPrefix, value, ttl);
613
+ if (typeof result === "boolean") return result;
614
+ return true;
615
+ }
616
+ /**
617
+ * Stores multiple entries in the store at once.
618
+ * Delegates to the store's native setMany if available, otherwise loops over set.
619
+ * @param entries - Array of entries containing key, value, and optional TTL
620
+ */
621
+ async setMany(entries) {
622
+ if (this._capabilities.methods.setMany.exists) {
623
+ const prefixedEntries = entries.map((entry) => ({
624
+ ...entry,
625
+ key: this.getKeyPrefix(entry.key, this._namespace)
626
+ }));
627
+ await this._store.setMany?.(prefixedEntries);
628
+ return entries.map(() => true);
629
+ }
630
+ const results = [];
631
+ for (const entry of entries) {
632
+ await this.set(entry.key, entry.value, entry.ttl);
633
+ results.push(true);
634
+ }
635
+ return results;
636
+ }
637
+ /**
638
+ * Checks if a key exists in the store and is not expired.
639
+ * Delegates to the store's native has if available.
640
+ * @param key - The key to check
641
+ * @returns True if the key exists and is not expired, false otherwise
642
+ */
643
+ async has(key) {
644
+ if (this._capabilities.methods.has.exists) {
645
+ const keyPrefix = this.getKeyPrefix(key, this._namespace);
646
+ /* v8 ignore next -- @preserve */
647
+ return this._store.has?.(keyPrefix) ?? false;
648
+ }
649
+ return await this.get(key) !== void 0;
650
+ }
651
+ /**
652
+ * Checks if multiple keys exist in the store and are not expired.
653
+ * Delegates to the store's native hasMany if available.
654
+ * @param keys - Array of keys to check
655
+ * @returns Array of booleans indicating existence for each key
656
+ */
657
+ async hasMany(keys) {
658
+ if (this._capabilities.methods.hasMany.exists) {
659
+ const prefixedKeys = keys.map((key) => this.getKeyPrefix(key, this._namespace));
660
+ /* v8 ignore next -- @preserve */
661
+ return this._store.hasMany?.(prefixedKeys) ?? [];
662
+ }
663
+ const results = [];
664
+ for (const key of keys) results.push(await this.has(key));
665
+ return results;
666
+ }
667
+ /**
668
+ * Deletes a value from the store by key.
669
+ * @param key - The key to delete
670
+ * @returns True if the key was deleted, false otherwise
671
+ */
672
+ async delete(key) {
673
+ const keyPrefix = this.getKeyPrefix(key, this._namespace);
674
+ return this._store.delete(keyPrefix);
675
+ }
676
+ /**
677
+ * Deletes multiple keys from the store at once.
678
+ * Delegates to the store's native deleteMany if available.
679
+ * @param keys - Array of keys to delete
680
+ * @returns Array of booleans indicating success for each key
681
+ */
682
+ async deleteMany(keys) {
683
+ if (this._capabilities.methods.deleteMany.exists) {
684
+ const prefixedKeys = keys.map((key) => this.getKeyPrefix(key, this._namespace));
685
+ /* v8 ignore next -- @preserve */
686
+ const result = await this._store.deleteMany?.(prefixedKeys) ?? [];
687
+ return Array.isArray(result) ? result : keys.map(() => result);
688
+ }
689
+ const results = [];
690
+ for (const key of keys) try {
691
+ const keyPrefix = this.getKeyPrefix(key, this._namespace);
692
+ const result = await this._store.delete(keyPrefix);
693
+ results.push(result);
694
+ } catch (error) {
695
+ this.emit(KeyvEvents.ERROR, error);
696
+ results.push(false);
697
+ }
698
+ return results;
699
+ }
700
+ /**
701
+ * Clears entries from the store. If a namespace is set and the store supports
702
+ * iteration, only entries within that namespace are removed. Otherwise, the
703
+ * entire store is cleared.
704
+ */
705
+ async clear() {
706
+ if (!this._namespace || !this._capabilities.methods.iterator.exists) {
707
+ await this._store.clear();
708
+ return;
709
+ }
710
+ const prefix = `${this._namespace}${this._keySeparator}`;
711
+ const keysToDelete = [];
712
+ /* v8 ignore next -- @preserve */
713
+ for await (const entry of this._store.iterator?.(this._namespace) ?? []) {
714
+ const key = Array.isArray(entry) ? entry[0] : entry;
715
+ if (typeof key === "string" && key.startsWith(prefix)) keysToDelete.push(key);
716
+ }
717
+ for (const key of keysToDelete) await this._store.delete(key);
718
+ }
719
+ /**
720
+ * Creates an async iterator for iterating over store entries.
721
+ * If the underlying store does not support iteration, returns an empty generator.
722
+ * @returns An async generator yielding [key, value] pairs
723
+ */
724
+ async *iterator() {
725
+ if (!this._capabilities.methods.iterator.exists) return;
726
+ const namespace = this._namespace;
727
+ const prefix = namespace ? `${namespace}${this._keySeparator}` : void 0;
728
+ /* v8 ignore next -- @preserve */
729
+ for await (const entry of this._store.iterator?.(this._namespace) ?? []) {
730
+ const [key, data] = Array.isArray(entry) ? entry : [entry];
731
+ if (prefix && typeof key === "string" && !key.startsWith(prefix)) continue;
732
+ if (data && isDataExpired(data)) {
733
+ await this._store.delete(key);
734
+ continue;
735
+ }
736
+ yield [prefix && typeof key === "string" ? key.slice(prefix.length) : key, data];
737
+ }
738
+ }
739
+ /**
740
+ * Disconnects from the underlying store.
741
+ * No-op if the store does not support disconnect.
742
+ */
743
+ async disconnect() {
744
+ if (this._capabilities.methods.disconnect.exists) await this._store.disconnect?.();
745
+ }
746
+ };
747
+ //#endregion
748
+ //#region src/json-serializer.ts
749
+ function getGlobalBuffer() {
750
+ return globalThis.Buffer;
751
+ }
752
+ function bytesToBase64(bytes) {
753
+ const buffer = getGlobalBuffer();
754
+ if (buffer) return buffer.from(bytes).toString("base64");
755
+ let binary = "";
756
+ for (const byte of bytes) binary += String.fromCharCode(byte);
757
+ return btoa(binary);
758
+ }
759
+ function base64ToBytes(value) {
760
+ const buffer = getGlobalBuffer();
761
+ if (buffer) return buffer.from(value, "base64");
762
+ const binary = atob(value);
763
+ const bytes = new Uint8Array(binary.length);
764
+ for (let index = 0; index < binary.length; index++) bytes[index] = binary.charCodeAt(index);
765
+ return bytes;
766
+ }
767
+ function isBinaryValue(value) {
768
+ if (getGlobalBuffer()?.isBuffer(value)) return true;
769
+ return value instanceof Uint8Array;
770
+ }
771
+ /**
772
+ * Pre-process a value tree, converting Buffer and BigInt to tagged strings
773
+ * before JSON.stringify can call toJSON() on them.
774
+ */
775
+ function prepare(value) {
776
+ if (value === null || value === void 0) return value;
777
+ if (isBinaryValue(value)) return `:base64:${bytesToBase64(value)}`;
778
+ if (typeof value === "bigint") return `:bigint:${value.toString()}`;
779
+ if (typeof value === "string") return value.startsWith(":") ? `:${value}` : value;
780
+ if (Array.isArray(value)) return value.map((item) => prepare(item));
781
+ if (typeof value === "object") {
782
+ if (typeof value.toJSON === "function") return prepare(value.toJSON());
783
+ const result = {};
784
+ for (const key of Object.keys(value)) if (value[key] !== void 0) result[key] = prepare(value[key]);
785
+ return result;
786
+ }
787
+ return value;
788
+ }
789
+ var KeyvJsonSerializer = class {
790
+ stringify(object) {
791
+ return JSON.stringify(prepare(object));
792
+ }
793
+ parse(data) {
794
+ return JSON.parse(data, (_, value) => {
795
+ if (typeof value === "string") {
796
+ if (value.startsWith(":bigint:")) return BigInt(value.slice(8));
797
+ if (value.startsWith(":base64:")) return base64ToBytes(value.slice(8));
798
+ return value.startsWith(":") ? value.slice(1) : value;
799
+ }
800
+ return value;
801
+ });
802
+ }
803
+ };
804
+ const jsonSerializer = new KeyvJsonSerializer();
805
+ //#endregion
806
+ //#region src/sanitize.ts
807
+ const categoryPatterns = {
808
+ sql: [
809
+ /;/g,
810
+ /--/g,
811
+ /\/\*/g
812
+ ],
813
+ mongo: [/^\$/g, /\{\s*\$/g],
814
+ escape: [
815
+ /\0/g,
816
+ /\r/g,
817
+ /\n/g
818
+ ],
819
+ path: [/\.\.\//g, /\.\.\\/g]
820
+ };
821
+ /**
822
+ * Compile an array of RegExp patterns from the enabled categories.
823
+ * Returns `undefined` when nothing is enabled.
824
+ */
825
+ function buildPatterns(options) {
826
+ const patterns = [];
827
+ for (const [category, regexes] of Object.entries(categoryPatterns)) if (options[category] !== false) patterns.push(...regexes);
828
+ return patterns.length > 0 ? patterns : void 0;
829
+ }
830
+ /**
831
+ * Run all patterns against a string, stripping matched sequences.
832
+ */
833
+ function applyPatterns(value, patterns) {
834
+ for (const pattern of patterns) {
835
+ pattern.lastIndex = 0;
836
+ value = value.replace(pattern, "");
837
+ }
838
+ return value;
839
+ }
840
+ const allOn = {
841
+ escape: true,
842
+ mongo: true,
843
+ path: true,
844
+ sql: true
845
+ };
846
+ const allOff = {
847
+ escape: false,
848
+ mongo: false,
849
+ path: false,
850
+ sql: false
851
+ };
852
+ /**
853
+ * Encapsulates key and namespace sanitization with an LRU result cache.
854
+ */
855
+ var KeyvSanitize = class {
856
+ _keys = { ...allOff };
857
+ _namespace = { ...allOff };
858
+ _keyPatterns;
859
+ _namespacePatterns;
860
+ _enabled = false;
861
+ _cacheKeys = /* @__PURE__ */ new Map();
862
+ _cacheNamespaces = /* @__PURE__ */ new Map();
863
+ _cacheMax = 1e4;
864
+ constructor(options) {
865
+ if (options !== void 0) this.updateOptions(options);
866
+ }
867
+ /**
868
+ * The key sanitization pattern configuration.
869
+ */
870
+ get keys() {
871
+ return this._keys;
872
+ }
873
+ /**
874
+ * Whether any sanitization pattern (keys or namespace) is enabled.
875
+ */
876
+ get enabled() {
877
+ return this._enabled;
878
+ }
879
+ /**
880
+ * The namespace sanitization pattern configuration.
881
+ */
882
+ get namespace() {
883
+ return this._namespace;
884
+ }
885
+ /**
886
+ * Update the sanitization configuration. Recompiles patterns and clears the cache.
887
+ */
888
+ updateOptions(options) {
889
+ this._keys = this.resolvePatterns(options.keys);
890
+ this._namespace = this.resolvePatterns(options.namespace);
891
+ this._enabled = Object.values(this._keys).some(Boolean) || Object.values(this._namespace).some(Boolean);
892
+ this._keyPatterns = buildPatterns(this._keys);
893
+ this._namespacePatterns = buildPatterns(this._namespace);
894
+ this._cacheKeys.clear();
895
+ this._cacheNamespaces.clear();
896
+ }
897
+ /**
898
+ * Sanitize a single key. Uses an LRU cache for repeated lookups.
899
+ */
900
+ cleanKey(key) {
901
+ if (!this._keyPatterns) return key;
902
+ const cached = this._cacheKeys.get(key);
903
+ if (cached !== void 0) {
904
+ this._cacheKeys.delete(key);
905
+ this._cacheKeys.set(key, cached);
906
+ return cached;
907
+ }
908
+ const result = applyPatterns(key, this._keyPatterns);
909
+ this._cacheKeys.set(key, result);
910
+ if (this._cacheKeys.size > this._cacheMax) {
911
+ const first = this._cacheKeys.keys().next().value;
912
+ if (first !== void 0) this._cacheKeys.delete(first);
913
+ }
914
+ return result;
915
+ }
916
+ /**
917
+ * Sanitize an array of keys.
918
+ */
919
+ cleanKeys(keys) {
920
+ if (!this._keyPatterns) return keys;
921
+ return keys.map((k) => this.cleanKey(k));
922
+ }
923
+ /**
924
+ * Sanitize a namespace string. Uses an LRU cache for repeated lookups.
925
+ */
926
+ cleanNamespace(ns) {
927
+ if (!this._namespacePatterns) return ns;
928
+ const cached = this._cacheNamespaces.get(ns);
929
+ if (cached !== void 0) {
930
+ this._cacheNamespaces.delete(ns);
931
+ this._cacheNamespaces.set(ns, cached);
932
+ return cached;
933
+ }
934
+ const result = applyPatterns(ns, this._namespacePatterns);
935
+ this._cacheNamespaces.set(ns, result);
936
+ if (this._cacheNamespaces.size > this._cacheMax) {
937
+ const first = this._cacheNamespaces.keys().next().value;
938
+ if (first !== void 0) this._cacheNamespaces.delete(first);
939
+ }
940
+ return result;
941
+ }
942
+ /**
943
+ * Clear the LRU caches.
944
+ */
945
+ clearCache() {
946
+ this._cacheKeys.clear();
947
+ this._cacheNamespaces.clear();
948
+ }
949
+ resolvePatterns(options) {
950
+ if (options === false || options === void 0) return { ...allOff };
951
+ if (options === true) return { ...allOn };
952
+ return {
953
+ sql: options.sql !== false,
954
+ mongo: options.mongo !== false,
955
+ escape: options.escape !== false,
956
+ path: options.path !== false
957
+ };
958
+ }
959
+ };
960
+ //#endregion
961
+ //#region src/stats.ts
962
+ var KeyvStats = class {
963
+ _hits = 0;
964
+ _misses = 0;
965
+ _sets = 0;
966
+ _deletes = 0;
967
+ _errors = 0;
968
+ _maxEntries = 1e3;
969
+ _enabled = false;
970
+ hitKeysMap = /* @__PURE__ */ new Map();
971
+ missKeysMap = /* @__PURE__ */ new Map();
972
+ setKeysMap = /* @__PURE__ */ new Map();
973
+ deleteKeysMap = /* @__PURE__ */ new Map();
974
+ errorKeysMap = /* @__PURE__ */ new Map();
975
+ _emitter;
976
+ _listeners = /* @__PURE__ */ new Map();
977
+ constructor(options) {
978
+ if (options?.maxEntries !== void 0) this._maxEntries = options.maxEntries;
979
+ if (options?.enabled !== void 0) this._enabled = options.enabled;
980
+ this.reset();
981
+ if (options?.emitter) if (this._enabled === true) this.subscribe(options.emitter);
982
+ else this._emitter = options.emitter;
983
+ }
984
+ /**
985
+ * Total number of cache hits.
986
+ */
987
+ get hits() {
988
+ return this._hits;
989
+ }
990
+ /**
991
+ * Total number of cache misses.
992
+ */
993
+ get misses() {
994
+ return this._misses;
995
+ }
996
+ /**
997
+ * Total number of cache sets.
998
+ */
999
+ get sets() {
1000
+ return this._sets;
1001
+ }
1002
+ /**
1003
+ * Total number of cache deletes.
1004
+ */
1005
+ get deletes() {
1006
+ return this._deletes;
1007
+ }
1008
+ /**
1009
+ * Total number of cache errors.
1010
+ */
1011
+ get errors() {
1012
+ return this._errors;
1013
+ }
1014
+ /**
1015
+ * LRU-bounded map of key to hit count.
1016
+ */
1017
+ get hitKeys() {
1018
+ return this.hitKeysMap;
1019
+ }
1020
+ /**
1021
+ * LRU-bounded map of key to miss count.
1022
+ */
1023
+ get missKeys() {
1024
+ return this.missKeysMap;
1025
+ }
1026
+ /**
1027
+ * LRU-bounded map of key to set count.
1028
+ */
1029
+ get setKeys() {
1030
+ return this.setKeysMap;
1031
+ }
1032
+ /**
1033
+ * LRU-bounded map of key to delete count.
1034
+ */
1035
+ get deleteKeys() {
1036
+ return this.deleteKeysMap;
1037
+ }
1038
+ /**
1039
+ * LRU-bounded map of key to error count.
1040
+ */
1041
+ get errorKeys() {
1042
+ return this.errorKeysMap;
1043
+ }
1044
+ /**
1045
+ * Maximum number of entries per event-type LRU map.
1046
+ * @default 1000
1047
+ */
1048
+ get maxEntries() {
1049
+ return this._maxEntries;
1050
+ }
1051
+ /**
1052
+ * Set the maximum number of entries per event-type LRU map.
1053
+ * @param {number} value the new maximum entries
1054
+ */
1055
+ /* v8 ignore next 3 -- @preserve */
1056
+ set maxEntries(value) {
1057
+ this._maxEntries = value;
1058
+ }
1059
+ /**
1060
+ * Whether stats tracking is enabled.
1061
+ * @default false
1062
+ */
1063
+ get enabled() {
1064
+ return this._enabled;
1065
+ }
1066
+ /**
1067
+ * Enable or disable stats tracking. If false it will unsubscribe from the events
1068
+ * @param {boolean} value true to enable, false to disable
1069
+ */
1070
+ set enabled(value) {
1071
+ this._enabled = value;
1072
+ if (this._enabled === false && this._listeners.size !== 0) {
1073
+ const emitter = this._emitter;
1074
+ this.unsubscribe();
1075
+ this._emitter = emitter;
1076
+ } else if (this._enabled === true && this._emitter && this._listeners.size === 0) this.subscribe(this._emitter);
1077
+ }
1078
+ /**
1079
+ * Build a composite key from a telemetry event.
1080
+ * Format: "namespace:key" if namespace is present, otherwise just "key".
1081
+ */
1082
+ buildKeyEventName(event) {
1083
+ if (event.namespace && event.key) return `${event.namespace}:${event.key}`;
1084
+ return event.key ?? "";
1085
+ }
1086
+ /**
1087
+ * Increment the count for a key in an LRU-bounded map.
1088
+ * Deletes and re-inserts to maintain LRU order, evicts the oldest entry when full.
1089
+ */
1090
+ incrementKeys(map, compositeKey) {
1091
+ if (this.maxEntries <= 0 || !compositeKey) return;
1092
+ const current = map.get(compositeKey) ?? 0;
1093
+ map.delete(compositeKey);
1094
+ map.set(compositeKey, current + 1);
1095
+ if (map.size > this.maxEntries) {
1096
+ const first = map.keys().next().value;
1097
+ if (first !== void 0) map.delete(first);
1098
+ }
1099
+ }
1100
+ /**
1101
+ * Subscribe to telemetry events from an emitter (e.g. a Keyv instance).
1102
+ * Automatically increments the corresponding stat counters and LRU key maps on each event.
1103
+ * @param {IEventEmitter} emitter the event emitter to subscribe to
1104
+ */
1105
+ subscribe(emitter) {
1106
+ this.unsubscribe();
1107
+ this._emitter = emitter;
1108
+ const hitListener = (event) => {
1109
+ this._hits++;
1110
+ this.incrementKeys(this.hitKeysMap, this.buildKeyEventName(event));
1111
+ };
1112
+ const missListener = (event) => {
1113
+ this._misses++;
1114
+ this.incrementKeys(this.missKeysMap, this.buildKeyEventName(event));
1115
+ };
1116
+ const setListener = (event) => {
1117
+ this._sets++;
1118
+ this.incrementKeys(this.setKeysMap, this.buildKeyEventName(event));
1119
+ };
1120
+ const deleteListener = (event) => {
1121
+ this._deletes++;
1122
+ this.incrementKeys(this.deleteKeysMap, this.buildKeyEventName(event));
1123
+ };
1124
+ const errorListener = (event) => {
1125
+ this._errors++;
1126
+ this.incrementKeys(this.errorKeysMap, this.buildKeyEventName(event));
1127
+ };
1128
+ this._listeners.set("stat:hit", hitListener);
1129
+ this._listeners.set("stat:miss", missListener);
1130
+ this._listeners.set("stat:set", setListener);
1131
+ this._listeners.set("stat:delete", deleteListener);
1132
+ this._listeners.set("stat:error", errorListener);
1133
+ emitter.on("stat:hit", hitListener);
1134
+ emitter.on("stat:miss", missListener);
1135
+ emitter.on("stat:set", setListener);
1136
+ emitter.on("stat:delete", deleteListener);
1137
+ emitter.on("stat:error", errorListener);
1138
+ }
1139
+ /**
1140
+ * Unsubscribe from the currently subscribed emitter, removing all telemetry event listeners.
1141
+ */
1142
+ unsubscribe() {
1143
+ if (!this._emitter) return;
1144
+ for (const [eventName, listener] of this._listeners) this._emitter.off(eventName, listener);
1145
+ this._listeners.clear();
1146
+ this._emitter = void 0;
1147
+ }
1148
+ /**
1149
+ * Reset all counters and LRU key maps to their initial state.
1150
+ */
1151
+ reset() {
1152
+ this._hits = 0;
1153
+ this._misses = 0;
1154
+ this._sets = 0;
1155
+ this._deletes = 0;
1156
+ this._errors = 0;
1157
+ this.hitKeysMap.clear();
1158
+ this.missKeysMap.clear();
1159
+ this.setKeysMap.clear();
1160
+ this.deleteKeysMap.clear();
1161
+ this.errorKeysMap.clear();
1162
+ }
1163
+ };
1164
+ //#endregion
1165
+ //#region src/keyv.ts
1166
+ var Keyv = class Keyv extends Hookified {
1167
+ /**
1168
+ * Stats manager for tracking cache operation metrics (hits, misses, sets, deletes, errors).
1169
+ * @default this is disabled.
1170
+ */
1171
+ _stats;
1172
+ /**
1173
+ * Default time to live in milliseconds. Can be overridden per-key via {@link set}.
1174
+ */
1175
+ _ttl;
1176
+ /**
1177
+ * Key prefix namespace used to isolate keys across different Keyv instances sharing the same store.
1178
+ */
1179
+ _namespace;
1180
+ /**
1181
+ * The underlying storage adapter. Defaults to an in-memory {@link Map}.
1182
+ */
1183
+ _store = new KeyvMemoryAdapter(/* @__PURE__ */ new Map());
1184
+ /**
1185
+ * Pluggable serialization adapter with `stringify` and `parse` methods.
1186
+ * When `undefined`, the built-in {@link KeyvJsonSerializer} is used.
1187
+ */
1188
+ _serialization;
1189
+ /**
1190
+ * Pluggable compression adapter with `compress` and `decompress` methods.
1191
+ */
1192
+ _compression;
1193
+ /**
1194
+ * Pluggable encryption adapter with `encrypt` and `decrypt` methods.
1195
+ */
1196
+ _encryption;
1197
+ /**
1198
+ * Sanitization handler for keys and namespaces. By default it is disabled.
1199
+ */
1200
+ _sanitize;
1201
+ /**
1202
+ * When true, Keyv checks expiry at its layer on get/getMany/has/hasMany.
1203
+ */
1204
+ _checkExpired = false;
1205
+ /**
1206
+ * Keyv Constructor
1207
+ * @param {KeyvStorageAdapter | KeyvOptions} store
1208
+ * @param {Omit<KeyvOptions, 'store'>} [options] if you provide the store you can then provide the Keyv Options
1209
+ */
1210
+ constructor(store, options) {
1211
+ const mergedOptions = Keyv.resolveOptions(store, options);
1212
+ super({
1213
+ throwOnHookError: false,
1214
+ throwOnEmptyListeners: true,
1215
+ throwOnEmitError: mergedOptions.throwOnErrors ?? false
1216
+ });
1217
+ this.deprecatedHooks = buildDeprecatedHooks();
1218
+ this._compression = mergedOptions.compression;
1219
+ this._encryption = mergedOptions.encryption;
1220
+ this.initSerialization(mergedOptions);
1221
+ this.initSanitize(mergedOptions);
1222
+ this.initNamespace(mergedOptions.namespace);
1223
+ this.initStats(mergedOptions);
1224
+ if (mergedOptions.store) this.setStore(mergedOptions.store);
1225
+ this.setTtl(mergedOptions.ttl);
1226
+ this._checkExpired = mergedOptions.checkExpired ?? false;
1227
+ }
1228
+ /**
1229
+ * Get the current storage adapter.
1230
+ * @returns {KeyvStorageAdapter} The current storage adapter.
1231
+ */
1232
+ get store() {
1233
+ return this._store;
1234
+ }
1235
+ /**
1236
+ * Set the storage adapter.
1237
+ * @param {KeyvStorageAdapter | Map<any, any> | any} store The storage adapter to set.
1238
+ */
1239
+ set store(store) {
1240
+ this.setStore(store);
1241
+ }
1242
+ /**
1243
+ * Get the current compression adapter.
1244
+ * @returns {KeyvCompressionAdapter | undefined} The current compression adapter.
1245
+ */
1246
+ get compression() {
1247
+ return this._compression;
1248
+ }
1249
+ /**
1250
+ * Set the compression adapter.
1251
+ * @param {KeyvCompressionAdapter | undefined} compress The compression adapter to set.
1252
+ */
1253
+ set compression(compress) {
1254
+ this._compression = compress;
1255
+ }
1256
+ /**
1257
+ * Get the current encryption adapter.
1258
+ * @returns {KeyvEncryptionAdapter | undefined} The current encryption adapter.
1259
+ */
1260
+ get encryption() {
1261
+ return this._encryption;
1262
+ }
1263
+ /**
1264
+ * Set the encryption adapter.
1265
+ * @param {KeyvEncryptionAdapter | undefined} encryption The encryption adapter to set.
1266
+ */
1267
+ set encryption(encryption) {
1268
+ this._encryption = encryption;
1269
+ }
1270
+ /**
1271
+ * Get the current namespace.
1272
+ * @returns {string | undefined} The current namespace.
1273
+ */
1274
+ get namespace() {
1275
+ return this._namespace;
1276
+ }
1277
+ /**
1278
+ * Set the current namespace.
1279
+ * @param {string | undefined} namespace The namespace to set.
1280
+ */
1281
+ set namespace(namespace) {
1282
+ this._namespace = namespace && this._sanitize.enabled ? this._sanitize.cleanNamespace(namespace) : namespace;
1283
+ this._store.namespace = this._namespace;
1284
+ }
1285
+ /**
1286
+ * Get the current TTL.
1287
+ * @returns {number} The current TTL in milliseconds.
1288
+ */
1289
+ get ttl() {
1290
+ return this._ttl;
1291
+ }
1292
+ /**
1293
+ * Set the current TTL.
1294
+ * @param {number} ttl The TTL to set in milliseconds.
1295
+ */
1296
+ set ttl(ttl) {
1297
+ this.setTtl(ttl);
1298
+ }
1299
+ /**
1300
+ * Get the current serialization adapter. If `undefined`, serialization is not enabled.
1301
+ * @returns {KeyvSerializationAdapter | undefined} The current serialization adapter.
1302
+ */
1303
+ get serialization() {
1304
+ return this._serialization;
1305
+ }
1306
+ /**
1307
+ * Set the current serialization adapter. Pass a `KeyvSerializationAdapter` to enable
1308
+ * custom serialization, or `undefined` to disable serialization entirely.
1309
+ * @param {KeyvSerializationAdapter | undefined} serialization The serialization adapter to set.
1310
+ */
1311
+ set serialization(serialization) {
1312
+ this._serialization = serialization === false ? void 0 : serialization;
1313
+ }
1314
+ /**
1315
+ * Get the current throwOnErrors value. When enabled, all errors with throw. By default, errors
1316
+ * will only throw if there are no listeners to the error event.
1317
+ * @return {boolean} The current throwOnErrors value.
1318
+ */
1319
+ get throwOnErrors() {
1320
+ return this.throwOnEmitError;
1321
+ }
1322
+ /**
1323
+ * Set the current throwOnErrors value. When enabled, all errors will throw. By default, errors
1324
+ * will only throw if there are no listeners to the error event.
1325
+ * @param {boolean} value The throwOnErrors value to set.
1326
+ */
1327
+ set throwOnErrors(value) {
1328
+ this.throwOnEmitError = value;
1329
+ }
1330
+ /**
1331
+ * Get the current sanitize adapter. Sanitization is disabled by default. To
1332
+ * enable it `sanitize.keys` or `sanitize.namespace` to true or set KeyvSanitizePatterns
1333
+ * to each.
1334
+ * @returns {KeyvSanitizeAdapter} The current sanitize adapter.
1335
+ */
1336
+ get sanitize() {
1337
+ return this._sanitize;
1338
+ }
1339
+ /**
1340
+ * Set the sanitize adapter directly and will run sanitization on namespace.
1341
+ * @param {KeyvSanitizeAdapter} value The sanitize adapter to use.
1342
+ */
1343
+ set sanitize(value) {
1344
+ this._sanitize = value;
1345
+ /* v8 ignore next -- @preserve */
1346
+ this._namespace = this._namespace && this._sanitize.enabled ? this._sanitize.cleanNamespace(this._namespace) : this._namespace;
1347
+ }
1348
+ /**
1349
+ * Get the stats. This is just for this instance
1350
+ * @returns {KeyvStats} The current stats.
1351
+ */
1352
+ get stats() {
1353
+ return this._stats;
1354
+ }
1355
+ /**
1356
+ * When true, Keyv checks expiry at its layer on get/getMany/has/hasMany.
1357
+ * When false (default), trusts the storage adapter.
1358
+ */
1359
+ get checkExpired() {
1360
+ return this._checkExpired;
1361
+ }
1362
+ /**
1363
+ * Set the stats. When setting a new instance it will unsubscribe the old listeners
1364
+ * and subscribe the new instance.
1365
+ * @param {KeyvStats} stats The stats instance to set.
1366
+ */
1367
+ set stats(stats) {
1368
+ this._stats.unsubscribe();
1369
+ this._stats = stats;
1370
+ this._stats.subscribe(this);
1371
+ }
1372
+ /**
1373
+ * Resolves a store to a fully-compliant KeyvStorageAdapter using a 3-tier detection chain:
1374
+ * 1. If the store already implements the full KeyvStorageAdapter interface, use it directly.
1375
+ * 2. If the store is map-like (synchronous get/set/delete/has), wrap it in KeyvMemoryAdapter.
1376
+ * 3. If the store has async get/set/delete/clear, wrap it in KeyvBridgeAdapter.
1377
+ * 4. Otherwise, emit an error and fall back to a default in-memory KeyvMemoryAdapter.
1378
+ *
1379
+ * NOTE: this is used for internal but provided public for custom adapter testing
1380
+ * @param {unknown} store The store to resolve.
1381
+ * @returns {KeyvStorageAdapter} A fully-compliant storage adapter.
1382
+ */
1383
+ resolveStore(store) {
1384
+ const cap = detectKeyvStorage(store);
1385
+ if (cap.store === "keyvStorage") return store;
1386
+ if (cap.store === "mapLike") return new KeyvMemoryAdapter(store);
1387
+ if (cap.store === "asyncMap") return new KeyvBridgeAdapter(store);
1388
+ this.emit(KeyvEvents.ERROR, /* @__PURE__ */ new Error("Could not use the provided storage adapter, falling back to KeyvMemoryAdapter with Map"));
1389
+ return new KeyvMemoryAdapter(/* @__PURE__ */ new Map());
1390
+ }
1391
+ /**
1392
+ * Sets the storage adapter by resolving it via {@link resolveStore}, then wires up
1393
+ * error forwarding and namespace propagation.
1394
+ * @param {KeyvStorageAdapter | Map<any, any> | any} store The storage adapter to set.
1395
+ */
1396
+ setStore(store) {
1397
+ this._store = this.resolveStore(store);
1398
+ if (typeof this._store.on === "function") this._store.on(KeyvEvents.ERROR, (error) => this.emit(KeyvEvents.ERROR, error));
1399
+ this._store.namespace = this._namespace;
1400
+ }
1401
+ /**
1402
+ * Sets the TTL, treating zero and negative values as undefined (no TTL).
1403
+ * @param {number | undefined} ttl The TTL to set in milliseconds.
1404
+ */
1405
+ setTtl(ttl) {
1406
+ if (typeof ttl === "number" && ttl <= 0) {
1407
+ this._ttl = void 0;
1408
+ return;
1409
+ }
1410
+ this._ttl = ttl;
1411
+ }
1412
+ async get(key) {
1413
+ if (Array.isArray(key)) return this.getMany(key);
1414
+ key = this._sanitize.enabled ? this._sanitize.cleanKey(key) : key;
1415
+ if (key === "") return;
1416
+ await this.hookWithDeprecated(KeyvHooks.BEFORE_GET, { key });
1417
+ let rawData;
1418
+ try {
1419
+ rawData = await this._store.get(key);
1420
+ } catch (error) {
1421
+ this.emit(KeyvEvents.ERROR, error);
1422
+ this.emitTelemetry(KeyvEvents.STAT_ERROR, key);
1423
+ }
1424
+ let data;
1425
+ if (this._checkExpired) [data] = await this.decodeWithExpire(key, rawData);
1426
+ else data = rawData === void 0 || rawData === null ? void 0 : typeof rawData === "string" ? await this.decode(rawData) : rawData;
1427
+ if (data === void 0) {
1428
+ await this.hookWithDeprecated(KeyvHooks.AFTER_GET, {
1429
+ key,
1430
+ value: void 0
1431
+ });
1432
+ this.emitTelemetry(KeyvEvents.STAT_MISS, key);
1433
+ return;
1434
+ }
1435
+ await this.hookWithDeprecated(KeyvHooks.AFTER_GET, {
1436
+ key,
1437
+ value: data
1438
+ });
1439
+ this.emitTelemetry(KeyvEvents.STAT_HIT, key);
1440
+ return data.value;
1441
+ }
1442
+ /**
1443
+ * Get many values of keys
1444
+ * @param {string[]} keys passing in a single key or multiple as an array
1445
+ */
1446
+ async getMany(keys) {
1447
+ keys = this._sanitize.enabled ? this._sanitize.cleanKeys(keys) : keys;
1448
+ await this.hookWithDeprecated(KeyvHooks.BEFORE_GET_MANY, { keys });
1449
+ const rawData = await this._store.getMany(keys);
1450
+ let deserialized;
1451
+ if (this._checkExpired) deserialized = await this.decodeWithExpire(keys, rawData);
1452
+ else deserialized = await Promise.all(rawData.map(async (row) => {
1453
+ if (row === void 0 || row === null) return;
1454
+ return typeof row === "string" ? this.decode(row) : row;
1455
+ }));
1456
+ const result = deserialized.map((row) => row !== void 0 ? row.value : void 0);
1457
+ await this.hookWithDeprecated(KeyvHooks.AFTER_GET_MANY, result);
1458
+ for (let i = 0; i < result.length; i++) if (result[i] === void 0) this.emitTelemetry(KeyvEvents.STAT_MISS, keys[i]);
1459
+ else this.emitTelemetry(KeyvEvents.STAT_HIT, keys[i]);
1460
+ return result;
1461
+ }
1462
+ /**
1463
+ * Get the raw value of a key. This is the replacement for setting raw to true in the get() method.
1464
+ * @param {string} key the key to get
1465
+ * @returns {Promise<KeyvStorageGetResult<Value>>} will return a KeyvStorageGetResult<Value> or undefined
1466
+ * if the key does not exist or is expired.
1467
+ */
1468
+ async getRaw(key) {
1469
+ key = this._sanitize.enabled ? this._sanitize.cleanKey(key) : key;
1470
+ if (key === "") return;
1471
+ await this.hookWithDeprecated(KeyvHooks.BEFORE_GET_RAW, { key });
1472
+ const rawData = await this._store.get(key);
1473
+ let data;
1474
+ if (this._checkExpired) [data] = await this.decodeWithExpire(key, rawData);
1475
+ else data = rawData === void 0 || rawData === null ? void 0 : typeof rawData === "string" ? await this.decode(rawData) : rawData;
1476
+ if (data === void 0) {
1477
+ await this.hookWithDeprecated(KeyvHooks.AFTER_GET_RAW, {
1478
+ key,
1479
+ value: void 0
1480
+ });
1481
+ this.emitTelemetry(KeyvEvents.STAT_MISS, key);
1482
+ return;
1483
+ }
1484
+ this.emitTelemetry(KeyvEvents.STAT_HIT, key);
1485
+ await this.hookWithDeprecated(KeyvHooks.AFTER_GET_RAW, {
1486
+ key,
1487
+ value: data
1488
+ });
1489
+ return data;
1490
+ }
1491
+ /**
1492
+ * Get the raw values of many keys. This is the replacement for setting raw to true in the getMany() method.
1493
+ * @param {string[]} keys the keys to get
1494
+ * @returns {Promise<Array<KeyvStorageGetResult<Value>>>} will return an array of KeyvStorageGetResult<Value> or undefined if the key does not exist or is expired.
1495
+ */
1496
+ async getManyRaw(keys) {
1497
+ /* v8 ignore next -- @preserve */
1498
+ keys = this._sanitize.enabled ? this._sanitize.cleanKeys(keys) : keys;
1499
+ await this.hookWithDeprecated(KeyvHooks.BEFORE_GET_MANY_RAW, { keys });
1500
+ if (keys.length === 0) {
1501
+ const result = [];
1502
+ await this.hookWithDeprecated(KeyvHooks.AFTER_GET_MANY_RAW, {
1503
+ keys,
1504
+ values: result
1505
+ });
1506
+ return result;
1507
+ }
1508
+ const rawData = await this._store.getMany(keys);
1509
+ let result;
1510
+ if (this._checkExpired) result = await this.decodeWithExpire(keys, rawData);
1511
+ else result = await Promise.all(rawData.map(async (row) => {
1512
+ if (row === void 0 || row === null) return;
1513
+ return typeof row === "string" ? this.decode(row) : row;
1514
+ }));
1515
+ for (let i = 0; i < result.length; i++) if (result[i] === void 0) this.emitTelemetry(KeyvEvents.STAT_MISS, keys[i]);
1516
+ else this.emitTelemetry(KeyvEvents.STAT_HIT, keys[i]);
1517
+ await this.hookWithDeprecated(KeyvHooks.AFTER_GET_MANY_RAW, {
1518
+ keys,
1519
+ values: result
1520
+ });
1521
+ return result;
1522
+ }
1523
+ /**
1524
+ * Set an item to the store
1525
+ * @param {string | Array<KeyvEntry<Value>>} key the key to use. If you pass in an array of KeyvEntry it will set many items
1526
+ * @param {Value} value the value of the key
1527
+ * @param {number} [ttl] time to live in milliseconds
1528
+ * @returns {boolean} if it sets then it will return a true. On failure will return false.
1529
+ */
1530
+ async set(key, value, ttl) {
1531
+ key = this._sanitize.enabled ? this._sanitize.cleanKey(key) : key;
1532
+ if (key === "") return false;
1533
+ const data = {
1534
+ key,
1535
+ value,
1536
+ ttl
1537
+ };
1538
+ await this.hookWithDeprecated(KeyvHooks.BEFORE_SET, data);
1539
+ data.ttl = resolveTtl(data.ttl, this._ttl);
1540
+ const expires = calculateExpires(data.ttl);
1541
+ if (typeof data.value === "symbol") {
1542
+ this.emit(KeyvEvents.ERROR, "symbol cannot be serialized");
1543
+ this.emitTelemetry(KeyvEvents.STAT_ERROR, key);
1544
+ return false;
1545
+ }
1546
+ const formattedValue = {
1547
+ value: data.value,
1548
+ expires
1549
+ };
1550
+ let result = true;
1551
+ let encodedValue = formattedValue;
1552
+ try {
1553
+ encodedValue = await this.encode(formattedValue);
1554
+ result = await this._store.set(data.key, encodedValue, data.ttl);
1555
+ } catch (error) {
1556
+ result = false;
1557
+ this.emit(KeyvEvents.ERROR, error);
1558
+ this.emitTelemetry(KeyvEvents.STAT_ERROR, key);
1559
+ }
1560
+ await this.hookWithDeprecated(KeyvHooks.AFTER_SET, {
1561
+ key,
1562
+ value: encodedValue,
1563
+ ttl
1564
+ });
1565
+ if (result) this.emitTelemetry(KeyvEvents.STAT_SET, key);
1566
+ return result;
1567
+ }
1568
+ /**
1569
+ * Set many items to the store
1570
+ * @param {Array<KeyvEntry<Value>>} entries the entries to set
1571
+ * @returns {boolean[]} will return an array of booleans if it sets then it will return a true. On failure will return false.
1572
+ */
1573
+ async setMany(entries) {
1574
+ entries = entries.map((e) => ({
1575
+ ...e,
1576
+ key: this._sanitize.enabled ? this._sanitize.cleanKey(e.key) : e.key
1577
+ }));
1578
+ const data = { entries };
1579
+ await this.hookWithDeprecated(KeyvHooks.BEFORE_SET_MANY, data);
1580
+ entries = data.entries;
1581
+ let results = [];
1582
+ try {
1583
+ const serializedEntries = await Promise.all(entries.map(async ({ key, value, ttl }) => {
1584
+ ttl = resolveTtl(ttl, this._ttl);
1585
+ /* v8 ignore next -- @preserve */
1586
+ const expires = calculateExpires(ttl);
1587
+ /* v8 ignore next -- @preserve */
1588
+ if (typeof value === "symbol") {
1589
+ this.emit(KeyvEvents.ERROR, "symbol cannot be serialized");
1590
+ this.emitTelemetry(KeyvEvents.STAT_ERROR, key);
1591
+ throw new Error("symbol cannot be serialized");
1592
+ }
1593
+ const formattedValue = {
1594
+ value,
1595
+ expires
1596
+ };
1597
+ return {
1598
+ key,
1599
+ value: await this.encode(formattedValue),
1600
+ ttl
1601
+ };
1602
+ }));
1603
+ const storeResult = await this._store.setMany(serializedEntries);
1604
+ /* v8 ignore next -- @preserve */
1605
+ results = Array.isArray(storeResult) ? storeResult : entries.map(() => true);
1606
+ this.emitTelemetry(KeyvEvents.STAT_SET, entries.map((e) => e.key));
1607
+ } catch (error) {
1608
+ this.emit(KeyvEvents.ERROR, error);
1609
+ this.emitTelemetry(KeyvEvents.STAT_ERROR, entries.map((e) => e.key));
1610
+ results = entries.map(() => false);
1611
+ }
1612
+ await this.hookWithDeprecated(KeyvHooks.AFTER_SET_MANY, {
1613
+ entries,
1614
+ values: results
1615
+ });
1616
+ return results;
1617
+ }
1618
+ /**
1619
+ * Set a raw value to the store without wrapping or serialization. This is the write-side counterpart to getRaw().
1620
+ * The value should be a KeyvValue object with { value, expires? }. If you need TTL-based expiration,
1621
+ * set `expires` on the value directly (e.g. `{ value: 'bar', expires: Date.now() + 60000 }`).
1622
+ * The store-level TTL is derived automatically from `value.expires`.
1623
+ * @param {string} key the key to set
1624
+ * @param {KeyvValue<Value>} value the raw value envelope to store
1625
+ * @returns {boolean} if it sets then it will return a true. On failure will return false.
1626
+ */
1627
+ async setRaw(key, value) {
1628
+ key = this._sanitize.enabled ? this._sanitize.cleanKey(key) : key;
1629
+ if (key === "") return false;
1630
+ const data = {
1631
+ key,
1632
+ value
1633
+ };
1634
+ await this.hookWithDeprecated(KeyvHooks.BEFORE_SET_RAW, data);
1635
+ const ttl = ttlFromExpires(data.value.expires);
1636
+ let result = true;
1637
+ try {
1638
+ const encodedValue = await this.encode(data.value);
1639
+ const storeResult = await this._store.set(data.key, encodedValue, ttl);
1640
+ if (typeof storeResult === "boolean") result = storeResult;
1641
+ } catch (error) {
1642
+ result = false;
1643
+ this.emit(KeyvEvents.ERROR, error);
1644
+ this.emitTelemetry(KeyvEvents.STAT_ERROR, key);
1645
+ }
1646
+ await this.hookWithDeprecated(KeyvHooks.AFTER_SET_RAW, {
1647
+ key,
1648
+ value: data.value,
1649
+ ttl
1650
+ });
1651
+ if (result) this.emitTelemetry(KeyvEvents.STAT_SET, key);
1652
+ return result;
1653
+ }
1654
+ /**
1655
+ * Set many raw values to the store without wrapping or serialization. This is the write-side counterpart to getManyRaw().
1656
+ * Each entry's value should be a KeyvValue object with { value, expires? }. If you need TTL-based expiration,
1657
+ * set `expires` on each value directly. The store-level TTL is derived automatically from `value.expires`.
1658
+ * @param {KeyvEntry<KeyvValue<Value>>[]} entries the raw entries to set
1659
+ * @returns {boolean[]} will return an array of booleans if it sets then it will return a true. On failure will return false.
1660
+ */
1661
+ async setManyRaw(entries) {
1662
+ entries = entries.map((e) => ({
1663
+ ...e,
1664
+ key: this._sanitize.enabled ? this._sanitize.cleanKey(e.key) : e.key
1665
+ }));
1666
+ let results = [];
1667
+ await this.hookWithDeprecated(KeyvHooks.BEFORE_SET_MANY_RAW, { entries });
1668
+ try {
1669
+ const rawEntries = await Promise.all(entries.map(async ({ key, value }) => {
1670
+ const ttl = ttlFromExpires(value.expires);
1671
+ return {
1672
+ key,
1673
+ value: await this.encode(value),
1674
+ ttl
1675
+ };
1676
+ }));
1677
+ const storeResult = await this._store.setMany(rawEntries);
1678
+ results = Array.isArray(storeResult) ? storeResult : entries.map(() => true);
1679
+ this.emitTelemetry(KeyvEvents.STAT_SET, entries.map((e) => e.key));
1680
+ } catch (error) {
1681
+ this.emit(KeyvEvents.ERROR, error);
1682
+ this.emitTelemetry(KeyvEvents.STAT_ERROR, entries.map((e) => e.key));
1683
+ results = entries.map(() => false);
1684
+ }
1685
+ await this.hookWithDeprecated(KeyvHooks.AFTER_SET_MANY_RAW, {
1686
+ entries,
1687
+ results
1688
+ });
1689
+ return results;
1690
+ }
1691
+ async delete(key) {
1692
+ if (Array.isArray(key)) return this.deleteMany(key);
1693
+ key = this._sanitize.enabled ? this._sanitize.cleanKey(key) : key;
1694
+ if (key === "") return false;
1695
+ await this.hookWithDeprecated(KeyvHooks.BEFORE_DELETE, { key });
1696
+ let result = true;
1697
+ try {
1698
+ result = await this._store.delete(key);
1699
+ } catch (error) {
1700
+ result = false;
1701
+ this.emit(KeyvEvents.ERROR, error);
1702
+ this.emitTelemetry(KeyvEvents.STAT_ERROR, key);
1703
+ }
1704
+ await this.hookWithDeprecated(KeyvHooks.AFTER_DELETE, {
1705
+ key,
1706
+ value: result
1707
+ });
1708
+ this.emitTelemetry(KeyvEvents.STAT_DELETE, key);
1709
+ return result;
1710
+ }
1711
+ /**
1712
+ * Delete many items from the store
1713
+ * @param {string[]} keys the keys to be deleted
1714
+ * @returns {boolean[]} array of booleans indicating success for each key
1715
+ */
1716
+ async deleteMany(keys) {
1717
+ /* v8 ignore next -- @preserve */
1718
+ keys = this._sanitize.enabled ? this._sanitize.cleanKeys(keys) : keys;
1719
+ await this.hookWithDeprecated(KeyvHooks.BEFORE_DELETE_MANY, { keys });
1720
+ await this.hookWithDeprecated(KeyvHooks.BEFORE_DELETE, { key: keys });
1721
+ let results;
1722
+ try {
1723
+ results = await this._store.deleteMany(keys);
1724
+ this.emitTelemetry(KeyvEvents.STAT_DELETE, keys);
1725
+ } catch (error) {
1726
+ this.emit(KeyvEvents.ERROR, error);
1727
+ this.emitTelemetry(KeyvEvents.STAT_ERROR, keys);
1728
+ results = keys.map(() => false);
1729
+ }
1730
+ await this.hookWithDeprecated(KeyvHooks.AFTER_DELETE_MANY, {
1731
+ keys,
1732
+ values: results
1733
+ });
1734
+ await this.hookWithDeprecated(KeyvHooks.AFTER_DELETE, {
1735
+ key: keys,
1736
+ value: results
1737
+ });
1738
+ return results;
1739
+ }
1740
+ async has(key) {
1741
+ if (Array.isArray(key)) return this.hasMany(key);
1742
+ key = this._sanitize.enabled ? this._sanitize.cleanKey(key) : key;
1743
+ if (key === "") return false;
1744
+ await this.hookWithDeprecated(KeyvHooks.BEFORE_HAS, { key });
1745
+ let result = false;
1746
+ try {
1747
+ if (this._checkExpired) {
1748
+ const rawData = await this._store.get(key);
1749
+ if (rawData !== void 0 && rawData !== null) {
1750
+ const [data] = await this.decodeWithExpire(key, rawData);
1751
+ result = data !== void 0;
1752
+ }
1753
+ } else result = await this._store.has(key);
1754
+ } catch (error) {
1755
+ this.emit(KeyvEvents.ERROR, error);
1756
+ this.emitTelemetry(KeyvEvents.STAT_ERROR, key);
1757
+ }
1758
+ await this.hookWithDeprecated(KeyvHooks.AFTER_HAS, {
1759
+ key,
1760
+ value: result
1761
+ });
1762
+ return result;
1763
+ }
1764
+ /**
1765
+ * Check if many keys exist
1766
+ * @param {string[]} keys the keys to check
1767
+ * @returns {boolean[]} will return an array of booleans if the keys exist
1768
+ */
1769
+ async hasMany(keys) {
1770
+ keys = this._sanitize.enabled ? this._sanitize.cleanKeys(keys) : keys;
1771
+ await this.hookWithDeprecated(KeyvHooks.BEFORE_HAS_MANY, { keys });
1772
+ let results = [];
1773
+ try {
1774
+ if (this._checkExpired) {
1775
+ const rawData = await this._store.getMany(keys);
1776
+ results = (await this.decodeWithExpire(keys, rawData)).map((row) => row !== void 0);
1777
+ } else results = await this._store.hasMany(keys);
1778
+ } catch (error) {
1779
+ this.emit(KeyvEvents.ERROR, error);
1780
+ this.emitTelemetry(KeyvEvents.STAT_ERROR, keys);
1781
+ results = keys.map(() => false);
1782
+ }
1783
+ await this.hookWithDeprecated(KeyvHooks.AFTER_HAS_MANY, {
1784
+ keys,
1785
+ values: results
1786
+ });
1787
+ return results;
1788
+ }
1789
+ /**
1790
+ * Clear the store
1791
+ * @returns {void}
1792
+ */
1793
+ async clear() {
1794
+ this.emit("clear");
1795
+ await this.hook(KeyvHooks.BEFORE_CLEAR, { namespace: this._namespace });
1796
+ try {
1797
+ await this._store.clear();
1798
+ } catch (error) {
1799
+ this.emit(KeyvEvents.ERROR, error);
1800
+ this.emitTelemetry(KeyvEvents.STAT_ERROR);
1801
+ }
1802
+ await this.hook(KeyvHooks.AFTER_CLEAR, { namespace: this._namespace });
1803
+ }
1804
+ /**
1805
+ * Will disconnect the store. This is only available if the store has a disconnect method
1806
+ * @returns {Promise<void>}
1807
+ */
1808
+ async disconnect() {
1809
+ this.emit("disconnect");
1810
+ await this.hook(KeyvHooks.BEFORE_DISCONNECT, { namespace: this._namespace });
1811
+ try {
1812
+ if (this._store.disconnect) await this._store.disconnect();
1813
+ } catch (error) {
1814
+ this.emit(KeyvEvents.ERROR, error);
1815
+ }
1816
+ await this.hook(KeyvHooks.AFTER_DISCONNECT, { namespace: this._namespace });
1817
+ }
1818
+ /**
1819
+ * Iterate over all key-value pairs in the store. Automatically deserializes values,
1820
+ * filters out expired entries, and deletes them from the store.
1821
+ * @returns {AsyncGenerator<Array<string | unknown>, void>} An async generator yielding `[key, value]` pairs.
1822
+ */
1823
+ async *iterator() {
1824
+ /* v8 ignore next 3 -- @preserve */
1825
+ if (this._store.iterator === void 0) return;
1826
+ for await (const [key, raw] of this._store.iterator()) {
1827
+ const data = await this.decode(raw);
1828
+ if (this._checkExpired && data && isDataExpired(data)) {
1829
+ await this.delete(key);
1830
+ continue;
1831
+ }
1832
+ yield [key, data?.value];
1833
+ }
1834
+ }
1835
+ /**
1836
+ * Encodes a value for storage. Pipeline: serialize → compress → encrypt.
1837
+ * If serialization is not configured, returns the data as-is.
1838
+ * @param {KeyvValue<T>} data The value envelope to encode.
1839
+ * @returns {Promise<unknown>} The encoded value, or the original data on failure.
1840
+ */
1841
+ async encode(data) {
1842
+ if (!this._serialization) return data;
1843
+ let result = await this._serialization.stringify(data);
1844
+ if (this._compression?.compress) result = await this._compression.compress(result);
1845
+ if (this._encryption?.encrypt) result = await this._encryption.encrypt(result);
1846
+ return result;
1847
+ }
1848
+ /**
1849
+ * Decodes a stored value. Pipeline: decrypt → decompress → deserialize (reverse of encode).
1850
+ * If serialization is not configured, returns the data as a KeyvValue or undefined for strings.
1851
+ * @param {unknown} data The raw data from the store.
1852
+ * @returns {Promise<KeyvValue<T> | undefined>} The decoded value envelope, or undefined on failure.
1853
+ */
1854
+ async decode(data) {
1855
+ if (data === void 0 || data === null) return;
1856
+ if (!this._serialization) return typeof data === "string" ? void 0 : data;
1857
+ try {
1858
+ let result = data;
1859
+ if (this._encryption?.decrypt) result = await this._encryption.decrypt(result);
1860
+ if (this._compression?.decompress) result = await this._compression.decompress(result);
1861
+ if (typeof result === "string") return await this._serialization.parse(result);
1862
+ return result;
1863
+ } catch (error) {
1864
+ this.emit(KeyvEvents.ERROR, error);
1865
+ return;
1866
+ }
1867
+ }
1868
+ /**
1869
+ * Deserializes raw data from the store, checks for expiry, and deletes expired keys.
1870
+ * Accepts a single key/value or arrays. Returns an array of decoded KeyvValue objects
1871
+ * (undefined for missing or expired entries).
1872
+ * @param {string | string[]} keys the key(s) to process
1873
+ * @param {unknown | unknown[]} rawData the raw data from the store
1874
+ * @returns {Promise<Array<KeyvValue<Value> | undefined>>} decoded values with expired entries removed
1875
+ */
1876
+ async decodeWithExpire(keys, rawData) {
1877
+ const keyArray = Array.isArray(keys) ? keys : [keys];
1878
+ const dataArray = Array.isArray(rawData) ? rawData : [rawData];
1879
+ const results = [];
1880
+ for (const row of dataArray) {
1881
+ if (row === void 0 || row === null) {
1882
+ results.push(void 0);
1883
+ continue;
1884
+ }
1885
+ const deserialized = typeof row === "string" ? await this.decode(row) : row;
1886
+ if (deserialized === void 0 || deserialized === null) {
1887
+ results.push(void 0);
1888
+ continue;
1889
+ }
1890
+ results.push(deserialized);
1891
+ }
1892
+ await deleteExpiredKeys(keyArray, results, this);
1893
+ return results;
1894
+ }
1895
+ /**
1896
+ * Fires a hook under its new name and also under the deprecated alias (if any),
1897
+ * so that integrations still subscribing to the old PRE_/POST_ names keep working.
1898
+ */
1899
+ async hookWithDeprecated(event, ...args) {
1900
+ await this.hook(event, ...args);
1901
+ const deprecated = deprecatedHookAliases.get(event);
1902
+ if (deprecated && this.getHooks(deprecated)?.length) await this.hook(deprecated, ...args);
1903
+ }
1904
+ /**
1905
+ * Emit a telemetry event for cache operations.
1906
+ * @param {KeyvEvents} event the telemetry event type
1907
+ * @param {string | string[]} [key] the cache key or keys (emits one event per key)
1908
+ */
1909
+ emitTelemetry(event, key) {
1910
+ if (key === void 0) {
1911
+ this.emit(event, {
1912
+ event: event.replace("stat:", ""),
1913
+ namespace: this._namespace,
1914
+ timestamp: Date.now()
1915
+ });
1916
+ return;
1917
+ }
1918
+ const keys = Array.isArray(key) ? key : [key];
1919
+ for (const k of keys) this.emit(event, {
1920
+ event: event.replace("stat:", ""),
1921
+ key: k,
1922
+ namespace: this._namespace,
1923
+ timestamp: Date.now()
1924
+ });
1925
+ }
1926
+ /**
1927
+ * Merges the overloaded constructor arguments into a single KeyvOptions object.
1928
+ */
1929
+ static resolveOptions(store, options) {
1930
+ options ??= {};
1931
+ store ??= {};
1932
+ const merged = { ...options };
1933
+ if (store && store.get) merged.store = store;
1934
+ else Object.assign(merged, store);
1935
+ return merged;
1936
+ }
1937
+ /**
1938
+ * Initializes the serialization adapter from options.
1939
+ */
1940
+ initSerialization(options) {
1941
+ if (options.serialization === false) this._serialization = void 0;
1942
+ else this._serialization = options.serialization ?? new KeyvJsonSerializer();
1943
+ }
1944
+ /**
1945
+ * Initializes the sanitization handler from options.
1946
+ */
1947
+ initSanitize(options) {
1948
+ const sanitize = new KeyvSanitize();
1949
+ if (options.sanitize) sanitize.updateOptions(options.sanitize);
1950
+ this._sanitize = sanitize;
1951
+ }
1952
+ /**
1953
+ * Initializes the stats manager from options.
1954
+ */
1955
+ initStats(options) {
1956
+ this._stats = new KeyvStats({
1957
+ emitter: this,
1958
+ enabled: options.stats ?? false
1959
+ });
1960
+ }
1961
+ /**
1962
+ * Initializes the namespace, applying sanitization if enabled.
1963
+ */
1964
+ initNamespace(namespace) {
1965
+ this._namespace = namespace;
1966
+ if (this._namespace && this._sanitize.enabled) this._namespace = this._sanitize.cleanNamespace(this._namespace);
1967
+ }
1968
+ };
1969
+ //#endregion
1970
+ //#region src/adapters/memory.ts
1971
+ /**
1972
+ * An in-memory storage adapter for Keyv that wraps any Map-like object.
1973
+ *
1974
+ * This class provides a unified interface for using various Map-like stores
1975
+ * with Keyv, handling namespace prefixing, TTL-based expiration, and batch operations.
1976
+ *
1977
+ * @example
1978
+ * ```typescript
1979
+ * // Using with a standard Map
1980
+ * const store = new KeyvMemoryAdapter(new Map(), { namespace: 'cache' });
1981
+ *
1982
+ * // Using with a custom store
1983
+ * const customStore = new KeyvMemoryAdapter(myCustomMapLikeStore, {
1984
+ * namespace: 'tenant-123',
1985
+ * keySeparator: ':'
1986
+ * });
1987
+ * ```
1988
+ */
1989
+ var KeyvMemoryAdapter = class extends Hookified {
1990
+ _store;
1991
+ _namespace;
1992
+ _keySeparator = ":";
1993
+ _capabilities;
1994
+ /**
1995
+ * Creates a new KeyvMemoryAdapter instance.
1996
+ * @param store - The underlying Map or Map-like object to use for storage
1997
+ * @param options - Configuration options for the store
1998
+ */
1999
+ constructor(store, options) {
2000
+ super({ throwOnHookError: false });
2001
+ this._store = store;
2002
+ this._capabilities = detectKeyvStorage(store);
2003
+ if (options?.keySeparator) this._keySeparator = options.keySeparator;
2004
+ if (options?.namespace) this._namespace = options?.namespace;
2005
+ }
2006
+ /**
2007
+ * Gets the detected capabilities of the underlying store.
2008
+ */
2009
+ get capabilities() {
2010
+ return this._capabilities;
2011
+ }
2012
+ /**
2013
+ * Gets the underlying store instance.
2014
+ */
2015
+ get store() {
2016
+ return this._store;
2017
+ }
2018
+ /**
2019
+ * Sets the underlying store instance.
2020
+ */
2021
+ set store(store) {
2022
+ this._store = store;
2023
+ }
2024
+ /**
2025
+ * Gets the current key separator used between namespace and key.
2026
+ */
2027
+ get keySeparator() {
2028
+ return this._keySeparator;
2029
+ }
2030
+ /**
2031
+ * Sets the key separator used between namespace and key.
2032
+ */
2033
+ set keySeparator(separator) {
2034
+ this._keySeparator = separator;
2035
+ }
2036
+ /**
2037
+ * Gets the current namespace.
2038
+ */
2039
+ get namespace() {
2040
+ return this._namespace;
2041
+ }
2042
+ /**
2043
+ * Sets the namespace.
2044
+ */
2045
+ set namespace(namespace) {
2046
+ this._namespace = namespace;
2047
+ }
2048
+ /**
2049
+ * Creates a prefixed key by combining the namespace and key with the separator.
2050
+ * @param key - The base key
2051
+ * @param namespace - Optional namespace to prefix the key with
2052
+ * @returns The prefixed key if namespace is provided, otherwise the original key
2053
+ */
2054
+ getKeyPrefix(key, namespace) {
2055
+ if (namespace) return `${namespace}${this._keySeparator}${key}`;
2056
+ return key;
2057
+ }
2058
+ /**
2059
+ * Parses a prefixed key to extract the namespace and original key.
2060
+ * @param key - The prefixed key to parse
2061
+ * @returns An object containing the namespace (if present) and the original key
2062
+ */
2063
+ getKeyPrefixData(key) {
2064
+ if (this._namespace && key.startsWith(`${this._namespace}${this._keySeparator}`)) return {
2065
+ namespace: this._namespace,
2066
+ key: key.slice(this._namespace.length + this._keySeparator.length)
2067
+ };
2068
+ return { key };
2069
+ }
2070
+ /**
2071
+ * Retrieves a value from the store by key.
2072
+ * Automatically handles namespace prefixing and TTL expiration.
2073
+ * @param key - The key to retrieve
2074
+ * @returns The stored data, or undefined if not found or expired
2075
+ */
2076
+ async get(key) {
2077
+ const keyPrefix = this.getKeyPrefix(key, this._namespace);
2078
+ const entry = this._store.get(keyPrefix);
2079
+ if (entry === void 0 || entry === null) return;
2080
+ if (entry.expires !== void 0 && Date.now() > entry.expires) {
2081
+ this._store.delete(keyPrefix);
2082
+ return;
2083
+ }
2084
+ return entry.value;
2085
+ }
2086
+ /**
2087
+ * Stores a value in the store with an optional TTL.
2088
+ * @param key - The key to store the value under
2089
+ * @param value - The value to store
2090
+ * @param ttl - Optional time-to-live in milliseconds
2091
+ * @returns Always returns true indicating success
2092
+ */
2093
+ async set(key, value, ttl) {
2094
+ const keyPrefix = this.getKeyPrefix(key, this._namespace);
2095
+ const entry = {
2096
+ value,
2097
+ expires: ttl ? Date.now() + ttl : void 0
2098
+ };
2099
+ this._store.set(keyPrefix, entry, ttl);
2100
+ return true;
2101
+ }
2102
+ /**
2103
+ * Stores multiple entries in the store at once.
2104
+ * @param entries - Array of entries containing key, value, and optional TTL
2105
+ */
2106
+ async setMany(entries) {
2107
+ const results = [];
2108
+ for (const entry of entries) {
2109
+ const keyPrefix = this.getKeyPrefix(entry.key, this._namespace);
2110
+ const memEntry = {
2111
+ value: entry.value,
2112
+ expires: entry.ttl ? Date.now() + entry.ttl : void 0
2113
+ };
2114
+ this._store.set(keyPrefix, memEntry, entry.ttl);
2115
+ results.push(true);
2116
+ }
2117
+ return results;
2118
+ }
2119
+ /**
2120
+ * Deletes a value from the store by key.
2121
+ * @param key - The key to delete
2122
+ * @returns True if the key was deleted, false otherwise
2123
+ */
2124
+ async delete(key) {
2125
+ const keyPrefix = this.getKeyPrefix(key, this._namespace);
2126
+ return this._store.delete(keyPrefix);
2127
+ }
2128
+ /**
2129
+ * Clears entries from the store. If a namespace is set, only entries
2130
+ * within that namespace are removed. Otherwise, the entire store is cleared.
2131
+ * NOTE: if there is no `keys()` then we just do a full clear.
2132
+ */
2133
+ async clear() {
2134
+ if (!this._namespace || typeof this._store.keys !== "function") {
2135
+ this._store.clear();
2136
+ return;
2137
+ }
2138
+ const prefix = `${this._namespace}${this._keySeparator}`;
2139
+ const keysToDelete = [];
2140
+ for (const key of this._store.keys()) if (key.startsWith(prefix)) keysToDelete.push(key);
2141
+ for (const key of keysToDelete) this._store.delete(key);
2142
+ }
2143
+ /**
2144
+ * Checks if a key exists in the store and is not expired.
2145
+ * @param key - The key to check
2146
+ * @returns True if the key exists and is not expired, false otherwise
2147
+ */
2148
+ async has(key) {
2149
+ const keyPrefix = this.getKeyPrefix(key, this._namespace);
2150
+ const entry = this._store.get(keyPrefix);
2151
+ if (entry === void 0 || entry === null) return false;
2152
+ if (entry.expires !== void 0 && Date.now() > entry.expires) {
2153
+ this._store.delete(keyPrefix);
2154
+ return false;
2155
+ }
2156
+ return true;
2157
+ }
2158
+ /**
2159
+ * Checks if multiple keys exist in the store and are not expired.
2160
+ * @param keys - Array of keys to check
2161
+ * @returns Array of booleans indicating existence for each key
2162
+ */
2163
+ async hasMany(keys) {
2164
+ const results = [];
2165
+ for (const key of keys) results.push(await this.has(key));
2166
+ return results;
2167
+ }
2168
+ /**
2169
+ * Retrieves multiple values from the store by their keys.
2170
+ * @param keys - Array of keys to retrieve
2171
+ * @returns Array of stored data in the same order as the input keys
2172
+ */
2173
+ async getMany(keys) {
2174
+ const values = [];
2175
+ for (const key of keys) {
2176
+ const keyPrefix = this.getKeyPrefix(key, this._namespace);
2177
+ const entry = this._store.get(keyPrefix);
2178
+ if (entry === void 0 || entry === null) {
2179
+ values.push(void 0);
2180
+ continue;
2181
+ }
2182
+ if (entry.expires !== void 0 && Date.now() > entry.expires) {
2183
+ this._store.delete(keyPrefix);
2184
+ values.push(void 0);
2185
+ continue;
2186
+ }
2187
+ values.push(entry.value);
2188
+ }
2189
+ return values;
2190
+ }
2191
+ /**
2192
+ * Deletes multiple keys from the store at once.
2193
+ * @param keys - Array of keys to delete
2194
+ * @returns Array of booleans indicating success for each key
2195
+ */
2196
+ async deleteMany(keys) {
2197
+ const results = [];
2198
+ for (const key of keys) try {
2199
+ const keyPrefix = this.getKeyPrefix(key, this._namespace);
2200
+ const existed = this._store.has(keyPrefix);
2201
+ this._store.delete(keyPrefix);
2202
+ results.push(existed);
2203
+ } catch (error) {
2204
+ this.emit(KeyvEvents.ERROR, error);
2205
+ results.push(false);
2206
+ }
2207
+ return results;
2208
+ }
2209
+ /**
2210
+ * Creates an async iterator for iterating over store entries.
2211
+ * If the underlying store does not support iteration, returns an empty generator.
2212
+ * @returns {AsyncGenerator<Array<string | Awaited<Value> | undefined>, void>} An async generator yielding [key, value] pairs
2213
+ */
2214
+ async *iterator() {
2215
+ if (typeof this._store.entries !== "function") return;
2216
+ const namespace = this._namespace;
2217
+ for (const [key, raw] of this._store.entries()) {
2218
+ if (namespace) {
2219
+ if (!key.startsWith(`${namespace}${this._keySeparator}`)) continue;
2220
+ }
2221
+ const entry = raw;
2222
+ if (entry?.expires !== void 0 && Date.now() > entry.expires) {
2223
+ this._store.delete(key);
2224
+ continue;
2225
+ }
2226
+ yield [namespace ? key.slice(namespace.length + this._keySeparator.length) : key, entry?.value];
2227
+ }
2228
+ }
2229
+ /**
2230
+ * No-op disconnect for in-memory stores.
2231
+ */
2232
+ async disconnect() {}
2233
+ };
2234
+ /**
2235
+ * Creates a Keyv instance with a memory adapter optimized for in-memory storage.
2236
+ *
2237
+ * This factory function configures Keyv to bypass serialization/deserialization
2238
+ * and key prefixing, resulting in faster performance for in-memory use cases
2239
+ * where data doesn't need to be persisted or transmitted.
2240
+ *
2241
+ * @param store - The underlying Map or Map-like object to use for storage
2242
+ * @param options - Configuration options for the memory adapter
2243
+ * @returns A configured Keyv instance with optimized settings for in-memory storage
2244
+ *
2245
+ * @example
2246
+ * ```typescript
2247
+ * // Create a simple in-memory cache
2248
+ * const cache = createKeyv(new Map());
2249
+ * await cache.set('user:1', { name: 'John' });
2250
+ *
2251
+ * // Create with namespace for multi-tenant scenarios
2252
+ * const tenantCache = createKeyv(new Map(), {
2253
+ * namespace: 'tenant-123',
2254
+ * keySeparator: ':'
2255
+ * });
2256
+ * ```
2257
+ */
2258
+ function createKeyv(store, options) {
2259
+ const { namespace, ...adapterOptions } = options ?? {};
2260
+ return new Keyv({
2261
+ store: new KeyvMemoryAdapter(store, adapterOptions),
2262
+ serialization: false,
2263
+ namespace
2264
+ });
2265
+ }
2266
+ //#endregion
2267
+ export { Keyv, Keyv as default, KeyvBridgeAdapter, KeyvEvents, KeyvHooks, KeyvJsonSerializer, KeyvMemoryAdapter, KeyvSanitize, KeyvStats, createKeyv, detectKeyv, detectKeyvCompression, detectKeyvEncryption, detectKeyvSerialization, detectKeyvStorage, jsonSerializer };