@keyv/redis 4.6.0 → 5.1.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
@@ -25,17 +25,24 @@ Redis storage adapter for [Keyv](https://github.com/jaredwray/keyv).
25
25
 
26
26
  # Table of Contents
27
27
  * [Usage](#usage)
28
+ * [Migrating from v4 to v5](#migrating-from-v4-to-v5)
29
+ * [Using the createKeyv function](#using-the-createkeyv-function)
30
+ * [Using the createKeyvNonBlocking function](#using-the-createkeyvnonblocking-function)
28
31
  * [Namespaces](#namespaces)
32
+ * [Fixing Double Prefixing of Keys](#fixing-double-prefixing-of-keys)
29
33
  * [Using Generic Types](#using-generic-types)
30
34
  * [Performance Considerations](#performance-considerations)
31
35
  * [High Memory Usage on Redis Server](#high-memory-usage-on-redis-server)
32
36
  * [Gracefully Handling Errors and Timeouts](#gracefully-handling-errors-and-timeouts)
33
37
  * [Using Cacheable with Redis](#using-cacheable-with-redis)
34
- * [Clustering and TLS Support](#clustering-and-tls-support)
38
+ * [Clustering](#clustering)
39
+ * [Sentinel](#sentinel)
40
+ * [TLS Support](#tls-support)
35
41
  * [API](#api)
36
42
  * [Using Custom Redis Client Events](#using-custom-redis-client-events)
37
43
  * [Migrating from v3 to v4](#migrating-from-v3-to-v4)
38
44
  * [About Redis Sets and its Support in v4](#about-redis-sets-and-its-support-in-v4)
45
+ * [Using with NestJS](#using-with-nestjs)
39
46
  * [License](#license)
40
47
 
41
48
  # Installation
@@ -100,6 +107,10 @@ const keyvRedis = new KeyvRedis(redis);
100
107
  const keyv = new Keyv({ store: keyvRedis});
101
108
  ```
102
109
 
110
+ # Migrating from v4 to v5
111
+
112
+ The major change from v4 to v5 is that we are now using v5 of the `@redis/client` library which has a new API. This means that some methods have changed but it should be a drop-in replacement for most use cases.
113
+
103
114
  # Keyv Redis Options
104
115
 
105
116
  You can pass in options to the `KeyvRedis` constructor. Here are the available options:
@@ -143,7 +154,7 @@ export type KeyvRedisOptions = {
143
154
  * and returns no-op responses.
144
155
  * @default false
145
156
  */
146
- throwErrors?: boolean;
157
+ throwOnErrors?: boolean;
147
158
 
148
159
  /**
149
160
  * Timeout in milliseconds for the connection. Default is undefined, which uses the default timeout of the Redis client.
@@ -180,6 +191,30 @@ const keyv = createKeyv('redis://user:pass@localhost:6379');
180
191
  keyv.store.namespace = 'my-namespace';
181
192
  ```
182
193
 
194
+ # Using the `createKeyv` function
195
+
196
+ The `createKeyv` function is a convenience function that creates a new `Keyv` instance with the `@keyv/redis` store. It automatically sets the `useKeyPrefix` option to `false`. Here is an example of how to use it:
197
+
198
+ ```js
199
+ import { createKeyv } from '@keyv/redis';
200
+ const keyv = createKeyv('redis://user:pass@localhost:6379');
201
+ ```
202
+
203
+ To use a namespace you can do it here and this will set Keyv up correctly to avoid the double namespace issue:
204
+
205
+ ```js
206
+ import { createKeyv } from '@keyv/redis';
207
+ const keyv = createKeyv('redis://user:pass@localhost:6379', {namespace: 'my-namespace'});
208
+ ```
209
+
210
+ # Using the `createKeyvNonBlocking` function
211
+
212
+ The `createKeyvNonBlocking` function is a convenience function that creates a new `Keyv` instance with the `@keyv/redis` store does what `createKeyv` does but also disables throwing errors, removes the offline queue redis functionality, and reconnect strategy so that when used as a secondary cache in libraries such as [cacheable](https://npmjs.org/package/cacheable) it does not block the primary cache. This is useful when you want to use Redis as a secondary cache and do not want to block the primary cache on connection errors or timeouts when using `nonBlocking`. Here is an example of how to use it:
213
+
214
+ ```js
215
+ import { createKeyvNonBlocking } from '@keyv/redis';
216
+ const keyv = createKeyvNonBlocking('redis://user:pass@localhost:6379');
217
+ ```
183
218
 
184
219
  # Namespaces
185
220
 
@@ -191,10 +226,17 @@ import KeyvRedis, { createClient } from '@keyv/redis';
191
226
 
192
227
  const redis = createClient('redis://user:pass@localhost:6379');
193
228
  const keyvRedis = new KeyvRedis(redis);
194
- const keyv = new Keyv({ store: keyvRedis, namespace: 'my-namespace' });
229
+ const keyv = new Keyv({ store: keyvRedis, namespace: 'my-namespace', useKeyPrefix: false });
230
+ ```
231
+
232
+ To make this easier, you can use the `createKeyv` function which will automatically set the `namespace` option to the `KeyvRedis` instance:
233
+
234
+ ```js
235
+ import { createKeyv } from '@keyv/redis';
236
+ const keyv = createKeyv('redis://user:pass@localhost:6379', { namespace: 'my-namespace' });
195
237
  ```
196
238
 
197
- This will prefix all keys with `my-namespace:`. You can also set the namespace after the fact:
239
+ This will prefix all keys with `my-namespace:` and will also set `useKeyPrefix` to `false`. This is done to avoid double prefixing of keys as we transition out of the legacy behavior in Keyv. You can also set the namespace after the fact:
198
240
 
199
241
  ```js
200
242
  keyv.namespace = 'my-namespace';
@@ -202,6 +244,24 @@ keyv.namespace = 'my-namespace';
202
244
 
203
245
  NOTE: If you plan to do many clears or deletes, it is recommended to read the [Performance Considerations](#performance-considerations) section.
204
246
 
247
+ # Fixing Double Prefixing of Keys
248
+
249
+ If you are using `Keyv` with `@keyv/redis` as the storage adapter, you may notice that keys are being prefixed twice. This is because `Keyv` has a default prefixing behavior that is applied to all keys. To fix this, you can set the `useKeyPrefix` option to `false` when creating the `Keyv` instance:
250
+
251
+ ```js
252
+ import Keyv from 'keyv';
253
+ import KeyvRedis from '@keyv/redis';
254
+
255
+ const keyv = new Keyv(new KeyvRedis('redis://user:pass@localhost:6379'), { useKeyPrefix: false });
256
+ ```
257
+
258
+ To make this easier, you can use the `createKeyv` function which will automatically set the `useKeyPrefix` option to `false`:
259
+
260
+ ```js
261
+ import { createKeyv } from '@keyv/redis';
262
+ const keyv = createKeyv('redis://user:pass@localhost:6379');
263
+ ```
264
+
205
265
  ## Using Generic Types
206
266
 
207
267
  When initializing `KeyvRedis`, you can specify the type of the values you are storing and you can also specify types when calling methods:
@@ -270,11 +330,21 @@ keyv.on('error', (error) => {
270
330
  });
271
331
  ```
272
332
 
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:
333
+ 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. If you want this to throw you will need to also set the Keyv instance to `throwOnErrors: true`:
334
+
335
+ ```js
336
+ import Keyv from 'keyv';
337
+ import KeyvRedis from '@keyv/redis';
338
+
339
+ const keyv = new Keyv(new KeyvRedis('redis://bad-uri:1111', { throwOnConnectError: false }));
340
+ keyv.throwOnErrors = true; // This will throw an error if the connection fails
341
+
342
+ await keyv.set('key', 'value'); // this will throw the connection error only.
343
+ ```
274
344
 
275
345
  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.
276
346
 
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:
347
+ If you want to handle connection errors, retries, and timeouts more gracefully, you can use the `throwOnErrors` option. This will throw an error if any operation fails, allowing you to catch it and handle it accordingly:
278
348
 
279
349
  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:
280
350
 
@@ -312,9 +382,9 @@ const cache = new Cacheable( { secondary, nonBlocking: true } );
312
382
 
313
383
  This will make it so that the secondary does not block the primary cache and will be very fast. 🚀
314
384
 
315
- # Clustering and TLS Support
385
+ # Clustering
316
386
 
317
- If you are using a Redis Cluster or need to use TLS, you can pass in the `redisOptions` directly. Here is an example of how to do that:
387
+ If you are using a Redis Cluster, you can pass in the `redisOptions` directly. Here is an example of how to do that:
318
388
 
319
389
  ```js
320
390
  import Keyv from 'keyv';
@@ -337,9 +407,42 @@ const cluster = createCluster({
337
407
  const keyv = new Keyv({ store: new KeyvRedis(cluster) });
338
408
  ```
339
409
 
340
- You can learn more about the `createCluster` function in the [documentation](https://github.com/redis/node-redis/blob/master/docs/clustering.md) at https://github.com/redis/node-redis/tree/master/docs.
410
+ You can learn more about the `createCluster` function in the [documentation](https://github.com/redis/node-redis/blob/master/docs/clustering.md) at https://github.com/redis/node-redis/tree/master/docs.
411
+
412
+ # Sentinel
413
+
414
+ If you are using Sentinel to provide high availability for your Redis instances, you can pass in the `redisOptions` directly. Here is an example of how to do that:
415
+
416
+ ```js
417
+ import Keyv from 'keyv';
418
+ import KeyvRedis, { createSentinel } from '@keyv/redis';
419
+
420
+ const sentinel = createSentinel({
421
+ name: 'sentinel-db',
422
+ sentinelRootNodes: [
423
+ {
424
+ host: '127.0.0.1',
425
+ port: 26379,
426
+ },
427
+ {
428
+ host: '127.0.0.1',
429
+ port: 26380,
430
+ },
431
+ {
432
+ host: '127.0.0.1',
433
+ port: 26381,
434
+ },
435
+ ],
436
+ });
437
+
438
+ const keyv = new Keyv({ store: new KeyvRedis(sentinel) });
439
+ ```
440
+
441
+ You can learn more about the `createSentinel` function in the [documentation](https://github.com/redis/node-redis/blob/master/docs/sentinel.md) at https://github.com/redis/node-redis/tree/master/docs.
442
+
443
+ # TLS Support
341
444
 
342
- Here is an example of how to use TLS:
445
+ Here is an example of how to use TLS using the `redisOptions`:
343
446
 
344
447
  ```js
345
448
  import Keyv from 'keyv';
@@ -433,6 +536,165 @@ This will make it so the storage adapter `@keyv/redis` will handle the namespace
433
536
 
434
537
  We no longer support redis sets. This is due to the fact that it caused significant performance issues and was not a good fit for the library.
435
538
 
539
+
540
+
541
+ # Using with NestJS
542
+
543
+ > You can integrate `@keyv/redis` with NestJS by creating a custom `CacheModule`. This allows you to use Keyv as a cache store in your application.
544
+
545
+ ### 1. Install Dependencies
546
+
547
+ ```bash
548
+ npm install @keyv/redis keyv @nestjs/cache-manager cache-manager cacheable
549
+ ```
550
+
551
+ ### 2. Create a Cache Module
552
+
553
+ Create a file `cache.module.ts`:
554
+
555
+ ```ts
556
+ import { Module } from '@nestjs/common';
557
+ import { CacheModule as NestCacheModule } from '@nestjs/cache-manager';
558
+ import { createKeyv } from '@keyv/redis';
559
+
560
+ @Module({
561
+ imports: [
562
+ NestCacheModule.registerAsync({
563
+ useFactory: () => ({
564
+ stores: [createKeyv('redis://localhost:6379')],
565
+ }),
566
+ }),
567
+ ],
568
+ providers: [],
569
+ exports: [],
570
+ })
571
+ export class CacheModule {}
572
+ ```
573
+
574
+ ### 3. Import the Cache Module in AppModule
575
+ Update `app.module.ts`:
576
+
577
+ ```ts
578
+ import { Module } from '@nestjs/common';
579
+ import { CacheModule } from './modules/config/cache/cache.module';
580
+
581
+ @Module({
582
+ imports: [
583
+ CacheModule, // Import your custom cache module
584
+ // other modules...
585
+ ],
586
+ })
587
+ export class AppModule {}
588
+ ```
589
+
590
+ ### 4. Create the Cache Service
591
+ Create a file `cache.service.ts`:
592
+
593
+ ```ts
594
+ import { CACHE_MANAGER } from '@nestjs/cache-manager';
595
+ import { Inject, Injectable } from '@nestjs/common';
596
+ import { Cache } from 'cache-manager';
597
+
598
+ @Injectable()
599
+ export class CacheService {
600
+ constructor(@Inject(CACHE_MANAGER) private cacheManager: Cache) {}
601
+
602
+ async get<T>(key: string): Promise<T | undefined> {
603
+ return this.cacheManager.get<T>(key);
604
+ }
605
+
606
+ async set<T>(key: string, value: T, ttl?: number): Promise<void> {
607
+ await this.cacheManager.set(key, value, ttl);
608
+ }
609
+
610
+ async delete(key: string): Promise<void> {
611
+ await this.cacheManager.del(key);
612
+ }
613
+ }
614
+ ```
615
+
616
+ ### 5. Register CacheService in CacheModule
617
+ Update `cache.module.ts`:
618
+
619
+ ```ts
620
+ import { Module } from '@nestjs/common';
621
+ import { CacheModule as NestCacheModule } from '@nestjs/cache-manager';
622
+ import { createKeyv } from '@keyv/redis';
623
+ import { CacheService } from './services/cache.service';
624
+
625
+ @Module({
626
+ imports: [
627
+ NestCacheModule.registerAsync({
628
+ useFactory: () => ({
629
+ stores: [createKeyv('redis://localhost:6379')],
630
+ }),
631
+ }),
632
+ ],
633
+ providers: [CacheService],
634
+ exports: [CacheService],
635
+ })
636
+ export class CacheModule {}
637
+ ```
638
+ ### 6. Import CacheModule in the Target Module (e.g. TaskModule)
639
+ ```ts
640
+ import { Module } from '@nestjs/common';
641
+ import { TaskService } from './task.service';
642
+ import { TaskRepository } from './repositories/task.repository';
643
+ import { CacheModule } from 'src/modules/config/cache/cache.module';
644
+
645
+ @Module({
646
+ imports: [CacheModule],
647
+ providers: [TaskService, TaskRepository],
648
+ })
649
+ export class TaskModule {}
650
+ ```
651
+
652
+ ### 7. Using the Cache in a Service
653
+
654
+ ```ts
655
+ import { Injectable, NotFoundException } from '@nestjs/common';
656
+ import { TaskRepository } from '../repositories/task.repository';
657
+ import { TaskDto } from '../dto/task.dto';
658
+ import { CacheService } from 'src/modules/config/cache/services/cache.service';
659
+
660
+ @Injectable()
661
+ export class TaskService {
662
+ constructor(
663
+ private readonly taskRepository: TaskRepository,
664
+ private readonly cache: CacheService, // Inject the CacheService
665
+ ) {}
666
+
667
+ async findById(id: number): Promise<TaskDto> {
668
+ const cacheKey = `task:${id}`;
669
+
670
+ // 1. Try to get from cache
671
+ const cached = await this.cache.get<TaskDto>(cacheKey);
672
+
673
+ if (cached) {
674
+ return cached;
675
+ }
676
+
677
+ // 2. If not found in cache, fetch from database
678
+ const task = await this.taskRepository.findById(id);
679
+
680
+ if (!task) {
681
+ throw new NotFoundException('Task not found');
682
+ }
683
+
684
+ // 3. Set in cache for future requests
685
+ await this.cache.set(cacheKey, task, 300 * 1000); // 5 minutes TTL
686
+ return task;
687
+ }
688
+ }
689
+ ```
690
+
691
+
692
+ You can learn more about caching in NestJS in the [official documentation](https://docs.nestjs.com/techniques/caching#in-memory-cache).
693
+
694
+
695
+ ---
696
+
697
+
436
698
  # License
437
699
 
438
700
  [MIT © Jared Wray](LISCENCE)
package/dist/index.cjs CHANGED
@@ -35,6 +35,8 @@ __export(index_exports, {
35
35
  createClient: () => import_client2.createClient,
36
36
  createCluster: () => import_client2.createCluster,
37
37
  createKeyv: () => createKeyv,
38
+ createKeyvNonBlocking: () => createKeyvNonBlocking,
39
+ createSentinel: () => import_client2.createSentinel,
38
40
  default: () => KeyvRedis,
39
41
  defaultReconnectStrategy: () => defaultReconnectStrategy
40
42
  });
@@ -62,7 +64,7 @@ var KeyvRedis = class extends import_hookified.Hookified {
62
64
  _useUnlink = true;
63
65
  _noNamespaceAffectsAll = false;
64
66
  _throwOnConnectError = true;
65
- _throwErrors = false;
67
+ _throwOnErrors = false;
66
68
  _connectionTimeout;
67
69
  /**
68
70
  * KeyvRedis constructor.
@@ -79,9 +81,21 @@ var KeyvRedis = class extends import_hookified.Hookified {
79
81
  if (typeof connect === "string") {
80
82
  this._client = (0, import_client.createClient)({ url: connect, socket });
81
83
  } else if (connect.connect !== void 0) {
82
- this._client = this.isClientCluster(connect) ? connect : connect;
84
+ if (this.isClientSentinel(connect)) {
85
+ this._client = connect;
86
+ } else if (this.isClientCluster(connect)) {
87
+ this._client = connect;
88
+ } else {
89
+ this._client = connect;
90
+ }
83
91
  } else if (connect instanceof Object) {
84
- this._client = connect.rootNodes === void 0 ? (0, import_client.createClient)(connect) : (0, import_client.createCluster)(connect);
92
+ if (connect.sentinelRootNodes !== void 0) {
93
+ this._client = (0, import_client.createSentinel)(connect);
94
+ } else if (connect.rootNodes === void 0) {
95
+ this._client = (0, import_client.createClient)(connect);
96
+ } else {
97
+ this._client = (0, import_client.createCluster)(connect);
98
+ }
85
99
  }
86
100
  }
87
101
  this.setOptions(options);
@@ -118,7 +132,7 @@ var KeyvRedis = class extends import_hookified.Hookified {
118
132
  noNamespaceAffectsAll: this._noNamespaceAffectsAll,
119
133
  useUnlink: this._useUnlink,
120
134
  throwOnConnectError: this._throwOnConnectError,
121
- throwErrors: this._throwErrors,
135
+ throwOnErrors: this._throwOnErrors,
122
136
  connectionTimeout: this._connectionTimeout,
123
137
  dialect: "redis",
124
138
  url
@@ -220,25 +234,25 @@ var KeyvRedis = class extends import_hookified.Hookified {
220
234
  this._throwOnConnectError = value;
221
235
  }
222
236
  /**
223
- * Get if throwErrors is set to true.
237
+ * Get if throwOnErrors is set to true.
224
238
  * This is used to throw an error if at any point there is a failure. Use this if you want to
225
239
  * ensure that all operations are successful and you want to handle errors. By default, this is
226
240
  * set to false so that it does not throw an error on every operation and instead emits an error event
227
241
  * and returns no-op responses.
228
242
  * @default false
229
243
  */
230
- get throwErrors() {
231
- return this._throwErrors;
244
+ get throwOnErrors() {
245
+ return this._throwOnErrors;
232
246
  }
233
247
  /**
234
- * Set if throwErrors is set to true.
248
+ * Set if throwOnErrors is set to true.
235
249
  * This is used to throw an error if at any point there is a failure. Use this if you want to
236
250
  * ensure that all operations are successful and you want to handle errors. By default, this is
237
251
  * set to false so that it does not throw an error on every operation and instead emits an error event
238
252
  * and returns no-op responses.
239
253
  */
240
- set throwErrors(value) {
241
- this._throwErrors = value;
254
+ set throwOnErrors(value) {
255
+ this._throwOnErrors = value;
242
256
  }
243
257
  /**
244
258
  * Get the connection timeout in milliseconds such as 5000 (5 seconds). Default is undefined. If undefined, it will use the default.
@@ -272,10 +286,10 @@ var KeyvRedis = class extends import_hookified.Hookified {
272
286
  }
273
287
  } catch (error) {
274
288
  this.emit("error", error);
289
+ await this.disconnect(true);
275
290
  if (this._throwOnConnectError) {
276
291
  throw new Error("Redis client is not connected or has failed to connect. This is thrown because throwOnConnectError is set to true." /* RedisClientNotConnectedThrown */);
277
292
  }
278
- await this.disconnect(true);
279
293
  }
280
294
  this.initClient();
281
295
  return this._client;
@@ -297,7 +311,7 @@ var KeyvRedis = class extends import_hookified.Hookified {
297
311
  }
298
312
  } catch (error) {
299
313
  this.emit("error", error);
300
- if (this._throwErrors) {
314
+ if (this._throwOnErrors) {
301
315
  throw error;
302
316
  }
303
317
  }
@@ -321,7 +335,7 @@ var KeyvRedis = class extends import_hookified.Hookified {
321
335
  await multi.exec();
322
336
  } catch (error) {
323
337
  this.emit("error", error);
324
- if (this._throwErrors) {
338
+ if (this._throwOnErrors) {
325
339
  throw error;
326
340
  }
327
341
  }
@@ -339,7 +353,7 @@ var KeyvRedis = class extends import_hookified.Hookified {
339
353
  return exists === 1;
340
354
  } catch (error) {
341
355
  this.emit("error", error);
342
- if (this._throwErrors) {
356
+ if (this._throwOnErrors) {
343
357
  throw error;
344
358
  }
345
359
  return false;
@@ -359,10 +373,10 @@ var KeyvRedis = class extends import_hookified.Hookified {
359
373
  multi.exists(prefixedKey);
360
374
  }
361
375
  const results = await multi.exec();
362
- return results.map((result) => result === 1);
376
+ return results.map((result) => typeof result === "number" && result === 1);
363
377
  } catch (error) {
364
378
  this.emit("error", error);
365
- if (this._throwErrors) {
379
+ if (this._throwOnErrors) {
366
380
  throw error;
367
381
  }
368
382
  return Array.from({ length: keys.length }).fill(false);
@@ -384,7 +398,7 @@ var KeyvRedis = class extends import_hookified.Hookified {
384
398
  return value;
385
399
  } catch (error) {
386
400
  this.emit("error", error);
387
- if (this._throwErrors) {
401
+ if (this._throwOnErrors) {
388
402
  throw error;
389
403
  }
390
404
  return void 0;
@@ -405,7 +419,7 @@ var KeyvRedis = class extends import_hookified.Hookified {
405
419
  return values;
406
420
  } catch (error) {
407
421
  this.emit("error", error);
408
- if (this._throwErrors) {
422
+ if (this._throwOnErrors) {
409
423
  throw error;
410
424
  }
411
425
  return Array.from({ length: keys.length }).fill(void 0);
@@ -425,7 +439,7 @@ var KeyvRedis = class extends import_hookified.Hookified {
425
439
  return deleted > 0;
426
440
  } catch (error) {
427
441
  this.emit("error", error);
428
- if (this._throwErrors) {
442
+ if (this._throwOnErrors) {
429
443
  throw error;
430
444
  }
431
445
  return false;
@@ -457,7 +471,7 @@ var KeyvRedis = class extends import_hookified.Hookified {
457
471
  }
458
472
  } catch (error) {
459
473
  this.emit("error", error);
460
- if (this._throwErrors) {
474
+ if (this._throwOnErrors) {
461
475
  throw error;
462
476
  }
463
477
  }
@@ -471,7 +485,7 @@ var KeyvRedis = class extends import_hookified.Hookified {
471
485
  */
472
486
  async disconnect(force) {
473
487
  if (this._client.isOpen) {
474
- await (force ? this._client.disconnect() : this._client.quit());
488
+ await (force ? this._client.destroy() : this._client.close());
475
489
  }
476
490
  }
477
491
  /**
@@ -505,6 +519,13 @@ var KeyvRedis = class extends import_hookified.Hookified {
505
519
  isCluster() {
506
520
  return this.isClientCluster(this._client);
507
521
  }
522
+ /**
523
+ * Is the client a sentinel.
524
+ * @returns {boolean} - true if the client is a sentinel, false if not
525
+ */
526
+ isSentinel() {
527
+ return this.isClientSentinel(this._client);
528
+ }
508
529
  /**
509
530
  * Get the master nodes in the cluster. If not a cluster, it will return the single client.
510
531
  *
@@ -513,7 +534,8 @@ var KeyvRedis = class extends import_hookified.Hookified {
513
534
  async getMasterNodes() {
514
535
  if (this.isCluster()) {
515
536
  const cluster = await this.getClient();
516
- return Promise.all(cluster.masters.map(async (main) => cluster.nodeClient(main)));
537
+ const nodes = cluster.masters.map(async (main) => cluster.nodeClient(main));
538
+ return Promise.all(nodes);
517
539
  }
518
540
  return [await this.getClient()];
519
541
  }
@@ -528,7 +550,7 @@ var KeyvRedis = class extends import_hookified.Hookified {
528
550
  const match = namespace ? `${namespace}${this._keyPrefixSeparator}*` : "*";
529
551
  let cursor = "0";
530
552
  do {
531
- const result = await client.scan(Number.parseInt(cursor, 10), { MATCH: match, TYPE: "string" });
553
+ const result = await client.scan(cursor, { MATCH: match, TYPE: "string" });
532
554
  cursor = result.cursor.toString();
533
555
  let { keys } = result;
534
556
  if (!namespace && !this._noNamespaceAffectsAll) {
@@ -565,7 +587,7 @@ var KeyvRedis = class extends import_hookified.Hookified {
565
587
  const match = this._namespace ? `${this._namespace}${this._keyPrefixSeparator}*` : "*";
566
588
  const deletePromises = [];
567
589
  do {
568
- const result = await client.scan(Number.parseInt(cursor, 10), { MATCH: match, COUNT: batchSize, TYPE: "string" });
590
+ const result = await client.scan(cursor, { MATCH: match, COUNT: batchSize, TYPE: "string" });
569
591
  cursor = result.cursor.toString();
570
592
  let { keys } = result;
571
593
  if (keys.length === 0) {
@@ -644,10 +666,10 @@ var KeyvRedis = class extends import_hookified.Hookified {
644
666
  return slotMap;
645
667
  }
646
668
  isClientCluster(client) {
647
- if (client.options === void 0 && client.scan === void 0) {
648
- return true;
649
- }
650
- return false;
669
+ return client.slots !== void 0;
670
+ }
671
+ isClientSentinel(client) {
672
+ return client.getSentinelNode !== void 0;
651
673
  }
652
674
  setOptions(options) {
653
675
  if (!options) {
@@ -671,8 +693,8 @@ var KeyvRedis = class extends import_hookified.Hookified {
671
693
  if (options.throwOnConnectError !== void 0) {
672
694
  this._throwOnConnectError = options.throwOnConnectError;
673
695
  }
674
- if (options.throwErrors !== void 0) {
675
- this._throwErrors = options.throwErrors;
696
+ if (options.throwOnErrors !== void 0) {
697
+ this._throwOnErrors = options.throwOnErrors;
676
698
  }
677
699
  if (options.connectionTimeout !== void 0) {
678
700
  this._connectionTimeout = options.connectionTimeout;
@@ -704,7 +726,40 @@ var KeyvRedis = class extends import_hookified.Hookified {
704
726
  function createKeyv(connect, options) {
705
727
  connect ??= "redis://localhost:6379";
706
728
  const adapter = new KeyvRedis(connect, options);
707
- const keyv = new import_keyv.Keyv(adapter, { namespace: options?.namespace, useKeyPrefix: false });
729
+ if (options?.namespace) {
730
+ adapter.namespace = options.namespace;
731
+ const keyv2 = new import_keyv.Keyv(adapter, { namespace: options?.namespace, useKeyPrefix: false });
732
+ if (options?.throwOnConnectError) {
733
+ keyv2.throwOnErrors = true;
734
+ }
735
+ if (options?.throwOnErrors) {
736
+ keyv2.throwOnErrors = true;
737
+ }
738
+ return keyv2;
739
+ }
740
+ const keyv = new import_keyv.Keyv(adapter, { useKeyPrefix: false });
741
+ if (options?.throwOnConnectError) {
742
+ keyv.throwOnErrors = true;
743
+ }
744
+ if (options?.throwOnErrors) {
745
+ keyv.throwOnErrors = true;
746
+ }
747
+ keyv.namespace = void 0;
748
+ return keyv;
749
+ }
750
+ function createKeyvNonBlocking(connect, options) {
751
+ const keyv = createKeyv(connect, options);
752
+ const keyvStore = keyv.store;
753
+ keyvStore.throwOnConnectError = false;
754
+ keyvStore.throwOnErrors = false;
755
+ const redisClient = keyvStore.client;
756
+ if (redisClient.options) {
757
+ redisClient.options.disableOfflineQueue = true;
758
+ if (redisClient.options.socket) {
759
+ redisClient.options.socket.reconnectStrategy = false;
760
+ }
761
+ }
762
+ keyv.throwOnErrors = false;
708
763
  return keyv;
709
764
  }
710
765
  // Annotate the CommonJS export names for ESM import in node:
@@ -714,5 +769,7 @@ function createKeyv(connect, options) {
714
769
  createClient,
715
770
  createCluster,
716
771
  createKeyv,
772
+ createKeyvNonBlocking,
773
+ createSentinel,
717
774
  defaultReconnectStrategy
718
775
  });
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { RedisClientOptions, RedisClusterOptions, RedisClientType, RedisClusterType, RedisModules, RedisFunctions, RedisScripts } from '@redis/client';
2
- export { RedisClientOptions, RedisClientType, RedisClusterOptions, RedisClusterType, createClient, createCluster } from '@redis/client';
1
+ import { RedisClientOptions, RedisClusterOptions, RedisSentinelOptions, RedisClientType, RedisModules, RedisFunctions, RedisScripts, RespVersions, TypeMapping, RedisClusterType, RedisSentinelType } from '@redis/client';
2
+ export { RedisClientOptions, RedisClientType, RedisClusterOptions, RedisClusterType, RedisSentinelType, createClient, createCluster, createSentinel } from '@redis/client';
3
3
  import { Hookified } from 'hookified';
4
4
  import { KeyvStoreAdapter, KeyvEntry, Keyv } from 'keyv';
5
5
  export { Keyv } from 'keyv';
@@ -39,7 +39,7 @@ type KeyvRedisOptions = {
39
39
  * and returns no-op responses.
40
40
  * @default false
41
41
  */
42
- throwErrors?: boolean;
42
+ throwOnErrors?: boolean;
43
43
  /**
44
44
  * Timeout in milliseconds for the connection. Default is undefined, which uses the default timeout of the Redis client.
45
45
  * If set, it will throw an error if the connection does not succeed within the specified time.
@@ -78,7 +78,10 @@ declare enum RedisErrorMessages {
78
78
  RedisClientNotConnectedThrown = "Redis client is not connected or has failed to connect. This is thrown because throwOnConnectError is set to true."
79
79
  }
80
80
  declare const defaultReconnectStrategy: (attempts: number) => number | Error;
81
- type RedisClientConnectionType = RedisClientType | RedisClusterType<RedisModules, RedisFunctions, RedisScripts>;
81
+ type RedisConnectionClientType = RedisClientType | RedisClientType<RedisModules, RedisFunctions, RedisScripts, RespVersions> | RedisClientType<RedisModules, RedisFunctions, RedisScripts, RespVersions, TypeMapping>;
82
+ type RedisConnectionClusterType = RedisClusterType | RedisClusterType<RedisModules, RedisFunctions, RedisScripts, RespVersions> | RedisClusterType<RedisModules, RedisFunctions, RedisScripts, RespVersions, TypeMapping>;
83
+ type RedisConnectionSentinelType = RedisSentinelType | RedisSentinelType<RedisModules, RedisFunctions, RedisScripts, RespVersions> | RedisSentinelType<RedisModules, RedisFunctions, RedisScripts, RespVersions, TypeMapping>;
84
+ type RedisClientConnectionType = RedisConnectionClientType | RedisConnectionClusterType | RedisConnectionSentinelType;
82
85
  declare class KeyvRedis<T> extends Hookified implements KeyvStoreAdapter {
83
86
  private _client;
84
87
  private _namespace;
@@ -87,14 +90,14 @@ declare class KeyvRedis<T> extends Hookified implements KeyvStoreAdapter {
87
90
  private _useUnlink;
88
91
  private _noNamespaceAffectsAll;
89
92
  private _throwOnConnectError;
90
- private _throwErrors;
93
+ private _throwOnErrors;
91
94
  private _connectionTimeout;
92
95
  /**
93
96
  * KeyvRedis constructor.
94
97
  * @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.
95
98
  * @param {KeyvRedisOptions} [options] Options for the adapter such as namespace, keyPrefixSeparator, and clearBatchSize.
96
99
  */
97
- constructor(connect?: string | RedisClientOptions | RedisClusterOptions | RedisClientConnectionType, options?: KeyvRedisOptions);
100
+ constructor(connect?: string | RedisClientOptions | RedisClusterOptions | RedisSentinelOptions | RedisClientConnectionType, options?: KeyvRedisOptions);
98
101
  /**
99
102
  * Get the Redis client.
100
103
  */
@@ -172,22 +175,22 @@ declare class KeyvRedis<T> extends Hookified implements KeyvStoreAdapter {
172
175
  */
173
176
  set throwOnConnectError(value: boolean);
174
177
  /**
175
- * Get if throwErrors is set to true.
178
+ * Get if throwOnErrors is set to true.
176
179
  * This is used to throw an error if at any point there is a failure. Use this if you want to
177
180
  * ensure that all operations are successful and you want to handle errors. By default, this is
178
181
  * set to false so that it does not throw an error on every operation and instead emits an error event
179
182
  * and returns no-op responses.
180
183
  * @default false
181
184
  */
182
- get throwErrors(): boolean;
185
+ get throwOnErrors(): boolean;
183
186
  /**
184
- * Set if throwErrors is set to true.
187
+ * Set if throwOnErrors is set to true.
185
188
  * This is used to throw an error if at any point there is a failure. Use this if you want to
186
189
  * ensure that all operations are successful and you want to handle errors. By default, this is
187
190
  * set to false so that it does not throw an error on every operation and instead emits an error event
188
191
  * and returns no-op responses.
189
192
  */
190
- set throwErrors(value: boolean);
193
+ set throwOnErrors(value: boolean);
191
194
  /**
192
195
  * Get the connection timeout in milliseconds such as 5000 (5 seconds). Default is undefined. If undefined, it will use the default.
193
196
  * @default undefined
@@ -276,6 +279,11 @@ declare class KeyvRedis<T> extends Hookified implements KeyvStoreAdapter {
276
279
  * @returns {boolean} - true if the client is a cluster, false if not
277
280
  */
278
281
  isCluster(): boolean;
282
+ /**
283
+ * Is the client a sentinel.
284
+ * @returns {boolean} - true if the client is a sentinel, false if not
285
+ */
286
+ isSentinel(): boolean;
279
287
  /**
280
288
  * Get the master nodes in the cluster. If not a cluster, it will return the single client.
281
289
  *
@@ -318,6 +326,7 @@ declare class KeyvRedis<T> extends Hookified implements KeyvStoreAdapter {
318
326
  */
319
327
  private getSlotMap;
320
328
  private isClientCluster;
329
+ private isClientSentinel;
321
330
  private setOptions;
322
331
  private initClient;
323
332
  private createTimeoutPromise;
@@ -329,5 +338,6 @@ declare class KeyvRedis<T> extends Hookified implements KeyvStoreAdapter {
329
338
  * @returns {Keyv} - Keyv instance with the Redis adapter
330
339
  */
331
340
  declare function createKeyv(connect?: string | RedisClientOptions | RedisClientType, options?: KeyvRedisOptions): Keyv;
341
+ declare function createKeyvNonBlocking(connect?: string | RedisClientOptions | RedisClientType, options?: KeyvRedisOptions): Keyv;
332
342
 
333
- export { type KeyvRedisEntry, type KeyvRedisOptions, type KeyvRedisPropertyOptions, type RedisClientConnectionType, RedisErrorMessages, createKeyv, KeyvRedis as default, defaultReconnectStrategy };
343
+ export { type KeyvRedisEntry, type KeyvRedisOptions, type KeyvRedisPropertyOptions, type RedisClientConnectionType, type RedisConnectionClientType, type RedisConnectionClusterType, type RedisConnectionSentinelType, RedisErrorMessages, createKeyv, createKeyvNonBlocking, KeyvRedis as default, defaultReconnectStrategy };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { RedisClientOptions, RedisClusterOptions, RedisClientType, RedisClusterType, RedisModules, RedisFunctions, RedisScripts } from '@redis/client';
2
- export { RedisClientOptions, RedisClientType, RedisClusterOptions, RedisClusterType, createClient, createCluster } from '@redis/client';
1
+ import { RedisClientOptions, RedisClusterOptions, RedisSentinelOptions, RedisClientType, RedisModules, RedisFunctions, RedisScripts, RespVersions, TypeMapping, RedisClusterType, RedisSentinelType } from '@redis/client';
2
+ export { RedisClientOptions, RedisClientType, RedisClusterOptions, RedisClusterType, RedisSentinelType, createClient, createCluster, createSentinel } from '@redis/client';
3
3
  import { Hookified } from 'hookified';
4
4
  import { KeyvStoreAdapter, KeyvEntry, Keyv } from 'keyv';
5
5
  export { Keyv } from 'keyv';
@@ -39,7 +39,7 @@ type KeyvRedisOptions = {
39
39
  * and returns no-op responses.
40
40
  * @default false
41
41
  */
42
- throwErrors?: boolean;
42
+ throwOnErrors?: boolean;
43
43
  /**
44
44
  * Timeout in milliseconds for the connection. Default is undefined, which uses the default timeout of the Redis client.
45
45
  * If set, it will throw an error if the connection does not succeed within the specified time.
@@ -78,7 +78,10 @@ declare enum RedisErrorMessages {
78
78
  RedisClientNotConnectedThrown = "Redis client is not connected or has failed to connect. This is thrown because throwOnConnectError is set to true."
79
79
  }
80
80
  declare const defaultReconnectStrategy: (attempts: number) => number | Error;
81
- type RedisClientConnectionType = RedisClientType | RedisClusterType<RedisModules, RedisFunctions, RedisScripts>;
81
+ type RedisConnectionClientType = RedisClientType | RedisClientType<RedisModules, RedisFunctions, RedisScripts, RespVersions> | RedisClientType<RedisModules, RedisFunctions, RedisScripts, RespVersions, TypeMapping>;
82
+ type RedisConnectionClusterType = RedisClusterType | RedisClusterType<RedisModules, RedisFunctions, RedisScripts, RespVersions> | RedisClusterType<RedisModules, RedisFunctions, RedisScripts, RespVersions, TypeMapping>;
83
+ type RedisConnectionSentinelType = RedisSentinelType | RedisSentinelType<RedisModules, RedisFunctions, RedisScripts, RespVersions> | RedisSentinelType<RedisModules, RedisFunctions, RedisScripts, RespVersions, TypeMapping>;
84
+ type RedisClientConnectionType = RedisConnectionClientType | RedisConnectionClusterType | RedisConnectionSentinelType;
82
85
  declare class KeyvRedis<T> extends Hookified implements KeyvStoreAdapter {
83
86
  private _client;
84
87
  private _namespace;
@@ -87,14 +90,14 @@ declare class KeyvRedis<T> extends Hookified implements KeyvStoreAdapter {
87
90
  private _useUnlink;
88
91
  private _noNamespaceAffectsAll;
89
92
  private _throwOnConnectError;
90
- private _throwErrors;
93
+ private _throwOnErrors;
91
94
  private _connectionTimeout;
92
95
  /**
93
96
  * KeyvRedis constructor.
94
97
  * @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.
95
98
  * @param {KeyvRedisOptions} [options] Options for the adapter such as namespace, keyPrefixSeparator, and clearBatchSize.
96
99
  */
97
- constructor(connect?: string | RedisClientOptions | RedisClusterOptions | RedisClientConnectionType, options?: KeyvRedisOptions);
100
+ constructor(connect?: string | RedisClientOptions | RedisClusterOptions | RedisSentinelOptions | RedisClientConnectionType, options?: KeyvRedisOptions);
98
101
  /**
99
102
  * Get the Redis client.
100
103
  */
@@ -172,22 +175,22 @@ declare class KeyvRedis<T> extends Hookified implements KeyvStoreAdapter {
172
175
  */
173
176
  set throwOnConnectError(value: boolean);
174
177
  /**
175
- * Get if throwErrors is set to true.
178
+ * Get if throwOnErrors is set to true.
176
179
  * This is used to throw an error if at any point there is a failure. Use this if you want to
177
180
  * ensure that all operations are successful and you want to handle errors. By default, this is
178
181
  * set to false so that it does not throw an error on every operation and instead emits an error event
179
182
  * and returns no-op responses.
180
183
  * @default false
181
184
  */
182
- get throwErrors(): boolean;
185
+ get throwOnErrors(): boolean;
183
186
  /**
184
- * Set if throwErrors is set to true.
187
+ * Set if throwOnErrors is set to true.
185
188
  * This is used to throw an error if at any point there is a failure. Use this if you want to
186
189
  * ensure that all operations are successful and you want to handle errors. By default, this is
187
190
  * set to false so that it does not throw an error on every operation and instead emits an error event
188
191
  * and returns no-op responses.
189
192
  */
190
- set throwErrors(value: boolean);
193
+ set throwOnErrors(value: boolean);
191
194
  /**
192
195
  * Get the connection timeout in milliseconds such as 5000 (5 seconds). Default is undefined. If undefined, it will use the default.
193
196
  * @default undefined
@@ -276,6 +279,11 @@ declare class KeyvRedis<T> extends Hookified implements KeyvStoreAdapter {
276
279
  * @returns {boolean} - true if the client is a cluster, false if not
277
280
  */
278
281
  isCluster(): boolean;
282
+ /**
283
+ * Is the client a sentinel.
284
+ * @returns {boolean} - true if the client is a sentinel, false if not
285
+ */
286
+ isSentinel(): boolean;
279
287
  /**
280
288
  * Get the master nodes in the cluster. If not a cluster, it will return the single client.
281
289
  *
@@ -318,6 +326,7 @@ declare class KeyvRedis<T> extends Hookified implements KeyvStoreAdapter {
318
326
  */
319
327
  private getSlotMap;
320
328
  private isClientCluster;
329
+ private isClientSentinel;
321
330
  private setOptions;
322
331
  private initClient;
323
332
  private createTimeoutPromise;
@@ -329,5 +338,6 @@ declare class KeyvRedis<T> extends Hookified implements KeyvStoreAdapter {
329
338
  * @returns {Keyv} - Keyv instance with the Redis adapter
330
339
  */
331
340
  declare function createKeyv(connect?: string | RedisClientOptions | RedisClientType, options?: KeyvRedisOptions): Keyv;
341
+ declare function createKeyvNonBlocking(connect?: string | RedisClientOptions | RedisClientType, options?: KeyvRedisOptions): Keyv;
332
342
 
333
- export { type KeyvRedisEntry, type KeyvRedisOptions, type KeyvRedisPropertyOptions, type RedisClientConnectionType, RedisErrorMessages, createKeyv, KeyvRedis as default, defaultReconnectStrategy };
343
+ export { type KeyvRedisEntry, type KeyvRedisOptions, type KeyvRedisPropertyOptions, type RedisClientConnectionType, type RedisConnectionClientType, type RedisConnectionClusterType, type RedisConnectionSentinelType, RedisErrorMessages, createKeyv, createKeyvNonBlocking, KeyvRedis as default, defaultReconnectStrategy };
package/dist/index.js CHANGED
@@ -1,14 +1,16 @@
1
1
  // src/index.ts
2
2
  import {
3
3
  createClient,
4
- createCluster
4
+ createCluster,
5
+ createSentinel
5
6
  } from "@redis/client";
6
7
  import { Hookified } from "hookified";
7
8
  import { Keyv } from "keyv";
8
9
  import calculateSlot from "cluster-key-slot";
9
10
  import {
10
11
  createClient as createClient2,
11
- createCluster as createCluster2
12
+ createCluster as createCluster2,
13
+ createSentinel as createSentinel2
12
14
  } from "@redis/client";
13
15
  import {
14
16
  Keyv as Keyv2
@@ -30,7 +32,7 @@ var KeyvRedis = class extends Hookified {
30
32
  _useUnlink = true;
31
33
  _noNamespaceAffectsAll = false;
32
34
  _throwOnConnectError = true;
33
- _throwErrors = false;
35
+ _throwOnErrors = false;
34
36
  _connectionTimeout;
35
37
  /**
36
38
  * KeyvRedis constructor.
@@ -47,9 +49,21 @@ var KeyvRedis = class extends Hookified {
47
49
  if (typeof connect === "string") {
48
50
  this._client = createClient({ url: connect, socket });
49
51
  } else if (connect.connect !== void 0) {
50
- this._client = this.isClientCluster(connect) ? connect : connect;
52
+ if (this.isClientSentinel(connect)) {
53
+ this._client = connect;
54
+ } else if (this.isClientCluster(connect)) {
55
+ this._client = connect;
56
+ } else {
57
+ this._client = connect;
58
+ }
51
59
  } else if (connect instanceof Object) {
52
- this._client = connect.rootNodes === void 0 ? createClient(connect) : createCluster(connect);
60
+ if (connect.sentinelRootNodes !== void 0) {
61
+ this._client = createSentinel(connect);
62
+ } else if (connect.rootNodes === void 0) {
63
+ this._client = createClient(connect);
64
+ } else {
65
+ this._client = createCluster(connect);
66
+ }
53
67
  }
54
68
  }
55
69
  this.setOptions(options);
@@ -86,7 +100,7 @@ var KeyvRedis = class extends Hookified {
86
100
  noNamespaceAffectsAll: this._noNamespaceAffectsAll,
87
101
  useUnlink: this._useUnlink,
88
102
  throwOnConnectError: this._throwOnConnectError,
89
- throwErrors: this._throwErrors,
103
+ throwOnErrors: this._throwOnErrors,
90
104
  connectionTimeout: this._connectionTimeout,
91
105
  dialect: "redis",
92
106
  url
@@ -188,25 +202,25 @@ var KeyvRedis = class extends Hookified {
188
202
  this._throwOnConnectError = value;
189
203
  }
190
204
  /**
191
- * Get if throwErrors is set to true.
205
+ * Get if throwOnErrors is set to true.
192
206
  * This is used to throw an error if at any point there is a failure. Use this if you want to
193
207
  * ensure that all operations are successful and you want to handle errors. By default, this is
194
208
  * set to false so that it does not throw an error on every operation and instead emits an error event
195
209
  * and returns no-op responses.
196
210
  * @default false
197
211
  */
198
- get throwErrors() {
199
- return this._throwErrors;
212
+ get throwOnErrors() {
213
+ return this._throwOnErrors;
200
214
  }
201
215
  /**
202
- * Set if throwErrors is set to true.
216
+ * Set if throwOnErrors is set to true.
203
217
  * This is used to throw an error if at any point there is a failure. Use this if you want to
204
218
  * ensure that all operations are successful and you want to handle errors. By default, this is
205
219
  * set to false so that it does not throw an error on every operation and instead emits an error event
206
220
  * and returns no-op responses.
207
221
  */
208
- set throwErrors(value) {
209
- this._throwErrors = value;
222
+ set throwOnErrors(value) {
223
+ this._throwOnErrors = value;
210
224
  }
211
225
  /**
212
226
  * Get the connection timeout in milliseconds such as 5000 (5 seconds). Default is undefined. If undefined, it will use the default.
@@ -240,10 +254,10 @@ var KeyvRedis = class extends Hookified {
240
254
  }
241
255
  } catch (error) {
242
256
  this.emit("error", error);
257
+ await this.disconnect(true);
243
258
  if (this._throwOnConnectError) {
244
259
  throw new Error("Redis client is not connected or has failed to connect. This is thrown because throwOnConnectError is set to true." /* RedisClientNotConnectedThrown */);
245
260
  }
246
- await this.disconnect(true);
247
261
  }
248
262
  this.initClient();
249
263
  return this._client;
@@ -265,7 +279,7 @@ var KeyvRedis = class extends Hookified {
265
279
  }
266
280
  } catch (error) {
267
281
  this.emit("error", error);
268
- if (this._throwErrors) {
282
+ if (this._throwOnErrors) {
269
283
  throw error;
270
284
  }
271
285
  }
@@ -289,7 +303,7 @@ var KeyvRedis = class extends Hookified {
289
303
  await multi.exec();
290
304
  } catch (error) {
291
305
  this.emit("error", error);
292
- if (this._throwErrors) {
306
+ if (this._throwOnErrors) {
293
307
  throw error;
294
308
  }
295
309
  }
@@ -307,7 +321,7 @@ var KeyvRedis = class extends Hookified {
307
321
  return exists === 1;
308
322
  } catch (error) {
309
323
  this.emit("error", error);
310
- if (this._throwErrors) {
324
+ if (this._throwOnErrors) {
311
325
  throw error;
312
326
  }
313
327
  return false;
@@ -327,10 +341,10 @@ var KeyvRedis = class extends Hookified {
327
341
  multi.exists(prefixedKey);
328
342
  }
329
343
  const results = await multi.exec();
330
- return results.map((result) => result === 1);
344
+ return results.map((result) => typeof result === "number" && result === 1);
331
345
  } catch (error) {
332
346
  this.emit("error", error);
333
- if (this._throwErrors) {
347
+ if (this._throwOnErrors) {
334
348
  throw error;
335
349
  }
336
350
  return Array.from({ length: keys.length }).fill(false);
@@ -352,7 +366,7 @@ var KeyvRedis = class extends Hookified {
352
366
  return value;
353
367
  } catch (error) {
354
368
  this.emit("error", error);
355
- if (this._throwErrors) {
369
+ if (this._throwOnErrors) {
356
370
  throw error;
357
371
  }
358
372
  return void 0;
@@ -373,7 +387,7 @@ var KeyvRedis = class extends Hookified {
373
387
  return values;
374
388
  } catch (error) {
375
389
  this.emit("error", error);
376
- if (this._throwErrors) {
390
+ if (this._throwOnErrors) {
377
391
  throw error;
378
392
  }
379
393
  return Array.from({ length: keys.length }).fill(void 0);
@@ -393,7 +407,7 @@ var KeyvRedis = class extends Hookified {
393
407
  return deleted > 0;
394
408
  } catch (error) {
395
409
  this.emit("error", error);
396
- if (this._throwErrors) {
410
+ if (this._throwOnErrors) {
397
411
  throw error;
398
412
  }
399
413
  return false;
@@ -425,7 +439,7 @@ var KeyvRedis = class extends Hookified {
425
439
  }
426
440
  } catch (error) {
427
441
  this.emit("error", error);
428
- if (this._throwErrors) {
442
+ if (this._throwOnErrors) {
429
443
  throw error;
430
444
  }
431
445
  }
@@ -439,7 +453,7 @@ var KeyvRedis = class extends Hookified {
439
453
  */
440
454
  async disconnect(force) {
441
455
  if (this._client.isOpen) {
442
- await (force ? this._client.disconnect() : this._client.quit());
456
+ await (force ? this._client.destroy() : this._client.close());
443
457
  }
444
458
  }
445
459
  /**
@@ -473,6 +487,13 @@ var KeyvRedis = class extends Hookified {
473
487
  isCluster() {
474
488
  return this.isClientCluster(this._client);
475
489
  }
490
+ /**
491
+ * Is the client a sentinel.
492
+ * @returns {boolean} - true if the client is a sentinel, false if not
493
+ */
494
+ isSentinel() {
495
+ return this.isClientSentinel(this._client);
496
+ }
476
497
  /**
477
498
  * Get the master nodes in the cluster. If not a cluster, it will return the single client.
478
499
  *
@@ -481,7 +502,8 @@ var KeyvRedis = class extends Hookified {
481
502
  async getMasterNodes() {
482
503
  if (this.isCluster()) {
483
504
  const cluster = await this.getClient();
484
- return Promise.all(cluster.masters.map(async (main) => cluster.nodeClient(main)));
505
+ const nodes = cluster.masters.map(async (main) => cluster.nodeClient(main));
506
+ return Promise.all(nodes);
485
507
  }
486
508
  return [await this.getClient()];
487
509
  }
@@ -496,7 +518,7 @@ var KeyvRedis = class extends Hookified {
496
518
  const match = namespace ? `${namespace}${this._keyPrefixSeparator}*` : "*";
497
519
  let cursor = "0";
498
520
  do {
499
- const result = await client.scan(Number.parseInt(cursor, 10), { MATCH: match, TYPE: "string" });
521
+ const result = await client.scan(cursor, { MATCH: match, TYPE: "string" });
500
522
  cursor = result.cursor.toString();
501
523
  let { keys } = result;
502
524
  if (!namespace && !this._noNamespaceAffectsAll) {
@@ -533,7 +555,7 @@ var KeyvRedis = class extends Hookified {
533
555
  const match = this._namespace ? `${this._namespace}${this._keyPrefixSeparator}*` : "*";
534
556
  const deletePromises = [];
535
557
  do {
536
- const result = await client.scan(Number.parseInt(cursor, 10), { MATCH: match, COUNT: batchSize, TYPE: "string" });
558
+ const result = await client.scan(cursor, { MATCH: match, COUNT: batchSize, TYPE: "string" });
537
559
  cursor = result.cursor.toString();
538
560
  let { keys } = result;
539
561
  if (keys.length === 0) {
@@ -612,10 +634,10 @@ var KeyvRedis = class extends Hookified {
612
634
  return slotMap;
613
635
  }
614
636
  isClientCluster(client) {
615
- if (client.options === void 0 && client.scan === void 0) {
616
- return true;
617
- }
618
- return false;
637
+ return client.slots !== void 0;
638
+ }
639
+ isClientSentinel(client) {
640
+ return client.getSentinelNode !== void 0;
619
641
  }
620
642
  setOptions(options) {
621
643
  if (!options) {
@@ -639,8 +661,8 @@ var KeyvRedis = class extends Hookified {
639
661
  if (options.throwOnConnectError !== void 0) {
640
662
  this._throwOnConnectError = options.throwOnConnectError;
641
663
  }
642
- if (options.throwErrors !== void 0) {
643
- this._throwErrors = options.throwErrors;
664
+ if (options.throwOnErrors !== void 0) {
665
+ this._throwOnErrors = options.throwOnErrors;
644
666
  }
645
667
  if (options.connectionTimeout !== void 0) {
646
668
  this._connectionTimeout = options.connectionTimeout;
@@ -672,7 +694,40 @@ var KeyvRedis = class extends Hookified {
672
694
  function createKeyv(connect, options) {
673
695
  connect ??= "redis://localhost:6379";
674
696
  const adapter = new KeyvRedis(connect, options);
675
- const keyv = new Keyv(adapter, { namespace: options?.namespace, useKeyPrefix: false });
697
+ if (options?.namespace) {
698
+ adapter.namespace = options.namespace;
699
+ const keyv2 = new Keyv(adapter, { namespace: options?.namespace, useKeyPrefix: false });
700
+ if (options?.throwOnConnectError) {
701
+ keyv2.throwOnErrors = true;
702
+ }
703
+ if (options?.throwOnErrors) {
704
+ keyv2.throwOnErrors = true;
705
+ }
706
+ return keyv2;
707
+ }
708
+ const keyv = new Keyv(adapter, { useKeyPrefix: false });
709
+ if (options?.throwOnConnectError) {
710
+ keyv.throwOnErrors = true;
711
+ }
712
+ if (options?.throwOnErrors) {
713
+ keyv.throwOnErrors = true;
714
+ }
715
+ keyv.namespace = void 0;
716
+ return keyv;
717
+ }
718
+ function createKeyvNonBlocking(connect, options) {
719
+ const keyv = createKeyv(connect, options);
720
+ const keyvStore = keyv.store;
721
+ keyvStore.throwOnConnectError = false;
722
+ keyvStore.throwOnErrors = false;
723
+ const redisClient = keyvStore.client;
724
+ if (redisClient.options) {
725
+ redisClient.options.disableOfflineQueue = true;
726
+ if (redisClient.options.socket) {
727
+ redisClient.options.socket.reconnectStrategy = false;
728
+ }
729
+ }
730
+ keyv.throwOnErrors = false;
676
731
  return keyv;
677
732
  }
678
733
  export {
@@ -681,6 +736,8 @@ export {
681
736
  createClient2 as createClient,
682
737
  createCluster2 as createCluster,
683
738
  createKeyv,
739
+ createKeyvNonBlocking,
740
+ createSentinel2 as createSentinel,
684
741
  KeyvRedis as default,
685
742
  defaultReconnectStrategy
686
743
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@keyv/redis",
3
- "version": "4.6.0",
3
+ "version": "5.1.0",
4
4
  "description": "Redis storage adapter for Keyv",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -34,12 +34,12 @@
34
34
  },
35
35
  "homepage": "https://github.com/jaredwray/keyv",
36
36
  "dependencies": {
37
- "@redis/client": "^1.6.0",
37
+ "@redis/client": "^5.7.0",
38
38
  "cluster-key-slot": "^1.1.2",
39
- "hookified": "^1.10.0"
39
+ "hookified": "^1.11.0"
40
40
  },
41
41
  "peerDependencies": {
42
- "keyv": "^5.3.4"
42
+ "keyv": "^5.5.0"
43
43
  },
44
44
  "devDependencies": {
45
45
  "@faker-js/faker": "^9.9.0",
@@ -48,8 +48,8 @@
48
48
  "timekeeper": "^2.3.1",
49
49
  "tsd": "^0.32.0",
50
50
  "vitest": "^3.2.4",
51
- "xo": "^1.1.1",
52
- "@keyv/test-suite": "^2.0.8"
51
+ "xo": "^1.2.1",
52
+ "@keyv/test-suite": "^2.1.0"
53
53
  },
54
54
  "tsd": {
55
55
  "directory": "test"