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