@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 +272 -10
- package/dist/index.cjs +88 -31
- package/dist/index.d.cts +21 -11
- package/dist/index.d.ts +21 -11
- package/dist/index.js +90 -33
- package/package.json +6 -6
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
|
|
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
|
-
|
|
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
|
|
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 `
|
|
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
|
|
385
|
+
# Clustering
|
|
316
386
|
|
|
317
|
-
If you are using a Redis Cluster
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
231
|
-
return this.
|
|
244
|
+
get throwOnErrors() {
|
|
245
|
+
return this._throwOnErrors;
|
|
232
246
|
}
|
|
233
247
|
/**
|
|
234
|
-
* Set if
|
|
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
|
|
241
|
-
this.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
return
|
|
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.
|
|
675
|
-
this.
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
185
|
+
get throwOnErrors(): boolean;
|
|
183
186
|
/**
|
|
184
|
-
* Set if
|
|
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
|
|
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,
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
185
|
+
get throwOnErrors(): boolean;
|
|
183
186
|
/**
|
|
184
|
-
* Set if
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
199
|
-
return this.
|
|
212
|
+
get throwOnErrors() {
|
|
213
|
+
return this._throwOnErrors;
|
|
200
214
|
}
|
|
201
215
|
/**
|
|
202
|
-
* Set if
|
|
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
|
|
209
|
-
this.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
return
|
|
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.
|
|
643
|
-
this.
|
|
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
|
-
|
|
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": "
|
|
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": "^
|
|
37
|
+
"@redis/client": "^5.7.0",
|
|
38
38
|
"cluster-key-slot": "^1.1.2",
|
|
39
|
-
"hookified": "^1.
|
|
39
|
+
"hookified": "^1.11.0"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
|
-
"keyv": "^5.
|
|
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.
|
|
52
|
-
"@keyv/test-suite": "^2.0
|
|
51
|
+
"xo": "^1.2.1",
|
|
52
|
+
"@keyv/test-suite": "^2.1.0"
|
|
53
53
|
},
|
|
54
54
|
"tsd": {
|
|
55
55
|
"directory": "test"
|