@jsnw/nestjs-ioredis 1.3.0 → 2.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 +3 -172
- package/dist/index.cjs +263 -0
- package/dist/index.d.cts +75 -0
- package/package.json +16 -25
- package/dist/index.js +0 -9
- package/dist/redis-core.module.js +0 -115
- package/dist/redis-instances-manager.js +0 -101
- package/dist/redis-lock.js +0 -93
- package/dist/redis.consts.js +0 -8
- package/dist/redis.decorators.js +0 -13
- package/dist/redis.helpers.js +0 -66
- package/dist/redis.module.js +0 -69
- package/dist/redis.types.js +0 -2
- package/dist/types/index.d.ts +0 -4
- package/dist/types/redis-core.module.d.ts +0 -15
- package/dist/types/redis-instances-manager.d.ts +0 -24
- package/dist/types/redis-lock.d.ts +0 -37
- package/dist/types/redis.consts.d.ts +0 -5
- package/dist/types/redis.decorators.d.ts +0 -6
- package/dist/types/redis.helpers.d.ts +0 -17
- package/dist/types/redis.module.d.ts +0 -9
- package/dist/types/redis.types.d.ts +0 -13
package/README.md
CHANGED
|
@@ -1,26 +1,15 @@
|
|
|
1
1
|
# @jsnw/nestjs-ioredis
|
|
2
2
|
|
|
3
|
-
NestJS module for integrating Redis using [ioredis](https://www.npmjs.com/package/ioredis)
|
|
4
|
-
|
|
5
|
-
## Features
|
|
6
|
-
|
|
7
|
-
- 🚀 Easy integration with NestJS applications
|
|
8
|
-
- 🔌 Support for multiple named Redis connections
|
|
9
|
-
- 💉 Dependency injection with `@InjectRedis()` decorator
|
|
10
|
-
- 🔄 Automatic connection lifecycle management
|
|
11
|
-
- 🏷️ Key prefix support with automatic formatting
|
|
12
|
-
- 🛡️ TypeScript support with full type definitions
|
|
3
|
+
NestJS module for integrating Redis using [ioredis](https://www.npmjs.com/package/ioredis)
|
|
13
4
|
|
|
14
5
|
## Installation
|
|
15
6
|
|
|
16
7
|
```bash
|
|
17
|
-
npm i -s @jsnw/nestjs-ioredis ioredis
|
|
8
|
+
npm i -s @jsnw/nestjs-ioredis ioredis @nestjs/common@11
|
|
18
9
|
```
|
|
19
10
|
|
|
20
11
|
## Quick Start
|
|
21
12
|
|
|
22
|
-
### Basic Usage
|
|
23
|
-
|
|
24
13
|
Import `RedisModule` in your app module and configure it with `forRoot()`:
|
|
25
14
|
|
|
26
15
|
```typescript
|
|
@@ -33,171 +22,13 @@ import { RedisModule } from '@jsnw/nestjs-ioredis';
|
|
|
33
22
|
hostname: 'localhost',
|
|
34
23
|
port: 6379,
|
|
35
24
|
username: 'default',
|
|
36
|
-
password: 'your-password'
|
|
37
|
-
isDefault: true,
|
|
25
|
+
password: 'your-password'
|
|
38
26
|
}),
|
|
39
27
|
],
|
|
40
28
|
})
|
|
41
29
|
export class AppModule {}
|
|
42
30
|
```
|
|
43
31
|
|
|
44
|
-
### Inject Redis in Services
|
|
45
|
-
|
|
46
|
-
Use the `@InjectRedis()` decorator to inject Redis instances into your services:
|
|
47
|
-
|
|
48
|
-
```typescript
|
|
49
|
-
import { Injectable } from '@nestjs/common';
|
|
50
|
-
import { InjectRedis } from '@jsnw/nestjs-ioredis';
|
|
51
|
-
import type { Redis } from 'ioredis';
|
|
52
|
-
|
|
53
|
-
@Injectable()
|
|
54
|
-
export class UserService {
|
|
55
|
-
constructor(
|
|
56
|
-
@InjectRedis() private readonly redis: Redis,
|
|
57
|
-
) {}
|
|
58
|
-
|
|
59
|
-
async cacheUser(userId: string, userData: any): Promise<void> {
|
|
60
|
-
await this.redis.set(`user:${userId}`, JSON.stringify(userData));
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
async getUser(userId: string): Promise<any> {
|
|
64
|
-
const data = await this.redis.get(`user:${userId}`);
|
|
65
|
-
return data ? JSON.parse(data) : null;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
## Configuration
|
|
71
|
-
|
|
72
|
-
### RedisForRootParams
|
|
73
|
-
|
|
74
|
-
| Parameter | Type | Required | Default | Description |
|
|
75
|
-
|-----------|------|----------|---------|-------------|
|
|
76
|
-
| `hostname` | `string` | Yes | - | Redis server hostname |
|
|
77
|
-
| `port` | `number` | Yes | - | Redis server port |
|
|
78
|
-
| `username` | `string` | Yes | - | Redis username |
|
|
79
|
-
| `password` | `string` | Yes | - | Redis password |
|
|
80
|
-
| `connectionName` | `string` | No | Auto-generated | Unique name for the connection |
|
|
81
|
-
| `db` | `number` | No | `0` | Redis database index |
|
|
82
|
-
| `keyPrefix` | `string` | No | `''` | Prefix for all keys (automatically adds ':' separator) |
|
|
83
|
-
| `connectionTimeout` | `number` | No | `10000` | Connection timeout in milliseconds |
|
|
84
|
-
| `lazy` | `boolean` | No | `false` | Enable lazy connection (connect on first command) |
|
|
85
|
-
| `isDefault` | `boolean` | No | `false` | Mark this connection as the default |
|
|
86
|
-
|
|
87
|
-
## Advanced Usage
|
|
88
|
-
|
|
89
|
-
### Multiple Named Connections
|
|
90
|
-
|
|
91
|
-
You can configure multiple Redis connections with different names:
|
|
92
|
-
|
|
93
|
-
```typescript
|
|
94
|
-
import { Module } from '@nestjs/common';
|
|
95
|
-
import { RedisModule } from '@jsnw/nestjs-ioredis';
|
|
96
|
-
|
|
97
|
-
@Module({
|
|
98
|
-
imports: [
|
|
99
|
-
RedisModule.forRoot({
|
|
100
|
-
connectionName: 'cache',
|
|
101
|
-
hostname: 'cache.redis.local',
|
|
102
|
-
port: 6379,
|
|
103
|
-
username: 'default',
|
|
104
|
-
password: 'cache-password',
|
|
105
|
-
isDefault: true, // This is the default connection
|
|
106
|
-
}),
|
|
107
|
-
RedisModule.forRoot({
|
|
108
|
-
connectionName: 'sessions',
|
|
109
|
-
hostname: 'sessions.redis.local',
|
|
110
|
-
port: 6379,
|
|
111
|
-
username: 'default',
|
|
112
|
-
password: 'sessions-password',
|
|
113
|
-
db: 1,
|
|
114
|
-
}),
|
|
115
|
-
],
|
|
116
|
-
})
|
|
117
|
-
export class AppModule {}
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
### Inject Named Connections
|
|
121
|
-
|
|
122
|
-
```typescript
|
|
123
|
-
import { Injectable } from '@nestjs/common';
|
|
124
|
-
import { InjectRedis } from '@jsnw/nestjs-ioredis';
|
|
125
|
-
import type { Redis } from 'ioredis';
|
|
126
|
-
|
|
127
|
-
@Injectable()
|
|
128
|
-
export class AppService {
|
|
129
|
-
constructor(
|
|
130
|
-
@InjectRedis('cache') private readonly cacheRedis: Redis,
|
|
131
|
-
@InjectRedis('sessions') private readonly sessionRedis: Redis,
|
|
132
|
-
@InjectRedis() private readonly defaultRedis: Redis, // Uses default connection
|
|
133
|
-
) {}
|
|
134
|
-
|
|
135
|
-
async cacheData(key: string, value: string): Promise<void> {
|
|
136
|
-
await this.cacheRedis.set(key, value);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
async saveSession(sessionId: string, data: any): Promise<void> {
|
|
140
|
-
await this.sessionRedis.set(sessionId, JSON.stringify(data), 'EX', 3600);
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
### Key Prefix
|
|
146
|
-
|
|
147
|
-
The `keyPrefix` option automatically adds a prefix to all keys with proper colon formatting:
|
|
148
|
-
|
|
149
|
-
```typescript
|
|
150
|
-
RedisModule.forRoot({
|
|
151
|
-
hostname: 'localhost',
|
|
152
|
-
port: 6379,
|
|
153
|
-
username: 'default',
|
|
154
|
-
password: 'password',
|
|
155
|
-
keyPrefix: 'myapp', // Will become 'myapp:'
|
|
156
|
-
isDefault: true,
|
|
157
|
-
})
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
Now when you use Redis commands:
|
|
161
|
-
|
|
162
|
-
```typescript
|
|
163
|
-
await redis.set('user:123', 'data'); // Actually stored as 'myapp:user:123'
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
### Lazy Connection
|
|
167
|
-
|
|
168
|
-
Enable lazy connection to defer the actual Redis connection until the first command:
|
|
169
|
-
|
|
170
|
-
```typescript
|
|
171
|
-
RedisModule.forRoot({
|
|
172
|
-
hostname: 'localhost',
|
|
173
|
-
port: 6379,
|
|
174
|
-
username: 'default',
|
|
175
|
-
password: 'password',
|
|
176
|
-
lazy: true,
|
|
177
|
-
isDefault: true,
|
|
178
|
-
})
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
## Automatic Cleanup
|
|
182
|
-
|
|
183
|
-
The module automatically closes all Redis connections when the NestJS application shuts down. No manual cleanup is required.
|
|
184
|
-
|
|
185
|
-
## API Reference
|
|
186
|
-
|
|
187
|
-
### `RedisModule`
|
|
188
|
-
|
|
189
|
-
#### `forRoot(params: RedisForRootParams): DynamicModule`
|
|
190
|
-
|
|
191
|
-
Configures the Redis module with the provided connection parameters.
|
|
192
|
-
|
|
193
|
-
### `@InjectRedis(connectionName?: string)`
|
|
194
|
-
|
|
195
|
-
Decorator for injecting Redis instances into your services. If no connection name is provided, the default connection will be injected.
|
|
196
|
-
|
|
197
|
-
### `RedisForRootParams`
|
|
198
|
-
|
|
199
|
-
TypeScript interface for configuring Redis connections. See [Configuration](#configuration) section for details.
|
|
200
|
-
|
|
201
32
|
## License
|
|
202
33
|
|
|
203
34
|
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
//#region \0rolldown/runtime.js
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
11
|
+
key = keys[i];
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
13
|
+
get: ((k) => from[k]).bind(null, key),
|
|
14
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
20
|
+
value: mod,
|
|
21
|
+
enumerable: true
|
|
22
|
+
}) : target, mod));
|
|
23
|
+
//#endregion
|
|
24
|
+
let _nestjs_common = require("@nestjs/common");
|
|
25
|
+
let ioredis = require("ioredis");
|
|
26
|
+
ioredis = __toESM(ioredis);
|
|
27
|
+
let node_crypto = require("node:crypto");
|
|
28
|
+
let node_timers_promises = require("node:timers/promises");
|
|
29
|
+
let dedent = require("dedent");
|
|
30
|
+
dedent = __toESM(dedent);
|
|
31
|
+
//#region src/redis.consts.ts
|
|
32
|
+
const REDIS_OPTIONS_TOKEN = Symbol("REDIS_OPTIONS");
|
|
33
|
+
//#endregion
|
|
34
|
+
//#region src/redis.helpers.ts
|
|
35
|
+
/**
|
|
36
|
+
* @param {string} prefix
|
|
37
|
+
* @return {string}
|
|
38
|
+
*/
|
|
39
|
+
const resolveKeyPrefix = (prefix) => {
|
|
40
|
+
if (!prefix) return "";
|
|
41
|
+
prefix = prefix.trim();
|
|
42
|
+
if (prefix.endsWith(":")) {
|
|
43
|
+
if (prefix.length === 1) return "";
|
|
44
|
+
return prefix;
|
|
45
|
+
}
|
|
46
|
+
return prefix + ":";
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* @param {number} iteration
|
|
50
|
+
* @param {RedisLockWaitAcquireParams["retryInterval"]} [retryInterval]
|
|
51
|
+
* @return {number}
|
|
52
|
+
*/
|
|
53
|
+
const getRetryInterval = (iteration, retryInterval) => {
|
|
54
|
+
if (retryInterval === void 0) return 500;
|
|
55
|
+
if (typeof retryInterval === "number") return Math.max(1, retryInterval);
|
|
56
|
+
if (Array.isArray(retryInterval)) {
|
|
57
|
+
if (retryInterval.length === 0) return 500;
|
|
58
|
+
if (retryInterval.length === 1) return retryInterval[0];
|
|
59
|
+
return iteration < retryInterval.length ? retryInterval[iteration] : retryInterval[retryInterval.length - 1];
|
|
60
|
+
}
|
|
61
|
+
if (typeof retryInterval === "function") return Math.max(1, retryInterval(iteration) ?? 500);
|
|
62
|
+
return 500;
|
|
63
|
+
};
|
|
64
|
+
//#endregion
|
|
65
|
+
//#region src/redis-lock.ts
|
|
66
|
+
var RedisLock = class {
|
|
67
|
+
redis;
|
|
68
|
+
locksHashKey;
|
|
69
|
+
name;
|
|
70
|
+
value;
|
|
71
|
+
acquireLockPromise = null;
|
|
72
|
+
/**
|
|
73
|
+
* @param {Redis} redis
|
|
74
|
+
* @param {string} name
|
|
75
|
+
* @param {string} locksHashKey
|
|
76
|
+
* @protected
|
|
77
|
+
*/
|
|
78
|
+
constructor(redis, name, locksHashKey) {
|
|
79
|
+
this.redis = redis;
|
|
80
|
+
this.name = name;
|
|
81
|
+
this.locksHashKey = locksHashKey;
|
|
82
|
+
this.value = (0, node_crypto.randomUUID)();
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* @param {RedisLockWaitAcquireParams} params
|
|
86
|
+
* @return {Promise<boolean>}
|
|
87
|
+
*/
|
|
88
|
+
async waitAcquire(params) {
|
|
89
|
+
const startedAt = Date.now();
|
|
90
|
+
let iteration = 0;
|
|
91
|
+
while (true) {
|
|
92
|
+
const acquired = await this.acquire(params.lockTTL), RETRY_DELAY_MS = getRetryInterval(iteration, params.retryInterval);
|
|
93
|
+
if (acquired) return true;
|
|
94
|
+
if (params.signal?.aborted) return false;
|
|
95
|
+
let delayMs = RETRY_DELAY_MS;
|
|
96
|
+
if (params.waitTimeout !== void 0) {
|
|
97
|
+
const elapsed = Date.now() - startedAt, timeLeft = params.waitTimeout - elapsed;
|
|
98
|
+
if (timeLeft <= 0) return false;
|
|
99
|
+
delayMs = Math.max(1, Math.min(RETRY_DELAY_MS, timeLeft));
|
|
100
|
+
}
|
|
101
|
+
try {
|
|
102
|
+
await (0, node_timers_promises.setTimeout)(delayMs, void 0, { signal: params.signal });
|
|
103
|
+
} catch (e) {
|
|
104
|
+
if (e instanceof Error && e.name === "AbortError") return false;
|
|
105
|
+
throw e;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* @param {number} ttlMs
|
|
111
|
+
* @return {Promise<boolean>}
|
|
112
|
+
*/
|
|
113
|
+
acquire(ttlMs) {
|
|
114
|
+
if (this.acquireLockPromise) return this.acquireLockPromise;
|
|
115
|
+
this.acquireLockPromise = new Promise((resolve, reject) => {
|
|
116
|
+
this.redis.call("HSETEX", this.locksHashKey, "FNX", "PX", ttlMs, "FIELDS", 1, this.name, this.value).then((res) => {
|
|
117
|
+
if (res === 1) return resolve(true);
|
|
118
|
+
return resolve(false);
|
|
119
|
+
}).catch(reject);
|
|
120
|
+
});
|
|
121
|
+
this.acquireLockPromise.finally(() => {
|
|
122
|
+
this.acquireLockPromise = null;
|
|
123
|
+
});
|
|
124
|
+
return this.acquireLockPromise;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* @return {Promise<void>}
|
|
128
|
+
*/
|
|
129
|
+
async release() {
|
|
130
|
+
await this.redis.eval(dedent.default`
|
|
131
|
+
if redis.call("hget", KEYS[1], ARGV[1]) == ARGV[2]
|
|
132
|
+
then
|
|
133
|
+
return redis.call("hdel", KEYS[1], ARGV[1])
|
|
134
|
+
else
|
|
135
|
+
return 0
|
|
136
|
+
end
|
|
137
|
+
`, 1, this.locksHashKey, this.name, this.value);
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
//#endregion
|
|
141
|
+
//#region \0@oxc-project+runtime@0.137.0/helpers/esm/decorateMetadata.js
|
|
142
|
+
function __decorateMetadata(k, v) {
|
|
143
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
144
|
+
}
|
|
145
|
+
//#endregion
|
|
146
|
+
//#region \0@oxc-project+runtime@0.137.0/helpers/esm/decorateParam.js
|
|
147
|
+
function __decorateParam(paramIndex, decorator) {
|
|
148
|
+
return function(target, key) {
|
|
149
|
+
decorator(target, key, paramIndex);
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
//#endregion
|
|
153
|
+
//#region \0@oxc-project+runtime@0.137.0/helpers/esm/decorate.js
|
|
154
|
+
function __decorate(decorators, target, key, desc) {
|
|
155
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
156
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
157
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
158
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
159
|
+
}
|
|
160
|
+
//#endregion
|
|
161
|
+
//#region src/redis-lock.factory.ts
|
|
162
|
+
let RedisLockFactory = class RedisLockFactory {
|
|
163
|
+
options;
|
|
164
|
+
redis;
|
|
165
|
+
/**
|
|
166
|
+
* @param {RedisRegisterOptions} options
|
|
167
|
+
* @param {Redis} redis
|
|
168
|
+
*/
|
|
169
|
+
constructor(options, redis) {
|
|
170
|
+
this.options = options;
|
|
171
|
+
this.redis = redis;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* @param {string} name
|
|
175
|
+
* @return {RedisLock}
|
|
176
|
+
*/
|
|
177
|
+
create(name) {
|
|
178
|
+
return new RedisLock(this.redis, name, this.options.locksHashKey);
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
RedisLockFactory = __decorate([
|
|
182
|
+
(0, _nestjs_common.Injectable)(),
|
|
183
|
+
__decorateParam(0, (0, _nestjs_common.Inject)(REDIS_OPTIONS_TOKEN)),
|
|
184
|
+
__decorateMetadata("design:paramtypes", [Object, typeof ioredis.default === "undefined" ? Object : ioredis.default])
|
|
185
|
+
], RedisLockFactory);
|
|
186
|
+
//#endregion
|
|
187
|
+
//#region src/redis-core.module.ts
|
|
188
|
+
var _RedisCoreModule;
|
|
189
|
+
let RedisCoreModule = _RedisCoreModule = class RedisCoreModule {
|
|
190
|
+
/**
|
|
191
|
+
* @param {RedisRegisterOptions} options
|
|
192
|
+
* @return {DynamicModule}
|
|
193
|
+
*/
|
|
194
|
+
static register(options) {
|
|
195
|
+
const optionsProvider = this.createOptionsProvider(options), redisProvider = this.createRedisProvider();
|
|
196
|
+
return {
|
|
197
|
+
module: _RedisCoreModule,
|
|
198
|
+
providers: [
|
|
199
|
+
optionsProvider,
|
|
200
|
+
redisProvider,
|
|
201
|
+
RedisLockFactory
|
|
202
|
+
],
|
|
203
|
+
exports: [redisProvider, RedisLockFactory]
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* @param {RedisRegisterOptions} options
|
|
208
|
+
* @return {ValueProvider}
|
|
209
|
+
* @private
|
|
210
|
+
*/
|
|
211
|
+
static createOptionsProvider(options) {
|
|
212
|
+
return {
|
|
213
|
+
provide: REDIS_OPTIONS_TOKEN,
|
|
214
|
+
useValue: {
|
|
215
|
+
...options,
|
|
216
|
+
keyPrefix: resolveKeyPrefix(options.keyPrefix),
|
|
217
|
+
locksHashKey: options.locksHashKey ?? "__locks__"
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* @return {FactoryProvider}
|
|
223
|
+
* @private
|
|
224
|
+
*/
|
|
225
|
+
static createRedisProvider() {
|
|
226
|
+
return {
|
|
227
|
+
provide: ioredis.default,
|
|
228
|
+
useFactory: (options) => new ioredis.default(options),
|
|
229
|
+
inject: [REDIS_OPTIONS_TOKEN]
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
RedisCoreModule = _RedisCoreModule = __decorate([(0, _nestjs_common.Global)(), (0, _nestjs_common.Module)({})], RedisCoreModule);
|
|
234
|
+
//#endregion
|
|
235
|
+
//#region src/redis.module.ts
|
|
236
|
+
var _RedisModule;
|
|
237
|
+
let RedisModule = _RedisModule = class RedisModule {
|
|
238
|
+
/**
|
|
239
|
+
* @param {RedisRegisterOptions} options
|
|
240
|
+
* @return {DynamicModule}
|
|
241
|
+
*/
|
|
242
|
+
static register(options) {
|
|
243
|
+
return {
|
|
244
|
+
module: _RedisModule,
|
|
245
|
+
imports: [RedisCoreModule.register(options)]
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
RedisModule = _RedisModule = __decorate([(0, _nestjs_common.Module)({})], RedisModule);
|
|
250
|
+
//#endregion
|
|
251
|
+
exports.RedisLock = RedisLock;
|
|
252
|
+
Object.defineProperty(exports, "RedisLockFactory", {
|
|
253
|
+
enumerable: true,
|
|
254
|
+
get: function() {
|
|
255
|
+
return RedisLockFactory;
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
Object.defineProperty(exports, "RedisModule", {
|
|
259
|
+
enumerable: true,
|
|
260
|
+
get: function() {
|
|
261
|
+
return RedisModule;
|
|
262
|
+
}
|
|
263
|
+
});
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { DynamicModule } from "@nestjs/common";
|
|
2
|
+
import Redis, { RedisOptions } from "ioredis";
|
|
3
|
+
|
|
4
|
+
//#region src/redis.types.d.ts
|
|
5
|
+
type RedisRegisterOptions = RedisOptions & {
|
|
6
|
+
keyPrefix?: string;
|
|
7
|
+
locksHashKey?: string;
|
|
8
|
+
};
|
|
9
|
+
//#endregion
|
|
10
|
+
//#region src/redis.module.d.ts
|
|
11
|
+
declare class RedisModule {
|
|
12
|
+
/**
|
|
13
|
+
* @param {RedisRegisterOptions} options
|
|
14
|
+
* @return {DynamicModule}
|
|
15
|
+
*/
|
|
16
|
+
static register(options: RedisRegisterOptions): DynamicModule;
|
|
17
|
+
}
|
|
18
|
+
//#endregion
|
|
19
|
+
//#region src/redis-lock.d.ts
|
|
20
|
+
type RedisLockWaitAcquireParams = {
|
|
21
|
+
lockTTL: number;
|
|
22
|
+
retryInterval?: number | number[] | ((iteration: number) => number);
|
|
23
|
+
} & ({
|
|
24
|
+
signal: AbortSignal;
|
|
25
|
+
waitTimeout?: number;
|
|
26
|
+
} | {
|
|
27
|
+
signal?: AbortSignal;
|
|
28
|
+
waitTimeout: number;
|
|
29
|
+
});
|
|
30
|
+
declare class RedisLock {
|
|
31
|
+
private readonly redis;
|
|
32
|
+
private readonly locksHashKey;
|
|
33
|
+
readonly name: string;
|
|
34
|
+
private readonly value;
|
|
35
|
+
private acquireLockPromise;
|
|
36
|
+
/**
|
|
37
|
+
* @param {Redis} redis
|
|
38
|
+
* @param {string} name
|
|
39
|
+
* @param {string} locksHashKey
|
|
40
|
+
* @protected
|
|
41
|
+
*/
|
|
42
|
+
constructor(redis: Redis, name: string, locksHashKey: string);
|
|
43
|
+
/**
|
|
44
|
+
* @param {RedisLockWaitAcquireParams} params
|
|
45
|
+
* @return {Promise<boolean>}
|
|
46
|
+
*/
|
|
47
|
+
waitAcquire(params: RedisLockWaitAcquireParams): Promise<boolean>;
|
|
48
|
+
/**
|
|
49
|
+
* @param {number} ttlMs
|
|
50
|
+
* @return {Promise<boolean>}
|
|
51
|
+
*/
|
|
52
|
+
acquire(ttlMs: number): Promise<boolean>;
|
|
53
|
+
/**
|
|
54
|
+
* @return {Promise<void>}
|
|
55
|
+
*/
|
|
56
|
+
release(): Promise<void>;
|
|
57
|
+
}
|
|
58
|
+
//#endregion
|
|
59
|
+
//#region src/redis-lock.factory.d.ts
|
|
60
|
+
declare class RedisLockFactory {
|
|
61
|
+
private readonly options;
|
|
62
|
+
private readonly redis;
|
|
63
|
+
/**
|
|
64
|
+
* @param {RedisRegisterOptions} options
|
|
65
|
+
* @param {Redis} redis
|
|
66
|
+
*/
|
|
67
|
+
constructor(options: RedisRegisterOptions, redis: Redis);
|
|
68
|
+
/**
|
|
69
|
+
* @param {string} name
|
|
70
|
+
* @return {RedisLock}
|
|
71
|
+
*/
|
|
72
|
+
create(name: string): RedisLock;
|
|
73
|
+
}
|
|
74
|
+
//#endregion
|
|
75
|
+
export { RedisLock, RedisLockFactory, type RedisLockWaitAcquireParams, RedisModule, type RedisRegisterOptions };
|
package/package.json
CHANGED
|
@@ -1,23 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jsnw/nestjs-ioredis",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "NestJS module for integrating Redis
|
|
5
|
-
"main": "./dist/index.js",
|
|
6
|
-
"types": "./dist/types/index.d.ts",
|
|
3
|
+
"version": "2.1.0",
|
|
4
|
+
"description": "NestJS module for integrating Redis",
|
|
7
5
|
"exports": {
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
"types": "./dist/
|
|
6
|
+
"require": {
|
|
7
|
+
"default": "./dist/index.cjs",
|
|
8
|
+
"types": "./dist/index.d.cts"
|
|
11
9
|
}
|
|
12
10
|
},
|
|
13
|
-
"scripts": {
|
|
14
|
-
"clean": "rimraf ./dist",
|
|
15
|
-
"build": "npm run clean && tsc -p tsconfig.json",
|
|
16
|
-
"test": "jest --passWithNoTests",
|
|
17
|
-
"test:watch": "jest --watch",
|
|
18
|
-
"test:coverage": "jest --coverage",
|
|
19
|
-
"prepublishOnly": "npm run build"
|
|
20
|
-
},
|
|
21
11
|
"files": [
|
|
22
12
|
"./dist",
|
|
23
13
|
"README.md"
|
|
@@ -40,19 +30,20 @@
|
|
|
40
30
|
"email": "jsnow0177@gmail.com"
|
|
41
31
|
},
|
|
42
32
|
"license": "MIT",
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"dedent": "^1.7.2"
|
|
35
|
+
},
|
|
43
36
|
"peerDependencies": {
|
|
44
|
-
"@nestjs/common": "^11.1.
|
|
45
|
-
"ioredis": "^5.
|
|
37
|
+
"@nestjs/common": "^11.1.27",
|
|
38
|
+
"ioredis": "^5.11.1"
|
|
46
39
|
},
|
|
47
40
|
"devDependencies": {
|
|
48
|
-
"@nestjs/common": "^11.1.
|
|
49
|
-
"ioredis": "^5.
|
|
50
|
-
"
|
|
51
|
-
"rimraf": "^6.1.3",
|
|
52
|
-
"ts-jest": "^29.4.6",
|
|
41
|
+
"@nestjs/common": "^11.1.27",
|
|
42
|
+
"ioredis": "^5.11.1",
|
|
43
|
+
"tsdown": "^0.22.3",
|
|
53
44
|
"typescript": "^5.9.3"
|
|
54
45
|
},
|
|
55
|
-
"
|
|
56
|
-
"
|
|
46
|
+
"scripts": {
|
|
47
|
+
"build": "tsdown"
|
|
57
48
|
}
|
|
58
|
-
}
|
|
49
|
+
}
|
package/dist/index.js
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.RedisLock = exports.InjectRedis = exports.RedisModule = void 0;
|
|
4
|
-
var redis_module_1 = require("./redis.module");
|
|
5
|
-
Object.defineProperty(exports, "RedisModule", { enumerable: true, get: function () { return redis_module_1.RedisModule; } });
|
|
6
|
-
var redis_decorators_1 = require("./redis.decorators");
|
|
7
|
-
Object.defineProperty(exports, "InjectRedis", { enumerable: true, get: function () { return redis_decorators_1.InjectRedis; } });
|
|
8
|
-
var redis_lock_1 = require("./redis-lock");
|
|
9
|
-
Object.defineProperty(exports, "RedisLock", { enumerable: true, get: function () { return redis_lock_1.RedisLock; } });
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
|
3
|
-
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
|
|
4
|
-
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
|
|
5
|
-
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
|
|
6
|
-
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
|
|
7
|
-
var _, done = false;
|
|
8
|
-
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
9
|
-
var context = {};
|
|
10
|
-
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
|
|
11
|
-
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
|
|
12
|
-
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
|
|
13
|
-
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
|
|
14
|
-
if (kind === "accessor") {
|
|
15
|
-
if (result === void 0) continue;
|
|
16
|
-
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
|
|
17
|
-
if (_ = accept(result.get)) descriptor.get = _;
|
|
18
|
-
if (_ = accept(result.set)) descriptor.set = _;
|
|
19
|
-
if (_ = accept(result.init)) initializers.unshift(_);
|
|
20
|
-
}
|
|
21
|
-
else if (_ = accept(result)) {
|
|
22
|
-
if (kind === "field") initializers.unshift(_);
|
|
23
|
-
else descriptor[key] = _;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
27
|
-
done = true;
|
|
28
|
-
};
|
|
29
|
-
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
|
|
30
|
-
var useValue = arguments.length > 2;
|
|
31
|
-
for (var i = 0; i < initializers.length; i++) {
|
|
32
|
-
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
33
|
-
}
|
|
34
|
-
return useValue ? value : void 0;
|
|
35
|
-
};
|
|
36
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
|
-
exports.RedisCoreModule = void 0;
|
|
38
|
-
const common_1 = require("@nestjs/common");
|
|
39
|
-
const ioredis_1 = require("ioredis");
|
|
40
|
-
const redis_instances_manager_1 = require("./redis-instances-manager");
|
|
41
|
-
const redis_consts_1 = require("./redis.consts");
|
|
42
|
-
const redis_helpers_1 = require("./redis.helpers");
|
|
43
|
-
let RedisCoreModule = (() => {
|
|
44
|
-
let _classDecorators = [(0, common_1.Global)(), (0, common_1.Module)({
|
|
45
|
-
providers: [
|
|
46
|
-
redis_instances_manager_1.RedisInstancesManager
|
|
47
|
-
]
|
|
48
|
-
})];
|
|
49
|
-
let _classDescriptor;
|
|
50
|
-
let _classExtraInitializers = [];
|
|
51
|
-
let _classThis;
|
|
52
|
-
var RedisCoreModule = class {
|
|
53
|
-
static { _classThis = this; }
|
|
54
|
-
static {
|
|
55
|
-
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
|
|
56
|
-
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
|
|
57
|
-
RedisCoreModule = _classThis = _classDescriptor.value;
|
|
58
|
-
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
59
|
-
__runInitializers(_classThis, _classExtraInitializers);
|
|
60
|
-
}
|
|
61
|
-
/**
|
|
62
|
-
* @param {RedisForRootParams} params
|
|
63
|
-
* @return {DynamicModule}
|
|
64
|
-
*/
|
|
65
|
-
static forRoot(params) {
|
|
66
|
-
const providers = [];
|
|
67
|
-
const redisProvider = this.createRedisProvider(params);
|
|
68
|
-
providers.push(redisProvider);
|
|
69
|
-
if (!!params.isDefault)
|
|
70
|
-
providers.push({
|
|
71
|
-
provide: (0, redis_helpers_1.getRedisToken)(redis_consts_1.REDIS_DEFAULT_CONNECTION_NAME),
|
|
72
|
-
useExisting: (0, redis_helpers_1.getRedisToken)(params)
|
|
73
|
-
});
|
|
74
|
-
return {
|
|
75
|
-
module: RedisCoreModule,
|
|
76
|
-
providers: providers,
|
|
77
|
-
exports: providers
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
/**
|
|
81
|
-
* @param {RedisForRootParams} params
|
|
82
|
-
* @return {FactoryProvider}
|
|
83
|
-
* @private
|
|
84
|
-
*/
|
|
85
|
-
static createRedisProvider(params) {
|
|
86
|
-
const redisToken = (0, redis_helpers_1.getRedisToken)(params), keyPrefix = (0, redis_helpers_1.resolveKeyPrefix)(params.keyPrefix);
|
|
87
|
-
return {
|
|
88
|
-
provide: redisToken,
|
|
89
|
-
useFactory: (im) => {
|
|
90
|
-
const existingInstance = im.getInstance(redisToken);
|
|
91
|
-
if (existingInstance)
|
|
92
|
-
return existingInstance;
|
|
93
|
-
const instance = new ioredis_1.Redis({
|
|
94
|
-
host: params.hostname,
|
|
95
|
-
port: params.port,
|
|
96
|
-
username: params.username,
|
|
97
|
-
password: params.password,
|
|
98
|
-
db: params.db ?? 0,
|
|
99
|
-
connectTimeout: params.connectionTimeout ?? 10_000,
|
|
100
|
-
lazyConnect: !!params.lazy,
|
|
101
|
-
keyPrefix: keyPrefix
|
|
102
|
-
});
|
|
103
|
-
instance[redis_consts_1.REDIS_LOCKS_HASH_KEY_PROP_NAME] = params.locksHashKey;
|
|
104
|
-
im.addInstance(redisToken, instance);
|
|
105
|
-
return instance;
|
|
106
|
-
},
|
|
107
|
-
inject: [
|
|
108
|
-
redis_instances_manager_1.RedisInstancesManager
|
|
109
|
-
]
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
};
|
|
113
|
-
return RedisCoreModule = _classThis;
|
|
114
|
-
})();
|
|
115
|
-
exports.RedisCoreModule = RedisCoreModule;
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
|
3
|
-
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
|
|
4
|
-
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
|
|
5
|
-
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
|
|
6
|
-
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
|
|
7
|
-
var _, done = false;
|
|
8
|
-
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
9
|
-
var context = {};
|
|
10
|
-
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
|
|
11
|
-
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
|
|
12
|
-
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
|
|
13
|
-
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
|
|
14
|
-
if (kind === "accessor") {
|
|
15
|
-
if (result === void 0) continue;
|
|
16
|
-
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
|
|
17
|
-
if (_ = accept(result.get)) descriptor.get = _;
|
|
18
|
-
if (_ = accept(result.set)) descriptor.set = _;
|
|
19
|
-
if (_ = accept(result.init)) initializers.unshift(_);
|
|
20
|
-
}
|
|
21
|
-
else if (_ = accept(result)) {
|
|
22
|
-
if (kind === "field") initializers.unshift(_);
|
|
23
|
-
else descriptor[key] = _;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
27
|
-
done = true;
|
|
28
|
-
};
|
|
29
|
-
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
|
|
30
|
-
var useValue = arguments.length > 2;
|
|
31
|
-
for (var i = 0; i < initializers.length; i++) {
|
|
32
|
-
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
33
|
-
}
|
|
34
|
-
return useValue ? value : void 0;
|
|
35
|
-
};
|
|
36
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
|
-
exports.RedisInstancesManager = void 0;
|
|
38
|
-
const common_1 = require("@nestjs/common");
|
|
39
|
-
const redis_helpers_1 = require("./redis.helpers");
|
|
40
|
-
let RedisInstancesManager = (() => {
|
|
41
|
-
let _classDecorators = [(0, common_1.Injectable)()];
|
|
42
|
-
let _classDescriptor;
|
|
43
|
-
let _classExtraInitializers = [];
|
|
44
|
-
let _classThis;
|
|
45
|
-
var RedisInstancesManager = class {
|
|
46
|
-
static { _classThis = this; }
|
|
47
|
-
static {
|
|
48
|
-
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
|
|
49
|
-
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
|
|
50
|
-
RedisInstancesManager = _classThis = _classDescriptor.value;
|
|
51
|
-
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
52
|
-
__runInitializers(_classThis, _classExtraInitializers);
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* @type {Map<string, Redis>}
|
|
56
|
-
* @private
|
|
57
|
-
*/
|
|
58
|
-
instances = new Map();
|
|
59
|
-
/**
|
|
60
|
-
* @param {string | RedisForRootParams} token
|
|
61
|
-
* @param {Redis} instance
|
|
62
|
-
*/
|
|
63
|
-
addInstance(token, instance) {
|
|
64
|
-
token = typeof token === 'string' ? token : (0, redis_helpers_1.getRedisToken)(token);
|
|
65
|
-
if (this.instances.has(token))
|
|
66
|
-
return;
|
|
67
|
-
this.instances.set(token, instance);
|
|
68
|
-
}
|
|
69
|
-
/**
|
|
70
|
-
* @param {string | RedisForRootParams} token
|
|
71
|
-
* @return {Redis | null}
|
|
72
|
-
*/
|
|
73
|
-
getInstance(token) {
|
|
74
|
-
token = typeof token === 'string' ? token : (0, redis_helpers_1.getRedisToken)(token);
|
|
75
|
-
return this.instances.get(token) ?? null;
|
|
76
|
-
}
|
|
77
|
-
/**
|
|
78
|
-
* @param {string | RedisForRootParams} token
|
|
79
|
-
* @return {boolean}
|
|
80
|
-
*/
|
|
81
|
-
hasInstance(token) {
|
|
82
|
-
token = typeof token === 'string' ? token : (0, redis_helpers_1.getRedisToken)(token);
|
|
83
|
-
return this.instances.has(token);
|
|
84
|
-
}
|
|
85
|
-
/**
|
|
86
|
-
* @return {Redis[]}
|
|
87
|
-
*/
|
|
88
|
-
getAllInstances() {
|
|
89
|
-
return Array.from(this.instances.values());
|
|
90
|
-
}
|
|
91
|
-
/**
|
|
92
|
-
* @return {Promise<void>}
|
|
93
|
-
*/
|
|
94
|
-
async onApplicationShutdown() {
|
|
95
|
-
await Promise.allSettled(this.getAllInstances()
|
|
96
|
-
.map(instance => instance.quit()));
|
|
97
|
-
}
|
|
98
|
-
};
|
|
99
|
-
return RedisInstancesManager = _classThis;
|
|
100
|
-
})();
|
|
101
|
-
exports.RedisInstancesManager = RedisInstancesManager;
|
package/dist/redis-lock.js
DELETED
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.RedisLock = void 0;
|
|
7
|
-
const node_crypto_1 = require("node:crypto");
|
|
8
|
-
const promises_1 = require("node:timers/promises");
|
|
9
|
-
const dedent_1 = __importDefault(require("dedent"));
|
|
10
|
-
const redis_consts_1 = require("./redis.consts");
|
|
11
|
-
const redis_helpers_1 = require("./redis.helpers");
|
|
12
|
-
class RedisLock {
|
|
13
|
-
redis;
|
|
14
|
-
locksHashKey;
|
|
15
|
-
name;
|
|
16
|
-
value;
|
|
17
|
-
acquireLockPromise = null;
|
|
18
|
-
/**
|
|
19
|
-
* @param {Redis} redis
|
|
20
|
-
* @param {string} name
|
|
21
|
-
*/
|
|
22
|
-
constructor(redis, name) {
|
|
23
|
-
this.redis = redis;
|
|
24
|
-
this.name = name;
|
|
25
|
-
this.locksHashKey = this.redis?.[redis_consts_1.REDIS_LOCKS_HASH_KEY_PROP_NAME] ?? redis_consts_1.REDIS_DEFAULT_LOCKS_HASH_KEY;
|
|
26
|
-
this.value = (0, node_crypto_1.randomUUID)();
|
|
27
|
-
}
|
|
28
|
-
/**
|
|
29
|
-
* @param {RedisLockWaitAcquireParams} params
|
|
30
|
-
* @return {Promise<boolean>}
|
|
31
|
-
*/
|
|
32
|
-
async waitAcquire(params) {
|
|
33
|
-
const startedAt = Date.now();
|
|
34
|
-
let iteration = 0;
|
|
35
|
-
while (true) {
|
|
36
|
-
const acquired = await this.acquire(params.lockTTL), RETRY_DELAY_MS = (0, redis_helpers_1.getRetryInterval)(iteration, params.retryInterval);
|
|
37
|
-
if (acquired)
|
|
38
|
-
return true;
|
|
39
|
-
if (params.signal?.aborted)
|
|
40
|
-
return false;
|
|
41
|
-
let delayMs = RETRY_DELAY_MS;
|
|
42
|
-
if (params.waitTimeout !== undefined) {
|
|
43
|
-
const elapsed = Date.now() - startedAt, timeLeft = params.waitTimeout - elapsed;
|
|
44
|
-
if (timeLeft <= 0)
|
|
45
|
-
return false;
|
|
46
|
-
delayMs = Math.max(1, Math.min(RETRY_DELAY_MS, timeLeft));
|
|
47
|
-
}
|
|
48
|
-
try {
|
|
49
|
-
await (0, promises_1.setTimeout)(delayMs, undefined, { signal: params.signal });
|
|
50
|
-
}
|
|
51
|
-
catch (e) {
|
|
52
|
-
if (e instanceof Error && e.name === 'AbortError')
|
|
53
|
-
return false;
|
|
54
|
-
throw e;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
/**
|
|
59
|
-
* @param {number} ttlMs
|
|
60
|
-
* @return {Promise<boolean>}
|
|
61
|
-
*/
|
|
62
|
-
acquire(ttlMs) {
|
|
63
|
-
if (this.acquireLockPromise)
|
|
64
|
-
return this.acquireLockPromise;
|
|
65
|
-
this.acquireLockPromise = new Promise((resolve, reject) => {
|
|
66
|
-
this.redis.call('HSETEX', this.locksHashKey, 'FNX', 'PX', ttlMs, 'FIELDS', 1, this.name, this.value)
|
|
67
|
-
.then(res => {
|
|
68
|
-
if (res === 1)
|
|
69
|
-
return resolve(true);
|
|
70
|
-
return resolve(false);
|
|
71
|
-
})
|
|
72
|
-
.catch(reject);
|
|
73
|
-
});
|
|
74
|
-
this.acquireLockPromise.finally(() => {
|
|
75
|
-
this.acquireLockPromise = null;
|
|
76
|
-
});
|
|
77
|
-
return this.acquireLockPromise;
|
|
78
|
-
}
|
|
79
|
-
/**
|
|
80
|
-
* @return {Promise<void>}
|
|
81
|
-
*/
|
|
82
|
-
async release() {
|
|
83
|
-
await this.redis.eval((0, dedent_1.default) `
|
|
84
|
-
if redis.call("hget", KEYS[1], ARGV[1]) == ARGV[2]
|
|
85
|
-
then
|
|
86
|
-
return redis.call("hdel", KEYS[1], ARGV[1])
|
|
87
|
-
else
|
|
88
|
-
return 0
|
|
89
|
-
end
|
|
90
|
-
`, 1, this.locksHashKey, this.name, this.value);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
exports.RedisLock = RedisLock;
|
package/dist/redis.consts.js
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.REDIS_DEFAULT_LOCK_ACQUIRE_RETRY_DELAY_MS = exports.REDIS_DEFAULT_LOCKS_HASH_KEY = exports.REDIS_LOCKS_HASH_KEY_PROP_NAME = exports.REDIS_DEFAULT_CONNECTION_NAME = exports.REDIS_INSTANCE_TOKEN_PREFIX = void 0;
|
|
4
|
-
exports.REDIS_INSTANCE_TOKEN_PREFIX = 'REDIS_INSTANCE_';
|
|
5
|
-
exports.REDIS_DEFAULT_CONNECTION_NAME = 'default';
|
|
6
|
-
exports.REDIS_LOCKS_HASH_KEY_PROP_NAME = '__$__locksHashKey__';
|
|
7
|
-
exports.REDIS_DEFAULT_LOCKS_HASH_KEY = '__locks__';
|
|
8
|
-
exports.REDIS_DEFAULT_LOCK_ACQUIRE_RETRY_DELAY_MS = 100;
|
package/dist/redis.decorators.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.InjectRedis = void 0;
|
|
4
|
-
const common_1 = require("@nestjs/common");
|
|
5
|
-
const redis_consts_1 = require("./redis.consts");
|
|
6
|
-
const redis_helpers_1 = require("./redis.helpers");
|
|
7
|
-
/**
|
|
8
|
-
* @param {string} [connectionName = REDIS_DEFAULT_CONNECTION_NAME]
|
|
9
|
-
* @return {PropertyDecorator & ParameterDecorator}
|
|
10
|
-
* @constructor
|
|
11
|
-
*/
|
|
12
|
-
const InjectRedis = (connectionName = redis_consts_1.REDIS_DEFAULT_CONNECTION_NAME) => (0, common_1.Inject)((0, redis_helpers_1.getRedisToken)(connectionName));
|
|
13
|
-
exports.InjectRedis = InjectRedis;
|
package/dist/redis.helpers.js
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getRetryInterval = exports.resolveKeyPrefix = void 0;
|
|
4
|
-
exports.resolveConnectionName = resolveConnectionName;
|
|
5
|
-
exports.getRedisToken = getRedisToken;
|
|
6
|
-
const redis_consts_1 = require("./redis.consts");
|
|
7
|
-
/**
|
|
8
|
-
* @param {string | RedisForRootParams} arg
|
|
9
|
-
* @return {string}
|
|
10
|
-
*/
|
|
11
|
-
function resolveConnectionName(arg) {
|
|
12
|
-
if (typeof arg === 'string')
|
|
13
|
-
return arg.toUpperCase();
|
|
14
|
-
if (arg.connectionName && arg.connectionName.trim() !== '')
|
|
15
|
-
return arg.connectionName.trim().toUpperCase();
|
|
16
|
-
return `${arg.hostname}:${arg.port}/db${arg.db ?? 0}>${arg.keyPrefix ?? ''}`.toUpperCase();
|
|
17
|
-
}
|
|
18
|
-
/**
|
|
19
|
-
* @param {string | RedisForRootParams} arg
|
|
20
|
-
* @return {string}
|
|
21
|
-
*/
|
|
22
|
-
function getRedisToken(arg) {
|
|
23
|
-
if (typeof arg === 'string')
|
|
24
|
-
return `${redis_consts_1.REDIS_INSTANCE_TOKEN_PREFIX}${arg}`.toUpperCase();
|
|
25
|
-
return `${redis_consts_1.REDIS_INSTANCE_TOKEN_PREFIX}${resolveConnectionName(arg)}`.toUpperCase();
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* @param {string} prefix
|
|
29
|
-
* @return {string}
|
|
30
|
-
*/
|
|
31
|
-
const resolveKeyPrefix = (prefix) => {
|
|
32
|
-
if (!prefix)
|
|
33
|
-
return '';
|
|
34
|
-
prefix = prefix.trim();
|
|
35
|
-
if (prefix.endsWith(':')) {
|
|
36
|
-
if (prefix.length === 1)
|
|
37
|
-
return '';
|
|
38
|
-
return prefix;
|
|
39
|
-
}
|
|
40
|
-
return prefix + ':';
|
|
41
|
-
};
|
|
42
|
-
exports.resolveKeyPrefix = resolveKeyPrefix;
|
|
43
|
-
/**
|
|
44
|
-
* @param {number} iteration
|
|
45
|
-
* @param {RedisLockWaitAcquireParams["retryInterval"]} [retryInterval]
|
|
46
|
-
* @return {number}
|
|
47
|
-
*/
|
|
48
|
-
const getRetryInterval = (iteration, retryInterval) => {
|
|
49
|
-
if (retryInterval === undefined)
|
|
50
|
-
return redis_consts_1.REDIS_DEFAULT_LOCK_ACQUIRE_RETRY_DELAY_MS;
|
|
51
|
-
if (typeof retryInterval === 'number')
|
|
52
|
-
return Math.max(1, retryInterval);
|
|
53
|
-
if (Array.isArray(retryInterval)) {
|
|
54
|
-
if (retryInterval.length === 0)
|
|
55
|
-
return redis_consts_1.REDIS_DEFAULT_LOCK_ACQUIRE_RETRY_DELAY_MS;
|
|
56
|
-
if (retryInterval.length === 1)
|
|
57
|
-
return retryInterval[0];
|
|
58
|
-
return iteration < retryInterval.length
|
|
59
|
-
? retryInterval[iteration]
|
|
60
|
-
: retryInterval[retryInterval.length - 1];
|
|
61
|
-
}
|
|
62
|
-
if (typeof retryInterval === 'function')
|
|
63
|
-
return Math.max(1, retryInterval(iteration) ?? redis_consts_1.REDIS_DEFAULT_LOCK_ACQUIRE_RETRY_DELAY_MS);
|
|
64
|
-
return redis_consts_1.REDIS_DEFAULT_LOCK_ACQUIRE_RETRY_DELAY_MS;
|
|
65
|
-
};
|
|
66
|
-
exports.getRetryInterval = getRetryInterval;
|
package/dist/redis.module.js
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
|
3
|
-
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
|
|
4
|
-
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
|
|
5
|
-
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
|
|
6
|
-
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
|
|
7
|
-
var _, done = false;
|
|
8
|
-
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
9
|
-
var context = {};
|
|
10
|
-
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
|
|
11
|
-
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
|
|
12
|
-
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
|
|
13
|
-
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
|
|
14
|
-
if (kind === "accessor") {
|
|
15
|
-
if (result === void 0) continue;
|
|
16
|
-
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
|
|
17
|
-
if (_ = accept(result.get)) descriptor.get = _;
|
|
18
|
-
if (_ = accept(result.set)) descriptor.set = _;
|
|
19
|
-
if (_ = accept(result.init)) initializers.unshift(_);
|
|
20
|
-
}
|
|
21
|
-
else if (_ = accept(result)) {
|
|
22
|
-
if (kind === "field") initializers.unshift(_);
|
|
23
|
-
else descriptor[key] = _;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
27
|
-
done = true;
|
|
28
|
-
};
|
|
29
|
-
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
|
|
30
|
-
var useValue = arguments.length > 2;
|
|
31
|
-
for (var i = 0; i < initializers.length; i++) {
|
|
32
|
-
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
33
|
-
}
|
|
34
|
-
return useValue ? value : void 0;
|
|
35
|
-
};
|
|
36
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
|
-
exports.RedisModule = void 0;
|
|
38
|
-
const common_1 = require("@nestjs/common");
|
|
39
|
-
const redis_core_module_1 = require("./redis-core.module");
|
|
40
|
-
let RedisModule = (() => {
|
|
41
|
-
let _classDecorators = [(0, common_1.Module)({})];
|
|
42
|
-
let _classDescriptor;
|
|
43
|
-
let _classExtraInitializers = [];
|
|
44
|
-
let _classThis;
|
|
45
|
-
var RedisModule = class {
|
|
46
|
-
static { _classThis = this; }
|
|
47
|
-
static {
|
|
48
|
-
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
|
|
49
|
-
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
|
|
50
|
-
RedisModule = _classThis = _classDescriptor.value;
|
|
51
|
-
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
52
|
-
__runInitializers(_classThis, _classExtraInitializers);
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* @param {RedisForRootParams} params
|
|
56
|
-
* @return {DynamicModule}
|
|
57
|
-
*/
|
|
58
|
-
static forRoot(params) {
|
|
59
|
-
return {
|
|
60
|
-
module: RedisModule,
|
|
61
|
-
imports: [
|
|
62
|
-
redis_core_module_1.RedisCoreModule.forRoot(params)
|
|
63
|
-
]
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
};
|
|
67
|
-
return RedisModule = _classThis;
|
|
68
|
-
})();
|
|
69
|
-
exports.RedisModule = RedisModule;
|
package/dist/redis.types.js
DELETED
package/dist/types/index.d.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { type DynamicModule } from '@nestjs/common';
|
|
2
|
-
import type { RedisForRootParams } from './redis.types';
|
|
3
|
-
export declare class RedisCoreModule {
|
|
4
|
-
/**
|
|
5
|
-
* @param {RedisForRootParams} params
|
|
6
|
-
* @return {DynamicModule}
|
|
7
|
-
*/
|
|
8
|
-
static forRoot(params: RedisForRootParams): DynamicModule;
|
|
9
|
-
/**
|
|
10
|
-
* @param {RedisForRootParams} params
|
|
11
|
-
* @return {FactoryProvider}
|
|
12
|
-
* @private
|
|
13
|
-
*/
|
|
14
|
-
private static createRedisProvider;
|
|
15
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { type OnApplicationShutdown } from '@nestjs/common';
|
|
2
|
-
import { type Redis } from 'ioredis';
|
|
3
|
-
import type { RedisForRootParams } from './redis.types';
|
|
4
|
-
export declare class RedisInstancesManager implements OnApplicationShutdown {
|
|
5
|
-
/**
|
|
6
|
-
* @type {Map<string, Redis>}
|
|
7
|
-
* @private
|
|
8
|
-
*/
|
|
9
|
-
private readonly instances;
|
|
10
|
-
addInstance(resolvedToken: string, instance: Redis): void;
|
|
11
|
-
addInstance(unresolvedToken: RedisForRootParams, instance: Redis): void;
|
|
12
|
-
getInstance(resolvedToken: string): Redis | null;
|
|
13
|
-
getInstance(unresolvedToken: RedisForRootParams): Redis | null;
|
|
14
|
-
hasInstance(resolvedToken: string): boolean;
|
|
15
|
-
hasInstance(unresolvedToken: RedisForRootParams): boolean;
|
|
16
|
-
/**
|
|
17
|
-
* @return {Redis[]}
|
|
18
|
-
*/
|
|
19
|
-
getAllInstances(): Redis[];
|
|
20
|
-
/**
|
|
21
|
-
* @return {Promise<void>}
|
|
22
|
-
*/
|
|
23
|
-
onApplicationShutdown(): Promise<void>;
|
|
24
|
-
}
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { type Redis } from 'ioredis';
|
|
2
|
-
export type RedisLockWaitAcquireParams = {
|
|
3
|
-
lockTTL: number;
|
|
4
|
-
retryInterval?: number | number[] | ((iteration: number) => number);
|
|
5
|
-
} & ({
|
|
6
|
-
signal: AbortSignal;
|
|
7
|
-
waitTimeout?: number;
|
|
8
|
-
} | {
|
|
9
|
-
signal?: AbortSignal;
|
|
10
|
-
waitTimeout: number;
|
|
11
|
-
});
|
|
12
|
-
export declare class RedisLock {
|
|
13
|
-
private readonly redis;
|
|
14
|
-
private readonly locksHashKey;
|
|
15
|
-
readonly name: string;
|
|
16
|
-
private readonly value;
|
|
17
|
-
private acquireLockPromise;
|
|
18
|
-
/**
|
|
19
|
-
* @param {Redis} redis
|
|
20
|
-
* @param {string} name
|
|
21
|
-
*/
|
|
22
|
-
constructor(redis: Redis, name: string);
|
|
23
|
-
/**
|
|
24
|
-
* @param {RedisLockWaitAcquireParams} params
|
|
25
|
-
* @return {Promise<boolean>}
|
|
26
|
-
*/
|
|
27
|
-
waitAcquire(params: RedisLockWaitAcquireParams): Promise<boolean>;
|
|
28
|
-
/**
|
|
29
|
-
* @param {number} ttlMs
|
|
30
|
-
* @return {Promise<boolean>}
|
|
31
|
-
*/
|
|
32
|
-
acquire(ttlMs: number): Promise<boolean>;
|
|
33
|
-
/**
|
|
34
|
-
* @return {Promise<void>}
|
|
35
|
-
*/
|
|
36
|
-
release(): Promise<void>;
|
|
37
|
-
}
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
export declare const REDIS_INSTANCE_TOKEN_PREFIX: "REDIS_INSTANCE_";
|
|
2
|
-
export declare const REDIS_DEFAULT_CONNECTION_NAME = "default";
|
|
3
|
-
export declare const REDIS_LOCKS_HASH_KEY_PROP_NAME = "__$__locksHashKey__";
|
|
4
|
-
export declare const REDIS_DEFAULT_LOCKS_HASH_KEY = "__locks__";
|
|
5
|
-
export declare const REDIS_DEFAULT_LOCK_ACQUIRE_RETRY_DELAY_MS = 100;
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import type { RedisForRootParams } from './redis.types';
|
|
2
|
-
import { type RedisLockWaitAcquireParams } from './redis-lock';
|
|
3
|
-
export declare function resolveConnectionName(instanceName: string): string;
|
|
4
|
-
export declare function resolveConnectionName(params: RedisForRootParams): string;
|
|
5
|
-
export declare function getRedisToken(connectionName: string): string;
|
|
6
|
-
export declare function getRedisToken(params: RedisForRootParams): string;
|
|
7
|
-
/**
|
|
8
|
-
* @param {string} prefix
|
|
9
|
-
* @return {string}
|
|
10
|
-
*/
|
|
11
|
-
export declare const resolveKeyPrefix: (prefix?: string) => string;
|
|
12
|
-
/**
|
|
13
|
-
* @param {number} iteration
|
|
14
|
-
* @param {RedisLockWaitAcquireParams["retryInterval"]} [retryInterval]
|
|
15
|
-
* @return {number}
|
|
16
|
-
*/
|
|
17
|
-
export declare const getRetryInterval: (iteration: number, retryInterval?: RedisLockWaitAcquireParams["retryInterval"]) => number;
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { type DynamicModule } from '@nestjs/common';
|
|
2
|
-
import type { RedisForRootParams } from './redis.types';
|
|
3
|
-
export declare class RedisModule {
|
|
4
|
-
/**
|
|
5
|
-
* @param {RedisForRootParams} params
|
|
6
|
-
* @return {DynamicModule}
|
|
7
|
-
*/
|
|
8
|
-
static forRoot(params: RedisForRootParams): DynamicModule;
|
|
9
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
export type RedisForRootParams = {
|
|
2
|
-
connectionName?: string;
|
|
3
|
-
hostname: string;
|
|
4
|
-
port: number;
|
|
5
|
-
username: string;
|
|
6
|
-
password: string;
|
|
7
|
-
keyPrefix?: string;
|
|
8
|
-
db?: number;
|
|
9
|
-
connectionTimeout?: number;
|
|
10
|
-
lazy?: boolean;
|
|
11
|
-
isDefault?: boolean;
|
|
12
|
-
locksHashKey?: string;
|
|
13
|
-
};
|