@keyv/redis 4.5.0 → 4.6.0

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/README.md CHANGED
@@ -29,7 +29,7 @@ Redis storage adapter for [Keyv](https://github.com/jaredwray/keyv).
29
29
  * [Using Generic Types](#using-generic-types)
30
30
  * [Performance Considerations](#performance-considerations)
31
31
  * [High Memory Usage on Redis Server](#high-memory-usage-on-redis-server)
32
- * [Gracefully Handling Connection Errors, Retries, and Timeouts](#gracefully-handling-connection-errors-retries-and-timeouts)
32
+ * [Gracefully Handling Errors and Timeouts](#gracefully-handling-errors-and-timeouts)
33
33
  * [Using Cacheable with Redis](#using-cacheable-with-redis)
34
34
  * [Clustering and TLS Support](#clustering-and-tls-support)
35
35
  * [API](#api)
@@ -131,10 +131,26 @@ export type KeyvRedisOptions = {
131
131
  noNamespaceAffectsAll?: boolean;
132
132
 
133
133
  /**
134
- * Timeout for connecting to Redis in milliseconds. This is used to prevent hanging indefinitely when connecting to Redis.
135
- * Defaults to `200`.
134
+ * This is used to throw an error if the client is not connected when trying to connect. By default, this is
135
+ * set to true so that it throws an error when trying to connect to the Redis server fails.
136
136
  */
137
- connectTimeout?: number;
137
+ throwOnConnectError?: boolean;
138
+
139
+ /**
140
+ * This is used to throw an error if at any point there is a failure. Use this if you want to
141
+ * ensure that all operations are successful and you want to handle errors. By default, this is
142
+ * set to false so that it does not throw an error on every operation and instead emits an error event
143
+ * and returns no-op responses.
144
+ * @default false
145
+ */
146
+ throwErrors?: boolean;
147
+
148
+ /**
149
+ * Timeout in milliseconds for the connection. Default is undefined, which uses the default timeout of the Redis client.
150
+ * If set, it will throw an error if the connection does not succeed within the specified time.
151
+ * @default undefined
152
+ */
153
+ connectionTimeout?: number;
138
154
  };
139
155
  ```
140
156
  You can pass these options when creating a new `KeyvRedis` instance:
@@ -241,7 +257,7 @@ const keyv = new Keyv(new KeyvRedis('redis://user:pass@localhost:6379', { useUnl
241
257
  keyv.useUnlink = false;
242
258
  ```
243
259
 
244
- # Gracefully Handling Connection Errors, Retries, and Timeouts
260
+ # Gracefully Handling Errors and Timeouts
245
261
 
246
262
  When using `@keyv/redis`, it is important to handle connection errors gracefully. You can do this by listening to the `error` event on the `KeyvRedis` instance. Here is an example of how to do that:
247
263
 
@@ -254,11 +270,13 @@ keyv.on('error', (error) => {
254
270
  });
255
271
  ```
256
272
 
257
- We also attempt to connect to Redis and have a `connectTimeout` option that defaults to `200ms`. If the connection is not established within this time, it will emit an error. You can catch this error and handle it accordingly.
273
+ By default, the `KeyvRedis` instance will `throw an error` if the connection fails to connect. You can disable this behavior by setting the `throwOnConnectError` option to `false` when creating the `KeyvRedis` instance:
274
+
275
+ On `get`, `getMany`, `set`, `setMany`, `delete`, and `deleteMany`, if the connection is lost, it will emit an error and return a no-op value. You can catch this error and handle it accordingly. This is important to ensure that your application does not crash due to a lost connection to Redis.
258
276
 
259
- On `get`, `getMany`, `set`, `setMany`, `delete`, `deleteMany`, and `clear` methods, if the connection is lost, it will emit an error and return a no-op value. You can catch this error and handle it accordingly. This is important to ensure that your application does not crash due to a lost connection to Redis.
277
+ If you want to handle connection errors, retries, and timeouts more gracefully, you can use the `throwErrors` option. This will throw an error if any operation fails, allowing you to catch it and handle it accordingly:
260
278
 
261
- If you pass in just a `uri` connection string we will automatically create a Redis client for you with the following reconnect strategy:
279
+ There is a default `Reconnect Strategy` if you pass in just a `uri` connection string we will automatically create a Redis client for you with the following reconnect strategy:
262
280
 
263
281
  ```typescript
264
282
  export const defaultReconnectStrategy = (attempts: number): number | Error => {
package/dist/index.cjs CHANGED
@@ -39,14 +39,14 @@ __export(index_exports, {
39
39
  defaultReconnectStrategy: () => defaultReconnectStrategy
40
40
  });
41
41
  module.exports = __toCommonJS(index_exports);
42
- var import_node_events = __toESM(require("events"), 1);
43
42
  var import_client = require("@redis/client");
43
+ var import_hookified = require("hookified");
44
44
  var import_keyv = require("keyv");
45
45
  var import_cluster_key_slot = __toESM(require("cluster-key-slot"), 1);
46
46
  var import_client2 = require("@redis/client");
47
47
  var import_keyv2 = require("keyv");
48
48
  var RedisErrorMessages = /* @__PURE__ */ ((RedisErrorMessages2) => {
49
- RedisErrorMessages2["RedisClientNotConnected"] = "Redis client is not connected or has failed to connect";
49
+ RedisErrorMessages2["RedisClientNotConnectedThrown"] = "Redis client is not connected or has failed to connect. This is thrown because throwOnConnectError is set to true.";
50
50
  return RedisErrorMessages2;
51
51
  })(RedisErrorMessages || {});
52
52
  var defaultReconnectStrategy = (attempts) => {
@@ -54,17 +54,16 @@ var defaultReconnectStrategy = (attempts) => {
54
54
  const jitter = (Math.random() - 0.5) * 100;
55
55
  return backoff + jitter;
56
56
  };
57
- var KeyvRedis = class extends import_node_events.default {
57
+ var KeyvRedis = class extends import_hookified.Hookified {
58
58
  _client = (0, import_client.createClient)();
59
59
  _namespace;
60
60
  _keyPrefixSeparator = "::";
61
61
  _clearBatchSize = 1e3;
62
62
  _useUnlink = true;
63
63
  _noNamespaceAffectsAll = false;
64
- _connectTimeout = 200;
65
- // Timeout for connecting to Redis in milliseconds
66
- _reconnectClient = false;
67
- // Whether to reconnect the client
64
+ _throwOnConnectError = true;
65
+ _throwErrors = false;
66
+ _connectionTimeout;
68
67
  /**
69
68
  * KeyvRedis constructor.
70
69
  * @param {string | RedisClientOptions | RedisClientType} [connect] How to connect to the Redis server. If string pass in the url, if object pass in the options, if RedisClient pass in the client.
@@ -74,6 +73,7 @@ var KeyvRedis = class extends import_node_events.default {
74
73
  super();
75
74
  const socket = {
76
75
  reconnectStrategy: defaultReconnectStrategy
76
+ // Default timeout for the connection
77
77
  };
78
78
  if (connect) {
79
79
  if (typeof connect === "string") {
@@ -116,6 +116,10 @@ var KeyvRedis = class extends import_node_events.default {
116
116
  keyPrefixSeparator: this._keyPrefixSeparator,
117
117
  clearBatchSize: this._clearBatchSize,
118
118
  noNamespaceAffectsAll: this._noNamespaceAffectsAll,
119
+ useUnlink: this._useUnlink,
120
+ throwOnConnectError: this._throwOnConnectError,
121
+ throwErrors: this._throwErrors,
122
+ connectionTimeout: this._connectionTimeout,
119
123
  dialect: "redis",
120
124
  url
121
125
  };
@@ -199,49 +203,82 @@ var KeyvRedis = class extends import_node_events.default {
199
203
  this._noNamespaceAffectsAll = value;
200
204
  }
201
205
  /**
202
- * Get the timeout for connecting to Redis in milliseconds. This is used to prevent hanging indefinitely when connecting to Redis.
203
- * @default 200
206
+ * Get if throwOnConnectError is set to true.
207
+ * This is used to throw an error if the client is not connected when trying to connect. By default, this is
208
+ * set to true so that it throws an error when trying to connect to the Redis server fails.
209
+ * @default true
204
210
  */
205
- get connectTimeout() {
206
- return this._connectTimeout;
211
+ get throwOnConnectError() {
212
+ return this._throwOnConnectError;
207
213
  }
208
214
  /**
209
- * Set the timeout for connecting to Redis in milliseconds. This is used to prevent hanging indefinitely when connecting to Redis.
210
- * @default 200
215
+ * Set if throwOnConnectError is set to true.
216
+ * This is used to throw an error if the client is not connected when trying to connect. By default, this is
217
+ * set to true so that it throws an error when trying to connect to the Redis server fails.
211
218
  */
212
- set connectTimeout(value) {
213
- if (value > 0) {
214
- this._connectTimeout = value;
215
- this._reconnectClient = true;
216
- } else {
217
- this.emit("error", "connectTimeout must be greater than 0");
218
- }
219
+ set throwOnConnectError(value) {
220
+ this._throwOnConnectError = value;
221
+ }
222
+ /**
223
+ * Get if throwErrors is set to true.
224
+ * This is used to throw an error if at any point there is a failure. Use this if you want to
225
+ * ensure that all operations are successful and you want to handle errors. By default, this is
226
+ * set to false so that it does not throw an error on every operation and instead emits an error event
227
+ * and returns no-op responses.
228
+ * @default false
229
+ */
230
+ get throwErrors() {
231
+ return this._throwErrors;
232
+ }
233
+ /**
234
+ * Set if throwErrors is set to true.
235
+ * This is used to throw an error if at any point there is a failure. Use this if you want to
236
+ * ensure that all operations are successful and you want to handle errors. By default, this is
237
+ * set to false so that it does not throw an error on every operation and instead emits an error event
238
+ * and returns no-op responses.
239
+ */
240
+ set throwErrors(value) {
241
+ this._throwErrors = value;
242
+ }
243
+ /**
244
+ * Get the connection timeout in milliseconds such as 5000 (5 seconds). Default is undefined. If undefined, it will use the default.
245
+ * @default undefined
246
+ */
247
+ get connectionTimeout() {
248
+ return this._connectionTimeout;
249
+ }
250
+ /**
251
+ * Set the connection timeout in milliseconds such as 5000 (5 seconds). Default is undefined. If undefined, it will use the default.
252
+ * @default undefined
253
+ */
254
+ set connectionTimeout(value) {
255
+ this._connectionTimeout = value;
219
256
  }
220
257
  /**
221
258
  * Get the Redis URL used to connect to the server. This is used to get a connected client.
222
259
  */
223
260
  async getClient() {
224
- if (this._client.isOpen && !this._reconnectClient) {
261
+ if (this._client.isOpen) {
225
262
  return this._client;
226
263
  }
227
- if (this._reconnectClient && this._client.isOpen) {
228
- await this._client.disconnect();
229
- }
230
264
  try {
231
- const timeoutPromise = new Promise((resolves, reject) => setTimeout(() => {
232
- reject(new Error("Redis client is not connected or has failed to connect" /* RedisClientNotConnected */));
233
- }, this._connectTimeout));
234
- await Promise.race([
235
- this._client.connect(),
236
- timeoutPromise
237
- ]);
238
- this._reconnectClient = false;
239
- this.initClient();
240
- return this._client;
265
+ if (this._connectionTimeout === void 0) {
266
+ await this._client.connect();
267
+ } else {
268
+ await Promise.race([
269
+ this._client.connect(),
270
+ this.createTimeoutPromise(this._connectionTimeout)
271
+ ]);
272
+ }
241
273
  } catch (error) {
242
274
  this.emit("error", error);
243
- return void 0;
275
+ if (this._throwOnConnectError) {
276
+ throw new Error("Redis client is not connected or has failed to connect. This is thrown because throwOnConnectError is set to true." /* RedisClientNotConnectedThrown */);
277
+ }
278
+ await this.disconnect(true);
244
279
  }
280
+ this.initClient();
281
+ return this._client;
245
282
  }
246
283
  /**
247
284
  * Set a key value pair in the store. TTL is in milliseconds.
@@ -251,15 +288,18 @@ var KeyvRedis = class extends import_node_events.default {
251
288
  */
252
289
  async set(key, value, ttl) {
253
290
  const client = await this.getClient();
254
- if (!client) {
255
- this.emit("error", new Error("Redis client is not connected or has failed to connect" /* RedisClientNotConnected */));
256
- return;
257
- }
258
- key = this.createKeyPrefix(key, this._namespace);
259
- if (ttl) {
260
- await client.set(key, value, { PX: ttl });
261
- } else {
262
- await client.set(key, value);
291
+ try {
292
+ key = this.createKeyPrefix(key, this._namespace);
293
+ if (ttl) {
294
+ await client.set(key, value, { PX: ttl });
295
+ } else {
296
+ await client.set(key, value);
297
+ }
298
+ } catch (error) {
299
+ this.emit("error", error);
300
+ if (this._throwErrors) {
301
+ throw error;
302
+ }
263
303
  }
264
304
  }
265
305
  /**
@@ -268,20 +308,23 @@ var KeyvRedis = class extends import_node_events.default {
268
308
  */
269
309
  async setMany(entries) {
270
310
  const client = await this.getClient();
271
- if (!client) {
272
- this.emit("error", new Error("Redis client is not connected or has failed to connect" /* RedisClientNotConnected */));
273
- return;
274
- }
275
- const multi = client.multi();
276
- for (const { key, value, ttl } of entries) {
277
- const prefixedKey = this.createKeyPrefix(key, this._namespace);
278
- if (ttl) {
279
- multi.set(prefixedKey, value, { PX: ttl });
280
- } else {
281
- multi.set(prefixedKey, value);
311
+ try {
312
+ const multi = client.multi();
313
+ for (const { key, value, ttl } of entries) {
314
+ const prefixedKey = this.createKeyPrefix(key, this._namespace);
315
+ if (ttl) {
316
+ multi.set(prefixedKey, value, { PX: ttl });
317
+ } else {
318
+ multi.set(prefixedKey, value);
319
+ }
320
+ }
321
+ await multi.exec();
322
+ } catch (error) {
323
+ this.emit("error", error);
324
+ if (this._throwErrors) {
325
+ throw error;
282
326
  }
283
327
  }
284
- await multi.exec();
285
328
  }
286
329
  /**
287
330
  * Check if a key exists in the store.
@@ -290,13 +333,17 @@ var KeyvRedis = class extends import_node_events.default {
290
333
  */
291
334
  async has(key) {
292
335
  const client = await this.getClient();
293
- if (!client) {
294
- this.emit("error", new Error("Redis client is not connected or has failed to connect" /* RedisClientNotConnected */));
336
+ try {
337
+ key = this.createKeyPrefix(key, this._namespace);
338
+ const exists = await client.exists(key);
339
+ return exists === 1;
340
+ } catch (error) {
341
+ this.emit("error", error);
342
+ if (this._throwErrors) {
343
+ throw error;
344
+ }
295
345
  return false;
296
346
  }
297
- key = this.createKeyPrefix(key, this._namespace);
298
- const exists = await client.exists(key);
299
- return exists === 1;
300
347
  }
301
348
  /**
302
349
  * Check if many keys exist in the store. This will be done as a single transaction.
@@ -305,17 +352,21 @@ var KeyvRedis = class extends import_node_events.default {
305
352
  */
306
353
  async hasMany(keys) {
307
354
  const client = await this.getClient();
308
- if (!client) {
309
- this.emit("error", new Error("Redis client is not connected or has failed to connect" /* RedisClientNotConnected */));
355
+ try {
356
+ const multi = client.multi();
357
+ for (const key of keys) {
358
+ const prefixedKey = this.createKeyPrefix(key, this._namespace);
359
+ multi.exists(prefixedKey);
360
+ }
361
+ const results = await multi.exec();
362
+ return results.map((result) => result === 1);
363
+ } catch (error) {
364
+ this.emit("error", error);
365
+ if (this._throwErrors) {
366
+ throw error;
367
+ }
310
368
  return Array.from({ length: keys.length }).fill(false);
311
369
  }
312
- const multi = client.multi();
313
- for (const key of keys) {
314
- const prefixedKey = this.createKeyPrefix(key, this._namespace);
315
- multi.exists(prefixedKey);
316
- }
317
- const results = await multi.exec();
318
- return results.map((result) => result === 1);
319
370
  }
320
371
  /**
321
372
  * Get a value from the store. If the key does not exist, it will return undefined.
@@ -324,16 +375,20 @@ var KeyvRedis = class extends import_node_events.default {
324
375
  */
325
376
  async get(key) {
326
377
  const client = await this.getClient();
327
- if (!client) {
328
- this.emit("error", new Error("Redis client is not connected or has failed to connect" /* RedisClientNotConnected */));
329
- return void 0;
330
- }
331
- key = this.createKeyPrefix(key, this._namespace);
332
- const value = await client.get(key);
333
- if (value === null) {
378
+ try {
379
+ key = this.createKeyPrefix(key, this._namespace);
380
+ const value = await client.get(key);
381
+ if (value === null) {
382
+ return void 0;
383
+ }
384
+ return value;
385
+ } catch (error) {
386
+ this.emit("error", error);
387
+ if (this._throwErrors) {
388
+ throw error;
389
+ }
334
390
  return void 0;
335
391
  }
336
- return value;
337
392
  }
338
393
  /**
339
394
  * Get many values from the store. If a key does not exist, it will return undefined.
@@ -350,6 +405,9 @@ var KeyvRedis = class extends import_node_events.default {
350
405
  return values;
351
406
  } catch (error) {
352
407
  this.emit("error", error);
408
+ if (this._throwErrors) {
409
+ throw error;
410
+ }
353
411
  return Array.from({ length: keys.length }).fill(void 0);
354
412
  }
355
413
  }
@@ -360,14 +418,18 @@ var KeyvRedis = class extends import_node_events.default {
360
418
  */
361
419
  async delete(key) {
362
420
  const client = await this.getClient();
363
- if (!client) {
364
- this.emit("error", new Error("Redis client is not connected or has failed to connect" /* RedisClientNotConnected */));
421
+ try {
422
+ key = this.createKeyPrefix(key, this._namespace);
423
+ let deleted = 0;
424
+ deleted = await (this._useUnlink ? client.unlink(key) : client.del(key));
425
+ return deleted > 0;
426
+ } catch (error) {
427
+ this.emit("error", error);
428
+ if (this._throwErrors) {
429
+ throw error;
430
+ }
365
431
  return false;
366
432
  }
367
- key = this.createKeyPrefix(key, this._namespace);
368
- let deleted = 0;
369
- deleted = await (this._useUnlink ? client.unlink(key) : client.del(key));
370
- return deleted > 0;
371
433
  }
372
434
  /**
373
435
  * Delete many keys from the store. This will be done as a single transaction.
@@ -377,23 +439,26 @@ var KeyvRedis = class extends import_node_events.default {
377
439
  async deleteMany(keys) {
378
440
  let result = false;
379
441
  const client = await this.getClient();
380
- if (!client) {
381
- this.emit("error", new Error("Redis client is not connected or has failed to connect" /* RedisClientNotConnected */));
382
- return false;
383
- }
384
- const multi = client.multi();
385
- for (const key of keys) {
386
- const prefixedKey = this.createKeyPrefix(key, this._namespace);
387
- if (this._useUnlink) {
388
- multi.unlink(prefixedKey);
389
- } else {
390
- multi.del(prefixedKey);
442
+ try {
443
+ const multi = client.multi();
444
+ for (const key of keys) {
445
+ const prefixedKey = this.createKeyPrefix(key, this._namespace);
446
+ if (this._useUnlink) {
447
+ multi.unlink(prefixedKey);
448
+ } else {
449
+ multi.del(prefixedKey);
450
+ }
391
451
  }
392
- }
393
- const results = await multi.exec();
394
- for (const deleted of results) {
395
- if (typeof deleted === "number" && deleted > 0) {
396
- result = true;
452
+ const results = await multi.exec();
453
+ for (const deleted of results) {
454
+ if (typeof deleted === "number" && deleted > 0) {
455
+ result = true;
456
+ }
457
+ }
458
+ } catch (error) {
459
+ this.emit("error", error);
460
+ if (this._throwErrors) {
461
+ throw error;
397
462
  }
398
463
  }
399
464
  return result;
@@ -551,9 +616,6 @@ var KeyvRedis = class extends import_node_events.default {
551
616
  */
552
617
  async getSlotMaster(slot) {
553
618
  const connection = await this.getClient();
554
- if (!connection) {
555
- throw new Error("Redis client is not connected or has failed to connect" /* RedisClientNotConnected */);
556
- }
557
619
  if (this.isCluster()) {
558
620
  const cluster = connection;
559
621
  const mainNode = cluster.slots[slot].master;
@@ -606,14 +668,17 @@ var KeyvRedis = class extends import_node_events.default {
606
668
  if (options.noNamespaceAffectsAll !== void 0) {
607
669
  this._noNamespaceAffectsAll = options.noNamespaceAffectsAll;
608
670
  }
609
- if (options.connectTimeout !== void 0 && options.connectTimeout > 0) {
610
- this._connectTimeout = options.connectTimeout;
671
+ if (options.throwOnConnectError !== void 0) {
672
+ this._throwOnConnectError = options.throwOnConnectError;
673
+ }
674
+ if (options.throwErrors !== void 0) {
675
+ this._throwErrors = options.throwErrors;
676
+ }
677
+ if (options.connectionTimeout !== void 0) {
678
+ this._connectionTimeout = options.connectionTimeout;
611
679
  }
612
680
  }
613
681
  initClient() {
614
- this._client.on("error", (error) => {
615
- this.emit("error", error);
616
- });
617
682
  this._client.on("connect", () => {
618
683
  this.emit("connect", this._client);
619
684
  });
@@ -624,6 +689,17 @@ var KeyvRedis = class extends import_node_events.default {
624
689
  this.emit("reconnecting", reconnectInfo);
625
690
  });
626
691
  }
692
+ async createTimeoutPromise(timeoutMs) {
693
+ return new Promise((_, reject) => (
694
+ // eslint-disable-next-line no-promise-executor-return
695
+ setTimeout(
696
+ () => {
697
+ reject(new Error(`Redis timed out after ${timeoutMs}ms`));
698
+ },
699
+ timeoutMs
700
+ )
701
+ ));
702
+ }
627
703
  };
628
704
  function createKeyv(connect, options) {
629
705
  connect ??= "redis://localhost:6379";
package/dist/index.d.cts CHANGED
@@ -1,6 +1,6 @@
1
- import EventEmitter from 'node:events';
2
1
  import { RedisClientOptions, RedisClusterOptions, RedisClientType, RedisClusterType, RedisModules, RedisFunctions, RedisScripts } from '@redis/client';
3
2
  export { RedisClientOptions, RedisClientType, RedisClusterOptions, RedisClusterType, createClient, createCluster } from '@redis/client';
3
+ import { Hookified } from 'hookified';
4
4
  import { KeyvStoreAdapter, KeyvEntry, Keyv } from 'keyv';
5
5
  export { Keyv } from 'keyv';
6
6
 
@@ -28,10 +28,24 @@ type KeyvRedisOptions = {
28
28
  */
29
29
  noNamespaceAffectsAll?: boolean;
30
30
  /**
31
- * Timeout for connecting to Redis in milliseconds. This is used to prevent hanging indefinitely when connecting to Redis.
32
- * Defaults to `200`.
31
+ * This is used to throw an error if the client is not connected when trying to connect. By default, this is
32
+ * set to true so that it throws an error when trying to connect to the Redis server fails.
33
33
  */
34
- connectTimeout?: number;
34
+ throwOnConnectError?: boolean;
35
+ /**
36
+ * This is used to throw an error if at any point there is a failure. Use this if you want to
37
+ * ensure that all operations are successful and you want to handle errors. By default, this is
38
+ * set to false so that it does not throw an error on every operation and instead emits an error event
39
+ * and returns no-op responses.
40
+ * @default false
41
+ */
42
+ throwErrors?: boolean;
43
+ /**
44
+ * Timeout in milliseconds for the connection. Default is undefined, which uses the default timeout of the Redis client.
45
+ * If set, it will throw an error if the connection does not succeed within the specified time.
46
+ * @default undefined
47
+ */
48
+ connectionTimeout?: number;
35
49
  };
36
50
  type KeyvRedisPropertyOptions = KeyvRedisOptions & {
37
51
  /**
@@ -59,21 +73,22 @@ type KeyvRedisEntry<T> = {
59
73
  };
60
74
  declare enum RedisErrorMessages {
61
75
  /**
62
- * Error message when the Redis client is not connected.
76
+ * Error message when the Redis client is not connected and throwOnConnectError is set to true.
63
77
  */
64
- RedisClientNotConnected = "Redis client is not connected or has failed to connect"
78
+ RedisClientNotConnectedThrown = "Redis client is not connected or has failed to connect. This is thrown because throwOnConnectError is set to true."
65
79
  }
66
80
  declare const defaultReconnectStrategy: (attempts: number) => number | Error;
67
81
  type RedisClientConnectionType = RedisClientType | RedisClusterType<RedisModules, RedisFunctions, RedisScripts>;
68
- declare class KeyvRedis<T> extends EventEmitter implements KeyvStoreAdapter {
82
+ declare class KeyvRedis<T> extends Hookified implements KeyvStoreAdapter {
69
83
  private _client;
70
84
  private _namespace;
71
85
  private _keyPrefixSeparator;
72
86
  private _clearBatchSize;
73
87
  private _useUnlink;
74
88
  private _noNamespaceAffectsAll;
75
- private _connectTimeout;
76
- private _reconnectClient;
89
+ private _throwOnConnectError;
90
+ private _throwErrors;
91
+ private _connectionTimeout;
77
92
  /**
78
93
  * KeyvRedis constructor.
79
94
  * @param {string | RedisClientOptions | RedisClientType} [connect] How to connect to the Redis server. If string pass in the url, if object pass in the options, if RedisClient pass in the client.
@@ -144,19 +159,49 @@ declare class KeyvRedis<T> extends EventEmitter implements KeyvStoreAdapter {
144
159
  */
145
160
  set noNamespaceAffectsAll(value: boolean);
146
161
  /**
147
- * Get the timeout for connecting to Redis in milliseconds. This is used to prevent hanging indefinitely when connecting to Redis.
148
- * @default 200
162
+ * Get if throwOnConnectError is set to true.
163
+ * This is used to throw an error if the client is not connected when trying to connect. By default, this is
164
+ * set to true so that it throws an error when trying to connect to the Redis server fails.
165
+ * @default true
166
+ */
167
+ get throwOnConnectError(): boolean;
168
+ /**
169
+ * Set if throwOnConnectError is set to true.
170
+ * This is used to throw an error if the client is not connected when trying to connect. By default, this is
171
+ * set to true so that it throws an error when trying to connect to the Redis server fails.
149
172
  */
150
- get connectTimeout(): number;
173
+ set throwOnConnectError(value: boolean);
151
174
  /**
152
- * Set the timeout for connecting to Redis in milliseconds. This is used to prevent hanging indefinitely when connecting to Redis.
153
- * @default 200
175
+ * Get if throwErrors is set to true.
176
+ * This is used to throw an error if at any point there is a failure. Use this if you want to
177
+ * ensure that all operations are successful and you want to handle errors. By default, this is
178
+ * set to false so that it does not throw an error on every operation and instead emits an error event
179
+ * and returns no-op responses.
180
+ * @default false
181
+ */
182
+ get throwErrors(): boolean;
183
+ /**
184
+ * Set if throwErrors is set to true.
185
+ * This is used to throw an error if at any point there is a failure. Use this if you want to
186
+ * ensure that all operations are successful and you want to handle errors. By default, this is
187
+ * set to false so that it does not throw an error on every operation and instead emits an error event
188
+ * and returns no-op responses.
189
+ */
190
+ set throwErrors(value: boolean);
191
+ /**
192
+ * Get the connection timeout in milliseconds such as 5000 (5 seconds). Default is undefined. If undefined, it will use the default.
193
+ * @default undefined
194
+ */
195
+ get connectionTimeout(): number | undefined;
196
+ /**
197
+ * Set the connection timeout in milliseconds such as 5000 (5 seconds). Default is undefined. If undefined, it will use the default.
198
+ * @default undefined
154
199
  */
155
- set connectTimeout(value: number);
200
+ set connectionTimeout(value: number | undefined);
156
201
  /**
157
202
  * Get the Redis URL used to connect to the server. This is used to get a connected client.
158
203
  */
159
- getClient(): Promise<RedisClientConnectionType | undefined>;
204
+ getClient(): Promise<RedisClientConnectionType>;
160
205
  /**
161
206
  * Set a key value pair in the store. TTL is in milliseconds.
162
207
  * @param {string} key - the key to set
@@ -275,6 +320,7 @@ declare class KeyvRedis<T> extends EventEmitter implements KeyvStoreAdapter {
275
320
  private isClientCluster;
276
321
  private setOptions;
277
322
  private initClient;
323
+ private createTimeoutPromise;
278
324
  }
279
325
  /**
280
326
  * Will create a Keyv instance with the Redis adapter. This will also set the namespace and useKeyPrefix to false.
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import EventEmitter from 'node:events';
2
1
  import { RedisClientOptions, RedisClusterOptions, RedisClientType, RedisClusterType, RedisModules, RedisFunctions, RedisScripts } from '@redis/client';
3
2
  export { RedisClientOptions, RedisClientType, RedisClusterOptions, RedisClusterType, createClient, createCluster } from '@redis/client';
3
+ import { Hookified } from 'hookified';
4
4
  import { KeyvStoreAdapter, KeyvEntry, Keyv } from 'keyv';
5
5
  export { Keyv } from 'keyv';
6
6
 
@@ -28,10 +28,24 @@ type KeyvRedisOptions = {
28
28
  */
29
29
  noNamespaceAffectsAll?: boolean;
30
30
  /**
31
- * Timeout for connecting to Redis in milliseconds. This is used to prevent hanging indefinitely when connecting to Redis.
32
- * Defaults to `200`.
31
+ * This is used to throw an error if the client is not connected when trying to connect. By default, this is
32
+ * set to true so that it throws an error when trying to connect to the Redis server fails.
33
33
  */
34
- connectTimeout?: number;
34
+ throwOnConnectError?: boolean;
35
+ /**
36
+ * This is used to throw an error if at any point there is a failure. Use this if you want to
37
+ * ensure that all operations are successful and you want to handle errors. By default, this is
38
+ * set to false so that it does not throw an error on every operation and instead emits an error event
39
+ * and returns no-op responses.
40
+ * @default false
41
+ */
42
+ throwErrors?: boolean;
43
+ /**
44
+ * Timeout in milliseconds for the connection. Default is undefined, which uses the default timeout of the Redis client.
45
+ * If set, it will throw an error if the connection does not succeed within the specified time.
46
+ * @default undefined
47
+ */
48
+ connectionTimeout?: number;
35
49
  };
36
50
  type KeyvRedisPropertyOptions = KeyvRedisOptions & {
37
51
  /**
@@ -59,21 +73,22 @@ type KeyvRedisEntry<T> = {
59
73
  };
60
74
  declare enum RedisErrorMessages {
61
75
  /**
62
- * Error message when the Redis client is not connected.
76
+ * Error message when the Redis client is not connected and throwOnConnectError is set to true.
63
77
  */
64
- RedisClientNotConnected = "Redis client is not connected or has failed to connect"
78
+ RedisClientNotConnectedThrown = "Redis client is not connected or has failed to connect. This is thrown because throwOnConnectError is set to true."
65
79
  }
66
80
  declare const defaultReconnectStrategy: (attempts: number) => number | Error;
67
81
  type RedisClientConnectionType = RedisClientType | RedisClusterType<RedisModules, RedisFunctions, RedisScripts>;
68
- declare class KeyvRedis<T> extends EventEmitter implements KeyvStoreAdapter {
82
+ declare class KeyvRedis<T> extends Hookified implements KeyvStoreAdapter {
69
83
  private _client;
70
84
  private _namespace;
71
85
  private _keyPrefixSeparator;
72
86
  private _clearBatchSize;
73
87
  private _useUnlink;
74
88
  private _noNamespaceAffectsAll;
75
- private _connectTimeout;
76
- private _reconnectClient;
89
+ private _throwOnConnectError;
90
+ private _throwErrors;
91
+ private _connectionTimeout;
77
92
  /**
78
93
  * KeyvRedis constructor.
79
94
  * @param {string | RedisClientOptions | RedisClientType} [connect] How to connect to the Redis server. If string pass in the url, if object pass in the options, if RedisClient pass in the client.
@@ -144,19 +159,49 @@ declare class KeyvRedis<T> extends EventEmitter implements KeyvStoreAdapter {
144
159
  */
145
160
  set noNamespaceAffectsAll(value: boolean);
146
161
  /**
147
- * Get the timeout for connecting to Redis in milliseconds. This is used to prevent hanging indefinitely when connecting to Redis.
148
- * @default 200
162
+ * Get if throwOnConnectError is set to true.
163
+ * This is used to throw an error if the client is not connected when trying to connect. By default, this is
164
+ * set to true so that it throws an error when trying to connect to the Redis server fails.
165
+ * @default true
166
+ */
167
+ get throwOnConnectError(): boolean;
168
+ /**
169
+ * Set if throwOnConnectError is set to true.
170
+ * This is used to throw an error if the client is not connected when trying to connect. By default, this is
171
+ * set to true so that it throws an error when trying to connect to the Redis server fails.
149
172
  */
150
- get connectTimeout(): number;
173
+ set throwOnConnectError(value: boolean);
151
174
  /**
152
- * Set the timeout for connecting to Redis in milliseconds. This is used to prevent hanging indefinitely when connecting to Redis.
153
- * @default 200
175
+ * Get if throwErrors is set to true.
176
+ * This is used to throw an error if at any point there is a failure. Use this if you want to
177
+ * ensure that all operations are successful and you want to handle errors. By default, this is
178
+ * set to false so that it does not throw an error on every operation and instead emits an error event
179
+ * and returns no-op responses.
180
+ * @default false
181
+ */
182
+ get throwErrors(): boolean;
183
+ /**
184
+ * Set if throwErrors is set to true.
185
+ * This is used to throw an error if at any point there is a failure. Use this if you want to
186
+ * ensure that all operations are successful and you want to handle errors. By default, this is
187
+ * set to false so that it does not throw an error on every operation and instead emits an error event
188
+ * and returns no-op responses.
189
+ */
190
+ set throwErrors(value: boolean);
191
+ /**
192
+ * Get the connection timeout in milliseconds such as 5000 (5 seconds). Default is undefined. If undefined, it will use the default.
193
+ * @default undefined
194
+ */
195
+ get connectionTimeout(): number | undefined;
196
+ /**
197
+ * Set the connection timeout in milliseconds such as 5000 (5 seconds). Default is undefined. If undefined, it will use the default.
198
+ * @default undefined
154
199
  */
155
- set connectTimeout(value: number);
200
+ set connectionTimeout(value: number | undefined);
156
201
  /**
157
202
  * Get the Redis URL used to connect to the server. This is used to get a connected client.
158
203
  */
159
- getClient(): Promise<RedisClientConnectionType | undefined>;
204
+ getClient(): Promise<RedisClientConnectionType>;
160
205
  /**
161
206
  * Set a key value pair in the store. TTL is in milliseconds.
162
207
  * @param {string} key - the key to set
@@ -275,6 +320,7 @@ declare class KeyvRedis<T> extends EventEmitter implements KeyvStoreAdapter {
275
320
  private isClientCluster;
276
321
  private setOptions;
277
322
  private initClient;
323
+ private createTimeoutPromise;
278
324
  }
279
325
  /**
280
326
  * Will create a Keyv instance with the Redis adapter. This will also set the namespace and useKeyPrefix to false.
package/dist/index.js CHANGED
@@ -1,9 +1,9 @@
1
1
  // src/index.ts
2
- import EventEmitter from "events";
3
2
  import {
4
3
  createClient,
5
4
  createCluster
6
5
  } from "@redis/client";
6
+ import { Hookified } from "hookified";
7
7
  import { Keyv } from "keyv";
8
8
  import calculateSlot from "cluster-key-slot";
9
9
  import {
@@ -14,7 +14,7 @@ import {
14
14
  Keyv as Keyv2
15
15
  } from "keyv";
16
16
  var RedisErrorMessages = /* @__PURE__ */ ((RedisErrorMessages2) => {
17
- RedisErrorMessages2["RedisClientNotConnected"] = "Redis client is not connected or has failed to connect";
17
+ RedisErrorMessages2["RedisClientNotConnectedThrown"] = "Redis client is not connected or has failed to connect. This is thrown because throwOnConnectError is set to true.";
18
18
  return RedisErrorMessages2;
19
19
  })(RedisErrorMessages || {});
20
20
  var defaultReconnectStrategy = (attempts) => {
@@ -22,17 +22,16 @@ var defaultReconnectStrategy = (attempts) => {
22
22
  const jitter = (Math.random() - 0.5) * 100;
23
23
  return backoff + jitter;
24
24
  };
25
- var KeyvRedis = class extends EventEmitter {
25
+ var KeyvRedis = class extends Hookified {
26
26
  _client = createClient();
27
27
  _namespace;
28
28
  _keyPrefixSeparator = "::";
29
29
  _clearBatchSize = 1e3;
30
30
  _useUnlink = true;
31
31
  _noNamespaceAffectsAll = false;
32
- _connectTimeout = 200;
33
- // Timeout for connecting to Redis in milliseconds
34
- _reconnectClient = false;
35
- // Whether to reconnect the client
32
+ _throwOnConnectError = true;
33
+ _throwErrors = false;
34
+ _connectionTimeout;
36
35
  /**
37
36
  * KeyvRedis constructor.
38
37
  * @param {string | RedisClientOptions | RedisClientType} [connect] How to connect to the Redis server. If string pass in the url, if object pass in the options, if RedisClient pass in the client.
@@ -42,6 +41,7 @@ var KeyvRedis = class extends EventEmitter {
42
41
  super();
43
42
  const socket = {
44
43
  reconnectStrategy: defaultReconnectStrategy
44
+ // Default timeout for the connection
45
45
  };
46
46
  if (connect) {
47
47
  if (typeof connect === "string") {
@@ -84,6 +84,10 @@ var KeyvRedis = class extends EventEmitter {
84
84
  keyPrefixSeparator: this._keyPrefixSeparator,
85
85
  clearBatchSize: this._clearBatchSize,
86
86
  noNamespaceAffectsAll: this._noNamespaceAffectsAll,
87
+ useUnlink: this._useUnlink,
88
+ throwOnConnectError: this._throwOnConnectError,
89
+ throwErrors: this._throwErrors,
90
+ connectionTimeout: this._connectionTimeout,
87
91
  dialect: "redis",
88
92
  url
89
93
  };
@@ -167,49 +171,82 @@ var KeyvRedis = class extends EventEmitter {
167
171
  this._noNamespaceAffectsAll = value;
168
172
  }
169
173
  /**
170
- * Get the timeout for connecting to Redis in milliseconds. This is used to prevent hanging indefinitely when connecting to Redis.
171
- * @default 200
174
+ * Get if throwOnConnectError is set to true.
175
+ * This is used to throw an error if the client is not connected when trying to connect. By default, this is
176
+ * set to true so that it throws an error when trying to connect to the Redis server fails.
177
+ * @default true
172
178
  */
173
- get connectTimeout() {
174
- return this._connectTimeout;
179
+ get throwOnConnectError() {
180
+ return this._throwOnConnectError;
175
181
  }
176
182
  /**
177
- * Set the timeout for connecting to Redis in milliseconds. This is used to prevent hanging indefinitely when connecting to Redis.
178
- * @default 200
183
+ * Set if throwOnConnectError is set to true.
184
+ * This is used to throw an error if the client is not connected when trying to connect. By default, this is
185
+ * set to true so that it throws an error when trying to connect to the Redis server fails.
179
186
  */
180
- set connectTimeout(value) {
181
- if (value > 0) {
182
- this._connectTimeout = value;
183
- this._reconnectClient = true;
184
- } else {
185
- this.emit("error", "connectTimeout must be greater than 0");
186
- }
187
+ set throwOnConnectError(value) {
188
+ this._throwOnConnectError = value;
189
+ }
190
+ /**
191
+ * Get if throwErrors is set to true.
192
+ * This is used to throw an error if at any point there is a failure. Use this if you want to
193
+ * ensure that all operations are successful and you want to handle errors. By default, this is
194
+ * set to false so that it does not throw an error on every operation and instead emits an error event
195
+ * and returns no-op responses.
196
+ * @default false
197
+ */
198
+ get throwErrors() {
199
+ return this._throwErrors;
200
+ }
201
+ /**
202
+ * Set if throwErrors is set to true.
203
+ * This is used to throw an error if at any point there is a failure. Use this if you want to
204
+ * ensure that all operations are successful and you want to handle errors. By default, this is
205
+ * set to false so that it does not throw an error on every operation and instead emits an error event
206
+ * and returns no-op responses.
207
+ */
208
+ set throwErrors(value) {
209
+ this._throwErrors = value;
210
+ }
211
+ /**
212
+ * Get the connection timeout in milliseconds such as 5000 (5 seconds). Default is undefined. If undefined, it will use the default.
213
+ * @default undefined
214
+ */
215
+ get connectionTimeout() {
216
+ return this._connectionTimeout;
217
+ }
218
+ /**
219
+ * Set the connection timeout in milliseconds such as 5000 (5 seconds). Default is undefined. If undefined, it will use the default.
220
+ * @default undefined
221
+ */
222
+ set connectionTimeout(value) {
223
+ this._connectionTimeout = value;
187
224
  }
188
225
  /**
189
226
  * Get the Redis URL used to connect to the server. This is used to get a connected client.
190
227
  */
191
228
  async getClient() {
192
- if (this._client.isOpen && !this._reconnectClient) {
229
+ if (this._client.isOpen) {
193
230
  return this._client;
194
231
  }
195
- if (this._reconnectClient && this._client.isOpen) {
196
- await this._client.disconnect();
197
- }
198
232
  try {
199
- const timeoutPromise = new Promise((resolves, reject) => setTimeout(() => {
200
- reject(new Error("Redis client is not connected or has failed to connect" /* RedisClientNotConnected */));
201
- }, this._connectTimeout));
202
- await Promise.race([
203
- this._client.connect(),
204
- timeoutPromise
205
- ]);
206
- this._reconnectClient = false;
207
- this.initClient();
208
- return this._client;
233
+ if (this._connectionTimeout === void 0) {
234
+ await this._client.connect();
235
+ } else {
236
+ await Promise.race([
237
+ this._client.connect(),
238
+ this.createTimeoutPromise(this._connectionTimeout)
239
+ ]);
240
+ }
209
241
  } catch (error) {
210
242
  this.emit("error", error);
211
- return void 0;
243
+ if (this._throwOnConnectError) {
244
+ throw new Error("Redis client is not connected or has failed to connect. This is thrown because throwOnConnectError is set to true." /* RedisClientNotConnectedThrown */);
245
+ }
246
+ await this.disconnect(true);
212
247
  }
248
+ this.initClient();
249
+ return this._client;
213
250
  }
214
251
  /**
215
252
  * Set a key value pair in the store. TTL is in milliseconds.
@@ -219,15 +256,18 @@ var KeyvRedis = class extends EventEmitter {
219
256
  */
220
257
  async set(key, value, ttl) {
221
258
  const client = await this.getClient();
222
- if (!client) {
223
- this.emit("error", new Error("Redis client is not connected or has failed to connect" /* RedisClientNotConnected */));
224
- return;
225
- }
226
- key = this.createKeyPrefix(key, this._namespace);
227
- if (ttl) {
228
- await client.set(key, value, { PX: ttl });
229
- } else {
230
- await client.set(key, value);
259
+ try {
260
+ key = this.createKeyPrefix(key, this._namespace);
261
+ if (ttl) {
262
+ await client.set(key, value, { PX: ttl });
263
+ } else {
264
+ await client.set(key, value);
265
+ }
266
+ } catch (error) {
267
+ this.emit("error", error);
268
+ if (this._throwErrors) {
269
+ throw error;
270
+ }
231
271
  }
232
272
  }
233
273
  /**
@@ -236,20 +276,23 @@ var KeyvRedis = class extends EventEmitter {
236
276
  */
237
277
  async setMany(entries) {
238
278
  const client = await this.getClient();
239
- if (!client) {
240
- this.emit("error", new Error("Redis client is not connected or has failed to connect" /* RedisClientNotConnected */));
241
- return;
242
- }
243
- const multi = client.multi();
244
- for (const { key, value, ttl } of entries) {
245
- const prefixedKey = this.createKeyPrefix(key, this._namespace);
246
- if (ttl) {
247
- multi.set(prefixedKey, value, { PX: ttl });
248
- } else {
249
- multi.set(prefixedKey, value);
279
+ try {
280
+ const multi = client.multi();
281
+ for (const { key, value, ttl } of entries) {
282
+ const prefixedKey = this.createKeyPrefix(key, this._namespace);
283
+ if (ttl) {
284
+ multi.set(prefixedKey, value, { PX: ttl });
285
+ } else {
286
+ multi.set(prefixedKey, value);
287
+ }
288
+ }
289
+ await multi.exec();
290
+ } catch (error) {
291
+ this.emit("error", error);
292
+ if (this._throwErrors) {
293
+ throw error;
250
294
  }
251
295
  }
252
- await multi.exec();
253
296
  }
254
297
  /**
255
298
  * Check if a key exists in the store.
@@ -258,13 +301,17 @@ var KeyvRedis = class extends EventEmitter {
258
301
  */
259
302
  async has(key) {
260
303
  const client = await this.getClient();
261
- if (!client) {
262
- this.emit("error", new Error("Redis client is not connected or has failed to connect" /* RedisClientNotConnected */));
304
+ try {
305
+ key = this.createKeyPrefix(key, this._namespace);
306
+ const exists = await client.exists(key);
307
+ return exists === 1;
308
+ } catch (error) {
309
+ this.emit("error", error);
310
+ if (this._throwErrors) {
311
+ throw error;
312
+ }
263
313
  return false;
264
314
  }
265
- key = this.createKeyPrefix(key, this._namespace);
266
- const exists = await client.exists(key);
267
- return exists === 1;
268
315
  }
269
316
  /**
270
317
  * Check if many keys exist in the store. This will be done as a single transaction.
@@ -273,17 +320,21 @@ var KeyvRedis = class extends EventEmitter {
273
320
  */
274
321
  async hasMany(keys) {
275
322
  const client = await this.getClient();
276
- if (!client) {
277
- this.emit("error", new Error("Redis client is not connected or has failed to connect" /* RedisClientNotConnected */));
323
+ try {
324
+ const multi = client.multi();
325
+ for (const key of keys) {
326
+ const prefixedKey = this.createKeyPrefix(key, this._namespace);
327
+ multi.exists(prefixedKey);
328
+ }
329
+ const results = await multi.exec();
330
+ return results.map((result) => result === 1);
331
+ } catch (error) {
332
+ this.emit("error", error);
333
+ if (this._throwErrors) {
334
+ throw error;
335
+ }
278
336
  return Array.from({ length: keys.length }).fill(false);
279
337
  }
280
- const multi = client.multi();
281
- for (const key of keys) {
282
- const prefixedKey = this.createKeyPrefix(key, this._namespace);
283
- multi.exists(prefixedKey);
284
- }
285
- const results = await multi.exec();
286
- return results.map((result) => result === 1);
287
338
  }
288
339
  /**
289
340
  * Get a value from the store. If the key does not exist, it will return undefined.
@@ -292,16 +343,20 @@ var KeyvRedis = class extends EventEmitter {
292
343
  */
293
344
  async get(key) {
294
345
  const client = await this.getClient();
295
- if (!client) {
296
- this.emit("error", new Error("Redis client is not connected or has failed to connect" /* RedisClientNotConnected */));
297
- return void 0;
298
- }
299
- key = this.createKeyPrefix(key, this._namespace);
300
- const value = await client.get(key);
301
- if (value === null) {
346
+ try {
347
+ key = this.createKeyPrefix(key, this._namespace);
348
+ const value = await client.get(key);
349
+ if (value === null) {
350
+ return void 0;
351
+ }
352
+ return value;
353
+ } catch (error) {
354
+ this.emit("error", error);
355
+ if (this._throwErrors) {
356
+ throw error;
357
+ }
302
358
  return void 0;
303
359
  }
304
- return value;
305
360
  }
306
361
  /**
307
362
  * Get many values from the store. If a key does not exist, it will return undefined.
@@ -318,6 +373,9 @@ var KeyvRedis = class extends EventEmitter {
318
373
  return values;
319
374
  } catch (error) {
320
375
  this.emit("error", error);
376
+ if (this._throwErrors) {
377
+ throw error;
378
+ }
321
379
  return Array.from({ length: keys.length }).fill(void 0);
322
380
  }
323
381
  }
@@ -328,14 +386,18 @@ var KeyvRedis = class extends EventEmitter {
328
386
  */
329
387
  async delete(key) {
330
388
  const client = await this.getClient();
331
- if (!client) {
332
- this.emit("error", new Error("Redis client is not connected or has failed to connect" /* RedisClientNotConnected */));
389
+ try {
390
+ key = this.createKeyPrefix(key, this._namespace);
391
+ let deleted = 0;
392
+ deleted = await (this._useUnlink ? client.unlink(key) : client.del(key));
393
+ return deleted > 0;
394
+ } catch (error) {
395
+ this.emit("error", error);
396
+ if (this._throwErrors) {
397
+ throw error;
398
+ }
333
399
  return false;
334
400
  }
335
- key = this.createKeyPrefix(key, this._namespace);
336
- let deleted = 0;
337
- deleted = await (this._useUnlink ? client.unlink(key) : client.del(key));
338
- return deleted > 0;
339
401
  }
340
402
  /**
341
403
  * Delete many keys from the store. This will be done as a single transaction.
@@ -345,23 +407,26 @@ var KeyvRedis = class extends EventEmitter {
345
407
  async deleteMany(keys) {
346
408
  let result = false;
347
409
  const client = await this.getClient();
348
- if (!client) {
349
- this.emit("error", new Error("Redis client is not connected or has failed to connect" /* RedisClientNotConnected */));
350
- return false;
351
- }
352
- const multi = client.multi();
353
- for (const key of keys) {
354
- const prefixedKey = this.createKeyPrefix(key, this._namespace);
355
- if (this._useUnlink) {
356
- multi.unlink(prefixedKey);
357
- } else {
358
- multi.del(prefixedKey);
410
+ try {
411
+ const multi = client.multi();
412
+ for (const key of keys) {
413
+ const prefixedKey = this.createKeyPrefix(key, this._namespace);
414
+ if (this._useUnlink) {
415
+ multi.unlink(prefixedKey);
416
+ } else {
417
+ multi.del(prefixedKey);
418
+ }
359
419
  }
360
- }
361
- const results = await multi.exec();
362
- for (const deleted of results) {
363
- if (typeof deleted === "number" && deleted > 0) {
364
- result = true;
420
+ const results = await multi.exec();
421
+ for (const deleted of results) {
422
+ if (typeof deleted === "number" && deleted > 0) {
423
+ result = true;
424
+ }
425
+ }
426
+ } catch (error) {
427
+ this.emit("error", error);
428
+ if (this._throwErrors) {
429
+ throw error;
365
430
  }
366
431
  }
367
432
  return result;
@@ -519,9 +584,6 @@ var KeyvRedis = class extends EventEmitter {
519
584
  */
520
585
  async getSlotMaster(slot) {
521
586
  const connection = await this.getClient();
522
- if (!connection) {
523
- throw new Error("Redis client is not connected or has failed to connect" /* RedisClientNotConnected */);
524
- }
525
587
  if (this.isCluster()) {
526
588
  const cluster = connection;
527
589
  const mainNode = cluster.slots[slot].master;
@@ -574,14 +636,17 @@ var KeyvRedis = class extends EventEmitter {
574
636
  if (options.noNamespaceAffectsAll !== void 0) {
575
637
  this._noNamespaceAffectsAll = options.noNamespaceAffectsAll;
576
638
  }
577
- if (options.connectTimeout !== void 0 && options.connectTimeout > 0) {
578
- this._connectTimeout = options.connectTimeout;
639
+ if (options.throwOnConnectError !== void 0) {
640
+ this._throwOnConnectError = options.throwOnConnectError;
641
+ }
642
+ if (options.throwErrors !== void 0) {
643
+ this._throwErrors = options.throwErrors;
644
+ }
645
+ if (options.connectionTimeout !== void 0) {
646
+ this._connectionTimeout = options.connectionTimeout;
579
647
  }
580
648
  }
581
649
  initClient() {
582
- this._client.on("error", (error) => {
583
- this.emit("error", error);
584
- });
585
650
  this._client.on("connect", () => {
586
651
  this.emit("connect", this._client);
587
652
  });
@@ -592,6 +657,17 @@ var KeyvRedis = class extends EventEmitter {
592
657
  this.emit("reconnecting", reconnectInfo);
593
658
  });
594
659
  }
660
+ async createTimeoutPromise(timeoutMs) {
661
+ return new Promise((_, reject) => (
662
+ // eslint-disable-next-line no-promise-executor-return
663
+ setTimeout(
664
+ () => {
665
+ reject(new Error(`Redis timed out after ${timeoutMs}ms`));
666
+ },
667
+ timeoutMs
668
+ )
669
+ ));
670
+ }
595
671
  };
596
672
  function createKeyv(connect, options) {
597
673
  connect ??= "redis://localhost:6379";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@keyv/redis",
3
- "version": "4.5.0",
3
+ "version": "4.6.0",
4
4
  "description": "Redis storage adapter for Keyv",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -35,19 +35,20 @@
35
35
  "homepage": "https://github.com/jaredwray/keyv",
36
36
  "dependencies": {
37
37
  "@redis/client": "^1.6.0",
38
- "cluster-key-slot": "^1.1.2"
38
+ "cluster-key-slot": "^1.1.2",
39
+ "hookified": "^1.10.0"
39
40
  },
40
41
  "peerDependencies": {
41
42
  "keyv": "^5.3.4"
42
43
  },
43
44
  "devDependencies": {
44
- "@faker-js/faker": "^9.8.0",
45
- "@vitest/coverage-v8": "^3.2.3",
45
+ "@faker-js/faker": "^9.9.0",
46
+ "@vitest/coverage-v8": "^3.2.4",
46
47
  "rimraf": "^6.0.1",
47
48
  "timekeeper": "^2.3.1",
48
49
  "tsd": "^0.32.0",
49
- "vitest": "^3.2.3",
50
- "xo": "^1.1.0",
50
+ "vitest": "^3.2.4",
51
+ "xo": "^1.1.1",
51
52
  "@keyv/test-suite": "^2.0.8"
52
53
  },
53
54
  "tsd": {