@nestjs-redisx/core 1.0.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/LICENSE +21 -0
- package/README.md +51 -0
- package/dist/index.d.ts +4917 -0
- package/dist/index.js +4925 -0
- package/dist/index.js.map +1 -0
- package/package.json +68 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,4925 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
30
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
31
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
32
|
+
if (decorator = decorators[i])
|
|
33
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
34
|
+
if (kind && result) __defProp(target, key, result);
|
|
35
|
+
return result;
|
|
36
|
+
};
|
|
37
|
+
var __decorateParam = (index, decorator) => (target, key) => decorator(target, key, index);
|
|
38
|
+
|
|
39
|
+
// src/index.ts
|
|
40
|
+
var index_exports = {};
|
|
41
|
+
__export(index_exports, {
|
|
42
|
+
BaseRedisDriver: () => BaseRedisDriver,
|
|
43
|
+
CLIENT_MANAGER: () => CLIENT_MANAGER,
|
|
44
|
+
CommandError: () => CommandError,
|
|
45
|
+
ConnectionError: () => ConnectionError,
|
|
46
|
+
ConnectionStatus: () => ConnectionStatus,
|
|
47
|
+
DEFAULT_CLIENT_NAME: () => DEFAULT_CLIENT_NAME,
|
|
48
|
+
DEFAULT_COMMAND_TIMEOUT: () => DEFAULT_COMMAND_TIMEOUT,
|
|
49
|
+
DEFAULT_CONNECTION_TIMEOUT: () => DEFAULT_CONNECTION_TIMEOUT,
|
|
50
|
+
DEFAULT_DRIVER_TYPE: () => DEFAULT_DRIVER_TYPE,
|
|
51
|
+
DEFAULT_GRACEFUL_SHUTDOWN_TIMEOUT: () => DEFAULT_GRACEFUL_SHUTDOWN_TIMEOUT,
|
|
52
|
+
DEFAULT_HEALTH_CHECK_INTERVAL: () => DEFAULT_HEALTH_CHECK_INTERVAL,
|
|
53
|
+
DriverError: () => DriverError,
|
|
54
|
+
DriverEvent: () => DriverEvent,
|
|
55
|
+
DriverFactory: () => DriverFactory,
|
|
56
|
+
ErrorCode: () => ErrorCode,
|
|
57
|
+
InjectRedis: () => InjectRedis,
|
|
58
|
+
IoRedisAdapter: () => IoRedisAdapter,
|
|
59
|
+
MODULE_NAME: () => MODULE_NAME,
|
|
60
|
+
ManagerEvent: () => ManagerEvent,
|
|
61
|
+
NodeRedisAdapter: () => NodeRedisAdapter,
|
|
62
|
+
PLUGIN_REGISTRY: () => PLUGIN_REGISTRY,
|
|
63
|
+
PluginRegistryService: () => PluginRegistryService,
|
|
64
|
+
REDISX_CONFIG: () => REDISX_CONFIG,
|
|
65
|
+
REDISX_LOGGER: () => REDISX_LOGGER,
|
|
66
|
+
REDIS_CLIENT: () => REDIS_CLIENT,
|
|
67
|
+
REDIS_CLIENTS_INITIALIZATION: () => REDIS_CLIENTS_INITIALIZATION,
|
|
68
|
+
REDIS_CLIENTS_MAP: () => REDIS_CLIENTS_MAP,
|
|
69
|
+
REDIS_DRIVER: () => REDIS_DRIVER,
|
|
70
|
+
REDIS_MODULE_OPTIONS: () => REDIS_MODULE_OPTIONS,
|
|
71
|
+
REGISTERED_PLUGINS: () => REGISTERED_PLUGINS,
|
|
72
|
+
RedisAuthError: () => RedisAuthError,
|
|
73
|
+
RedisClientManager: () => RedisClientManager,
|
|
74
|
+
RedisClusterError: () => RedisClusterError,
|
|
75
|
+
RedisConfigError: () => RedisConfigError,
|
|
76
|
+
RedisConnectionError: () => RedisConnectionError,
|
|
77
|
+
RedisInvalidArgsError: () => RedisInvalidArgsError,
|
|
78
|
+
RedisKeyNotFoundError: () => RedisKeyNotFoundError,
|
|
79
|
+
RedisMaxRetriesError: () => RedisMaxRetriesError,
|
|
80
|
+
RedisModule: () => RedisModule,
|
|
81
|
+
RedisNotConnectedError: () => RedisNotConnectedError,
|
|
82
|
+
RedisNotSupportedError: () => RedisNotSupportedError,
|
|
83
|
+
RedisOperationError: () => RedisOperationError,
|
|
84
|
+
RedisOutOfMemoryError: () => RedisOutOfMemoryError,
|
|
85
|
+
RedisPipelineError: () => RedisPipelineError,
|
|
86
|
+
RedisPoolExhaustedError: () => RedisPoolExhaustedError,
|
|
87
|
+
RedisReadOnlyError: () => RedisReadOnlyError,
|
|
88
|
+
RedisScriptError: () => RedisScriptError,
|
|
89
|
+
RedisSentinelError: () => RedisSentinelError,
|
|
90
|
+
RedisService: () => RedisService,
|
|
91
|
+
RedisTLSError: () => RedisTLSError,
|
|
92
|
+
RedisTimeoutError: () => RedisTimeoutError,
|
|
93
|
+
RedisTransactionError: () => RedisTransactionError,
|
|
94
|
+
RedisTypeMismatchError: () => RedisTypeMismatchError,
|
|
95
|
+
RedisValidationError: () => RedisValidationError,
|
|
96
|
+
RedisXError: () => RedisXError,
|
|
97
|
+
TimeoutError: () => TimeoutError,
|
|
98
|
+
ValidationErrorCollector: () => ValidationErrorCollector,
|
|
99
|
+
createDriver: () => createDriver,
|
|
100
|
+
createDrivers: () => createDrivers,
|
|
101
|
+
detectAvailableDriver: () => detectAvailableDriver,
|
|
102
|
+
getClientToken: () => getClientToken,
|
|
103
|
+
getDriverToken: () => getDriverToken,
|
|
104
|
+
getErrorDomain: () => getErrorDomain,
|
|
105
|
+
getRecommendedDriver: () => getRecommendedDriver,
|
|
106
|
+
hasClass: () => hasClass,
|
|
107
|
+
hasExisting: () => hasExisting,
|
|
108
|
+
hasFactory: () => hasFactory,
|
|
109
|
+
isClusterConnection: () => isClusterConnection,
|
|
110
|
+
isErrorCode: () => isErrorCode,
|
|
111
|
+
isErrorDomain: () => isErrorDomain,
|
|
112
|
+
isSentinelConnection: () => isSentinelConnection,
|
|
113
|
+
isSingleConnection: () => isSingleConnection
|
|
114
|
+
});
|
|
115
|
+
module.exports = __toCommonJS(index_exports);
|
|
116
|
+
|
|
117
|
+
// src/api/redis.module.ts
|
|
118
|
+
var import_common5 = require("@nestjs/common");
|
|
119
|
+
var import_core = require("@nestjs/core");
|
|
120
|
+
|
|
121
|
+
// src/client/application/redis-client.manager.ts
|
|
122
|
+
var import_events2 = __toESM(require("events"));
|
|
123
|
+
var import_common2 = require("@nestjs/common");
|
|
124
|
+
|
|
125
|
+
// src/driver/infrastructure/base.driver.ts
|
|
126
|
+
var import_events = __toESM(require("events"));
|
|
127
|
+
var import_common = require("@nestjs/common");
|
|
128
|
+
|
|
129
|
+
// src/interfaces/redis-driver.interface.ts
|
|
130
|
+
var DriverEvent = /* @__PURE__ */ ((DriverEvent2) => {
|
|
131
|
+
DriverEvent2["CONNECT"] = "connect";
|
|
132
|
+
DriverEvent2["READY"] = "ready";
|
|
133
|
+
DriverEvent2["DISCONNECT"] = "disconnect";
|
|
134
|
+
DriverEvent2["CLOSE"] = "close";
|
|
135
|
+
DriverEvent2["ERROR"] = "error";
|
|
136
|
+
DriverEvent2["RECONNECTING"] = "reconnecting";
|
|
137
|
+
DriverEvent2["END"] = "end";
|
|
138
|
+
return DriverEvent2;
|
|
139
|
+
})(DriverEvent || {});
|
|
140
|
+
|
|
141
|
+
// src/errors/error-codes.ts
|
|
142
|
+
var ErrorCode = /* @__PURE__ */ ((ErrorCode2) => {
|
|
143
|
+
ErrorCode2["CONN_FAILED"] = "CONN_FAILED";
|
|
144
|
+
ErrorCode2["CONN_TIMEOUT"] = "CONN_TIMEOUT";
|
|
145
|
+
ErrorCode2["CONN_DISCONNECTED"] = "CONN_DISCONNECTED";
|
|
146
|
+
ErrorCode2["CONN_REFUSED"] = "CONN_REFUSED";
|
|
147
|
+
ErrorCode2["CONN_AUTH_FAILED"] = "CONN_AUTH_FAILED";
|
|
148
|
+
ErrorCode2["CONN_TLS_ERROR"] = "CONN_TLS_ERROR";
|
|
149
|
+
ErrorCode2["CONN_CLUSTER_DOWN"] = "CONN_CLUSTER_DOWN";
|
|
150
|
+
ErrorCode2["CONN_CLUSTER_MOVED"] = "CONN_CLUSTER_MOVED";
|
|
151
|
+
ErrorCode2["CONN_CLUSTER_ASK"] = "CONN_CLUSTER_ASK";
|
|
152
|
+
ErrorCode2["CONN_CLUSTER_ERROR"] = "CONN_CLUSTER_ERROR";
|
|
153
|
+
ErrorCode2["CONN_SENTINEL_FAILOVER"] = "CONN_SENTINEL_FAILOVER";
|
|
154
|
+
ErrorCode2["CONN_SENTINEL_NO_MASTER"] = "CONN_SENTINEL_NO_MASTER";
|
|
155
|
+
ErrorCode2["CONN_SENTINEL_ERROR"] = "CONN_SENTINEL_ERROR";
|
|
156
|
+
ErrorCode2["DRIVER_NOT_CONNECTED"] = "DRIVER_NOT_CONNECTED";
|
|
157
|
+
ErrorCode2["CONN_MAX_RETRIES"] = "CONN_MAX_RETRIES";
|
|
158
|
+
ErrorCode2["CONN_POOL_EXHAUSTED"] = "CONN_POOL_EXHAUSTED";
|
|
159
|
+
ErrorCode2["OP_FAILED"] = "OP_FAILED";
|
|
160
|
+
ErrorCode2["OP_TIMEOUT"] = "OP_TIMEOUT";
|
|
161
|
+
ErrorCode2["OP_KEY_NOT_FOUND"] = "OP_KEY_NOT_FOUND";
|
|
162
|
+
ErrorCode2["OP_TYPE_MISMATCH"] = "OP_TYPE_MISMATCH";
|
|
163
|
+
ErrorCode2["OP_SCRIPT_ERROR"] = "OP_SCRIPT_ERROR";
|
|
164
|
+
ErrorCode2["OP_SCRIPT_TIMEOUT"] = "OP_SCRIPT_TIMEOUT";
|
|
165
|
+
ErrorCode2["OP_TRANSACTION_FAILED"] = "OP_TRANSACTION_FAILED";
|
|
166
|
+
ErrorCode2["OP_PIPELINE_FAILED"] = "OP_PIPELINE_FAILED";
|
|
167
|
+
ErrorCode2["OP_INVALID_ARGS"] = "OP_INVALID_ARGS";
|
|
168
|
+
ErrorCode2["OP_NOT_SUPPORTED"] = "OP_NOT_SUPPORTED";
|
|
169
|
+
ErrorCode2["OP_OUT_OF_MEMORY"] = "OP_OUT_OF_MEMORY";
|
|
170
|
+
ErrorCode2["OP_NO_AUTH"] = "OP_NO_AUTH";
|
|
171
|
+
ErrorCode2["OP_WRONG_PASS"] = "OP_WRONG_PASS";
|
|
172
|
+
ErrorCode2["OP_READONLY"] = "OP_READONLY";
|
|
173
|
+
ErrorCode2["OP_BUSY_KEY"] = "OP_BUSY_KEY";
|
|
174
|
+
ErrorCode2["OP_NOT_CONNECTED"] = "OP_NOT_CONNECTED";
|
|
175
|
+
ErrorCode2["CFG_INVALID"] = "CFG_INVALID";
|
|
176
|
+
ErrorCode2["CFG_MISSING_REQUIRED"] = "CFG_MISSING_REQUIRED";
|
|
177
|
+
ErrorCode2["CFG_INVALID_CONNECTION_TYPE"] = "CFG_INVALID_CONNECTION_TYPE";
|
|
178
|
+
ErrorCode2["CFG_INVALID_HOST_PORT"] = "CFG_INVALID_HOST_PORT";
|
|
179
|
+
ErrorCode2["CFG_INVALID_DB"] = "CFG_INVALID_DB";
|
|
180
|
+
ErrorCode2["CFG_INVALID_TTL"] = "CFG_INVALID_TTL";
|
|
181
|
+
ErrorCode2["CFG_INVALID_TIMEOUT"] = "CFG_INVALID_TIMEOUT";
|
|
182
|
+
ErrorCode2["CFG_INVALID_RETRY"] = "CFG_INVALID_RETRY";
|
|
183
|
+
ErrorCode2["CFG_VALIDATION_FAILED"] = "CFG_VALIDATION_FAILED";
|
|
184
|
+
ErrorCode2["CFG_INCOMPATIBLE"] = "CFG_INCOMPATIBLE";
|
|
185
|
+
ErrorCode2["CFG_DRIVER_NOT_SUPPORTED"] = "CFG_DRIVER_NOT_SUPPORTED";
|
|
186
|
+
ErrorCode2["CFG_INVALID_CLUSTER_NODES"] = "CFG_INVALID_CLUSTER_NODES";
|
|
187
|
+
ErrorCode2["CFG_INVALID_SENTINEL"] = "CFG_INVALID_SENTINEL";
|
|
188
|
+
ErrorCode2["CFG_INVALID_TLS"] = "CFG_INVALID_TLS";
|
|
189
|
+
ErrorCode2["CACHE_KEY_INVALID"] = "CACHE_KEY_INVALID";
|
|
190
|
+
ErrorCode2["CACHE_KEY_TOO_LONG"] = "CACHE_KEY_TOO_LONG";
|
|
191
|
+
ErrorCode2["CACHE_SERIALIZATION_FAILED"] = "CACHE_SERIALIZATION_FAILED";
|
|
192
|
+
ErrorCode2["CACHE_DESERIALIZATION_FAILED"] = "CACHE_DESERIALIZATION_FAILED";
|
|
193
|
+
ErrorCode2["CACHE_TAG_INDEX_CORRUPTED"] = "CACHE_TAG_INDEX_CORRUPTED";
|
|
194
|
+
ErrorCode2["CACHE_STAMPEDE_TIMEOUT"] = "CACHE_STAMPEDE_TIMEOUT";
|
|
195
|
+
ErrorCode2["CACHE_LOADER_FAILED"] = "CACHE_LOADER_FAILED";
|
|
196
|
+
ErrorCode2["CACHE_L1_ERROR"] = "CACHE_L1_ERROR";
|
|
197
|
+
ErrorCode2["CACHE_L2_ERROR"] = "CACHE_L2_ERROR";
|
|
198
|
+
ErrorCode2["CACHE_SET_FAILED"] = "CACHE_SET_FAILED";
|
|
199
|
+
ErrorCode2["CACHE_DELETE_FAILED"] = "CACHE_DELETE_FAILED";
|
|
200
|
+
ErrorCode2["CACHE_CLEAR_FAILED"] = "CACHE_CLEAR_FAILED";
|
|
201
|
+
ErrorCode2["CACHE_OPERATION_FAILED"] = "CACHE_OPERATION_FAILED";
|
|
202
|
+
ErrorCode2["CACHE_OPERATION_TIMEOUT"] = "CACHE_OPERATION_TIMEOUT";
|
|
203
|
+
ErrorCode2["CACHE_TAG_INVALIDATION_FAILED"] = "CACHE_TAG_INVALIDATION_FAILED";
|
|
204
|
+
ErrorCode2["LOCK_ACQUISITION_FAILED"] = "LOCK_ACQUISITION_FAILED";
|
|
205
|
+
ErrorCode2["LOCK_ACQUISITION_TIMEOUT"] = "LOCK_ACQUISITION_TIMEOUT";
|
|
206
|
+
ErrorCode2["LOCK_EXTENSION_FAILED"] = "LOCK_EXTENSION_FAILED";
|
|
207
|
+
ErrorCode2["LOCK_RELEASE_FAILED"] = "LOCK_RELEASE_FAILED";
|
|
208
|
+
ErrorCode2["LOCK_NOT_OWNED"] = "LOCK_NOT_OWNED";
|
|
209
|
+
ErrorCode2["LOCK_EXPIRED"] = "LOCK_EXPIRED";
|
|
210
|
+
ErrorCode2["RATE_LIMIT_EXCEEDED"] = "RATE_LIMIT_EXCEEDED";
|
|
211
|
+
ErrorCode2["RATE_LIMIT_SCRIPT_ERROR"] = "RATE_LIMIT_SCRIPT_ERROR";
|
|
212
|
+
ErrorCode2["IDEMPOTENCY_KEY_INVALID"] = "IDEMPOTENCY_KEY_INVALID";
|
|
213
|
+
ErrorCode2["IDEMPOTENCY_IN_PROGRESS"] = "IDEMPOTENCY_IN_PROGRESS";
|
|
214
|
+
ErrorCode2["IDEMPOTENCY_PREVIOUS_FAILED"] = "IDEMPOTENCY_PREVIOUS_FAILED";
|
|
215
|
+
ErrorCode2["STREAM_CONSUMER_GROUP_ERROR"] = "STREAM_CONSUMER_GROUP_ERROR";
|
|
216
|
+
ErrorCode2["STREAM_ACK_FAILED"] = "STREAM_ACK_FAILED";
|
|
217
|
+
ErrorCode2["STREAM_READ_FAILED"] = "STREAM_READ_FAILED";
|
|
218
|
+
ErrorCode2["PLUGIN_INVALID"] = "PLUGIN_INVALID";
|
|
219
|
+
ErrorCode2["PLUGIN_DUPLICATE"] = "PLUGIN_DUPLICATE";
|
|
220
|
+
ErrorCode2["PLUGIN_NOT_FOUND"] = "PLUGIN_NOT_FOUND";
|
|
221
|
+
ErrorCode2["PLUGIN_REGISTER_FAILED"] = "PLUGIN_REGISTER_FAILED";
|
|
222
|
+
ErrorCode2["PLUGIN_INIT_FAILED"] = "PLUGIN_INIT_FAILED";
|
|
223
|
+
ErrorCode2["PLUGIN_CIRCULAR_DEPENDENCY"] = "PLUGIN_CIRCULAR_DEPENDENCY";
|
|
224
|
+
ErrorCode2["PLUGIN_DEPENDENCY_MISSING"] = "PLUGIN_DEPENDENCY_MISSING";
|
|
225
|
+
ErrorCode2["CLIENT_NOT_FOUND"] = "CLIENT_NOT_FOUND";
|
|
226
|
+
ErrorCode2["CLIENT_ALREADY_EXISTS"] = "CLIENT_ALREADY_EXISTS";
|
|
227
|
+
ErrorCode2["NOT_INITIALIZED"] = "NOT_INITIALIZED";
|
|
228
|
+
ErrorCode2["OPERATION_FAILED"] = "OPERATION_FAILED";
|
|
229
|
+
ErrorCode2["OPERATION_TIMEOUT"] = "OPERATION_TIMEOUT";
|
|
230
|
+
ErrorCode2["SERIALIZATION_FAILED"] = "SERIALIZATION_FAILED";
|
|
231
|
+
ErrorCode2["VALIDATION_FAILED"] = "VALIDATION_FAILED";
|
|
232
|
+
ErrorCode2["UNKNOWN"] = "UNKNOWN";
|
|
233
|
+
return ErrorCode2;
|
|
234
|
+
})(ErrorCode || {});
|
|
235
|
+
function isErrorCode(value) {
|
|
236
|
+
return Object.values(ErrorCode).includes(value);
|
|
237
|
+
}
|
|
238
|
+
function getErrorDomain(code) {
|
|
239
|
+
const parts = code.split("_");
|
|
240
|
+
return parts[0] || "UNKNOWN";
|
|
241
|
+
}
|
|
242
|
+
function isErrorDomain(code, domain) {
|
|
243
|
+
return getErrorDomain(code) === domain;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// src/errors/base.error.ts
|
|
247
|
+
var RedisXError = class _RedisXError extends Error {
|
|
248
|
+
/**
|
|
249
|
+
* Creates a new RedisXError.
|
|
250
|
+
*
|
|
251
|
+
* @param message - Human-readable error message
|
|
252
|
+
* @param code - Error code for programmatic handling
|
|
253
|
+
* @param cause - Original error that caused this error
|
|
254
|
+
* @param context - Additional context for debugging
|
|
255
|
+
*/
|
|
256
|
+
constructor(message, code, cause, context) {
|
|
257
|
+
super(message);
|
|
258
|
+
this.code = code;
|
|
259
|
+
this.cause = cause;
|
|
260
|
+
this.context = context;
|
|
261
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
262
|
+
this.name = this.constructor.name;
|
|
263
|
+
this.timestamp = /* @__PURE__ */ new Date();
|
|
264
|
+
Error.captureStackTrace(this, this.constructor);
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Timestamp when error was created.
|
|
268
|
+
*/
|
|
269
|
+
timestamp;
|
|
270
|
+
/**
|
|
271
|
+
* Checks if error is of specific code.
|
|
272
|
+
*
|
|
273
|
+
* @param code - Error code to check
|
|
274
|
+
* @returns True if error matches code
|
|
275
|
+
*
|
|
276
|
+
* @example
|
|
277
|
+
* ```typescript
|
|
278
|
+
* if (error.is(ErrorCode.CONN_FAILED)) {
|
|
279
|
+
* // Handle connection error
|
|
280
|
+
* }
|
|
281
|
+
* ```
|
|
282
|
+
*/
|
|
283
|
+
is(code) {
|
|
284
|
+
return this.code === code;
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Checks if error is any of specified codes.
|
|
288
|
+
*
|
|
289
|
+
* @param codes - Array of error codes to check
|
|
290
|
+
* @returns True if error matches any code
|
|
291
|
+
*
|
|
292
|
+
* @example
|
|
293
|
+
* ```typescript
|
|
294
|
+
* if (error.isAnyOf([ErrorCode.LOCK_EXPIRED, ErrorCode.LOCK_NOT_OWNED])) {
|
|
295
|
+
* // Handle lock error
|
|
296
|
+
* }
|
|
297
|
+
* ```
|
|
298
|
+
*/
|
|
299
|
+
isAnyOf(codes) {
|
|
300
|
+
return codes.includes(this.code);
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Serializes error to JSON for logging.
|
|
304
|
+
*
|
|
305
|
+
* @returns JSON representation of error
|
|
306
|
+
*
|
|
307
|
+
* @example
|
|
308
|
+
* ```typescript
|
|
309
|
+
* logger.error('Operation failed', error.toJSON());
|
|
310
|
+
* ```
|
|
311
|
+
*/
|
|
312
|
+
toJSON() {
|
|
313
|
+
return {
|
|
314
|
+
name: this.name,
|
|
315
|
+
message: this.message,
|
|
316
|
+
code: this.code,
|
|
317
|
+
timestamp: this.timestamp.toISOString(),
|
|
318
|
+
context: this.context,
|
|
319
|
+
cause: this.cause ? {
|
|
320
|
+
name: this.cause.name,
|
|
321
|
+
message: this.cause.message,
|
|
322
|
+
stack: this.cause.stack
|
|
323
|
+
} : void 0,
|
|
324
|
+
stack: this.stack
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Creates error string for logging.
|
|
329
|
+
*
|
|
330
|
+
* @returns Formatted error string
|
|
331
|
+
*/
|
|
332
|
+
toString() {
|
|
333
|
+
let str = `${this.name} [${this.code}]: ${this.message}`;
|
|
334
|
+
if (this.cause) {
|
|
335
|
+
str += `
|
|
336
|
+
Caused by: ${this.cause.message}`;
|
|
337
|
+
}
|
|
338
|
+
if (this.context && Object.keys(this.context).length > 0) {
|
|
339
|
+
str += `
|
|
340
|
+
Context: ${JSON.stringify(this.context)}`;
|
|
341
|
+
}
|
|
342
|
+
return str;
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Wraps unknown error in RedisXError.
|
|
346
|
+
*
|
|
347
|
+
* @param error - Error to wrap
|
|
348
|
+
* @param code - Error code to assign
|
|
349
|
+
* @returns RedisXError instance
|
|
350
|
+
*
|
|
351
|
+
* @example
|
|
352
|
+
* ```typescript
|
|
353
|
+
* try {
|
|
354
|
+
* await someOperation();
|
|
355
|
+
* } catch (err) {
|
|
356
|
+
* throw RedisXError.wrap(err, ErrorCode.CACHE_LOADER_FAILED);
|
|
357
|
+
* }
|
|
358
|
+
* ```
|
|
359
|
+
*/
|
|
360
|
+
static wrap(error, code = "UNKNOWN" /* UNKNOWN */) {
|
|
361
|
+
if (error instanceof _RedisXError) {
|
|
362
|
+
return error;
|
|
363
|
+
}
|
|
364
|
+
if (error instanceof Error) {
|
|
365
|
+
return new GenericRedisXError(error.message, code, error);
|
|
366
|
+
}
|
|
367
|
+
return new GenericRedisXError(String(error), code, void 0, { originalError: error });
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Checks if value is a RedisXError.
|
|
371
|
+
*
|
|
372
|
+
* @param error - Value to check
|
|
373
|
+
* @returns True if value is RedisXError
|
|
374
|
+
*/
|
|
375
|
+
static isRedisXError(error) {
|
|
376
|
+
return error instanceof _RedisXError;
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
var GenericRedisXError = class extends RedisXError {
|
|
380
|
+
constructor(message, code, cause, context) {
|
|
381
|
+
super(message, code, cause, context);
|
|
382
|
+
}
|
|
383
|
+
};
|
|
384
|
+
|
|
385
|
+
// src/errors/connection.errors.ts
|
|
386
|
+
var RedisConnectionError = class _RedisConnectionError extends RedisXError {
|
|
387
|
+
constructor(message, code, host, port, cause, context) {
|
|
388
|
+
super(message, code, cause, {
|
|
389
|
+
...context,
|
|
390
|
+
host,
|
|
391
|
+
port
|
|
392
|
+
});
|
|
393
|
+
this.host = host;
|
|
394
|
+
this.port = port;
|
|
395
|
+
}
|
|
396
|
+
/**
|
|
397
|
+
* Creates connection error with default code.
|
|
398
|
+
*/
|
|
399
|
+
static create(message, host, port, cause) {
|
|
400
|
+
return new _RedisConnectionError(message, "CONN_FAILED" /* CONN_FAILED */, host, port, cause);
|
|
401
|
+
}
|
|
402
|
+
};
|
|
403
|
+
var RedisTimeoutError = class extends RedisConnectionError {
|
|
404
|
+
constructor(operation, timeoutMs, host, port, cause) {
|
|
405
|
+
const message = operation === "Connection" ? `Connection to ${host}:${port} timed out after ${timeoutMs}ms` : `Operation "${operation}" timed out after ${timeoutMs}ms`;
|
|
406
|
+
super(message, operation === "Connection" ? "CONN_TIMEOUT" /* CONN_TIMEOUT */ : "OP_TIMEOUT" /* OP_TIMEOUT */, host, port, cause, { operation, timeoutMs });
|
|
407
|
+
this.operation = operation;
|
|
408
|
+
this.timeoutMs = timeoutMs;
|
|
409
|
+
}
|
|
410
|
+
};
|
|
411
|
+
var RedisClusterError = class _RedisClusterError extends RedisConnectionError {
|
|
412
|
+
constructor(message, code, host, port, cause, context) {
|
|
413
|
+
super(message, code, host, port, cause, context);
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Creates cluster down error.
|
|
417
|
+
*/
|
|
418
|
+
static clusterDown(host, port) {
|
|
419
|
+
return new _RedisClusterError("Redis cluster is down or unreachable", "CONN_CLUSTER_DOWN" /* CONN_CLUSTER_DOWN */, host, port);
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* Creates MOVED redirection error.
|
|
423
|
+
*/
|
|
424
|
+
static moved(slot, targetHost, targetPort, cause) {
|
|
425
|
+
return new _RedisClusterError(`Slot ${slot} moved to ${targetHost}:${targetPort}`, "CONN_CLUSTER_MOVED" /* CONN_CLUSTER_MOVED */, targetHost, targetPort, cause, { slot, targetHost, targetPort });
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Creates ASK redirection error.
|
|
429
|
+
*/
|
|
430
|
+
static ask(slot, targetHost, targetPort, cause) {
|
|
431
|
+
return new _RedisClusterError(`ASK redirection for slot ${slot} to ${targetHost}:${targetPort}`, "CONN_CLUSTER_ASK" /* CONN_CLUSTER_ASK */, targetHost, targetPort, cause, { slot, targetHost, targetPort });
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* Creates generic cluster error.
|
|
435
|
+
*/
|
|
436
|
+
static generic(message, host, port, cause) {
|
|
437
|
+
return new _RedisClusterError(message, "CONN_CLUSTER_ERROR" /* CONN_CLUSTER_ERROR */, host, port, cause);
|
|
438
|
+
}
|
|
439
|
+
};
|
|
440
|
+
var RedisSentinelError = class _RedisSentinelError extends RedisConnectionError {
|
|
441
|
+
constructor(message, code, masterName, host, port, cause, context) {
|
|
442
|
+
super(message, code, host, port, cause, {
|
|
443
|
+
...context,
|
|
444
|
+
masterName
|
|
445
|
+
});
|
|
446
|
+
this.masterName = masterName;
|
|
447
|
+
}
|
|
448
|
+
/**
|
|
449
|
+
* Creates no master found error.
|
|
450
|
+
*/
|
|
451
|
+
static noMaster(masterName, sentinels) {
|
|
452
|
+
return new _RedisSentinelError(`No master found for "${masterName}"`, "CONN_SENTINEL_NO_MASTER" /* CONN_SENTINEL_NO_MASTER */, masterName, void 0, void 0, void 0, { sentinels });
|
|
453
|
+
}
|
|
454
|
+
/**
|
|
455
|
+
* Creates sentinel failover error.
|
|
456
|
+
*/
|
|
457
|
+
static failover(masterName, oldMaster, newMaster) {
|
|
458
|
+
return new _RedisSentinelError(`Sentinel failover in progress for "${masterName}"`, "CONN_SENTINEL_FAILOVER" /* CONN_SENTINEL_FAILOVER */, masterName, void 0, void 0, void 0, { oldMaster, newMaster });
|
|
459
|
+
}
|
|
460
|
+
/**
|
|
461
|
+
* Creates generic sentinel error.
|
|
462
|
+
*/
|
|
463
|
+
static generic(message, masterName, cause) {
|
|
464
|
+
return new _RedisSentinelError(message, "CONN_SENTINEL_ERROR" /* CONN_SENTINEL_ERROR */, masterName, void 0, void 0, cause);
|
|
465
|
+
}
|
|
466
|
+
};
|
|
467
|
+
var RedisAuthError = class extends RedisConnectionError {
|
|
468
|
+
constructor(message = "Redis authentication failed", host, port, cause) {
|
|
469
|
+
super(message, "CONN_AUTH_FAILED" /* CONN_AUTH_FAILED */, host, port, cause);
|
|
470
|
+
}
|
|
471
|
+
};
|
|
472
|
+
var RedisTLSError = class extends RedisConnectionError {
|
|
473
|
+
constructor(message = "TLS/SSL connection failed", host, port, cause) {
|
|
474
|
+
super(message, "CONN_TLS_ERROR" /* CONN_TLS_ERROR */, host, port, cause);
|
|
475
|
+
}
|
|
476
|
+
};
|
|
477
|
+
var RedisMaxRetriesError = class extends RedisConnectionError {
|
|
478
|
+
constructor(maxRetries, host, port, cause) {
|
|
479
|
+
super(`Maximum connection retries (${maxRetries}) exceeded`, "CONN_MAX_RETRIES" /* CONN_MAX_RETRIES */, host, port, cause, { maxRetries });
|
|
480
|
+
this.maxRetries = maxRetries;
|
|
481
|
+
}
|
|
482
|
+
};
|
|
483
|
+
var RedisPoolExhaustedError = class extends RedisConnectionError {
|
|
484
|
+
constructor(poolSize, waitingClients, cause) {
|
|
485
|
+
super(`Connection pool exhausted (size: ${poolSize}, waiting: ${waitingClients})`, "CONN_POOL_EXHAUSTED" /* CONN_POOL_EXHAUSTED */, void 0, void 0, cause, { poolSize, waitingClients });
|
|
486
|
+
this.poolSize = poolSize;
|
|
487
|
+
this.waitingClients = waitingClients;
|
|
488
|
+
}
|
|
489
|
+
};
|
|
490
|
+
|
|
491
|
+
// src/errors/operation.errors.ts
|
|
492
|
+
var RedisOperationError = class _RedisOperationError extends RedisXError {
|
|
493
|
+
constructor(message, code, command, cause, context) {
|
|
494
|
+
super(message, code, cause, {
|
|
495
|
+
...context,
|
|
496
|
+
command
|
|
497
|
+
});
|
|
498
|
+
this.command = command;
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
501
|
+
* Creates operation error with default code.
|
|
502
|
+
*/
|
|
503
|
+
static create(message, command, cause) {
|
|
504
|
+
return new _RedisOperationError(message, "OP_FAILED" /* OP_FAILED */, command, cause);
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* Creates operation error from command and arguments.
|
|
508
|
+
*/
|
|
509
|
+
static fromCommand(command, args, cause) {
|
|
510
|
+
return new _RedisOperationError(`Command "${command}" failed: ${cause?.message ?? "Unknown error"}`, "OP_FAILED" /* OP_FAILED */, command, cause, { argsCount: args.length });
|
|
511
|
+
}
|
|
512
|
+
};
|
|
513
|
+
var RedisKeyNotFoundError = class extends RedisOperationError {
|
|
514
|
+
constructor(key, command, cause) {
|
|
515
|
+
super(`Key "${key}" not found`, "OP_KEY_NOT_FOUND" /* OP_KEY_NOT_FOUND */, command, cause, { key });
|
|
516
|
+
this.key = key;
|
|
517
|
+
}
|
|
518
|
+
};
|
|
519
|
+
var RedisTypeMismatchError = class _RedisTypeMismatchError extends RedisOperationError {
|
|
520
|
+
constructor(key, expected, actual, command, cause) {
|
|
521
|
+
super(`Type mismatch for key "${key}": expected ${expected}, got ${actual}`, "OP_TYPE_MISMATCH" /* OP_TYPE_MISMATCH */, command, cause, { key, expected, actual });
|
|
522
|
+
this.key = key;
|
|
523
|
+
this.expected = expected;
|
|
524
|
+
this.actual = actual;
|
|
525
|
+
}
|
|
526
|
+
/**
|
|
527
|
+
* Creates type mismatch from Redis WRONGTYPE error.
|
|
528
|
+
*/
|
|
529
|
+
static fromRedisError(key, command, cause) {
|
|
530
|
+
const message = cause.message.toLowerCase();
|
|
531
|
+
let expected = "unknown";
|
|
532
|
+
const actual = "unknown";
|
|
533
|
+
if (message.includes("wrongtype")) {
|
|
534
|
+
if (command.startsWith("L") || command.startsWith("R") || command.startsWith("BLPOP")) {
|
|
535
|
+
expected = "list";
|
|
536
|
+
} else if (command.startsWith("S") && !command.startsWith("SET")) {
|
|
537
|
+
expected = "set";
|
|
538
|
+
} else if (command.startsWith("Z")) {
|
|
539
|
+
expected = "zset";
|
|
540
|
+
} else if (command.startsWith("H")) {
|
|
541
|
+
expected = "hash";
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
return new _RedisTypeMismatchError(key, expected, actual, command, cause);
|
|
545
|
+
}
|
|
546
|
+
};
|
|
547
|
+
var RedisScriptError = class _RedisScriptError extends RedisOperationError {
|
|
548
|
+
constructor(message, scriptSha, cause, context) {
|
|
549
|
+
super(message, "OP_SCRIPT_ERROR" /* OP_SCRIPT_ERROR */, "EVALSHA", cause, {
|
|
550
|
+
...context,
|
|
551
|
+
scriptSha
|
|
552
|
+
});
|
|
553
|
+
this.scriptSha = scriptSha;
|
|
554
|
+
}
|
|
555
|
+
/**
|
|
556
|
+
* Creates script timeout error.
|
|
557
|
+
*/
|
|
558
|
+
static timeout(timeoutMs, scriptSha) {
|
|
559
|
+
return new _RedisScriptError(`Script execution timed out after ${timeoutMs}ms`, scriptSha, void 0, { timeoutMs });
|
|
560
|
+
}
|
|
561
|
+
/**
|
|
562
|
+
* Creates script not found error (NOSCRIPT).
|
|
563
|
+
*/
|
|
564
|
+
static notFound(scriptSha) {
|
|
565
|
+
return new _RedisScriptError(`Script not found in Redis cache: ${scriptSha}`, scriptSha, void 0, { reason: "NOSCRIPT" });
|
|
566
|
+
}
|
|
567
|
+
};
|
|
568
|
+
var RedisTransactionError = class _RedisTransactionError extends RedisOperationError {
|
|
569
|
+
constructor(message, cause, context) {
|
|
570
|
+
super(message, "OP_TRANSACTION_FAILED" /* OP_TRANSACTION_FAILED */, "EXEC", cause, context);
|
|
571
|
+
}
|
|
572
|
+
/**
|
|
573
|
+
* Creates transaction aborted error (WATCH failure).
|
|
574
|
+
*/
|
|
575
|
+
static aborted() {
|
|
576
|
+
return new _RedisTransactionError("Transaction aborted: watched key was modified", void 0, { reason: "WATCH" });
|
|
577
|
+
}
|
|
578
|
+
};
|
|
579
|
+
var RedisPipelineError = class extends RedisOperationError {
|
|
580
|
+
constructor(message, totalCommands, failedCommands, cause) {
|
|
581
|
+
super(message, "OP_PIPELINE_FAILED" /* OP_PIPELINE_FAILED */, "PIPELINE", cause, { totalCommands, failedCommands });
|
|
582
|
+
this.totalCommands = totalCommands;
|
|
583
|
+
this.failedCommands = failedCommands;
|
|
584
|
+
}
|
|
585
|
+
};
|
|
586
|
+
var RedisInvalidArgsError = class extends RedisOperationError {
|
|
587
|
+
constructor(command, reason, args) {
|
|
588
|
+
super(`Invalid arguments for ${command}: ${reason}`, "OP_INVALID_ARGS" /* OP_INVALID_ARGS */, command, void 0, { args, reason });
|
|
589
|
+
this.args = args;
|
|
590
|
+
}
|
|
591
|
+
};
|
|
592
|
+
var RedisOutOfMemoryError = class extends RedisOperationError {
|
|
593
|
+
constructor(command, key, cause) {
|
|
594
|
+
super(`Redis out of memory while executing ${command}${key ? ` on key "${key}"` : ""}`, "OP_OUT_OF_MEMORY" /* OP_OUT_OF_MEMORY */, command, cause, { key });
|
|
595
|
+
this.key = key;
|
|
596
|
+
}
|
|
597
|
+
};
|
|
598
|
+
var RedisReadOnlyError = class extends RedisOperationError {
|
|
599
|
+
constructor(command, key, cause) {
|
|
600
|
+
super(`Cannot write to read-only replica: ${command}${key ? ` (key: ${key})` : ""}`, "OP_READONLY" /* OP_READONLY */, command, cause, { key });
|
|
601
|
+
this.key = key;
|
|
602
|
+
}
|
|
603
|
+
};
|
|
604
|
+
var RedisNotSupportedError = class extends RedisOperationError {
|
|
605
|
+
constructor(command, reason) {
|
|
606
|
+
super(`Command ${command} not supported: ${reason}`, "OP_NOT_SUPPORTED" /* OP_NOT_SUPPORTED */, command, void 0, { reason });
|
|
607
|
+
this.reason = reason;
|
|
608
|
+
}
|
|
609
|
+
};
|
|
610
|
+
var RedisNotConnectedError = class extends RedisOperationError {
|
|
611
|
+
constructor(command) {
|
|
612
|
+
super(`Cannot execute ${command ?? "operation"}: driver not connected`, "OP_NOT_CONNECTED" /* OP_NOT_CONNECTED */, command);
|
|
613
|
+
}
|
|
614
|
+
};
|
|
615
|
+
|
|
616
|
+
// src/errors/config.errors.ts
|
|
617
|
+
var RedisConfigError = class _RedisConfigError extends RedisXError {
|
|
618
|
+
constructor(message, code, cause, context) {
|
|
619
|
+
super(message, code, cause, context);
|
|
620
|
+
}
|
|
621
|
+
/**
|
|
622
|
+
* Creates generic config error.
|
|
623
|
+
*/
|
|
624
|
+
static create(message, cause, context) {
|
|
625
|
+
return new _RedisConfigError(message, "CFG_INVALID" /* CFG_INVALID */, cause, context);
|
|
626
|
+
}
|
|
627
|
+
/**
|
|
628
|
+
* Creates missing required field error.
|
|
629
|
+
*/
|
|
630
|
+
static missingRequired(field, parent) {
|
|
631
|
+
const path = parent ? `${parent}.${field}` : field;
|
|
632
|
+
return new _RedisConfigError(`Missing required configuration field: ${path}`, "CFG_MISSING_REQUIRED" /* CFG_MISSING_REQUIRED */, void 0, { field, parent, path });
|
|
633
|
+
}
|
|
634
|
+
/**
|
|
635
|
+
* Creates invalid connection type error.
|
|
636
|
+
*/
|
|
637
|
+
static invalidConnectionType(type, validTypes) {
|
|
638
|
+
return new _RedisConfigError(`Invalid connection type "${type}". Must be one of: ${validTypes.join(", ")}`, "CFG_INVALID_CONNECTION_TYPE" /* CFG_INVALID_CONNECTION_TYPE */, void 0, { type, validTypes });
|
|
639
|
+
}
|
|
640
|
+
/**
|
|
641
|
+
* Creates invalid host/port error.
|
|
642
|
+
*/
|
|
643
|
+
static invalidHostPort(host, port, reason) {
|
|
644
|
+
const message = reason ? `Invalid host/port configuration: ${reason}` : `Invalid host "${host}" or port "${port}"`;
|
|
645
|
+
return new _RedisConfigError(message, "CFG_INVALID_HOST_PORT" /* CFG_INVALID_HOST_PORT */, void 0, { host, port, reason });
|
|
646
|
+
}
|
|
647
|
+
/**
|
|
648
|
+
* Creates invalid database number error.
|
|
649
|
+
*/
|
|
650
|
+
static invalidDb(db, max = 15) {
|
|
651
|
+
return new _RedisConfigError(`Invalid database number ${db}. Must be between 0 and ${max}`, "CFG_INVALID_DB" /* CFG_INVALID_DB */, void 0, { db, max });
|
|
652
|
+
}
|
|
653
|
+
/**
|
|
654
|
+
* Creates invalid TTL error.
|
|
655
|
+
*/
|
|
656
|
+
static invalidTTL(ttl, reason) {
|
|
657
|
+
const message = reason ? `Invalid TTL ${ttl}: ${reason}` : `Invalid TTL ${ttl}. Must be a positive number`;
|
|
658
|
+
return new _RedisConfigError(message, "CFG_INVALID_TTL" /* CFG_INVALID_TTL */, void 0, { ttl, reason });
|
|
659
|
+
}
|
|
660
|
+
/**
|
|
661
|
+
* Creates invalid timeout error.
|
|
662
|
+
*/
|
|
663
|
+
static invalidTimeout(timeout, field, reason) {
|
|
664
|
+
const message = reason ? `Invalid ${field} timeout ${timeout}: ${reason}` : `Invalid ${field} timeout ${timeout}. Must be a positive number`;
|
|
665
|
+
return new _RedisConfigError(message, "CFG_INVALID_TIMEOUT" /* CFG_INVALID_TIMEOUT */, void 0, { timeout, field, reason });
|
|
666
|
+
}
|
|
667
|
+
/**
|
|
668
|
+
* Creates invalid retry configuration error.
|
|
669
|
+
*/
|
|
670
|
+
static invalidRetry(reason, context) {
|
|
671
|
+
return new _RedisConfigError(`Invalid retry configuration: ${reason}`, "CFG_INVALID_RETRY" /* CFG_INVALID_RETRY */, void 0, context);
|
|
672
|
+
}
|
|
673
|
+
/**
|
|
674
|
+
* Creates incompatible configuration error.
|
|
675
|
+
*/
|
|
676
|
+
static incompatible(option1, option2, reason) {
|
|
677
|
+
const message = reason ? `Incompatible configuration: ${option1} and ${option2} - ${reason}` : `Configuration options "${option1}" and "${option2}" are incompatible`;
|
|
678
|
+
return new _RedisConfigError(message, "CFG_INCOMPATIBLE" /* CFG_INCOMPATIBLE */, void 0, { option1, option2, reason });
|
|
679
|
+
}
|
|
680
|
+
/**
|
|
681
|
+
* Creates driver not supported error.
|
|
682
|
+
*/
|
|
683
|
+
static driverNotSupported(driver, supportedDrivers) {
|
|
684
|
+
return new _RedisConfigError(`Driver "${driver}" is not supported. Available drivers: ${supportedDrivers.join(", ")}`, "CFG_DRIVER_NOT_SUPPORTED" /* CFG_DRIVER_NOT_SUPPORTED */, void 0, { driver, supportedDrivers });
|
|
685
|
+
}
|
|
686
|
+
/**
|
|
687
|
+
* Creates invalid cluster nodes error.
|
|
688
|
+
*/
|
|
689
|
+
static invalidClusterNodes(reason, nodes) {
|
|
690
|
+
return new _RedisConfigError(`Invalid cluster nodes configuration: ${reason}`, "CFG_INVALID_CLUSTER_NODES" /* CFG_INVALID_CLUSTER_NODES */, void 0, { reason, nodes, nodeCount: nodes?.length });
|
|
691
|
+
}
|
|
692
|
+
/**
|
|
693
|
+
* Creates invalid sentinel configuration error.
|
|
694
|
+
*/
|
|
695
|
+
static invalidSentinel(reason, context) {
|
|
696
|
+
return new _RedisConfigError(`Invalid sentinel configuration: ${reason}`, "CFG_INVALID_SENTINEL" /* CFG_INVALID_SENTINEL */, void 0, context);
|
|
697
|
+
}
|
|
698
|
+
/**
|
|
699
|
+
* Creates invalid TLS configuration error.
|
|
700
|
+
*/
|
|
701
|
+
static invalidTLS(reason, context) {
|
|
702
|
+
return new _RedisConfigError(`Invalid TLS configuration: ${reason}`, "CFG_INVALID_TLS" /* CFG_INVALID_TLS */, void 0, context);
|
|
703
|
+
}
|
|
704
|
+
};
|
|
705
|
+
var RedisValidationError = class _RedisValidationError extends RedisConfigError {
|
|
706
|
+
constructor(field, errors, cause) {
|
|
707
|
+
const errorMessages = errors.map((e) => `${e.field}: ${e.message}`).join("; ");
|
|
708
|
+
super(`Validation failed for "${field}": ${errorMessages}`, "CFG_VALIDATION_FAILED" /* CFG_VALIDATION_FAILED */, cause, { field, errors, errorCount: errors.length });
|
|
709
|
+
this.field = field;
|
|
710
|
+
this.errors = errors;
|
|
711
|
+
}
|
|
712
|
+
/**
|
|
713
|
+
* Creates validation error from single field.
|
|
714
|
+
*/
|
|
715
|
+
static single(field, message, value) {
|
|
716
|
+
return new _RedisValidationError(field, [{ field, message, value }]);
|
|
717
|
+
}
|
|
718
|
+
/**
|
|
719
|
+
* Creates validation error from multiple fields.
|
|
720
|
+
*/
|
|
721
|
+
static multiple(parent, errors) {
|
|
722
|
+
return new _RedisValidationError(parent, errors);
|
|
723
|
+
}
|
|
724
|
+
/**
|
|
725
|
+
* Adds a validation error to the list.
|
|
726
|
+
*/
|
|
727
|
+
addError(field, message, value) {
|
|
728
|
+
this.errors.push({ field, message, value });
|
|
729
|
+
return this;
|
|
730
|
+
}
|
|
731
|
+
/**
|
|
732
|
+
* Checks if validation has errors.
|
|
733
|
+
*/
|
|
734
|
+
hasErrors() {
|
|
735
|
+
return this.errors.length > 0;
|
|
736
|
+
}
|
|
737
|
+
/**
|
|
738
|
+
* Gets all error messages.
|
|
739
|
+
*/
|
|
740
|
+
getMessages() {
|
|
741
|
+
return this.errors.map((e) => `${e.field}: ${e.message}`);
|
|
742
|
+
}
|
|
743
|
+
/**
|
|
744
|
+
* Gets errors for specific field.
|
|
745
|
+
*/
|
|
746
|
+
getFieldErrors(field) {
|
|
747
|
+
return this.errors.filter((e) => e.field === field);
|
|
748
|
+
}
|
|
749
|
+
};
|
|
750
|
+
var ValidationErrorCollector = class {
|
|
751
|
+
constructor(parent) {
|
|
752
|
+
this.parent = parent;
|
|
753
|
+
}
|
|
754
|
+
errors = [];
|
|
755
|
+
/**
|
|
756
|
+
* Adds a validation error.
|
|
757
|
+
*/
|
|
758
|
+
add(field, message, value) {
|
|
759
|
+
this.errors.push({ field, message, value });
|
|
760
|
+
return this;
|
|
761
|
+
}
|
|
762
|
+
/**
|
|
763
|
+
* Checks if there are any errors.
|
|
764
|
+
*/
|
|
765
|
+
hasErrors() {
|
|
766
|
+
return this.errors.length > 0;
|
|
767
|
+
}
|
|
768
|
+
/**
|
|
769
|
+
* Gets error count.
|
|
770
|
+
*/
|
|
771
|
+
count() {
|
|
772
|
+
return this.errors.length;
|
|
773
|
+
}
|
|
774
|
+
/**
|
|
775
|
+
* Throws if there are any errors.
|
|
776
|
+
*/
|
|
777
|
+
throwIfErrors() {
|
|
778
|
+
if (this.hasErrors()) {
|
|
779
|
+
throw new RedisValidationError(this.parent, this.errors);
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
/**
|
|
783
|
+
* Gets all errors.
|
|
784
|
+
*/
|
|
785
|
+
getErrors() {
|
|
786
|
+
return [...this.errors];
|
|
787
|
+
}
|
|
788
|
+
/**
|
|
789
|
+
* Clears all errors.
|
|
790
|
+
*/
|
|
791
|
+
clear() {
|
|
792
|
+
this.errors = [];
|
|
793
|
+
}
|
|
794
|
+
};
|
|
795
|
+
|
|
796
|
+
// src/shared/errors/driver.error.ts
|
|
797
|
+
var DriverError = class extends RedisConnectionError {
|
|
798
|
+
constructor(message, code, cause, context) {
|
|
799
|
+
super(message, code, void 0, void 0, cause, context);
|
|
800
|
+
}
|
|
801
|
+
};
|
|
802
|
+
var ConnectionError = class extends RedisConnectionError {
|
|
803
|
+
constructor(message, cause, host, port) {
|
|
804
|
+
super(message, "CONN_FAILED" /* CONN_FAILED */, host, port, cause);
|
|
805
|
+
this.host = host;
|
|
806
|
+
this.port = port;
|
|
807
|
+
}
|
|
808
|
+
};
|
|
809
|
+
var TimeoutError = class extends RedisTimeoutError {
|
|
810
|
+
constructor(operation, timeoutMs, cause) {
|
|
811
|
+
super(operation, timeoutMs, void 0, void 0, cause);
|
|
812
|
+
}
|
|
813
|
+
};
|
|
814
|
+
var CommandError = class extends RedisOperationError {
|
|
815
|
+
command;
|
|
816
|
+
args;
|
|
817
|
+
constructor(command, args, cause) {
|
|
818
|
+
const message = `Command "${command}" failed: ${cause?.message ?? "Unknown error"}`;
|
|
819
|
+
super(message, "OP_FAILED" /* OP_FAILED */, command, cause, { argsCount: args.length });
|
|
820
|
+
this.command = command;
|
|
821
|
+
this.args = args;
|
|
822
|
+
}
|
|
823
|
+
};
|
|
824
|
+
|
|
825
|
+
// src/driver/infrastructure/base.driver.ts
|
|
826
|
+
var BaseRedisDriver = class {
|
|
827
|
+
constructor(config, options) {
|
|
828
|
+
this.config = config;
|
|
829
|
+
this.eventEmitter = new import_events.default();
|
|
830
|
+
this.logger = new import_common.Logger(this.constructor.name);
|
|
831
|
+
this.enableLogging = options?.enableLogging ?? false;
|
|
832
|
+
}
|
|
833
|
+
eventEmitter;
|
|
834
|
+
logger;
|
|
835
|
+
connected = false;
|
|
836
|
+
connecting = false;
|
|
837
|
+
enableLogging;
|
|
838
|
+
async connect() {
|
|
839
|
+
if (this.connected) {
|
|
840
|
+
this.log("Already connected, skipping connect");
|
|
841
|
+
return;
|
|
842
|
+
}
|
|
843
|
+
if (this.connecting) {
|
|
844
|
+
this.log("Connection in progress, waiting...");
|
|
845
|
+
return new Promise((resolve, reject) => {
|
|
846
|
+
const onConnect = () => {
|
|
847
|
+
cleanup();
|
|
848
|
+
resolve();
|
|
849
|
+
};
|
|
850
|
+
const onError = (error) => {
|
|
851
|
+
cleanup();
|
|
852
|
+
reject(error);
|
|
853
|
+
};
|
|
854
|
+
const cleanup = () => {
|
|
855
|
+
this.off("ready" /* READY */, onConnect);
|
|
856
|
+
this.off("error" /* ERROR */, onError);
|
|
857
|
+
};
|
|
858
|
+
this.once("ready" /* READY */, onConnect);
|
|
859
|
+
this.once("error" /* ERROR */, onError);
|
|
860
|
+
});
|
|
861
|
+
}
|
|
862
|
+
try {
|
|
863
|
+
this.connecting = true;
|
|
864
|
+
this.log("Connecting to Redis...");
|
|
865
|
+
this.emit("connect" /* CONNECT */);
|
|
866
|
+
await this.doConnect();
|
|
867
|
+
this.connected = true;
|
|
868
|
+
this.connecting = false;
|
|
869
|
+
this.log("Connected to Redis");
|
|
870
|
+
this.emit("ready" /* READY */);
|
|
871
|
+
} catch (error) {
|
|
872
|
+
this.connecting = false;
|
|
873
|
+
this.log("Connection failed", error);
|
|
874
|
+
this.emit("error" /* ERROR */, error);
|
|
875
|
+
throw error;
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
async disconnect() {
|
|
879
|
+
if (!this.connected) {
|
|
880
|
+
this.log("Not connected, skipping disconnect");
|
|
881
|
+
return;
|
|
882
|
+
}
|
|
883
|
+
try {
|
|
884
|
+
this.log("Disconnecting from Redis...");
|
|
885
|
+
this.emit("disconnect" /* DISCONNECT */);
|
|
886
|
+
await this.doDisconnect();
|
|
887
|
+
this.connected = false;
|
|
888
|
+
this.log("Disconnected from Redis");
|
|
889
|
+
this.emit("close" /* CLOSE */);
|
|
890
|
+
} catch (error) {
|
|
891
|
+
this.log("Disconnect failed", error);
|
|
892
|
+
this.emit("error" /* ERROR */, error);
|
|
893
|
+
throw error;
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
isConnected() {
|
|
897
|
+
return this.connected;
|
|
898
|
+
}
|
|
899
|
+
async ping(message) {
|
|
900
|
+
this.assertConnected();
|
|
901
|
+
const result = message ? await this.executeCommand("PING", message) : await this.executeCommand("PING");
|
|
902
|
+
return String(result);
|
|
903
|
+
}
|
|
904
|
+
async select(db) {
|
|
905
|
+
this.assertConnected();
|
|
906
|
+
await this.executeCommand("SELECT", db);
|
|
907
|
+
}
|
|
908
|
+
async get(key) {
|
|
909
|
+
this.assertConnected();
|
|
910
|
+
const result = await this.executeCommand("GET", key);
|
|
911
|
+
return result === null || result === void 0 ? null : String(result);
|
|
912
|
+
}
|
|
913
|
+
async set(key, value, options) {
|
|
914
|
+
this.assertConnected();
|
|
915
|
+
const args = [key, value];
|
|
916
|
+
if (options) {
|
|
917
|
+
if (options.ex !== void 0) {
|
|
918
|
+
args.push("EX", options.ex);
|
|
919
|
+
}
|
|
920
|
+
if (options.px !== void 0) {
|
|
921
|
+
args.push("PX", options.px);
|
|
922
|
+
}
|
|
923
|
+
if (options.exat !== void 0) {
|
|
924
|
+
args.push("EXAT", options.exat);
|
|
925
|
+
}
|
|
926
|
+
if (options.pxat !== void 0) {
|
|
927
|
+
args.push("PXAT", options.pxat);
|
|
928
|
+
}
|
|
929
|
+
if (options.nx) {
|
|
930
|
+
args.push("NX");
|
|
931
|
+
}
|
|
932
|
+
if (options.xx) {
|
|
933
|
+
args.push("XX");
|
|
934
|
+
}
|
|
935
|
+
if (options.get) {
|
|
936
|
+
args.push("GET");
|
|
937
|
+
}
|
|
938
|
+
if (options.keepttl) {
|
|
939
|
+
args.push("KEEPTTL");
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
const result = await this.executeCommand("SET", ...args);
|
|
943
|
+
return result === "OK" ? "OK" : null;
|
|
944
|
+
}
|
|
945
|
+
async mget(...keys) {
|
|
946
|
+
this.assertConnected();
|
|
947
|
+
const result = await this.executeCommand("MGET", ...keys);
|
|
948
|
+
return result.map((v) => v === null || v === void 0 ? null : String(v));
|
|
949
|
+
}
|
|
950
|
+
async mset(data) {
|
|
951
|
+
this.assertConnected();
|
|
952
|
+
const args = [];
|
|
953
|
+
for (const [key, value] of Object.entries(data)) {
|
|
954
|
+
args.push(key, value);
|
|
955
|
+
}
|
|
956
|
+
await this.executeCommand("MSET", ...args);
|
|
957
|
+
return "OK";
|
|
958
|
+
}
|
|
959
|
+
async setnx(key, value) {
|
|
960
|
+
this.assertConnected();
|
|
961
|
+
const result = await this.executeCommand("SETNX", key, value);
|
|
962
|
+
return Number(result);
|
|
963
|
+
}
|
|
964
|
+
async setex(key, seconds, value) {
|
|
965
|
+
this.assertConnected();
|
|
966
|
+
await this.executeCommand("SETEX", key, seconds, value);
|
|
967
|
+
return "OK";
|
|
968
|
+
}
|
|
969
|
+
async getdel(key) {
|
|
970
|
+
this.assertConnected();
|
|
971
|
+
const result = await this.executeCommand("GETDEL", key);
|
|
972
|
+
return result === null || result === void 0 ? null : String(result);
|
|
973
|
+
}
|
|
974
|
+
async getex(key, options) {
|
|
975
|
+
this.assertConnected();
|
|
976
|
+
const args = [key];
|
|
977
|
+
if (options.ex !== void 0) {
|
|
978
|
+
args.push("EX", options.ex);
|
|
979
|
+
}
|
|
980
|
+
if (options.px !== void 0) {
|
|
981
|
+
args.push("PX", options.px);
|
|
982
|
+
}
|
|
983
|
+
const result = await this.executeCommand("GETEX", ...args);
|
|
984
|
+
return result === null || result === void 0 ? null : String(result);
|
|
985
|
+
}
|
|
986
|
+
async incr(key) {
|
|
987
|
+
this.assertConnected();
|
|
988
|
+
const result = await this.executeCommand("INCR", key);
|
|
989
|
+
return Number(result);
|
|
990
|
+
}
|
|
991
|
+
async incrby(key, increment) {
|
|
992
|
+
this.assertConnected();
|
|
993
|
+
const result = await this.executeCommand("INCRBY", key, increment);
|
|
994
|
+
return Number(result);
|
|
995
|
+
}
|
|
996
|
+
async decr(key) {
|
|
997
|
+
this.assertConnected();
|
|
998
|
+
const result = await this.executeCommand("DECR", key);
|
|
999
|
+
return Number(result);
|
|
1000
|
+
}
|
|
1001
|
+
async decrby(key, decrement) {
|
|
1002
|
+
this.assertConnected();
|
|
1003
|
+
const result = await this.executeCommand("DECRBY", key, decrement);
|
|
1004
|
+
return Number(result);
|
|
1005
|
+
}
|
|
1006
|
+
async append(key, value) {
|
|
1007
|
+
this.assertConnected();
|
|
1008
|
+
const result = await this.executeCommand("APPEND", key, value);
|
|
1009
|
+
return Number(result);
|
|
1010
|
+
}
|
|
1011
|
+
async strlen(key) {
|
|
1012
|
+
this.assertConnected();
|
|
1013
|
+
const result = await this.executeCommand("STRLEN", key);
|
|
1014
|
+
return Number(result);
|
|
1015
|
+
}
|
|
1016
|
+
async incrbyfloat(key, increment) {
|
|
1017
|
+
this.assertConnected();
|
|
1018
|
+
const result = await this.executeCommand("INCRBYFLOAT", key, increment);
|
|
1019
|
+
return String(result);
|
|
1020
|
+
}
|
|
1021
|
+
async getrange(key, start, end) {
|
|
1022
|
+
this.assertConnected();
|
|
1023
|
+
const result = await this.executeCommand("GETRANGE", key, start, end);
|
|
1024
|
+
return String(result);
|
|
1025
|
+
}
|
|
1026
|
+
async setrange(key, offset, value) {
|
|
1027
|
+
this.assertConnected();
|
|
1028
|
+
const result = await this.executeCommand("SETRANGE", key, offset, value);
|
|
1029
|
+
return Number(result);
|
|
1030
|
+
}
|
|
1031
|
+
async msetnx(data) {
|
|
1032
|
+
this.assertConnected();
|
|
1033
|
+
const args = [];
|
|
1034
|
+
for (const [key, value] of Object.entries(data)) {
|
|
1035
|
+
args.push(key, value);
|
|
1036
|
+
}
|
|
1037
|
+
const result = await this.executeCommand("MSETNX", ...args);
|
|
1038
|
+
return Number(result);
|
|
1039
|
+
}
|
|
1040
|
+
async del(...keys) {
|
|
1041
|
+
this.assertConnected();
|
|
1042
|
+
const result = await this.executeCommand("DEL", ...keys);
|
|
1043
|
+
return Number(result);
|
|
1044
|
+
}
|
|
1045
|
+
async exists(...keys) {
|
|
1046
|
+
this.assertConnected();
|
|
1047
|
+
const result = await this.executeCommand("EXISTS", ...keys);
|
|
1048
|
+
return Number(result);
|
|
1049
|
+
}
|
|
1050
|
+
async expire(key, seconds) {
|
|
1051
|
+
this.assertConnected();
|
|
1052
|
+
const result = await this.executeCommand("EXPIRE", key, seconds);
|
|
1053
|
+
return Number(result);
|
|
1054
|
+
}
|
|
1055
|
+
async pexpire(key, milliseconds) {
|
|
1056
|
+
this.assertConnected();
|
|
1057
|
+
const result = await this.executeCommand("PEXPIRE", key, milliseconds);
|
|
1058
|
+
return Number(result);
|
|
1059
|
+
}
|
|
1060
|
+
async expireat(key, timestamp) {
|
|
1061
|
+
this.assertConnected();
|
|
1062
|
+
const result = await this.executeCommand("EXPIREAT", key, timestamp);
|
|
1063
|
+
return Number(result);
|
|
1064
|
+
}
|
|
1065
|
+
async ttl(key) {
|
|
1066
|
+
this.assertConnected();
|
|
1067
|
+
const result = await this.executeCommand("TTL", key);
|
|
1068
|
+
return Number(result);
|
|
1069
|
+
}
|
|
1070
|
+
async pttl(key) {
|
|
1071
|
+
this.assertConnected();
|
|
1072
|
+
const result = await this.executeCommand("PTTL", key);
|
|
1073
|
+
return Number(result);
|
|
1074
|
+
}
|
|
1075
|
+
async persist(key) {
|
|
1076
|
+
this.assertConnected();
|
|
1077
|
+
const result = await this.executeCommand("PERSIST", key);
|
|
1078
|
+
return Number(result);
|
|
1079
|
+
}
|
|
1080
|
+
async rename(key, newKey) {
|
|
1081
|
+
this.assertConnected();
|
|
1082
|
+
await this.executeCommand("RENAME", key, newKey);
|
|
1083
|
+
return "OK";
|
|
1084
|
+
}
|
|
1085
|
+
async renamenx(key, newKey) {
|
|
1086
|
+
this.assertConnected();
|
|
1087
|
+
const result = await this.executeCommand("RENAMENX", key, newKey);
|
|
1088
|
+
return Number(result);
|
|
1089
|
+
}
|
|
1090
|
+
async type(key) {
|
|
1091
|
+
this.assertConnected();
|
|
1092
|
+
const result = await this.executeCommand("TYPE", key);
|
|
1093
|
+
return String(result);
|
|
1094
|
+
}
|
|
1095
|
+
async scan(cursor, options) {
|
|
1096
|
+
this.assertConnected();
|
|
1097
|
+
const args = [cursor];
|
|
1098
|
+
if (options?.match) {
|
|
1099
|
+
args.push("MATCH", options.match);
|
|
1100
|
+
}
|
|
1101
|
+
if (options?.count) {
|
|
1102
|
+
args.push("COUNT", options.count);
|
|
1103
|
+
}
|
|
1104
|
+
if (options?.type) {
|
|
1105
|
+
args.push("TYPE", options.type);
|
|
1106
|
+
}
|
|
1107
|
+
const result = await this.executeCommand("SCAN", ...args);
|
|
1108
|
+
const [newCursor, keys] = result;
|
|
1109
|
+
return [String(newCursor), keys];
|
|
1110
|
+
}
|
|
1111
|
+
async unlink(...keys) {
|
|
1112
|
+
this.assertConnected();
|
|
1113
|
+
const result = await this.executeCommand("UNLINK", ...keys);
|
|
1114
|
+
return Number(result);
|
|
1115
|
+
}
|
|
1116
|
+
async copy(source, destination, options) {
|
|
1117
|
+
this.assertConnected();
|
|
1118
|
+
const args = [source, destination];
|
|
1119
|
+
if (options?.db !== void 0) {
|
|
1120
|
+
args.push("DB", options.db);
|
|
1121
|
+
}
|
|
1122
|
+
if (options?.replace) {
|
|
1123
|
+
args.push("REPLACE");
|
|
1124
|
+
}
|
|
1125
|
+
const result = await this.executeCommand("COPY", ...args);
|
|
1126
|
+
return Number(result);
|
|
1127
|
+
}
|
|
1128
|
+
async keys(pattern) {
|
|
1129
|
+
this.assertConnected();
|
|
1130
|
+
const result = await this.executeCommand("KEYS", pattern);
|
|
1131
|
+
return result;
|
|
1132
|
+
}
|
|
1133
|
+
async touch(...keys) {
|
|
1134
|
+
this.assertConnected();
|
|
1135
|
+
const result = await this.executeCommand("TOUCH", ...keys);
|
|
1136
|
+
return Number(result);
|
|
1137
|
+
}
|
|
1138
|
+
async object(subcommand, key) {
|
|
1139
|
+
this.assertConnected();
|
|
1140
|
+
const result = await this.executeCommand("OBJECT", subcommand, key);
|
|
1141
|
+
if (result === null) return null;
|
|
1142
|
+
if (subcommand === "ENCODING") return String(result);
|
|
1143
|
+
return Number(result);
|
|
1144
|
+
}
|
|
1145
|
+
async dump(key) {
|
|
1146
|
+
this.assertConnected();
|
|
1147
|
+
const result = await this.executeCommand("DUMP", key);
|
|
1148
|
+
return result === null ? null : String(result);
|
|
1149
|
+
}
|
|
1150
|
+
async restore(key, ttl, serializedValue, options) {
|
|
1151
|
+
this.assertConnected();
|
|
1152
|
+
const args = [key, ttl, serializedValue];
|
|
1153
|
+
if (options?.replace) {
|
|
1154
|
+
args.push("REPLACE");
|
|
1155
|
+
}
|
|
1156
|
+
if (options?.absttl) {
|
|
1157
|
+
args.push("ABSTTL");
|
|
1158
|
+
}
|
|
1159
|
+
if (options?.idletime !== void 0) {
|
|
1160
|
+
args.push("IDLETIME", options.idletime);
|
|
1161
|
+
}
|
|
1162
|
+
if (options?.freq !== void 0) {
|
|
1163
|
+
args.push("FREQ", options.freq);
|
|
1164
|
+
}
|
|
1165
|
+
await this.executeCommand("RESTORE", ...args);
|
|
1166
|
+
return "OK";
|
|
1167
|
+
}
|
|
1168
|
+
async time() {
|
|
1169
|
+
this.assertConnected();
|
|
1170
|
+
const result = await this.executeCommand("TIME");
|
|
1171
|
+
const [seconds, microseconds] = result;
|
|
1172
|
+
return [String(seconds), String(microseconds)];
|
|
1173
|
+
}
|
|
1174
|
+
async hget(key, field) {
|
|
1175
|
+
this.assertConnected();
|
|
1176
|
+
const result = await this.executeCommand("HGET", key, field);
|
|
1177
|
+
return result === null || result === void 0 ? null : String(result);
|
|
1178
|
+
}
|
|
1179
|
+
async hset(key, field, value) {
|
|
1180
|
+
this.assertConnected();
|
|
1181
|
+
const result = await this.executeCommand("HSET", key, field, value);
|
|
1182
|
+
return Number(result);
|
|
1183
|
+
}
|
|
1184
|
+
async hmset(key, data) {
|
|
1185
|
+
this.assertConnected();
|
|
1186
|
+
const args = [key];
|
|
1187
|
+
for (const [field, value] of Object.entries(data)) {
|
|
1188
|
+
args.push(field, value);
|
|
1189
|
+
}
|
|
1190
|
+
await this.executeCommand("HMSET", ...args);
|
|
1191
|
+
return "OK";
|
|
1192
|
+
}
|
|
1193
|
+
async hmget(key, ...fields) {
|
|
1194
|
+
this.assertConnected();
|
|
1195
|
+
const result = await this.executeCommand("HMGET", key, ...fields);
|
|
1196
|
+
return result.map((v) => v === null || v === void 0 ? null : String(v));
|
|
1197
|
+
}
|
|
1198
|
+
async hgetall(key) {
|
|
1199
|
+
this.assertConnected();
|
|
1200
|
+
const result = await this.executeCommand("HGETALL", key);
|
|
1201
|
+
return result;
|
|
1202
|
+
}
|
|
1203
|
+
async hdel(key, ...fields) {
|
|
1204
|
+
this.assertConnected();
|
|
1205
|
+
const result = await this.executeCommand("HDEL", key, ...fields);
|
|
1206
|
+
return Number(result);
|
|
1207
|
+
}
|
|
1208
|
+
async hexists(key, field) {
|
|
1209
|
+
this.assertConnected();
|
|
1210
|
+
const result = await this.executeCommand("HEXISTS", key, field);
|
|
1211
|
+
return Number(result);
|
|
1212
|
+
}
|
|
1213
|
+
async hkeys(key) {
|
|
1214
|
+
this.assertConnected();
|
|
1215
|
+
const result = await this.executeCommand("HKEYS", key);
|
|
1216
|
+
return result;
|
|
1217
|
+
}
|
|
1218
|
+
async hvals(key) {
|
|
1219
|
+
this.assertConnected();
|
|
1220
|
+
const result = await this.executeCommand("HVALS", key);
|
|
1221
|
+
return result;
|
|
1222
|
+
}
|
|
1223
|
+
async hlen(key) {
|
|
1224
|
+
this.assertConnected();
|
|
1225
|
+
const result = await this.executeCommand("HLEN", key);
|
|
1226
|
+
return Number(result);
|
|
1227
|
+
}
|
|
1228
|
+
async hincrby(key, field, increment) {
|
|
1229
|
+
this.assertConnected();
|
|
1230
|
+
const result = await this.executeCommand("HINCRBY", key, field, increment);
|
|
1231
|
+
return Number(result);
|
|
1232
|
+
}
|
|
1233
|
+
async hscan(key, cursor, options) {
|
|
1234
|
+
this.assertConnected();
|
|
1235
|
+
const args = [key, cursor];
|
|
1236
|
+
if (options?.match) {
|
|
1237
|
+
args.push("MATCH", options.match);
|
|
1238
|
+
}
|
|
1239
|
+
if (options?.count) {
|
|
1240
|
+
args.push("COUNT", options.count);
|
|
1241
|
+
}
|
|
1242
|
+
const result = await this.executeCommand("HSCAN", ...args);
|
|
1243
|
+
const [newCursor, fields] = result;
|
|
1244
|
+
return [String(newCursor), fields];
|
|
1245
|
+
}
|
|
1246
|
+
async hsetnx(key, field, value) {
|
|
1247
|
+
this.assertConnected();
|
|
1248
|
+
const result = await this.executeCommand("HSETNX", key, field, value);
|
|
1249
|
+
return Number(result);
|
|
1250
|
+
}
|
|
1251
|
+
async hincrbyfloat(key, field, increment) {
|
|
1252
|
+
this.assertConnected();
|
|
1253
|
+
const result = await this.executeCommand("HINCRBYFLOAT", key, field, increment);
|
|
1254
|
+
return String(result);
|
|
1255
|
+
}
|
|
1256
|
+
async hstrlen(key, field) {
|
|
1257
|
+
this.assertConnected();
|
|
1258
|
+
const result = await this.executeCommand("HSTRLEN", key, field);
|
|
1259
|
+
return Number(result);
|
|
1260
|
+
}
|
|
1261
|
+
async hrandfield(key, count, withValues) {
|
|
1262
|
+
this.assertConnected();
|
|
1263
|
+
const args = [key];
|
|
1264
|
+
if (count !== void 0) {
|
|
1265
|
+
args.push(count);
|
|
1266
|
+
if (withValues) {
|
|
1267
|
+
args.push("WITHVALUES");
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
const result = await this.executeCommand("HRANDFIELD", ...args);
|
|
1271
|
+
return result;
|
|
1272
|
+
}
|
|
1273
|
+
async lpush(key, ...values) {
|
|
1274
|
+
this.assertConnected();
|
|
1275
|
+
const result = await this.executeCommand("LPUSH", key, ...values);
|
|
1276
|
+
return Number(result);
|
|
1277
|
+
}
|
|
1278
|
+
async rpush(key, ...values) {
|
|
1279
|
+
this.assertConnected();
|
|
1280
|
+
const result = await this.executeCommand("RPUSH", key, ...values);
|
|
1281
|
+
return Number(result);
|
|
1282
|
+
}
|
|
1283
|
+
async lpop(key) {
|
|
1284
|
+
this.assertConnected();
|
|
1285
|
+
const result = await this.executeCommand("LPOP", key);
|
|
1286
|
+
return result === null || result === void 0 ? null : String(result);
|
|
1287
|
+
}
|
|
1288
|
+
async rpop(key) {
|
|
1289
|
+
this.assertConnected();
|
|
1290
|
+
const result = await this.executeCommand("RPOP", key);
|
|
1291
|
+
return result === null || result === void 0 ? null : String(result);
|
|
1292
|
+
}
|
|
1293
|
+
async llen(key) {
|
|
1294
|
+
this.assertConnected();
|
|
1295
|
+
const result = await this.executeCommand("LLEN", key);
|
|
1296
|
+
return Number(result);
|
|
1297
|
+
}
|
|
1298
|
+
async lrange(key, start, stop) {
|
|
1299
|
+
this.assertConnected();
|
|
1300
|
+
const result = await this.executeCommand("LRANGE", key, start, stop);
|
|
1301
|
+
return result;
|
|
1302
|
+
}
|
|
1303
|
+
async ltrim(key, start, stop) {
|
|
1304
|
+
this.assertConnected();
|
|
1305
|
+
await this.executeCommand("LTRIM", key, start, stop);
|
|
1306
|
+
return "OK";
|
|
1307
|
+
}
|
|
1308
|
+
async lindex(key, index) {
|
|
1309
|
+
this.assertConnected();
|
|
1310
|
+
const result = await this.executeCommand("LINDEX", key, index);
|
|
1311
|
+
return result === null || result === void 0 ? null : String(result);
|
|
1312
|
+
}
|
|
1313
|
+
async lset(key, index, value) {
|
|
1314
|
+
this.assertConnected();
|
|
1315
|
+
await this.executeCommand("LSET", key, index, value);
|
|
1316
|
+
return "OK";
|
|
1317
|
+
}
|
|
1318
|
+
async linsert(key, position, pivot, element) {
|
|
1319
|
+
this.assertConnected();
|
|
1320
|
+
const result = await this.executeCommand("LINSERT", key, position, pivot, element);
|
|
1321
|
+
return Number(result);
|
|
1322
|
+
}
|
|
1323
|
+
async lrem(key, count, element) {
|
|
1324
|
+
this.assertConnected();
|
|
1325
|
+
const result = await this.executeCommand("LREM", key, count, element);
|
|
1326
|
+
return Number(result);
|
|
1327
|
+
}
|
|
1328
|
+
async lpos(key, element, options) {
|
|
1329
|
+
this.assertConnected();
|
|
1330
|
+
const args = [key, element];
|
|
1331
|
+
if (options?.rank !== void 0) {
|
|
1332
|
+
args.push("RANK", options.rank);
|
|
1333
|
+
}
|
|
1334
|
+
if (options?.count !== void 0) {
|
|
1335
|
+
args.push("COUNT", options.count);
|
|
1336
|
+
}
|
|
1337
|
+
if (options?.maxlen !== void 0) {
|
|
1338
|
+
args.push("MAXLEN", options.maxlen);
|
|
1339
|
+
}
|
|
1340
|
+
const result = await this.executeCommand("LPOS", ...args);
|
|
1341
|
+
if (result === null) return null;
|
|
1342
|
+
if (Array.isArray(result)) return result.map(Number);
|
|
1343
|
+
return Number(result);
|
|
1344
|
+
}
|
|
1345
|
+
async blpop(keys, timeout) {
|
|
1346
|
+
this.assertConnected();
|
|
1347
|
+
const result = await this.executeCommand("BLPOP", ...keys, timeout);
|
|
1348
|
+
if (!result) return null;
|
|
1349
|
+
const [key, value] = result;
|
|
1350
|
+
return [key, value];
|
|
1351
|
+
}
|
|
1352
|
+
async brpop(keys, timeout) {
|
|
1353
|
+
this.assertConnected();
|
|
1354
|
+
const result = await this.executeCommand("BRPOP", ...keys, timeout);
|
|
1355
|
+
if (!result) return null;
|
|
1356
|
+
const [key, value] = result;
|
|
1357
|
+
return [key, value];
|
|
1358
|
+
}
|
|
1359
|
+
async lmove(source, destination, from, to) {
|
|
1360
|
+
this.assertConnected();
|
|
1361
|
+
const result = await this.executeCommand("LMOVE", source, destination, from, to);
|
|
1362
|
+
return result === null ? null : String(result);
|
|
1363
|
+
}
|
|
1364
|
+
async blmove(source, destination, from, to, timeout) {
|
|
1365
|
+
this.assertConnected();
|
|
1366
|
+
const result = await this.executeCommand("BLMOVE", source, destination, from, to, timeout);
|
|
1367
|
+
return result === null ? null : String(result);
|
|
1368
|
+
}
|
|
1369
|
+
async sadd(key, ...members) {
|
|
1370
|
+
this.assertConnected();
|
|
1371
|
+
const result = await this.executeCommand("SADD", key, ...members);
|
|
1372
|
+
return Number(result);
|
|
1373
|
+
}
|
|
1374
|
+
async srem(key, ...members) {
|
|
1375
|
+
this.assertConnected();
|
|
1376
|
+
const result = await this.executeCommand("SREM", key, ...members);
|
|
1377
|
+
return Number(result);
|
|
1378
|
+
}
|
|
1379
|
+
async smembers(key) {
|
|
1380
|
+
this.assertConnected();
|
|
1381
|
+
const result = await this.executeCommand("SMEMBERS", key);
|
|
1382
|
+
return result;
|
|
1383
|
+
}
|
|
1384
|
+
async sismember(key, member) {
|
|
1385
|
+
this.assertConnected();
|
|
1386
|
+
const result = await this.executeCommand("SISMEMBER", key, member);
|
|
1387
|
+
return Number(result);
|
|
1388
|
+
}
|
|
1389
|
+
async scard(key) {
|
|
1390
|
+
this.assertConnected();
|
|
1391
|
+
const result = await this.executeCommand("SCARD", key);
|
|
1392
|
+
return Number(result);
|
|
1393
|
+
}
|
|
1394
|
+
async srandmember(key, count) {
|
|
1395
|
+
this.assertConnected();
|
|
1396
|
+
const args = [key];
|
|
1397
|
+
if (count !== void 0) {
|
|
1398
|
+
args.push(count);
|
|
1399
|
+
}
|
|
1400
|
+
const result = await this.executeCommand("SRANDMEMBER", ...args);
|
|
1401
|
+
return result;
|
|
1402
|
+
}
|
|
1403
|
+
async spop(key, count) {
|
|
1404
|
+
this.assertConnected();
|
|
1405
|
+
const args = [key];
|
|
1406
|
+
if (count !== void 0) {
|
|
1407
|
+
args.push(count);
|
|
1408
|
+
}
|
|
1409
|
+
const result = await this.executeCommand("SPOP", ...args);
|
|
1410
|
+
return result;
|
|
1411
|
+
}
|
|
1412
|
+
async sscan(key, cursor, options) {
|
|
1413
|
+
this.assertConnected();
|
|
1414
|
+
const args = [key, cursor];
|
|
1415
|
+
if (options?.match) {
|
|
1416
|
+
args.push("MATCH", options.match);
|
|
1417
|
+
}
|
|
1418
|
+
if (options?.count) {
|
|
1419
|
+
args.push("COUNT", options.count);
|
|
1420
|
+
}
|
|
1421
|
+
const result = await this.executeCommand("SSCAN", ...args);
|
|
1422
|
+
const [newCursor, members] = result;
|
|
1423
|
+
return [String(newCursor), members];
|
|
1424
|
+
}
|
|
1425
|
+
async smove(source, destination, member) {
|
|
1426
|
+
this.assertConnected();
|
|
1427
|
+
const result = await this.executeCommand("SMOVE", source, destination, member);
|
|
1428
|
+
return Number(result);
|
|
1429
|
+
}
|
|
1430
|
+
async sinter(...keys) {
|
|
1431
|
+
this.assertConnected();
|
|
1432
|
+
const result = await this.executeCommand("SINTER", ...keys);
|
|
1433
|
+
return result;
|
|
1434
|
+
}
|
|
1435
|
+
async sinterstore(destination, ...keys) {
|
|
1436
|
+
this.assertConnected();
|
|
1437
|
+
const result = await this.executeCommand("SINTERSTORE", destination, ...keys);
|
|
1438
|
+
return Number(result);
|
|
1439
|
+
}
|
|
1440
|
+
async sunion(...keys) {
|
|
1441
|
+
this.assertConnected();
|
|
1442
|
+
const result = await this.executeCommand("SUNION", ...keys);
|
|
1443
|
+
return result;
|
|
1444
|
+
}
|
|
1445
|
+
async sunionstore(destination, ...keys) {
|
|
1446
|
+
this.assertConnected();
|
|
1447
|
+
const result = await this.executeCommand("SUNIONSTORE", destination, ...keys);
|
|
1448
|
+
return Number(result);
|
|
1449
|
+
}
|
|
1450
|
+
async sdiff(...keys) {
|
|
1451
|
+
this.assertConnected();
|
|
1452
|
+
const result = await this.executeCommand("SDIFF", ...keys);
|
|
1453
|
+
return result;
|
|
1454
|
+
}
|
|
1455
|
+
async sdiffstore(destination, ...keys) {
|
|
1456
|
+
this.assertConnected();
|
|
1457
|
+
const result = await this.executeCommand("SDIFFSTORE", destination, ...keys);
|
|
1458
|
+
return Number(result);
|
|
1459
|
+
}
|
|
1460
|
+
async smismember(key, ...members) {
|
|
1461
|
+
this.assertConnected();
|
|
1462
|
+
const result = await this.executeCommand("SMISMEMBER", key, ...members);
|
|
1463
|
+
return result.map(Number);
|
|
1464
|
+
}
|
|
1465
|
+
async zadd(key, ...args) {
|
|
1466
|
+
this.assertConnected();
|
|
1467
|
+
const result = await this.executeCommand("ZADD", key, ...args);
|
|
1468
|
+
return Number(result);
|
|
1469
|
+
}
|
|
1470
|
+
async zrem(key, ...members) {
|
|
1471
|
+
this.assertConnected();
|
|
1472
|
+
const result = await this.executeCommand("ZREM", key, ...members);
|
|
1473
|
+
return Number(result);
|
|
1474
|
+
}
|
|
1475
|
+
async zrange(key, start, stop, withScores) {
|
|
1476
|
+
this.assertConnected();
|
|
1477
|
+
const args = [key, start, stop];
|
|
1478
|
+
if (withScores) {
|
|
1479
|
+
args.push("WITHSCORES");
|
|
1480
|
+
}
|
|
1481
|
+
const result = await this.executeCommand("ZRANGE", ...args);
|
|
1482
|
+
return result;
|
|
1483
|
+
}
|
|
1484
|
+
async zrangebyscore(key, min, max, withScores) {
|
|
1485
|
+
this.assertConnected();
|
|
1486
|
+
const args = [key, min, max];
|
|
1487
|
+
if (withScores) {
|
|
1488
|
+
args.push("WITHSCORES");
|
|
1489
|
+
}
|
|
1490
|
+
const result = await this.executeCommand("ZRANGEBYSCORE", ...args);
|
|
1491
|
+
return result;
|
|
1492
|
+
}
|
|
1493
|
+
async zscore(key, member) {
|
|
1494
|
+
this.assertConnected();
|
|
1495
|
+
const result = await this.executeCommand("ZSCORE", key, member);
|
|
1496
|
+
return result === null || result === void 0 ? null : String(result);
|
|
1497
|
+
}
|
|
1498
|
+
async zcard(key) {
|
|
1499
|
+
this.assertConnected();
|
|
1500
|
+
const result = await this.executeCommand("ZCARD", key);
|
|
1501
|
+
return Number(result);
|
|
1502
|
+
}
|
|
1503
|
+
async zrank(key, member) {
|
|
1504
|
+
this.assertConnected();
|
|
1505
|
+
const result = await this.executeCommand("ZRANK", key, member);
|
|
1506
|
+
return result === null || result === void 0 ? null : Number(result);
|
|
1507
|
+
}
|
|
1508
|
+
async zincrby(key, increment, member) {
|
|
1509
|
+
this.assertConnected();
|
|
1510
|
+
const result = await this.executeCommand("ZINCRBY", key, increment, member);
|
|
1511
|
+
return String(result);
|
|
1512
|
+
}
|
|
1513
|
+
async zscan(key, cursor, options) {
|
|
1514
|
+
this.assertConnected();
|
|
1515
|
+
const args = [key, cursor];
|
|
1516
|
+
if (options?.match) {
|
|
1517
|
+
args.push("MATCH", options.match);
|
|
1518
|
+
}
|
|
1519
|
+
if (options?.count) {
|
|
1520
|
+
args.push("COUNT", options.count);
|
|
1521
|
+
}
|
|
1522
|
+
const result = await this.executeCommand("ZSCAN", ...args);
|
|
1523
|
+
const [newCursor, members] = result;
|
|
1524
|
+
return [String(newCursor), members];
|
|
1525
|
+
}
|
|
1526
|
+
async zrevrank(key, member) {
|
|
1527
|
+
this.assertConnected();
|
|
1528
|
+
const result = await this.executeCommand("ZREVRANK", key, member);
|
|
1529
|
+
return result === null || result === void 0 ? null : Number(result);
|
|
1530
|
+
}
|
|
1531
|
+
async zcount(key, min, max) {
|
|
1532
|
+
this.assertConnected();
|
|
1533
|
+
const result = await this.executeCommand("ZCOUNT", key, min, max);
|
|
1534
|
+
return Number(result);
|
|
1535
|
+
}
|
|
1536
|
+
async zlexcount(key, min, max) {
|
|
1537
|
+
this.assertConnected();
|
|
1538
|
+
const result = await this.executeCommand("ZLEXCOUNT", key, min, max);
|
|
1539
|
+
return Number(result);
|
|
1540
|
+
}
|
|
1541
|
+
async zpopmin(key, count) {
|
|
1542
|
+
this.assertConnected();
|
|
1543
|
+
const args = [key];
|
|
1544
|
+
if (count !== void 0) {
|
|
1545
|
+
args.push(count);
|
|
1546
|
+
}
|
|
1547
|
+
const result = await this.executeCommand("ZPOPMIN", ...args);
|
|
1548
|
+
return result;
|
|
1549
|
+
}
|
|
1550
|
+
async zpopmax(key, count) {
|
|
1551
|
+
this.assertConnected();
|
|
1552
|
+
const args = [key];
|
|
1553
|
+
if (count !== void 0) {
|
|
1554
|
+
args.push(count);
|
|
1555
|
+
}
|
|
1556
|
+
const result = await this.executeCommand("ZPOPMAX", ...args);
|
|
1557
|
+
return result;
|
|
1558
|
+
}
|
|
1559
|
+
async bzpopmin(keys, timeout) {
|
|
1560
|
+
this.assertConnected();
|
|
1561
|
+
const result = await this.executeCommand("BZPOPMIN", ...keys, timeout);
|
|
1562
|
+
if (!result) return null;
|
|
1563
|
+
const [key, member, score] = result;
|
|
1564
|
+
return [key, member, String(score)];
|
|
1565
|
+
}
|
|
1566
|
+
async bzpopmax(keys, timeout) {
|
|
1567
|
+
this.assertConnected();
|
|
1568
|
+
const result = await this.executeCommand("BZPOPMAX", ...keys, timeout);
|
|
1569
|
+
if (!result) return null;
|
|
1570
|
+
const [key, member, score] = result;
|
|
1571
|
+
return [key, member, String(score)];
|
|
1572
|
+
}
|
|
1573
|
+
async zunionstore(destination, keys, options) {
|
|
1574
|
+
this.assertConnected();
|
|
1575
|
+
const args = [destination, keys.length, ...keys];
|
|
1576
|
+
if (options?.weights) {
|
|
1577
|
+
args.push("WEIGHTS", ...options.weights);
|
|
1578
|
+
}
|
|
1579
|
+
if (options?.aggregate) {
|
|
1580
|
+
args.push("AGGREGATE", options.aggregate);
|
|
1581
|
+
}
|
|
1582
|
+
const result = await this.executeCommand("ZUNIONSTORE", ...args);
|
|
1583
|
+
return Number(result);
|
|
1584
|
+
}
|
|
1585
|
+
async zinterstore(destination, keys, options) {
|
|
1586
|
+
this.assertConnected();
|
|
1587
|
+
const args = [destination, keys.length, ...keys];
|
|
1588
|
+
if (options?.weights) {
|
|
1589
|
+
args.push("WEIGHTS", ...options.weights);
|
|
1590
|
+
}
|
|
1591
|
+
if (options?.aggregate) {
|
|
1592
|
+
args.push("AGGREGATE", options.aggregate);
|
|
1593
|
+
}
|
|
1594
|
+
const result = await this.executeCommand("ZINTERSTORE", ...args);
|
|
1595
|
+
return Number(result);
|
|
1596
|
+
}
|
|
1597
|
+
async zmscore(key, ...members) {
|
|
1598
|
+
this.assertConnected();
|
|
1599
|
+
const result = await this.executeCommand("ZMSCORE", key, ...members);
|
|
1600
|
+
return result.map((v) => v === null || v === void 0 ? null : String(v));
|
|
1601
|
+
}
|
|
1602
|
+
async zrandmember(key, count, withScores) {
|
|
1603
|
+
this.assertConnected();
|
|
1604
|
+
const args = [key];
|
|
1605
|
+
if (count !== void 0) {
|
|
1606
|
+
args.push(count);
|
|
1607
|
+
if (withScores) {
|
|
1608
|
+
args.push("WITHSCORES");
|
|
1609
|
+
}
|
|
1610
|
+
}
|
|
1611
|
+
const result = await this.executeCommand("ZRANDMEMBER", ...args);
|
|
1612
|
+
return result;
|
|
1613
|
+
}
|
|
1614
|
+
async zrevrangebyscore(key, max, min, options) {
|
|
1615
|
+
this.assertConnected();
|
|
1616
|
+
const args = [key, max, min];
|
|
1617
|
+
if (options?.withScores) {
|
|
1618
|
+
args.push("WITHSCORES");
|
|
1619
|
+
}
|
|
1620
|
+
if (options?.limit) {
|
|
1621
|
+
args.push("LIMIT", options.limit.offset, options.limit.count);
|
|
1622
|
+
}
|
|
1623
|
+
const result = await this.executeCommand("ZREVRANGEBYSCORE", ...args);
|
|
1624
|
+
return result;
|
|
1625
|
+
}
|
|
1626
|
+
async publish(channel, message) {
|
|
1627
|
+
this.assertConnected();
|
|
1628
|
+
const result = await this.executeCommand("PUBLISH", channel, message);
|
|
1629
|
+
return Number(result);
|
|
1630
|
+
}
|
|
1631
|
+
async subscribe(...channels) {
|
|
1632
|
+
this.assertConnected();
|
|
1633
|
+
await this.executeCommand("SUBSCRIBE", ...channels);
|
|
1634
|
+
}
|
|
1635
|
+
async unsubscribe(...channels) {
|
|
1636
|
+
this.assertConnected();
|
|
1637
|
+
await this.executeCommand("UNSUBSCRIBE", ...channels);
|
|
1638
|
+
}
|
|
1639
|
+
async psubscribe(...patterns) {
|
|
1640
|
+
this.assertConnected();
|
|
1641
|
+
await this.executeCommand("PSUBSCRIBE", ...patterns);
|
|
1642
|
+
}
|
|
1643
|
+
async punsubscribe(...patterns) {
|
|
1644
|
+
this.assertConnected();
|
|
1645
|
+
await this.executeCommand("PUNSUBSCRIBE", ...patterns);
|
|
1646
|
+
}
|
|
1647
|
+
async xadd(key, id, fields, options) {
|
|
1648
|
+
this.assertConnected();
|
|
1649
|
+
const args = [key];
|
|
1650
|
+
if (options?.noMkStream) {
|
|
1651
|
+
args.push("NOMKSTREAM");
|
|
1652
|
+
}
|
|
1653
|
+
if (options?.maxLen !== void 0) {
|
|
1654
|
+
args.push("MAXLEN");
|
|
1655
|
+
if (options.approximate) {
|
|
1656
|
+
args.push("~");
|
|
1657
|
+
}
|
|
1658
|
+
args.push(options.maxLen);
|
|
1659
|
+
}
|
|
1660
|
+
if (options?.minId !== void 0) {
|
|
1661
|
+
args.push("MINID");
|
|
1662
|
+
if (options.approximate) {
|
|
1663
|
+
args.push("~");
|
|
1664
|
+
}
|
|
1665
|
+
args.push(options.minId);
|
|
1666
|
+
}
|
|
1667
|
+
args.push(id);
|
|
1668
|
+
for (const [field, value] of Object.entries(fields)) {
|
|
1669
|
+
args.push(field, value);
|
|
1670
|
+
}
|
|
1671
|
+
const result = await this.executeCommand("XADD", ...args);
|
|
1672
|
+
return String(result);
|
|
1673
|
+
}
|
|
1674
|
+
async xread(streams, options) {
|
|
1675
|
+
this.assertConnected();
|
|
1676
|
+
const args = [];
|
|
1677
|
+
if (options?.count !== void 0) {
|
|
1678
|
+
args.push("COUNT", options.count);
|
|
1679
|
+
}
|
|
1680
|
+
if (options?.block !== void 0) {
|
|
1681
|
+
args.push("BLOCK", options.block);
|
|
1682
|
+
}
|
|
1683
|
+
args.push("STREAMS");
|
|
1684
|
+
for (const stream of streams) {
|
|
1685
|
+
args.push(stream.key);
|
|
1686
|
+
}
|
|
1687
|
+
for (const stream of streams) {
|
|
1688
|
+
args.push(stream.id);
|
|
1689
|
+
}
|
|
1690
|
+
const result = await this.executeCommand("XREAD", ...args);
|
|
1691
|
+
if (!result) return null;
|
|
1692
|
+
return this.parseStreamReadResult(result);
|
|
1693
|
+
}
|
|
1694
|
+
async xreadgroup(group, consumer, streams, options) {
|
|
1695
|
+
this.assertConnected();
|
|
1696
|
+
const args = ["GROUP", group, consumer];
|
|
1697
|
+
if (options?.count !== void 0) {
|
|
1698
|
+
args.push("COUNT", options.count);
|
|
1699
|
+
}
|
|
1700
|
+
if (options?.block !== void 0) {
|
|
1701
|
+
args.push("BLOCK", options.block);
|
|
1702
|
+
}
|
|
1703
|
+
if (options?.noAck) {
|
|
1704
|
+
args.push("NOACK");
|
|
1705
|
+
}
|
|
1706
|
+
args.push("STREAMS");
|
|
1707
|
+
for (const stream of streams) {
|
|
1708
|
+
args.push(stream.key);
|
|
1709
|
+
}
|
|
1710
|
+
for (const stream of streams) {
|
|
1711
|
+
args.push(stream.id);
|
|
1712
|
+
}
|
|
1713
|
+
const result = await this.executeCommand("XREADGROUP", ...args);
|
|
1714
|
+
if (!result) return null;
|
|
1715
|
+
return this.parseStreamReadResult(result);
|
|
1716
|
+
}
|
|
1717
|
+
async xrange(key, start, end, options) {
|
|
1718
|
+
this.assertConnected();
|
|
1719
|
+
const args = [key, start, end];
|
|
1720
|
+
if (options?.count !== void 0) {
|
|
1721
|
+
args.push("COUNT", options.count);
|
|
1722
|
+
}
|
|
1723
|
+
const result = await this.executeCommand("XRANGE", ...args);
|
|
1724
|
+
return this.parseStreamEntries(result);
|
|
1725
|
+
}
|
|
1726
|
+
async xrevrange(key, end, start, options) {
|
|
1727
|
+
this.assertConnected();
|
|
1728
|
+
const args = [key, end, start];
|
|
1729
|
+
if (options?.count !== void 0) {
|
|
1730
|
+
args.push("COUNT", options.count);
|
|
1731
|
+
}
|
|
1732
|
+
const result = await this.executeCommand("XREVRANGE", ...args);
|
|
1733
|
+
return this.parseStreamEntries(result);
|
|
1734
|
+
}
|
|
1735
|
+
async xlen(key) {
|
|
1736
|
+
this.assertConnected();
|
|
1737
|
+
const result = await this.executeCommand("XLEN", key);
|
|
1738
|
+
return Number(result);
|
|
1739
|
+
}
|
|
1740
|
+
async xinfo(key) {
|
|
1741
|
+
this.assertConnected();
|
|
1742
|
+
const result = await this.executeCommand("XINFO", "STREAM", key);
|
|
1743
|
+
return this.parseIStreamInfo(result);
|
|
1744
|
+
}
|
|
1745
|
+
async xtrim(key, maxLen, approximate) {
|
|
1746
|
+
this.assertConnected();
|
|
1747
|
+
const args = [key, "MAXLEN"];
|
|
1748
|
+
if (approximate) {
|
|
1749
|
+
args.push("~");
|
|
1750
|
+
}
|
|
1751
|
+
args.push(maxLen);
|
|
1752
|
+
const result = await this.executeCommand("XTRIM", ...args);
|
|
1753
|
+
return Number(result);
|
|
1754
|
+
}
|
|
1755
|
+
async xgroupCreate(key, group, id, mkstream) {
|
|
1756
|
+
this.assertConnected();
|
|
1757
|
+
const args = [key, group, id];
|
|
1758
|
+
if (mkstream) {
|
|
1759
|
+
args.push("MKSTREAM");
|
|
1760
|
+
}
|
|
1761
|
+
await this.executeCommand("XGROUP", "CREATE", ...args);
|
|
1762
|
+
return "OK";
|
|
1763
|
+
}
|
|
1764
|
+
async xgroupDestroy(key, group) {
|
|
1765
|
+
this.assertConnected();
|
|
1766
|
+
const result = await this.executeCommand("XGROUP", "DESTROY", key, group);
|
|
1767
|
+
return Number(result);
|
|
1768
|
+
}
|
|
1769
|
+
async xgroupDelConsumer(key, group, consumer) {
|
|
1770
|
+
this.assertConnected();
|
|
1771
|
+
const result = await this.executeCommand("XGROUP", "DELCONSUMER", key, group, consumer);
|
|
1772
|
+
return Number(result);
|
|
1773
|
+
}
|
|
1774
|
+
async xgroupSetId(key, group, id) {
|
|
1775
|
+
this.assertConnected();
|
|
1776
|
+
await this.executeCommand("XGROUP", "SETID", key, group, id);
|
|
1777
|
+
return "OK";
|
|
1778
|
+
}
|
|
1779
|
+
async xack(key, group, ...ids) {
|
|
1780
|
+
this.assertConnected();
|
|
1781
|
+
const result = await this.executeCommand("XACK", key, group, ...ids);
|
|
1782
|
+
return Number(result);
|
|
1783
|
+
}
|
|
1784
|
+
async xpending(key, group) {
|
|
1785
|
+
this.assertConnected();
|
|
1786
|
+
const result = await this.executeCommand("XPENDING", key, group);
|
|
1787
|
+
return this.parseIStreamPendingInfo(result);
|
|
1788
|
+
}
|
|
1789
|
+
async xpendingRange(key, group, start, end, count, consumer) {
|
|
1790
|
+
this.assertConnected();
|
|
1791
|
+
const args = [key, group, start, end, count];
|
|
1792
|
+
if (consumer) {
|
|
1793
|
+
args.push(consumer);
|
|
1794
|
+
}
|
|
1795
|
+
const result = await this.executeCommand("XPENDING", ...args);
|
|
1796
|
+
return this.parseStreamPendingEntries(result);
|
|
1797
|
+
}
|
|
1798
|
+
async xclaim(key, group, consumer, minIdleTime, ...ids) {
|
|
1799
|
+
this.assertConnected();
|
|
1800
|
+
const result = await this.executeCommand("XCLAIM", key, group, consumer, minIdleTime, ...ids);
|
|
1801
|
+
return this.parseStreamEntries(result);
|
|
1802
|
+
}
|
|
1803
|
+
async xdel(key, ...ids) {
|
|
1804
|
+
this.assertConnected();
|
|
1805
|
+
const result = await this.executeCommand("XDEL", key, ...ids);
|
|
1806
|
+
return Number(result);
|
|
1807
|
+
}
|
|
1808
|
+
parseStreamReadResult(result) {
|
|
1809
|
+
return result.map((streamData) => {
|
|
1810
|
+
const [key, entries] = streamData;
|
|
1811
|
+
return {
|
|
1812
|
+
key,
|
|
1813
|
+
entries: this.parseStreamEntries(entries)
|
|
1814
|
+
};
|
|
1815
|
+
});
|
|
1816
|
+
}
|
|
1817
|
+
parseStreamEntries(entries) {
|
|
1818
|
+
return entries.map((entry) => {
|
|
1819
|
+
const [id, fields] = entry;
|
|
1820
|
+
return {
|
|
1821
|
+
id,
|
|
1822
|
+
fields: this.parseFieldArray(fields)
|
|
1823
|
+
};
|
|
1824
|
+
});
|
|
1825
|
+
}
|
|
1826
|
+
parseFieldArray(fields) {
|
|
1827
|
+
const result = {};
|
|
1828
|
+
for (let i = 0; i < fields.length; i += 2) {
|
|
1829
|
+
const key = fields[i];
|
|
1830
|
+
const value = fields[i + 1];
|
|
1831
|
+
if (key !== void 0 && value !== void 0) {
|
|
1832
|
+
result[key] = value;
|
|
1833
|
+
}
|
|
1834
|
+
}
|
|
1835
|
+
return result;
|
|
1836
|
+
}
|
|
1837
|
+
parseIStreamInfo(result) {
|
|
1838
|
+
const info = {};
|
|
1839
|
+
for (let i = 0; i < result.length; i += 2) {
|
|
1840
|
+
const key = String(result[i]).toLowerCase().replace(/-/g, "");
|
|
1841
|
+
info[key] = result[i + 1];
|
|
1842
|
+
}
|
|
1843
|
+
return {
|
|
1844
|
+
length: Number(info["length"] ?? 0),
|
|
1845
|
+
groups: Number(info["groups"] ?? 0),
|
|
1846
|
+
firstEntry: info["firstentry"] ? this.parseStreamEntries([info["firstentry"]])[0] ?? null : null,
|
|
1847
|
+
lastEntry: info["lastentry"] ? this.parseStreamEntries([info["lastentry"]])[0] ?? null : null,
|
|
1848
|
+
lastGeneratedId: String(info["lastgeneratedid"] ?? "0-0"),
|
|
1849
|
+
radixTreeKeys: Number(info["radixtreekeys"] ?? 0),
|
|
1850
|
+
radixTreeNodes: Number(info["radixtreenodes"] ?? 0)
|
|
1851
|
+
};
|
|
1852
|
+
}
|
|
1853
|
+
parseIStreamPendingInfo(result) {
|
|
1854
|
+
const [count, minId, maxId, consumers] = result;
|
|
1855
|
+
return {
|
|
1856
|
+
count: Number(count),
|
|
1857
|
+
minId,
|
|
1858
|
+
maxId,
|
|
1859
|
+
consumers: consumers ? consumers.map((c) => {
|
|
1860
|
+
const [name, pending] = c;
|
|
1861
|
+
return { name, count: Number(pending) };
|
|
1862
|
+
}) : []
|
|
1863
|
+
};
|
|
1864
|
+
}
|
|
1865
|
+
parseStreamPendingEntries(result) {
|
|
1866
|
+
return result.map((entry) => {
|
|
1867
|
+
const [id, consumer, idleTime, deliveryCount] = entry;
|
|
1868
|
+
return {
|
|
1869
|
+
id,
|
|
1870
|
+
consumer,
|
|
1871
|
+
idleTime: Number(idleTime),
|
|
1872
|
+
deliveryCount: Number(deliveryCount)
|
|
1873
|
+
};
|
|
1874
|
+
});
|
|
1875
|
+
}
|
|
1876
|
+
pipeline() {
|
|
1877
|
+
this.assertConnected();
|
|
1878
|
+
return this.createPipeline();
|
|
1879
|
+
}
|
|
1880
|
+
multi() {
|
|
1881
|
+
this.assertConnected();
|
|
1882
|
+
return this.createMulti();
|
|
1883
|
+
}
|
|
1884
|
+
/** Registry mapping SHA → script source for NOSCRIPT fallback */
|
|
1885
|
+
scriptRegistry = /* @__PURE__ */ new Map();
|
|
1886
|
+
async eval(script, keys, args) {
|
|
1887
|
+
this.assertConnected();
|
|
1888
|
+
const result = await this.executeCommand("EVAL", script, keys.length, ...keys, ...args);
|
|
1889
|
+
return result;
|
|
1890
|
+
}
|
|
1891
|
+
async evalsha(sha, keys, args) {
|
|
1892
|
+
this.assertConnected();
|
|
1893
|
+
try {
|
|
1894
|
+
return await this.executeCommand("EVALSHA", sha, keys.length, ...keys, ...args);
|
|
1895
|
+
} catch (error) {
|
|
1896
|
+
if (this.isNoscriptError(error)) {
|
|
1897
|
+
const script = this.scriptRegistry.get(sha);
|
|
1898
|
+
if (script) {
|
|
1899
|
+
return this.eval(script, keys, args);
|
|
1900
|
+
}
|
|
1901
|
+
}
|
|
1902
|
+
throw error;
|
|
1903
|
+
}
|
|
1904
|
+
}
|
|
1905
|
+
async scriptLoad(script) {
|
|
1906
|
+
this.assertConnected();
|
|
1907
|
+
const result = await this.executeCommand("SCRIPT", "LOAD", script);
|
|
1908
|
+
const sha = String(result);
|
|
1909
|
+
this.scriptRegistry.set(sha, script);
|
|
1910
|
+
return sha;
|
|
1911
|
+
}
|
|
1912
|
+
isNoscriptError(error) {
|
|
1913
|
+
const msg = error?.message ?? "";
|
|
1914
|
+
return msg.includes("NOSCRIPT");
|
|
1915
|
+
}
|
|
1916
|
+
async scriptExists(...shas) {
|
|
1917
|
+
this.assertConnected();
|
|
1918
|
+
const result = await this.executeCommand("SCRIPT", "EXISTS", ...shas);
|
|
1919
|
+
return result.map(Number);
|
|
1920
|
+
}
|
|
1921
|
+
async scriptFlush() {
|
|
1922
|
+
this.assertConnected();
|
|
1923
|
+
await this.executeCommand("SCRIPT", "FLUSH");
|
|
1924
|
+
return "OK";
|
|
1925
|
+
}
|
|
1926
|
+
async pfadd(key, ...elements) {
|
|
1927
|
+
this.assertConnected();
|
|
1928
|
+
const result = await this.executeCommand("PFADD", key, ...elements);
|
|
1929
|
+
return Number(result);
|
|
1930
|
+
}
|
|
1931
|
+
async pfcount(...keys) {
|
|
1932
|
+
this.assertConnected();
|
|
1933
|
+
const result = await this.executeCommand("PFCOUNT", ...keys);
|
|
1934
|
+
return Number(result);
|
|
1935
|
+
}
|
|
1936
|
+
async pfmerge(destination, ...sources) {
|
|
1937
|
+
this.assertConnected();
|
|
1938
|
+
await this.executeCommand("PFMERGE", destination, ...sources);
|
|
1939
|
+
return "OK";
|
|
1940
|
+
}
|
|
1941
|
+
async geoadd(key, ...members) {
|
|
1942
|
+
this.assertConnected();
|
|
1943
|
+
const result = await this.executeCommand("GEOADD", key, ...members);
|
|
1944
|
+
return Number(result);
|
|
1945
|
+
}
|
|
1946
|
+
async geodist(key, member1, member2, unit) {
|
|
1947
|
+
this.assertConnected();
|
|
1948
|
+
const args = [key, member1, member2];
|
|
1949
|
+
if (unit) {
|
|
1950
|
+
args.push(unit);
|
|
1951
|
+
}
|
|
1952
|
+
const result = await this.executeCommand("GEODIST", ...args);
|
|
1953
|
+
return result === null ? null : String(result);
|
|
1954
|
+
}
|
|
1955
|
+
async geohash(key, ...members) {
|
|
1956
|
+
this.assertConnected();
|
|
1957
|
+
const result = await this.executeCommand("GEOHASH", key, ...members);
|
|
1958
|
+
return result.map((v) => v === null ? null : String(v));
|
|
1959
|
+
}
|
|
1960
|
+
async geopos(key, ...members) {
|
|
1961
|
+
this.assertConnected();
|
|
1962
|
+
const result = await this.executeCommand("GEOPOS", key, ...members);
|
|
1963
|
+
return result.map((pos) => {
|
|
1964
|
+
if (!pos) return null;
|
|
1965
|
+
return [String(pos[0]), String(pos[1])];
|
|
1966
|
+
});
|
|
1967
|
+
}
|
|
1968
|
+
async geosearch(key, options) {
|
|
1969
|
+
this.assertConnected();
|
|
1970
|
+
const args = [key];
|
|
1971
|
+
if (options.member) {
|
|
1972
|
+
args.push("FROMMEMBER", options.member);
|
|
1973
|
+
} else if (options.coord) {
|
|
1974
|
+
args.push("FROMLONLAT", options.coord.longitude, options.coord.latitude);
|
|
1975
|
+
}
|
|
1976
|
+
if (options.radius) {
|
|
1977
|
+
args.push("BYRADIUS", options.radius.value, options.radius.unit);
|
|
1978
|
+
} else if (options.box) {
|
|
1979
|
+
args.push("BYBOX", options.box.width, options.box.height, options.box.unit);
|
|
1980
|
+
}
|
|
1981
|
+
if (options.sort) {
|
|
1982
|
+
args.push(options.sort);
|
|
1983
|
+
}
|
|
1984
|
+
if (options.count !== void 0) {
|
|
1985
|
+
args.push("COUNT", options.count);
|
|
1986
|
+
if (options.any) {
|
|
1987
|
+
args.push("ANY");
|
|
1988
|
+
}
|
|
1989
|
+
}
|
|
1990
|
+
if (options.withCoord) {
|
|
1991
|
+
args.push("WITHCOORD");
|
|
1992
|
+
}
|
|
1993
|
+
if (options.withDist) {
|
|
1994
|
+
args.push("WITHDIST");
|
|
1995
|
+
}
|
|
1996
|
+
if (options.withHash) {
|
|
1997
|
+
args.push("WITHHASH");
|
|
1998
|
+
}
|
|
1999
|
+
const result = await this.executeCommand("GEOSEARCH", ...args);
|
|
2000
|
+
return this.parseIGeoSearchResult(result, options);
|
|
2001
|
+
}
|
|
2002
|
+
async geosearchstore(destination, source, options) {
|
|
2003
|
+
this.assertConnected();
|
|
2004
|
+
const args = [destination, source];
|
|
2005
|
+
if (options.member) {
|
|
2006
|
+
args.push("FROMMEMBER", options.member);
|
|
2007
|
+
} else if (options.coord) {
|
|
2008
|
+
args.push("FROMLONLAT", options.coord.longitude, options.coord.latitude);
|
|
2009
|
+
}
|
|
2010
|
+
if (options.radius) {
|
|
2011
|
+
args.push("BYRADIUS", options.radius.value, options.radius.unit);
|
|
2012
|
+
} else if (options.box) {
|
|
2013
|
+
args.push("BYBOX", options.box.width, options.box.height, options.box.unit);
|
|
2014
|
+
}
|
|
2015
|
+
if (options.sort) {
|
|
2016
|
+
args.push(options.sort);
|
|
2017
|
+
}
|
|
2018
|
+
if (options.count !== void 0) {
|
|
2019
|
+
args.push("COUNT", options.count);
|
|
2020
|
+
if (options.any) {
|
|
2021
|
+
args.push("ANY");
|
|
2022
|
+
}
|
|
2023
|
+
}
|
|
2024
|
+
if (options.storedist) {
|
|
2025
|
+
args.push("STOREDIST");
|
|
2026
|
+
}
|
|
2027
|
+
const result = await this.executeCommand("GEOSEARCHSTORE", ...args);
|
|
2028
|
+
return Number(result);
|
|
2029
|
+
}
|
|
2030
|
+
parseIGeoSearchResult(result, options) {
|
|
2031
|
+
const hasExtra = options.withCoord || options.withDist || options.withHash;
|
|
2032
|
+
if (!hasExtra) {
|
|
2033
|
+
return result;
|
|
2034
|
+
}
|
|
2035
|
+
return result.map((item) => {
|
|
2036
|
+
if (!Array.isArray(item)) {
|
|
2037
|
+
return item;
|
|
2038
|
+
}
|
|
2039
|
+
const geoResult = { member: String(item[0]) };
|
|
2040
|
+
let idx = 1;
|
|
2041
|
+
if (options.withDist && item[idx] !== void 0) {
|
|
2042
|
+
geoResult.distance = String(item[idx]);
|
|
2043
|
+
idx++;
|
|
2044
|
+
}
|
|
2045
|
+
if (options.withHash && item[idx] !== void 0) {
|
|
2046
|
+
geoResult.hash = Number(item[idx]);
|
|
2047
|
+
idx++;
|
|
2048
|
+
}
|
|
2049
|
+
if (options.withCoord && item[idx] !== void 0) {
|
|
2050
|
+
const coords = item[idx];
|
|
2051
|
+
geoResult.coordinates = [String(coords[0]), String(coords[1])];
|
|
2052
|
+
}
|
|
2053
|
+
return geoResult;
|
|
2054
|
+
});
|
|
2055
|
+
}
|
|
2056
|
+
async setbit(key, offset, value) {
|
|
2057
|
+
this.assertConnected();
|
|
2058
|
+
const result = await this.executeCommand("SETBIT", key, offset, value);
|
|
2059
|
+
return Number(result);
|
|
2060
|
+
}
|
|
2061
|
+
async getbit(key, offset) {
|
|
2062
|
+
this.assertConnected();
|
|
2063
|
+
const result = await this.executeCommand("GETBIT", key, offset);
|
|
2064
|
+
return Number(result);
|
|
2065
|
+
}
|
|
2066
|
+
async bitcount(key, start, end, mode) {
|
|
2067
|
+
this.assertConnected();
|
|
2068
|
+
const args = [key];
|
|
2069
|
+
if (start !== void 0 && end !== void 0) {
|
|
2070
|
+
args.push(start, end);
|
|
2071
|
+
if (mode) {
|
|
2072
|
+
args.push(mode);
|
|
2073
|
+
}
|
|
2074
|
+
}
|
|
2075
|
+
const result = await this.executeCommand("BITCOUNT", ...args);
|
|
2076
|
+
return Number(result);
|
|
2077
|
+
}
|
|
2078
|
+
async bitop(operation, destKey, ...keys) {
|
|
2079
|
+
this.assertConnected();
|
|
2080
|
+
const result = await this.executeCommand("BITOP", operation, destKey, ...keys);
|
|
2081
|
+
return Number(result);
|
|
2082
|
+
}
|
|
2083
|
+
async bitpos(key, bit, start, end, mode) {
|
|
2084
|
+
this.assertConnected();
|
|
2085
|
+
const args = [key, bit];
|
|
2086
|
+
if (start !== void 0) {
|
|
2087
|
+
args.push(start);
|
|
2088
|
+
if (end !== void 0) {
|
|
2089
|
+
args.push(end);
|
|
2090
|
+
if (mode) {
|
|
2091
|
+
args.push(mode);
|
|
2092
|
+
}
|
|
2093
|
+
}
|
|
2094
|
+
}
|
|
2095
|
+
const result = await this.executeCommand("BITPOS", ...args);
|
|
2096
|
+
return Number(result);
|
|
2097
|
+
}
|
|
2098
|
+
async flushdb() {
|
|
2099
|
+
this.assertConnected();
|
|
2100
|
+
await this.executeCommand("FLUSHDB");
|
|
2101
|
+
return "OK";
|
|
2102
|
+
}
|
|
2103
|
+
async flushall() {
|
|
2104
|
+
this.assertConnected();
|
|
2105
|
+
await this.executeCommand("FLUSHALL");
|
|
2106
|
+
return "OK";
|
|
2107
|
+
}
|
|
2108
|
+
async info(section) {
|
|
2109
|
+
this.assertConnected();
|
|
2110
|
+
const args = section ? [section] : [];
|
|
2111
|
+
const result = await this.executeCommand("INFO", ...args);
|
|
2112
|
+
return String(result);
|
|
2113
|
+
}
|
|
2114
|
+
async dbsize() {
|
|
2115
|
+
this.assertConnected();
|
|
2116
|
+
const result = await this.executeCommand("DBSIZE");
|
|
2117
|
+
return Number(result);
|
|
2118
|
+
}
|
|
2119
|
+
async cluster(subcommand, ...args) {
|
|
2120
|
+
this.assertConnected();
|
|
2121
|
+
return this.executeCommand("CLUSTER", subcommand, ...args);
|
|
2122
|
+
}
|
|
2123
|
+
async sentinel(subcommand, ...args) {
|
|
2124
|
+
this.assertConnected();
|
|
2125
|
+
return this.executeCommand("SENTINEL", subcommand, ...args);
|
|
2126
|
+
}
|
|
2127
|
+
on(event, handler) {
|
|
2128
|
+
this.eventEmitter.on(event, handler);
|
|
2129
|
+
}
|
|
2130
|
+
once(event, handler) {
|
|
2131
|
+
this.eventEmitter.once(event, handler);
|
|
2132
|
+
}
|
|
2133
|
+
off(event, handler) {
|
|
2134
|
+
this.eventEmitter.off(event, handler);
|
|
2135
|
+
}
|
|
2136
|
+
removeAllListeners(event) {
|
|
2137
|
+
if (event) {
|
|
2138
|
+
this.eventEmitter.removeAllListeners(event);
|
|
2139
|
+
} else {
|
|
2140
|
+
this.eventEmitter.removeAllListeners();
|
|
2141
|
+
}
|
|
2142
|
+
}
|
|
2143
|
+
/**
|
|
2144
|
+
* Asserts that driver is connected.
|
|
2145
|
+
* @throws DriverError if not connected
|
|
2146
|
+
*/
|
|
2147
|
+
assertConnected() {
|
|
2148
|
+
if (!this.connected) {
|
|
2149
|
+
throw new DriverError("Driver is not connected. Call connect() first.", "DRIVER_NOT_CONNECTED");
|
|
2150
|
+
}
|
|
2151
|
+
}
|
|
2152
|
+
/**
|
|
2153
|
+
* Emits driver event.
|
|
2154
|
+
*/
|
|
2155
|
+
emit(event, data) {
|
|
2156
|
+
this.eventEmitter.emit(event, data);
|
|
2157
|
+
}
|
|
2158
|
+
/**
|
|
2159
|
+
* Logs message if logging is enabled.
|
|
2160
|
+
*/
|
|
2161
|
+
log(message, data) {
|
|
2162
|
+
if (this.enableLogging) {
|
|
2163
|
+
if (data) {
|
|
2164
|
+
this.logger.debug(`${message} ${JSON.stringify(data)}`);
|
|
2165
|
+
} else {
|
|
2166
|
+
this.logger.debug(message);
|
|
2167
|
+
}
|
|
2168
|
+
}
|
|
2169
|
+
}
|
|
2170
|
+
/**
|
|
2171
|
+
* Wraps execution with timeout.
|
|
2172
|
+
*/
|
|
2173
|
+
async withTimeout(promise, timeoutMs, operation) {
|
|
2174
|
+
return Promise.race([promise, new Promise((_, reject) => setTimeout(() => reject(new TimeoutError(operation, timeoutMs)), timeoutMs))]);
|
|
2175
|
+
}
|
|
2176
|
+
};
|
|
2177
|
+
|
|
2178
|
+
// src/driver/infrastructure/ioredis.adapter.ts
|
|
2179
|
+
var import_ioredis = __toESM(require("ioredis"));
|
|
2180
|
+
|
|
2181
|
+
// src/types/index.ts
|
|
2182
|
+
var ConnectionStatus = /* @__PURE__ */ ((ConnectionStatus2) => {
|
|
2183
|
+
ConnectionStatus2["DISCONNECTED"] = "disconnected";
|
|
2184
|
+
ConnectionStatus2["CONNECTING"] = "connecting";
|
|
2185
|
+
ConnectionStatus2["CONNECTED"] = "connected";
|
|
2186
|
+
ConnectionStatus2["RECONNECTING"] = "reconnecting";
|
|
2187
|
+
ConnectionStatus2["ERROR"] = "error";
|
|
2188
|
+
return ConnectionStatus2;
|
|
2189
|
+
})(ConnectionStatus || {});
|
|
2190
|
+
function isSingleConnection(config) {
|
|
2191
|
+
return !("type" in config) || config.type === "single";
|
|
2192
|
+
}
|
|
2193
|
+
function isClusterConnection(config) {
|
|
2194
|
+
return "type" in config && config.type === "cluster";
|
|
2195
|
+
}
|
|
2196
|
+
function isSentinelConnection(config) {
|
|
2197
|
+
return "type" in config && config.type === "sentinel";
|
|
2198
|
+
}
|
|
2199
|
+
function hasFactory(options) {
|
|
2200
|
+
return options.useFactory !== void 0;
|
|
2201
|
+
}
|
|
2202
|
+
function hasExisting(options) {
|
|
2203
|
+
return options.useExisting !== void 0;
|
|
2204
|
+
}
|
|
2205
|
+
function hasClass(options) {
|
|
2206
|
+
return options.useClass !== void 0;
|
|
2207
|
+
}
|
|
2208
|
+
|
|
2209
|
+
// src/driver/infrastructure/ioredis.adapter.ts
|
|
2210
|
+
var IoRedisAdapter = class extends BaseRedisDriver {
|
|
2211
|
+
client = null;
|
|
2212
|
+
constructor(config, options) {
|
|
2213
|
+
super(config, options);
|
|
2214
|
+
}
|
|
2215
|
+
async doConnect() {
|
|
2216
|
+
try {
|
|
2217
|
+
if (isSingleConnection(this.config)) {
|
|
2218
|
+
this.client = this.createSingleClient(this.config);
|
|
2219
|
+
} else if (isClusterConnection(this.config)) {
|
|
2220
|
+
this.client = this.createClusterClient(this.config);
|
|
2221
|
+
} else if (isSentinelConnection(this.config)) {
|
|
2222
|
+
this.client = this.createSentinelClient(this.config);
|
|
2223
|
+
} else {
|
|
2224
|
+
throw new ConnectionError("Unknown connection type");
|
|
2225
|
+
}
|
|
2226
|
+
this.setupEventHandlers();
|
|
2227
|
+
await new Promise((resolve, reject) => {
|
|
2228
|
+
const onReady = () => {
|
|
2229
|
+
cleanup();
|
|
2230
|
+
resolve();
|
|
2231
|
+
};
|
|
2232
|
+
const onError = (error) => {
|
|
2233
|
+
cleanup();
|
|
2234
|
+
reject(new ConnectionError("Connection failed", error));
|
|
2235
|
+
};
|
|
2236
|
+
const cleanup = () => {
|
|
2237
|
+
this.client?.off("ready", onReady);
|
|
2238
|
+
this.client?.off("error", onError);
|
|
2239
|
+
};
|
|
2240
|
+
this.client?.once("ready", onReady);
|
|
2241
|
+
this.client?.once("error", onError);
|
|
2242
|
+
if (this.client) {
|
|
2243
|
+
if (this.client instanceof import_ioredis.default) {
|
|
2244
|
+
this.client.connect().catch(reject);
|
|
2245
|
+
} else if (this.client instanceof import_ioredis.Cluster) {
|
|
2246
|
+
this.client.connect().catch(reject);
|
|
2247
|
+
}
|
|
2248
|
+
}
|
|
2249
|
+
});
|
|
2250
|
+
} catch (error) {
|
|
2251
|
+
if (this.client) {
|
|
2252
|
+
this.client.disconnect();
|
|
2253
|
+
this.client = null;
|
|
2254
|
+
}
|
|
2255
|
+
throw error instanceof ConnectionError ? error : new ConnectionError("Failed to connect", error);
|
|
2256
|
+
}
|
|
2257
|
+
}
|
|
2258
|
+
async doDisconnect() {
|
|
2259
|
+
if (!this.client) {
|
|
2260
|
+
return;
|
|
2261
|
+
}
|
|
2262
|
+
try {
|
|
2263
|
+
await this.client.quit();
|
|
2264
|
+
} catch {
|
|
2265
|
+
this.client.disconnect();
|
|
2266
|
+
} finally {
|
|
2267
|
+
this.client = null;
|
|
2268
|
+
}
|
|
2269
|
+
}
|
|
2270
|
+
async executeCommand(command, ...args) {
|
|
2271
|
+
if (!this.client) {
|
|
2272
|
+
throw new ConnectionError("Client not initialized");
|
|
2273
|
+
}
|
|
2274
|
+
try {
|
|
2275
|
+
const method = command.toLowerCase();
|
|
2276
|
+
const fn = this.client[method];
|
|
2277
|
+
if (typeof fn !== "function") {
|
|
2278
|
+
throw new CommandError(command, args);
|
|
2279
|
+
}
|
|
2280
|
+
return await fn.apply(this.client, args);
|
|
2281
|
+
} catch (error) {
|
|
2282
|
+
throw new CommandError(command, args, error);
|
|
2283
|
+
}
|
|
2284
|
+
}
|
|
2285
|
+
createPipeline() {
|
|
2286
|
+
if (!this.client) {
|
|
2287
|
+
throw new ConnectionError("Client not initialized");
|
|
2288
|
+
}
|
|
2289
|
+
const ioPipeline = this.client.pipeline();
|
|
2290
|
+
return new IoRedisPipelineAdapter(ioPipeline);
|
|
2291
|
+
}
|
|
2292
|
+
createMulti() {
|
|
2293
|
+
if (!this.client) {
|
|
2294
|
+
throw new ConnectionError("Client not initialized");
|
|
2295
|
+
}
|
|
2296
|
+
const ioMulti = this.client.multi();
|
|
2297
|
+
return new IoRedisMultiAdapter(ioMulti);
|
|
2298
|
+
}
|
|
2299
|
+
createSingleClient(config) {
|
|
2300
|
+
if (!isSingleConnection(config)) {
|
|
2301
|
+
throw new ConnectionError("Invalid single connection configuration");
|
|
2302
|
+
}
|
|
2303
|
+
const options = {
|
|
2304
|
+
host: config.host ?? "localhost",
|
|
2305
|
+
port: config.port ?? 6379,
|
|
2306
|
+
password: config.password,
|
|
2307
|
+
db: config.db ?? 0,
|
|
2308
|
+
keyPrefix: config.keyPrefix,
|
|
2309
|
+
connectTimeout: config.connectTimeout ?? 1e4,
|
|
2310
|
+
commandTimeout: config.commandTimeout ?? 5e3,
|
|
2311
|
+
keepAlive: config.keepAlive ?? 0,
|
|
2312
|
+
enableReadyCheck: config.enableAutoReconnect ?? true,
|
|
2313
|
+
maxRetriesPerRequest: config.maxRetriesPerRequest ?? 3,
|
|
2314
|
+
retryStrategy: config.retryStrategy,
|
|
2315
|
+
reconnectOnError: config.reconnectOnError,
|
|
2316
|
+
lazyConnect: true
|
|
2317
|
+
};
|
|
2318
|
+
if (config.tls?.enabled) {
|
|
2319
|
+
options.tls = {
|
|
2320
|
+
ca: config.tls.ca,
|
|
2321
|
+
cert: config.tls.cert,
|
|
2322
|
+
key: config.tls.key,
|
|
2323
|
+
rejectUnauthorized: config.tls.rejectUnauthorized ?? true
|
|
2324
|
+
};
|
|
2325
|
+
}
|
|
2326
|
+
return new import_ioredis.default(options);
|
|
2327
|
+
}
|
|
2328
|
+
createClusterClient(config) {
|
|
2329
|
+
if (!isClusterConnection(config)) {
|
|
2330
|
+
throw new ConnectionError("Invalid cluster configuration");
|
|
2331
|
+
}
|
|
2332
|
+
const nodes = config.nodes.map((node) => ({
|
|
2333
|
+
host: node.host,
|
|
2334
|
+
port: node.port
|
|
2335
|
+
}));
|
|
2336
|
+
const options = {
|
|
2337
|
+
redisOptions: {
|
|
2338
|
+
password: config.password,
|
|
2339
|
+
db: config.db ?? 0,
|
|
2340
|
+
keyPrefix: config.keyPrefix,
|
|
2341
|
+
connectTimeout: config.connectTimeout ?? 1e4,
|
|
2342
|
+
commandTimeout: config.commandTimeout ?? 5e3,
|
|
2343
|
+
maxRetriesPerRequest: config.maxRetriesPerRequest ?? 3
|
|
2344
|
+
},
|
|
2345
|
+
clusterRetryStrategy: config.retryStrategy,
|
|
2346
|
+
enableReadyCheck: config.clusterOptions?.enableReadyCheck ?? false,
|
|
2347
|
+
maxRedirections: config.clusterOptions?.maxRedirections ?? 16,
|
|
2348
|
+
retryDelayOnClusterDown: config.clusterOptions?.retryDelayOnClusterDown ?? 100,
|
|
2349
|
+
retryDelayOnFailover: config.clusterOptions?.retryDelayOnFailover ?? 100,
|
|
2350
|
+
scaleReads: config.clusterOptions?.scaleReads ?? "master",
|
|
2351
|
+
lazyConnect: true,
|
|
2352
|
+
// Pass through any additional cluster options (e.g., natMap)
|
|
2353
|
+
...config.clusterOptions
|
|
2354
|
+
};
|
|
2355
|
+
return new import_ioredis.Cluster(nodes, options);
|
|
2356
|
+
}
|
|
2357
|
+
createSentinelClient(config) {
|
|
2358
|
+
if (!isSentinelConnection(config)) {
|
|
2359
|
+
throw new ConnectionError("Invalid sentinel configuration");
|
|
2360
|
+
}
|
|
2361
|
+
const options = {
|
|
2362
|
+
sentinels: config.sentinels,
|
|
2363
|
+
name: config.name,
|
|
2364
|
+
password: config.password,
|
|
2365
|
+
db: config.db ?? 0,
|
|
2366
|
+
keyPrefix: config.keyPrefix,
|
|
2367
|
+
connectTimeout: config.connectTimeout ?? 1e4,
|
|
2368
|
+
commandTimeout: config.commandTimeout ?? 5e3,
|
|
2369
|
+
maxRetriesPerRequest: config.maxRetriesPerRequest ?? 3,
|
|
2370
|
+
retryStrategy: config.retryStrategy,
|
|
2371
|
+
sentinelRetryStrategy: config.sentinelOptions?.sentinelRetryStrategy,
|
|
2372
|
+
sentinelPassword: config.sentinelOptions?.sentinelPassword,
|
|
2373
|
+
enableTLSForSentinelMode: config.sentinelOptions?.enableTLSForSentinelMode,
|
|
2374
|
+
lazyConnect: true,
|
|
2375
|
+
// NAT mapping for Docker/firewall scenarios
|
|
2376
|
+
// Maps internal Redis addresses to external accessible addresses
|
|
2377
|
+
natMap: config.sentinelOptions?.natMap
|
|
2378
|
+
};
|
|
2379
|
+
return new import_ioredis.default(options);
|
|
2380
|
+
}
|
|
2381
|
+
setupEventHandlers() {
|
|
2382
|
+
if (!this.client) {
|
|
2383
|
+
return;
|
|
2384
|
+
}
|
|
2385
|
+
this.client.on("connect", () => {
|
|
2386
|
+
this.emit("connect" /* CONNECT */);
|
|
2387
|
+
});
|
|
2388
|
+
this.client.on("ready", () => {
|
|
2389
|
+
this.emit("ready" /* READY */);
|
|
2390
|
+
});
|
|
2391
|
+
this.client.on("error", (error) => {
|
|
2392
|
+
this.emit("error" /* ERROR */, error);
|
|
2393
|
+
});
|
|
2394
|
+
this.client.on("close", () => {
|
|
2395
|
+
this.emit("close" /* CLOSE */);
|
|
2396
|
+
});
|
|
2397
|
+
this.client.on("reconnecting", () => {
|
|
2398
|
+
this.emit("reconnecting" /* RECONNECTING */);
|
|
2399
|
+
});
|
|
2400
|
+
this.client.on("end", () => {
|
|
2401
|
+
this.emit("end" /* END */);
|
|
2402
|
+
});
|
|
2403
|
+
}
|
|
2404
|
+
/**
|
|
2405
|
+
* Gets underlying ioredis client.
|
|
2406
|
+
* For advanced usage only.
|
|
2407
|
+
*/
|
|
2408
|
+
getClient() {
|
|
2409
|
+
return this.client;
|
|
2410
|
+
}
|
|
2411
|
+
};
|
|
2412
|
+
var IoRedisPipelineAdapter = class {
|
|
2413
|
+
constructor(pipeline) {
|
|
2414
|
+
this.pipeline = pipeline;
|
|
2415
|
+
}
|
|
2416
|
+
async exec() {
|
|
2417
|
+
const results = await this.pipeline.exec();
|
|
2418
|
+
if (!results) {
|
|
2419
|
+
return [];
|
|
2420
|
+
}
|
|
2421
|
+
return results.map(([error, result]) => [error, result]);
|
|
2422
|
+
}
|
|
2423
|
+
get(key) {
|
|
2424
|
+
this.pipeline.get(key);
|
|
2425
|
+
return this;
|
|
2426
|
+
}
|
|
2427
|
+
set(key, value, options) {
|
|
2428
|
+
if (options?.ex) {
|
|
2429
|
+
this.pipeline.set(key, value, "EX", options.ex);
|
|
2430
|
+
} else if (options?.px) {
|
|
2431
|
+
this.pipeline.set(key, value, "PX", options.px);
|
|
2432
|
+
} else {
|
|
2433
|
+
this.pipeline.set(key, value);
|
|
2434
|
+
}
|
|
2435
|
+
return this;
|
|
2436
|
+
}
|
|
2437
|
+
del(...keys) {
|
|
2438
|
+
this.pipeline.del(...keys);
|
|
2439
|
+
return this;
|
|
2440
|
+
}
|
|
2441
|
+
mget(...keys) {
|
|
2442
|
+
this.pipeline.mget(...keys);
|
|
2443
|
+
return this;
|
|
2444
|
+
}
|
|
2445
|
+
mset(data) {
|
|
2446
|
+
this.pipeline.mset(data);
|
|
2447
|
+
return this;
|
|
2448
|
+
}
|
|
2449
|
+
expire(key, seconds) {
|
|
2450
|
+
this.pipeline.expire(key, seconds);
|
|
2451
|
+
return this;
|
|
2452
|
+
}
|
|
2453
|
+
ttl(key) {
|
|
2454
|
+
this.pipeline.ttl(key);
|
|
2455
|
+
return this;
|
|
2456
|
+
}
|
|
2457
|
+
incr(key) {
|
|
2458
|
+
this.pipeline.incr(key);
|
|
2459
|
+
return this;
|
|
2460
|
+
}
|
|
2461
|
+
incrby(key, increment) {
|
|
2462
|
+
this.pipeline.incrby(key, increment);
|
|
2463
|
+
return this;
|
|
2464
|
+
}
|
|
2465
|
+
hget(key, field) {
|
|
2466
|
+
this.pipeline.hget(key, field);
|
|
2467
|
+
return this;
|
|
2468
|
+
}
|
|
2469
|
+
hset(key, field, value) {
|
|
2470
|
+
this.pipeline.hset(key, field, value);
|
|
2471
|
+
return this;
|
|
2472
|
+
}
|
|
2473
|
+
hmset(key, data) {
|
|
2474
|
+
this.pipeline.hmset(key, data);
|
|
2475
|
+
return this;
|
|
2476
|
+
}
|
|
2477
|
+
hgetall(key) {
|
|
2478
|
+
this.pipeline.hgetall(key);
|
|
2479
|
+
return this;
|
|
2480
|
+
}
|
|
2481
|
+
lpush(key, ...values) {
|
|
2482
|
+
this.pipeline.lpush(key, ...values);
|
|
2483
|
+
return this;
|
|
2484
|
+
}
|
|
2485
|
+
rpush(key, ...values) {
|
|
2486
|
+
this.pipeline.rpush(key, ...values);
|
|
2487
|
+
return this;
|
|
2488
|
+
}
|
|
2489
|
+
sadd(key, ...members) {
|
|
2490
|
+
this.pipeline.sadd(key, ...members);
|
|
2491
|
+
return this;
|
|
2492
|
+
}
|
|
2493
|
+
srem(key, ...members) {
|
|
2494
|
+
this.pipeline.srem(key, ...members);
|
|
2495
|
+
return this;
|
|
2496
|
+
}
|
|
2497
|
+
zadd(key, ...args) {
|
|
2498
|
+
this.pipeline.zadd(key, ...args);
|
|
2499
|
+
return this;
|
|
2500
|
+
}
|
|
2501
|
+
zrem(key, ...members) {
|
|
2502
|
+
this.pipeline.zrem(key, ...members);
|
|
2503
|
+
return this;
|
|
2504
|
+
}
|
|
2505
|
+
};
|
|
2506
|
+
var IoRedisMultiAdapter = class extends IoRedisPipelineAdapter {
|
|
2507
|
+
constructor(multi) {
|
|
2508
|
+
super(multi);
|
|
2509
|
+
this.multi = multi;
|
|
2510
|
+
}
|
|
2511
|
+
discard() {
|
|
2512
|
+
this.multi.discard();
|
|
2513
|
+
}
|
|
2514
|
+
};
|
|
2515
|
+
|
|
2516
|
+
// src/driver/infrastructure/node-redis.adapter.ts
|
|
2517
|
+
var import_redis = require("redis");
|
|
2518
|
+
var RETRY_BASE_DELAY_MS = 100;
|
|
2519
|
+
var RETRY_MAX_DELAY_MS = 3e4;
|
|
2520
|
+
var NodeRedisAdapter = class extends BaseRedisDriver {
|
|
2521
|
+
client = null;
|
|
2522
|
+
isSentinel = false;
|
|
2523
|
+
isCluster = false;
|
|
2524
|
+
keyPrefix;
|
|
2525
|
+
constructor(config, options) {
|
|
2526
|
+
super(config, options);
|
|
2527
|
+
this.keyPrefix = config.keyPrefix ?? "";
|
|
2528
|
+
}
|
|
2529
|
+
async doConnect() {
|
|
2530
|
+
try {
|
|
2531
|
+
if (isSingleConnection(this.config)) {
|
|
2532
|
+
this.client = this.createSingleClient(this.config);
|
|
2533
|
+
this.isSentinel = false;
|
|
2534
|
+
} else if (isClusterConnection(this.config)) {
|
|
2535
|
+
this.client = this.createClusterClient(this.config);
|
|
2536
|
+
this.isSentinel = false;
|
|
2537
|
+
this.isCluster = true;
|
|
2538
|
+
} else if (isSentinelConnection(this.config)) {
|
|
2539
|
+
this.client = await this.createSentinelClient(this.config);
|
|
2540
|
+
this.isSentinel = true;
|
|
2541
|
+
} else {
|
|
2542
|
+
throw new ConnectionError("Unknown connection type");
|
|
2543
|
+
}
|
|
2544
|
+
this.setupEventHandlers();
|
|
2545
|
+
if (!this.isSentinel) {
|
|
2546
|
+
await this.client.connect();
|
|
2547
|
+
}
|
|
2548
|
+
} catch (error) {
|
|
2549
|
+
if (this.client) {
|
|
2550
|
+
await this.safeDisconnect();
|
|
2551
|
+
this.client = null;
|
|
2552
|
+
}
|
|
2553
|
+
throw error instanceof ConnectionError ? error : new ConnectionError("Failed to connect", error);
|
|
2554
|
+
}
|
|
2555
|
+
}
|
|
2556
|
+
async doDisconnect() {
|
|
2557
|
+
if (!this.client) {
|
|
2558
|
+
return;
|
|
2559
|
+
}
|
|
2560
|
+
try {
|
|
2561
|
+
if (this.isSentinel) {
|
|
2562
|
+
await this.client.close();
|
|
2563
|
+
} else {
|
|
2564
|
+
await this.client.quit();
|
|
2565
|
+
}
|
|
2566
|
+
} catch {
|
|
2567
|
+
await this.safeDisconnect();
|
|
2568
|
+
} finally {
|
|
2569
|
+
this.client = null;
|
|
2570
|
+
this.isSentinel = false;
|
|
2571
|
+
this.isCluster = false;
|
|
2572
|
+
}
|
|
2573
|
+
}
|
|
2574
|
+
async executeCommand(command, ...args) {
|
|
2575
|
+
if (!this.client) {
|
|
2576
|
+
throw new ConnectionError("Client not initialized");
|
|
2577
|
+
}
|
|
2578
|
+
try {
|
|
2579
|
+
const commandArgs = this.convertArgs(args);
|
|
2580
|
+
const prefixedArgs = this.applyKeyPrefix(command, commandArgs);
|
|
2581
|
+
if (this.isSentinel) {
|
|
2582
|
+
return await this.client.use(async (client) => {
|
|
2583
|
+
return await client.sendCommand([command, ...prefixedArgs]);
|
|
2584
|
+
});
|
|
2585
|
+
}
|
|
2586
|
+
if (this.isCluster) {
|
|
2587
|
+
const firstKey = this.getFirstKey(command, prefixedArgs);
|
|
2588
|
+
const isReadonly = this.isReadonlyCommand(command);
|
|
2589
|
+
return await this.client.sendCommand(firstKey, isReadonly, [command, ...prefixedArgs]);
|
|
2590
|
+
}
|
|
2591
|
+
return await this.client.sendCommand([command, ...prefixedArgs]);
|
|
2592
|
+
} catch (error) {
|
|
2593
|
+
throw new CommandError(command, args, error);
|
|
2594
|
+
}
|
|
2595
|
+
}
|
|
2596
|
+
createPipeline() {
|
|
2597
|
+
if (!this.client) {
|
|
2598
|
+
throw new ConnectionError("Client not initialized");
|
|
2599
|
+
}
|
|
2600
|
+
if (this.isSentinel) {
|
|
2601
|
+
return new NodeRedisSentinelPipelineAdapter(this.client, this.keyPrefix);
|
|
2602
|
+
}
|
|
2603
|
+
return new NodeRedisPipelineAdapter(this.client, this.keyPrefix);
|
|
2604
|
+
}
|
|
2605
|
+
createMulti() {
|
|
2606
|
+
if (!this.client) {
|
|
2607
|
+
throw new ConnectionError("Client not initialized");
|
|
2608
|
+
}
|
|
2609
|
+
if (this.isSentinel) {
|
|
2610
|
+
return new NodeRedisSentinelMultiAdapter(this.client, this.keyPrefix);
|
|
2611
|
+
}
|
|
2612
|
+
return new NodeRedisMultiAdapter(this.client, this.keyPrefix);
|
|
2613
|
+
}
|
|
2614
|
+
/**
|
|
2615
|
+
* Override hgetall to convert array response to object.
|
|
2616
|
+
* Node-redis sendCommand returns ['field1', 'value1', 'field2', 'value2']
|
|
2617
|
+
* but we need { field1: 'value1', field2: 'value2' }
|
|
2618
|
+
*/
|
|
2619
|
+
async hgetall(key) {
|
|
2620
|
+
this.assertConnected();
|
|
2621
|
+
const result = await this.executeCommand("HGETALL", key);
|
|
2622
|
+
if (Array.isArray(result)) {
|
|
2623
|
+
const obj = {};
|
|
2624
|
+
for (let i = 0; i < result.length; i += 2) {
|
|
2625
|
+
obj[result[i]] = result[i + 1];
|
|
2626
|
+
}
|
|
2627
|
+
return obj;
|
|
2628
|
+
}
|
|
2629
|
+
return result ?? {};
|
|
2630
|
+
}
|
|
2631
|
+
/**
|
|
2632
|
+
* Override scan to normalize cursor response.
|
|
2633
|
+
* Node-redis sendCommand returns [cursor, [keys...]]
|
|
2634
|
+
*/
|
|
2635
|
+
async scan(cursor, options) {
|
|
2636
|
+
this.assertConnected();
|
|
2637
|
+
const args = [cursor];
|
|
2638
|
+
if (options?.match) {
|
|
2639
|
+
args.push("MATCH", options.match);
|
|
2640
|
+
}
|
|
2641
|
+
if (options?.count) {
|
|
2642
|
+
args.push("COUNT", options.count);
|
|
2643
|
+
}
|
|
2644
|
+
const result = await this.executeCommand("SCAN", ...args);
|
|
2645
|
+
if (Array.isArray(result) && result.length === 2) {
|
|
2646
|
+
return [String(result[0]), result[1]];
|
|
2647
|
+
}
|
|
2648
|
+
return ["0", []];
|
|
2649
|
+
}
|
|
2650
|
+
/**
|
|
2651
|
+
* Override hscan to normalize cursor response.
|
|
2652
|
+
*/
|
|
2653
|
+
async hscan(key, cursor, options) {
|
|
2654
|
+
this.assertConnected();
|
|
2655
|
+
const args = [key, cursor];
|
|
2656
|
+
if (options?.match) {
|
|
2657
|
+
args.push("MATCH", options.match);
|
|
2658
|
+
}
|
|
2659
|
+
if (options?.count) {
|
|
2660
|
+
args.push("COUNT", options.count);
|
|
2661
|
+
}
|
|
2662
|
+
const result = await this.executeCommand("HSCAN", ...args);
|
|
2663
|
+
if (Array.isArray(result) && result.length === 2) {
|
|
2664
|
+
return [String(result[0]), result[1]];
|
|
2665
|
+
}
|
|
2666
|
+
return ["0", []];
|
|
2667
|
+
}
|
|
2668
|
+
/**
|
|
2669
|
+
* Override sscan to normalize cursor response.
|
|
2670
|
+
*/
|
|
2671
|
+
async sscan(key, cursor, options) {
|
|
2672
|
+
this.assertConnected();
|
|
2673
|
+
const args = [key, cursor];
|
|
2674
|
+
if (options?.match) {
|
|
2675
|
+
args.push("MATCH", options.match);
|
|
2676
|
+
}
|
|
2677
|
+
if (options?.count) {
|
|
2678
|
+
args.push("COUNT", options.count);
|
|
2679
|
+
}
|
|
2680
|
+
const result = await this.executeCommand("SSCAN", ...args);
|
|
2681
|
+
if (Array.isArray(result) && result.length === 2) {
|
|
2682
|
+
return [String(result[0]), result[1]];
|
|
2683
|
+
}
|
|
2684
|
+
return ["0", []];
|
|
2685
|
+
}
|
|
2686
|
+
/**
|
|
2687
|
+
* Override zscan to normalize cursor response.
|
|
2688
|
+
*/
|
|
2689
|
+
async zscan(key, cursor, options) {
|
|
2690
|
+
this.assertConnected();
|
|
2691
|
+
const args = [key, cursor];
|
|
2692
|
+
if (options?.match) {
|
|
2693
|
+
args.push("MATCH", options.match);
|
|
2694
|
+
}
|
|
2695
|
+
if (options?.count) {
|
|
2696
|
+
args.push("COUNT", options.count);
|
|
2697
|
+
}
|
|
2698
|
+
const result = await this.executeCommand("ZSCAN", ...args);
|
|
2699
|
+
if (Array.isArray(result) && result.length === 2) {
|
|
2700
|
+
return [String(result[0]), result[1]];
|
|
2701
|
+
}
|
|
2702
|
+
return ["0", []];
|
|
2703
|
+
}
|
|
2704
|
+
/**
|
|
2705
|
+
* Override info to return string.
|
|
2706
|
+
*/
|
|
2707
|
+
async info(section) {
|
|
2708
|
+
this.assertConnected();
|
|
2709
|
+
const result = await this.executeCommand("INFO", ...section ? [section] : []);
|
|
2710
|
+
return String(result ?? "");
|
|
2711
|
+
}
|
|
2712
|
+
createSingleClient(config) {
|
|
2713
|
+
if (!isSingleConnection(config)) {
|
|
2714
|
+
throw new ConnectionError("Invalid single connection configuration");
|
|
2715
|
+
}
|
|
2716
|
+
const options = {
|
|
2717
|
+
socket: {
|
|
2718
|
+
host: config.host ?? "localhost",
|
|
2719
|
+
port: config.port ?? 6379,
|
|
2720
|
+
connectTimeout: config.connectTimeout ?? 1e4,
|
|
2721
|
+
reconnectStrategy: config.retryStrategy ? (retries) => config.retryStrategy?.(retries) ?? false : this.getDefaultReconnectStrategy()
|
|
2722
|
+
},
|
|
2723
|
+
password: config.password,
|
|
2724
|
+
database: config.db ?? 0,
|
|
2725
|
+
commandsQueueMaxLength: config.enableOfflineQueue === false ? 0 : void 0
|
|
2726
|
+
};
|
|
2727
|
+
if (config.tls?.enabled) {
|
|
2728
|
+
options.socket = {
|
|
2729
|
+
...options.socket,
|
|
2730
|
+
tls: true,
|
|
2731
|
+
ca: config.tls.ca,
|
|
2732
|
+
cert: config.tls.cert,
|
|
2733
|
+
key: config.tls.key,
|
|
2734
|
+
rejectUnauthorized: config.tls.rejectUnauthorized ?? true
|
|
2735
|
+
};
|
|
2736
|
+
}
|
|
2737
|
+
return (0, import_redis.createClient)(options);
|
|
2738
|
+
}
|
|
2739
|
+
createClusterClient(config) {
|
|
2740
|
+
if (!isClusterConnection(config)) {
|
|
2741
|
+
throw new ConnectionError("Invalid cluster configuration");
|
|
2742
|
+
}
|
|
2743
|
+
const options = {
|
|
2744
|
+
rootNodes: config.nodes.map((node) => ({
|
|
2745
|
+
socket: {
|
|
2746
|
+
host: node.host,
|
|
2747
|
+
port: node.port
|
|
2748
|
+
}
|
|
2749
|
+
})),
|
|
2750
|
+
defaults: {
|
|
2751
|
+
password: config.password,
|
|
2752
|
+
database: config.db ?? 0,
|
|
2753
|
+
socket: {
|
|
2754
|
+
connectTimeout: config.connectTimeout ?? 1e4,
|
|
2755
|
+
reconnectStrategy: config.retryStrategy ? (retries) => config.retryStrategy?.(retries) ?? false : this.getDefaultReconnectStrategy()
|
|
2756
|
+
}
|
|
2757
|
+
},
|
|
2758
|
+
maxCommandRedirections: config.clusterOptions?.maxRedirections ?? 16,
|
|
2759
|
+
useReplicas: config.clusterOptions?.scaleReads === "slave"
|
|
2760
|
+
};
|
|
2761
|
+
if (config.clusterOptions?.natMap) {
|
|
2762
|
+
options.nodeAddressMap = config.clusterOptions.natMap;
|
|
2763
|
+
}
|
|
2764
|
+
return (0, import_redis.createCluster)(options);
|
|
2765
|
+
}
|
|
2766
|
+
/**
|
|
2767
|
+
* Creates a Redis Sentinel client using the proper createSentinel API.
|
|
2768
|
+
* Returns already connected sentinel instance.
|
|
2769
|
+
*
|
|
2770
|
+
* NOTE: node-redis does not support natMap/nodeAddressMap for Sentinel.
|
|
2771
|
+
* For Docker/NAT environments, use ioredis driver which supports sentinelOptions.natMap.
|
|
2772
|
+
*/
|
|
2773
|
+
async createSentinelClient(config) {
|
|
2774
|
+
if (!isSentinelConnection(config)) {
|
|
2775
|
+
throw new ConnectionError("Invalid sentinel configuration");
|
|
2776
|
+
}
|
|
2777
|
+
const sentinelConfig = config;
|
|
2778
|
+
const sentinelRootNodes = sentinelConfig.sentinels.map((sentinel2) => ({
|
|
2779
|
+
host: sentinel2.host,
|
|
2780
|
+
port: sentinel2.port
|
|
2781
|
+
}));
|
|
2782
|
+
const options = {
|
|
2783
|
+
name: sentinelConfig.name,
|
|
2784
|
+
sentinelRootNodes,
|
|
2785
|
+
// Pool size for master connections
|
|
2786
|
+
masterPoolSize: sentinelConfig.sentinelOptions?.masterPoolSize ?? 1,
|
|
2787
|
+
// Pool size for replica connections (0 = no replicas)
|
|
2788
|
+
replicaPoolSize: sentinelConfig.sentinelOptions?.replicaPoolSize ?? 0,
|
|
2789
|
+
// Interval to scan for topology changes (ms)
|
|
2790
|
+
scanInterval: sentinelConfig.sentinelOptions?.scanInterval ?? 1e4,
|
|
2791
|
+
// Max retries on topology change
|
|
2792
|
+
maxCommandRediscovers: sentinelConfig.sentinelOptions?.maxCommandRediscovers ?? 16
|
|
2793
|
+
};
|
|
2794
|
+
const nodeClientOptions = {
|
|
2795
|
+
password: sentinelConfig.password,
|
|
2796
|
+
database: sentinelConfig.db ?? 0,
|
|
2797
|
+
socket: {
|
|
2798
|
+
connectTimeout: sentinelConfig.connectTimeout ?? 1e4,
|
|
2799
|
+
reconnectStrategy: sentinelConfig.retryStrategy ? (retries) => sentinelConfig.retryStrategy?.(retries) ?? false : this.getDefaultReconnectStrategy()
|
|
2800
|
+
}
|
|
2801
|
+
};
|
|
2802
|
+
if (sentinelConfig.tls?.enabled) {
|
|
2803
|
+
nodeClientOptions.socket = {
|
|
2804
|
+
...nodeClientOptions.socket,
|
|
2805
|
+
tls: true,
|
|
2806
|
+
ca: sentinelConfig.tls.ca,
|
|
2807
|
+
cert: sentinelConfig.tls.cert,
|
|
2808
|
+
key: sentinelConfig.tls.key,
|
|
2809
|
+
rejectUnauthorized: sentinelConfig.tls.rejectUnauthorized ?? true
|
|
2810
|
+
};
|
|
2811
|
+
}
|
|
2812
|
+
options.nodeClientOptions = nodeClientOptions;
|
|
2813
|
+
const sentinelClientOptions = {
|
|
2814
|
+
socket: {
|
|
2815
|
+
connectTimeout: sentinelConfig.connectTimeout ?? 1e4
|
|
2816
|
+
}
|
|
2817
|
+
};
|
|
2818
|
+
if (sentinelConfig.sentinelOptions?.sentinelPassword) {
|
|
2819
|
+
sentinelClientOptions.password = sentinelConfig.sentinelOptions.sentinelPassword;
|
|
2820
|
+
}
|
|
2821
|
+
if (sentinelConfig.sentinelOptions?.enableTLSForSentinelMode && sentinelConfig.tls?.enabled) {
|
|
2822
|
+
sentinelClientOptions.socket = {
|
|
2823
|
+
...sentinelClientOptions.socket,
|
|
2824
|
+
tls: true,
|
|
2825
|
+
ca: sentinelConfig.tls.ca,
|
|
2826
|
+
cert: sentinelConfig.tls.cert,
|
|
2827
|
+
key: sentinelConfig.tls.key,
|
|
2828
|
+
rejectUnauthorized: sentinelConfig.tls.rejectUnauthorized ?? true
|
|
2829
|
+
};
|
|
2830
|
+
}
|
|
2831
|
+
options.sentinelClientOptions = sentinelClientOptions;
|
|
2832
|
+
const sentinel = (0, import_redis.createSentinel)(options);
|
|
2833
|
+
sentinel.on("error", (error) => {
|
|
2834
|
+
this.emit("error" /* ERROR */, error);
|
|
2835
|
+
});
|
|
2836
|
+
await sentinel.connect();
|
|
2837
|
+
return sentinel;
|
|
2838
|
+
}
|
|
2839
|
+
/**
|
|
2840
|
+
* Safely disconnect the client, ignoring errors.
|
|
2841
|
+
*/
|
|
2842
|
+
async safeDisconnect() {
|
|
2843
|
+
try {
|
|
2844
|
+
if (this.isSentinel) {
|
|
2845
|
+
await this.client?.close();
|
|
2846
|
+
} else if (this.client) {
|
|
2847
|
+
await this.client.disconnect();
|
|
2848
|
+
}
|
|
2849
|
+
} catch {
|
|
2850
|
+
}
|
|
2851
|
+
}
|
|
2852
|
+
/**
|
|
2853
|
+
* Default reconnection strategy: exponential backoff with max 30s delay.
|
|
2854
|
+
* Returns a function that calculates delay based on retry count.
|
|
2855
|
+
*/
|
|
2856
|
+
getDefaultReconnectStrategy() {
|
|
2857
|
+
return (retries) => {
|
|
2858
|
+
if (retries > 10) {
|
|
2859
|
+
return false;
|
|
2860
|
+
}
|
|
2861
|
+
return Math.min(retries * RETRY_BASE_DELAY_MS * Math.pow(2, retries - 1), RETRY_MAX_DELAY_MS);
|
|
2862
|
+
};
|
|
2863
|
+
}
|
|
2864
|
+
/**
|
|
2865
|
+
* Get the first key from command arguments for cluster slot routing.
|
|
2866
|
+
* Returns undefined for commands that don't require a key.
|
|
2867
|
+
*/
|
|
2868
|
+
getFirstKey(command, args) {
|
|
2869
|
+
const cmd = command.toUpperCase();
|
|
2870
|
+
const noKeyCommands = /* @__PURE__ */ new Set(["PING", "INFO", "DBSIZE", "TIME", "LASTSAVE", "BGSAVE", "BGREWRITEAOF", "SLAVEOF", "REPLICAOF", "DEBUG", "CONFIG", "CLIENT", "CLUSTER", "COMMAND", "FLUSHALL", "FLUSHDB", "SELECT", "SWAPDB", "PUBLISH", "PUBSUB", "SCRIPT", "SLOWLOG", "ACL", "MODULE", "LATENCY", "MEMORY", "OBJECT", "WAIT", "SHUTDOWN", "SYNC", "PSYNC", "REPLCONF", "ASKING", "READONLY", "READWRITE", "AUTH", "ECHO", "QUIT"]);
|
|
2871
|
+
if (noKeyCommands.has(cmd) || args.length === 0) {
|
|
2872
|
+
return void 0;
|
|
2873
|
+
}
|
|
2874
|
+
if ((cmd === "EVAL" || cmd === "EVALSHA") && args.length > 2) {
|
|
2875
|
+
const numkeys = parseInt(args[1], 10);
|
|
2876
|
+
if (numkeys > 0) {
|
|
2877
|
+
return args[2];
|
|
2878
|
+
}
|
|
2879
|
+
return void 0;
|
|
2880
|
+
}
|
|
2881
|
+
return args[0];
|
|
2882
|
+
}
|
|
2883
|
+
/**
|
|
2884
|
+
* Check if a command is readonly (doesn't modify data).
|
|
2885
|
+
* Used for cluster to route reads to replicas if enabled.
|
|
2886
|
+
*/
|
|
2887
|
+
isReadonlyCommand(command) {
|
|
2888
|
+
const readonlyCommands = /* @__PURE__ */ new Set(["GET", "MGET", "STRLEN", "GETRANGE", "SUBSTR", "BITCOUNT", "BITPOS", "GETBIT", "EXISTS", "TYPE", "TTL", "PTTL", "EXPIRETIME", "PEXPIRETIME", "HGET", "HMGET", "HGETALL", "HKEYS", "HVALS", "HLEN", "HEXISTS", "HSCAN", "HSTRLEN", "HRANDFIELD", "LINDEX", "LLEN", "LRANGE", "LPOS", "SMEMBERS", "SISMEMBER", "SCARD", "SRANDMEMBER", "SSCAN", "SMISMEMBER", "ZRANGE", "ZREVRANGE", "ZRANGEBYSCORE", "ZREVRANGEBYSCORE", "ZRANK", "ZREVRANK", "ZCOUNT", "ZLEXCOUNT", "ZCARD", "ZSCORE", "ZMSCORE", "ZRANDMEMBER", "ZSCAN", "PFCOUNT", "GEODIST", "GEOHASH", "GEOPOS", "GEORADIUS_RO", "GEORADIUSBYMEMBER_RO", "GEOSEARCH", "XLEN", "XRANGE", "XREVRANGE", "XREAD", "XINFO", "XPENDING", "SCAN", "KEYS", "DBSIZE", "DEBUG", "DUMP", "OBJECT", "RANDOMKEY", "INFO", "PING", "TIME", "LASTSAVE", "ECHO"]);
|
|
2889
|
+
return readonlyCommands.has(command.toUpperCase());
|
|
2890
|
+
}
|
|
2891
|
+
/**
|
|
2892
|
+
* Convert arguments to proper types for sendCommand.
|
|
2893
|
+
* sendCommand expects string[] but we need to handle numbers properly.
|
|
2894
|
+
*/
|
|
2895
|
+
convertArgs(args) {
|
|
2896
|
+
return args.map((arg) => {
|
|
2897
|
+
if (arg === null || arg === void 0) {
|
|
2898
|
+
return "";
|
|
2899
|
+
}
|
|
2900
|
+
if (typeof arg === "number") {
|
|
2901
|
+
return String(arg);
|
|
2902
|
+
}
|
|
2903
|
+
if (typeof arg === "boolean") {
|
|
2904
|
+
return arg ? "1" : "0";
|
|
2905
|
+
}
|
|
2906
|
+
if (Buffer.isBuffer(arg)) {
|
|
2907
|
+
return arg.toString();
|
|
2908
|
+
}
|
|
2909
|
+
if (Array.isArray(arg)) {
|
|
2910
|
+
return arg.map((item) => String(item)).join(" ");
|
|
2911
|
+
}
|
|
2912
|
+
if (typeof arg === "object") {
|
|
2913
|
+
return JSON.stringify(arg);
|
|
2914
|
+
}
|
|
2915
|
+
return String(arg);
|
|
2916
|
+
});
|
|
2917
|
+
}
|
|
2918
|
+
/**
|
|
2919
|
+
* Apply key prefix to key-based commands.
|
|
2920
|
+
* This emulates ioredis keyPrefix functionality.
|
|
2921
|
+
*/
|
|
2922
|
+
applyKeyPrefix(command, args) {
|
|
2923
|
+
if (!this.keyPrefix || args.length === 0) {
|
|
2924
|
+
return args;
|
|
2925
|
+
}
|
|
2926
|
+
const singleKeyCommands = /* @__PURE__ */ new Set(["GET", "SET", "DEL", "EXISTS", "EXPIRE", "EXPIREAT", "PEXPIRE", "PEXPIREAT", "TTL", "PTTL", "PERSIST", "TYPE", "RENAME", "RENAMENX", "DUMP", "RESTORE", "INCR", "INCRBY", "INCRBYFLOAT", "DECR", "DECRBY", "APPEND", "STRLEN", "GETRANGE", "SETRANGE", "GETSET", "SETNX", "SETEX", "PSETEX", "GETEX", "GETDEL", "HGET", "HSET", "HSETNX", "HDEL", "HEXISTS", "HGETALL", "HINCRBY", "HINCRBYFLOAT", "HKEYS", "HVALS", "HLEN", "HMGET", "HMSET", "HSCAN", "HSTRLEN", "HRANDFIELD", "LPUSH", "RPUSH", "LPOP", "RPOP", "LLEN", "LRANGE", "LINDEX", "LSET", "LREM", "LTRIM", "LINSERT", "LPOS", "LPUSHX", "RPUSHX", "SADD", "SREM", "SMEMBERS", "SISMEMBER", "SCARD", "SPOP", "SRANDMEMBER", "SSCAN", "SMISMEMBER", "ZADD", "ZREM", "ZSCORE", "ZRANK", "ZREVRANK", "ZRANGE", "ZREVRANGE", "ZRANGEBYSCORE", "ZREVRANGEBYSCORE", "ZCOUNT", "ZLEXCOUNT", "ZCARD", "ZINCRBY", "ZPOPMIN", "ZPOPMAX", "ZMSCORE", "ZRANDMEMBER", "ZSCAN", "PFADD", "PFCOUNT", "GEOADD", "GEODIST", "GEOHASH", "GEOPOS", "GEOSEARCH", "SETBIT", "GETBIT", "BITCOUNT", "BITPOS", "XADD", "XLEN", "XRANGE", "XREVRANGE", "XREAD", "XTRIM", "XINFO", "XDEL", "XGROUP", "XREADGROUP", "XACK", "XPENDING", "XCLAIM", "XAUTOCLAIM", "WATCH", "UNWATCH", "OBJECT", "DEBUG", "MEMORY", "COPY", "TOUCH", "UNLINK", "SCAN"]);
|
|
2927
|
+
const multiKeyCommands = /* @__PURE__ */ new Set(["MGET", "DEL", "EXISTS", "TOUCH", "UNLINK", "WATCH"]);
|
|
2928
|
+
const destSourceCommands = /* @__PURE__ */ new Set(["RENAME", "RENAMENX", "COPY", "BITOP", "SINTERSTORE", "SUNIONSTORE", "SDIFFSTORE", "ZUNIONSTORE", "ZINTERSTORE", "PFMERGE", "GEOSEARCHSTORE", "LMOVE", "BLMOVE"]);
|
|
2929
|
+
const cmd = command.toUpperCase();
|
|
2930
|
+
if (multiKeyCommands.has(cmd)) {
|
|
2931
|
+
return args.map((arg) => this.keyPrefix + arg);
|
|
2932
|
+
}
|
|
2933
|
+
if (destSourceCommands.has(cmd)) {
|
|
2934
|
+
if (args.length > 0) {
|
|
2935
|
+
const result = [...args];
|
|
2936
|
+
result[0] = this.keyPrefix + result[0];
|
|
2937
|
+
if (args.length > 1 && ["RENAME", "RENAMENX", "COPY", "LMOVE", "BLMOVE"].includes(cmd)) {
|
|
2938
|
+
result[1] = this.keyPrefix + result[1];
|
|
2939
|
+
}
|
|
2940
|
+
return result;
|
|
2941
|
+
}
|
|
2942
|
+
}
|
|
2943
|
+
if (singleKeyCommands.has(cmd)) {
|
|
2944
|
+
if (args.length > 0) {
|
|
2945
|
+
const result = [...args];
|
|
2946
|
+
result[0] = this.keyPrefix + result[0];
|
|
2947
|
+
return result;
|
|
2948
|
+
}
|
|
2949
|
+
}
|
|
2950
|
+
if (cmd === "MSET" || cmd === "MSETNX") {
|
|
2951
|
+
const result = [];
|
|
2952
|
+
for (let i = 0; i < args.length; i += 2) {
|
|
2953
|
+
result.push(this.keyPrefix + args[i]);
|
|
2954
|
+
const value = args[i + 1];
|
|
2955
|
+
if (value !== void 0) {
|
|
2956
|
+
result.push(value);
|
|
2957
|
+
}
|
|
2958
|
+
}
|
|
2959
|
+
return result;
|
|
2960
|
+
}
|
|
2961
|
+
return args;
|
|
2962
|
+
}
|
|
2963
|
+
setupEventHandlers() {
|
|
2964
|
+
if (!this.client) {
|
|
2965
|
+
return;
|
|
2966
|
+
}
|
|
2967
|
+
if (this.isSentinel) {
|
|
2968
|
+
return;
|
|
2969
|
+
}
|
|
2970
|
+
this.client.on("connect", () => {
|
|
2971
|
+
this.emit("connect" /* CONNECT */);
|
|
2972
|
+
});
|
|
2973
|
+
this.client.on("ready", () => {
|
|
2974
|
+
this.emit("ready" /* READY */);
|
|
2975
|
+
});
|
|
2976
|
+
this.client.on("error", (error) => {
|
|
2977
|
+
this.emit("error" /* ERROR */, error);
|
|
2978
|
+
});
|
|
2979
|
+
this.client.on("end", () => {
|
|
2980
|
+
this.emit("end" /* END */);
|
|
2981
|
+
});
|
|
2982
|
+
this.client.on("reconnecting", () => {
|
|
2983
|
+
this.emit("reconnecting" /* RECONNECTING */);
|
|
2984
|
+
});
|
|
2985
|
+
}
|
|
2986
|
+
/**
|
|
2987
|
+
* Gets underlying node-redis client.
|
|
2988
|
+
* For advanced usage only.
|
|
2989
|
+
*/
|
|
2990
|
+
getClient() {
|
|
2991
|
+
return this.client;
|
|
2992
|
+
}
|
|
2993
|
+
/**
|
|
2994
|
+
* Returns true if connected to Redis Sentinel.
|
|
2995
|
+
*/
|
|
2996
|
+
isSentinelMode() {
|
|
2997
|
+
return this.isSentinel;
|
|
2998
|
+
}
|
|
2999
|
+
};
|
|
3000
|
+
var NodeRedisPipelineAdapter = class {
|
|
3001
|
+
constructor(client, keyPrefix = "") {
|
|
3002
|
+
this.client = client;
|
|
3003
|
+
this.keyPrefix = keyPrefix;
|
|
3004
|
+
}
|
|
3005
|
+
commands = [];
|
|
3006
|
+
async exec() {
|
|
3007
|
+
const multi = this.client.multi();
|
|
3008
|
+
for (const { command, args } of this.commands) {
|
|
3009
|
+
multi.addCommand([command, ...args.map(String)]);
|
|
3010
|
+
}
|
|
3011
|
+
try {
|
|
3012
|
+
const results = await multi.exec();
|
|
3013
|
+
return results.map((result) => [null, result]);
|
|
3014
|
+
} catch (error) {
|
|
3015
|
+
return [[error, null]];
|
|
3016
|
+
}
|
|
3017
|
+
}
|
|
3018
|
+
prefixKey(key) {
|
|
3019
|
+
return this.keyPrefix ? this.keyPrefix + key : key;
|
|
3020
|
+
}
|
|
3021
|
+
get(key) {
|
|
3022
|
+
this.commands.push({ command: "GET", args: [this.prefixKey(key)] });
|
|
3023
|
+
return this;
|
|
3024
|
+
}
|
|
3025
|
+
set(key, value, options) {
|
|
3026
|
+
const args = [this.prefixKey(key), value];
|
|
3027
|
+
if (options?.ex) {
|
|
3028
|
+
args.push("EX", options.ex);
|
|
3029
|
+
} else if (options?.px) {
|
|
3030
|
+
args.push("PX", options.px);
|
|
3031
|
+
}
|
|
3032
|
+
this.commands.push({ command: "SET", args });
|
|
3033
|
+
return this;
|
|
3034
|
+
}
|
|
3035
|
+
del(...keys) {
|
|
3036
|
+
this.commands.push({ command: "DEL", args: keys.map((k) => this.prefixKey(k)) });
|
|
3037
|
+
return this;
|
|
3038
|
+
}
|
|
3039
|
+
mget(...keys) {
|
|
3040
|
+
this.commands.push({ command: "MGET", args: keys.map((k) => this.prefixKey(k)) });
|
|
3041
|
+
return this;
|
|
3042
|
+
}
|
|
3043
|
+
mset(data) {
|
|
3044
|
+
const args = [];
|
|
3045
|
+
for (const [key, value] of Object.entries(data)) {
|
|
3046
|
+
args.push(this.prefixKey(key), value);
|
|
3047
|
+
}
|
|
3048
|
+
this.commands.push({ command: "MSET", args });
|
|
3049
|
+
return this;
|
|
3050
|
+
}
|
|
3051
|
+
expire(key, seconds) {
|
|
3052
|
+
this.commands.push({ command: "EXPIRE", args: [this.prefixKey(key), seconds] });
|
|
3053
|
+
return this;
|
|
3054
|
+
}
|
|
3055
|
+
ttl(key) {
|
|
3056
|
+
this.commands.push({ command: "TTL", args: [this.prefixKey(key)] });
|
|
3057
|
+
return this;
|
|
3058
|
+
}
|
|
3059
|
+
incr(key) {
|
|
3060
|
+
this.commands.push({ command: "INCR", args: [this.prefixKey(key)] });
|
|
3061
|
+
return this;
|
|
3062
|
+
}
|
|
3063
|
+
incrby(key, increment) {
|
|
3064
|
+
this.commands.push({ command: "INCRBY", args: [this.prefixKey(key), increment] });
|
|
3065
|
+
return this;
|
|
3066
|
+
}
|
|
3067
|
+
hget(key, field) {
|
|
3068
|
+
this.commands.push({ command: "HGET", args: [this.prefixKey(key), field] });
|
|
3069
|
+
return this;
|
|
3070
|
+
}
|
|
3071
|
+
hset(key, field, value) {
|
|
3072
|
+
this.commands.push({ command: "HSET", args: [this.prefixKey(key), field, value] });
|
|
3073
|
+
return this;
|
|
3074
|
+
}
|
|
3075
|
+
hmset(key, data) {
|
|
3076
|
+
const args = [this.prefixKey(key)];
|
|
3077
|
+
for (const [field, value] of Object.entries(data)) {
|
|
3078
|
+
args.push(field, value);
|
|
3079
|
+
}
|
|
3080
|
+
this.commands.push({ command: "HMSET", args });
|
|
3081
|
+
return this;
|
|
3082
|
+
}
|
|
3083
|
+
hgetall(key) {
|
|
3084
|
+
this.commands.push({ command: "HGETALL", args: [this.prefixKey(key)] });
|
|
3085
|
+
return this;
|
|
3086
|
+
}
|
|
3087
|
+
lpush(key, ...values) {
|
|
3088
|
+
this.commands.push({ command: "LPUSH", args: [this.prefixKey(key), ...values] });
|
|
3089
|
+
return this;
|
|
3090
|
+
}
|
|
3091
|
+
rpush(key, ...values) {
|
|
3092
|
+
this.commands.push({ command: "RPUSH", args: [this.prefixKey(key), ...values] });
|
|
3093
|
+
return this;
|
|
3094
|
+
}
|
|
3095
|
+
sadd(key, ...members) {
|
|
3096
|
+
this.commands.push({ command: "SADD", args: [this.prefixKey(key), ...members] });
|
|
3097
|
+
return this;
|
|
3098
|
+
}
|
|
3099
|
+
srem(key, ...members) {
|
|
3100
|
+
this.commands.push({ command: "SREM", args: [this.prefixKey(key), ...members] });
|
|
3101
|
+
return this;
|
|
3102
|
+
}
|
|
3103
|
+
zadd(key, ...args) {
|
|
3104
|
+
this.commands.push({ command: "ZADD", args: [this.prefixKey(key), ...args] });
|
|
3105
|
+
return this;
|
|
3106
|
+
}
|
|
3107
|
+
zrem(key, ...members) {
|
|
3108
|
+
this.commands.push({ command: "ZREM", args: [this.prefixKey(key), ...members] });
|
|
3109
|
+
return this;
|
|
3110
|
+
}
|
|
3111
|
+
};
|
|
3112
|
+
var NodeRedisMultiAdapter = class extends NodeRedisPipelineAdapter {
|
|
3113
|
+
constructor(client, keyPrefix = "") {
|
|
3114
|
+
super(client, keyPrefix);
|
|
3115
|
+
}
|
|
3116
|
+
discard() {
|
|
3117
|
+
this.commands = [];
|
|
3118
|
+
}
|
|
3119
|
+
};
|
|
3120
|
+
var NodeRedisSentinelPipelineAdapter = class {
|
|
3121
|
+
constructor(sentinel, keyPrefix = "") {
|
|
3122
|
+
this.sentinel = sentinel;
|
|
3123
|
+
this.keyPrefix = keyPrefix;
|
|
3124
|
+
}
|
|
3125
|
+
commands = [];
|
|
3126
|
+
async exec() {
|
|
3127
|
+
return await this.sentinel.use(async (client) => {
|
|
3128
|
+
const multi = client.multi();
|
|
3129
|
+
for (const { command, args } of this.commands) {
|
|
3130
|
+
multi.addCommand([command, ...args.map(String)]);
|
|
3131
|
+
}
|
|
3132
|
+
try {
|
|
3133
|
+
const results = await multi.exec();
|
|
3134
|
+
return results.map((result) => [null, result]);
|
|
3135
|
+
} catch (error) {
|
|
3136
|
+
return [[error, null]];
|
|
3137
|
+
}
|
|
3138
|
+
});
|
|
3139
|
+
}
|
|
3140
|
+
prefixKey(key) {
|
|
3141
|
+
return this.keyPrefix ? this.keyPrefix + key : key;
|
|
3142
|
+
}
|
|
3143
|
+
get(key) {
|
|
3144
|
+
this.commands.push({ command: "GET", args: [this.prefixKey(key)] });
|
|
3145
|
+
return this;
|
|
3146
|
+
}
|
|
3147
|
+
set(key, value, options) {
|
|
3148
|
+
const args = [this.prefixKey(key), value];
|
|
3149
|
+
if (options?.ex) {
|
|
3150
|
+
args.push("EX", options.ex);
|
|
3151
|
+
} else if (options?.px) {
|
|
3152
|
+
args.push("PX", options.px);
|
|
3153
|
+
}
|
|
3154
|
+
this.commands.push({ command: "SET", args });
|
|
3155
|
+
return this;
|
|
3156
|
+
}
|
|
3157
|
+
del(...keys) {
|
|
3158
|
+
this.commands.push({ command: "DEL", args: keys.map((k) => this.prefixKey(k)) });
|
|
3159
|
+
return this;
|
|
3160
|
+
}
|
|
3161
|
+
mget(...keys) {
|
|
3162
|
+
this.commands.push({ command: "MGET", args: keys.map((k) => this.prefixKey(k)) });
|
|
3163
|
+
return this;
|
|
3164
|
+
}
|
|
3165
|
+
mset(data) {
|
|
3166
|
+
const args = [];
|
|
3167
|
+
for (const [key, value] of Object.entries(data)) {
|
|
3168
|
+
args.push(this.prefixKey(key), value);
|
|
3169
|
+
}
|
|
3170
|
+
this.commands.push({ command: "MSET", args });
|
|
3171
|
+
return this;
|
|
3172
|
+
}
|
|
3173
|
+
expire(key, seconds) {
|
|
3174
|
+
this.commands.push({ command: "EXPIRE", args: [this.prefixKey(key), seconds] });
|
|
3175
|
+
return this;
|
|
3176
|
+
}
|
|
3177
|
+
ttl(key) {
|
|
3178
|
+
this.commands.push({ command: "TTL", args: [this.prefixKey(key)] });
|
|
3179
|
+
return this;
|
|
3180
|
+
}
|
|
3181
|
+
incr(key) {
|
|
3182
|
+
this.commands.push({ command: "INCR", args: [this.prefixKey(key)] });
|
|
3183
|
+
return this;
|
|
3184
|
+
}
|
|
3185
|
+
incrby(key, increment) {
|
|
3186
|
+
this.commands.push({ command: "INCRBY", args: [this.prefixKey(key), increment] });
|
|
3187
|
+
return this;
|
|
3188
|
+
}
|
|
3189
|
+
hget(key, field) {
|
|
3190
|
+
this.commands.push({ command: "HGET", args: [this.prefixKey(key), field] });
|
|
3191
|
+
return this;
|
|
3192
|
+
}
|
|
3193
|
+
hset(key, field, value) {
|
|
3194
|
+
this.commands.push({ command: "HSET", args: [this.prefixKey(key), field, value] });
|
|
3195
|
+
return this;
|
|
3196
|
+
}
|
|
3197
|
+
hmset(key, data) {
|
|
3198
|
+
const args = [this.prefixKey(key)];
|
|
3199
|
+
for (const [field, value] of Object.entries(data)) {
|
|
3200
|
+
args.push(field, value);
|
|
3201
|
+
}
|
|
3202
|
+
this.commands.push({ command: "HMSET", args });
|
|
3203
|
+
return this;
|
|
3204
|
+
}
|
|
3205
|
+
hgetall(key) {
|
|
3206
|
+
this.commands.push({ command: "HGETALL", args: [this.prefixKey(key)] });
|
|
3207
|
+
return this;
|
|
3208
|
+
}
|
|
3209
|
+
lpush(key, ...values) {
|
|
3210
|
+
this.commands.push({ command: "LPUSH", args: [this.prefixKey(key), ...values] });
|
|
3211
|
+
return this;
|
|
3212
|
+
}
|
|
3213
|
+
rpush(key, ...values) {
|
|
3214
|
+
this.commands.push({ command: "RPUSH", args: [this.prefixKey(key), ...values] });
|
|
3215
|
+
return this;
|
|
3216
|
+
}
|
|
3217
|
+
sadd(key, ...members) {
|
|
3218
|
+
this.commands.push({ command: "SADD", args: [this.prefixKey(key), ...members] });
|
|
3219
|
+
return this;
|
|
3220
|
+
}
|
|
3221
|
+
srem(key, ...members) {
|
|
3222
|
+
this.commands.push({ command: "SREM", args: [this.prefixKey(key), ...members] });
|
|
3223
|
+
return this;
|
|
3224
|
+
}
|
|
3225
|
+
zadd(key, ...args) {
|
|
3226
|
+
this.commands.push({ command: "ZADD", args: [this.prefixKey(key), ...args] });
|
|
3227
|
+
return this;
|
|
3228
|
+
}
|
|
3229
|
+
zrem(key, ...members) {
|
|
3230
|
+
this.commands.push({ command: "ZREM", args: [this.prefixKey(key), ...members] });
|
|
3231
|
+
return this;
|
|
3232
|
+
}
|
|
3233
|
+
};
|
|
3234
|
+
var NodeRedisSentinelMultiAdapter = class extends NodeRedisSentinelPipelineAdapter {
|
|
3235
|
+
constructor(sentinel, keyPrefix = "") {
|
|
3236
|
+
super(sentinel, keyPrefix);
|
|
3237
|
+
}
|
|
3238
|
+
discard() {
|
|
3239
|
+
this.commands = [];
|
|
3240
|
+
}
|
|
3241
|
+
};
|
|
3242
|
+
|
|
3243
|
+
// src/driver/application/driver.factory.ts
|
|
3244
|
+
function createDriver(config, options) {
|
|
3245
|
+
const driverType = options?.type ?? "ioredis";
|
|
3246
|
+
const enableLogging = options?.enableLogging ?? false;
|
|
3247
|
+
switch (driverType) {
|
|
3248
|
+
case "ioredis":
|
|
3249
|
+
return new IoRedisAdapter(config, { enableLogging });
|
|
3250
|
+
case "node-redis":
|
|
3251
|
+
return new NodeRedisAdapter(config, { enableLogging });
|
|
3252
|
+
default:
|
|
3253
|
+
throw new RedisXError(`Unsupported driver type: ${driverType}. Supported types: ioredis, node-redis`, "CFG_INVALID" /* CFG_INVALID */, void 0, { driverType });
|
|
3254
|
+
}
|
|
3255
|
+
}
|
|
3256
|
+
var DriverFactory = class {
|
|
3257
|
+
/**
|
|
3258
|
+
* Creates a driver instance.
|
|
3259
|
+
*/
|
|
3260
|
+
create(config, options) {
|
|
3261
|
+
return createDriver(config, options);
|
|
3262
|
+
}
|
|
3263
|
+
/**
|
|
3264
|
+
* Gets supported driver types.
|
|
3265
|
+
*/
|
|
3266
|
+
getSupportedTypes() {
|
|
3267
|
+
return ["ioredis", "node-redis"];
|
|
3268
|
+
}
|
|
3269
|
+
/**
|
|
3270
|
+
* Checks if driver type is supported.
|
|
3271
|
+
*/
|
|
3272
|
+
isSupported(type) {
|
|
3273
|
+
return type === "ioredis" || type === "node-redis";
|
|
3274
|
+
}
|
|
3275
|
+
/**
|
|
3276
|
+
* Gets default driver type.
|
|
3277
|
+
*/
|
|
3278
|
+
getDefaultType() {
|
|
3279
|
+
return "ioredis";
|
|
3280
|
+
}
|
|
3281
|
+
};
|
|
3282
|
+
function createDrivers(configs, options) {
|
|
3283
|
+
const drivers = {};
|
|
3284
|
+
for (const [name, config] of Object.entries(configs)) {
|
|
3285
|
+
drivers[name] = createDriver(config, options);
|
|
3286
|
+
}
|
|
3287
|
+
return drivers;
|
|
3288
|
+
}
|
|
3289
|
+
function detectAvailableDriver() {
|
|
3290
|
+
try {
|
|
3291
|
+
require.resolve("ioredis");
|
|
3292
|
+
return "ioredis";
|
|
3293
|
+
} catch {
|
|
3294
|
+
try {
|
|
3295
|
+
require.resolve("redis");
|
|
3296
|
+
return "node-redis";
|
|
3297
|
+
} catch {
|
|
3298
|
+
return null;
|
|
3299
|
+
}
|
|
3300
|
+
}
|
|
3301
|
+
}
|
|
3302
|
+
function getRecommendedDriver() {
|
|
3303
|
+
return "ioredis";
|
|
3304
|
+
}
|
|
3305
|
+
|
|
3306
|
+
// src/shared/constants/injection-tokens.constants.ts
|
|
3307
|
+
var REDIS_MODULE_OPTIONS = /* @__PURE__ */ Symbol.for("REDIS_MODULE_OPTIONS");
|
|
3308
|
+
var REDIS_CLIENT = /* @__PURE__ */ Symbol.for("REDIS_CLIENT");
|
|
3309
|
+
var REDIS_CLIENTS_MAP = /* @__PURE__ */ Symbol.for("REDIS_CLIENTS_MAP");
|
|
3310
|
+
var CLIENT_MANAGER = /* @__PURE__ */ Symbol.for("CLIENT_MANAGER");
|
|
3311
|
+
var REDIS_DRIVER = /* @__PURE__ */ Symbol.for("REDIS_DRIVER");
|
|
3312
|
+
var REDISX_CONFIG = /* @__PURE__ */ Symbol.for("REDISX_CONFIG");
|
|
3313
|
+
var PLUGIN_REGISTRY = /* @__PURE__ */ Symbol.for("PLUGIN_REGISTRY");
|
|
3314
|
+
var REGISTERED_PLUGINS = /* @__PURE__ */ Symbol.for("REGISTERED_PLUGINS");
|
|
3315
|
+
var REDISX_LOGGER = /* @__PURE__ */ Symbol.for("REDISX_LOGGER");
|
|
3316
|
+
function getClientToken(name) {
|
|
3317
|
+
return /* @__PURE__ */ Symbol.for(`REDIS_CLIENT:${name}`);
|
|
3318
|
+
}
|
|
3319
|
+
function getDriverToken(name) {
|
|
3320
|
+
return /* @__PURE__ */ Symbol.for(`REDIS_DRIVER:${name}`);
|
|
3321
|
+
}
|
|
3322
|
+
var DEFAULT_CLIENT_NAME = "default";
|
|
3323
|
+
var DEFAULT_DRIVER_TYPE = "ioredis";
|
|
3324
|
+
var MODULE_NAME = "RedisXModule";
|
|
3325
|
+
var DEFAULT_CONNECTION_TIMEOUT = 1e4;
|
|
3326
|
+
var DEFAULT_COMMAND_TIMEOUT = 5e3;
|
|
3327
|
+
var DEFAULT_GRACEFUL_SHUTDOWN_TIMEOUT = 5e3;
|
|
3328
|
+
var REDIS_CLIENTS_INITIALIZATION = /* @__PURE__ */ Symbol.for("REDIS_CLIENTS_INITIALIZATION");
|
|
3329
|
+
var DEFAULT_HEALTH_CHECK_INTERVAL = 3e4;
|
|
3330
|
+
|
|
3331
|
+
// src/client/domain/interfaces/client-manager.interface.ts
|
|
3332
|
+
var ManagerEvent = /* @__PURE__ */ ((ManagerEvent2) => {
|
|
3333
|
+
ManagerEvent2["CONNECTED"] = "manager:connected";
|
|
3334
|
+
ManagerEvent2["DISCONNECTED"] = "manager:disconnected";
|
|
3335
|
+
ManagerEvent2["RECONNECTING"] = "manager:reconnecting";
|
|
3336
|
+
ManagerEvent2["ERROR"] = "manager:error";
|
|
3337
|
+
ManagerEvent2["CREATED"] = "manager:created";
|
|
3338
|
+
ManagerEvent2["REMOVED"] = "manager:removed";
|
|
3339
|
+
return ManagerEvent2;
|
|
3340
|
+
})(ManagerEvent || {});
|
|
3341
|
+
|
|
3342
|
+
// src/client/application/redis-client.manager.ts
|
|
3343
|
+
var RedisClientManager = class {
|
|
3344
|
+
logger = new import_common2.Logger(RedisClientManager.name);
|
|
3345
|
+
/**
|
|
3346
|
+
* Registered clients.
|
|
3347
|
+
*/
|
|
3348
|
+
clients = /* @__PURE__ */ new Map();
|
|
3349
|
+
/**
|
|
3350
|
+
* Event emitter for client events.
|
|
3351
|
+
*/
|
|
3352
|
+
eventEmitter = new import_events2.default();
|
|
3353
|
+
/**
|
|
3354
|
+
* Whether manager is shutting down.
|
|
3355
|
+
*/
|
|
3356
|
+
isShuttingDown = false;
|
|
3357
|
+
/**
|
|
3358
|
+
* Default reconnection options.
|
|
3359
|
+
*/
|
|
3360
|
+
defaultIReconnectionOptions = {
|
|
3361
|
+
maxAttempts: Infinity,
|
|
3362
|
+
initialDelay: 1e3,
|
|
3363
|
+
maxDelay: 3e4,
|
|
3364
|
+
backoffMultiplier: 2,
|
|
3365
|
+
enableJitter: true
|
|
3366
|
+
};
|
|
3367
|
+
/**
|
|
3368
|
+
* Default driver type.
|
|
3369
|
+
*/
|
|
3370
|
+
defaultDriverType = "ioredis";
|
|
3371
|
+
/**
|
|
3372
|
+
* Gets Redis client by name with lazy connection.
|
|
3373
|
+
*/
|
|
3374
|
+
async getClient(name = DEFAULT_CLIENT_NAME) {
|
|
3375
|
+
if (this.isShuttingDown) {
|
|
3376
|
+
throw new RedisXError("Client manager is shutting down", "CONN_FAILED" /* CONN_FAILED */, void 0, { clientName: name });
|
|
3377
|
+
}
|
|
3378
|
+
const managedClient = this.clients.get(name);
|
|
3379
|
+
if (!managedClient) {
|
|
3380
|
+
throw new RedisXError(`Client "${name}" not found. Available clients: ${Array.from(this.clients.keys()).join(", ")}`, "CFG_INVALID" /* CFG_INVALID */, void 0, { clientName: name });
|
|
3381
|
+
}
|
|
3382
|
+
if (!managedClient.driver.isConnected() && !managedClient.everConnected) {
|
|
3383
|
+
await this.connectClient(managedClient);
|
|
3384
|
+
}
|
|
3385
|
+
return managedClient.driver;
|
|
3386
|
+
}
|
|
3387
|
+
/**
|
|
3388
|
+
* Creates and registers a new Redis client.
|
|
3389
|
+
*/
|
|
3390
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
3391
|
+
async createClient(name, config, options) {
|
|
3392
|
+
if (this.clients.has(name)) {
|
|
3393
|
+
throw new RedisXError(`Client "${name}" already exists`, "CFG_INVALID" /* CFG_INVALID */, void 0, { clientName: name });
|
|
3394
|
+
}
|
|
3395
|
+
const driver = createDriver(config, {
|
|
3396
|
+
type: options?.driverType ?? this.defaultDriverType,
|
|
3397
|
+
enableLogging: false
|
|
3398
|
+
});
|
|
3399
|
+
const reconnectionOptions = {
|
|
3400
|
+
...this.defaultIReconnectionOptions,
|
|
3401
|
+
...options?.reconnection
|
|
3402
|
+
};
|
|
3403
|
+
const metadata = {
|
|
3404
|
+
name,
|
|
3405
|
+
config,
|
|
3406
|
+
status: "disconnected" /* DISCONNECTED */,
|
|
3407
|
+
reconnectAttempts: 0,
|
|
3408
|
+
...options?.metadata || {}
|
|
3409
|
+
};
|
|
3410
|
+
const managedClient = {
|
|
3411
|
+
name,
|
|
3412
|
+
driver,
|
|
3413
|
+
config,
|
|
3414
|
+
metadata,
|
|
3415
|
+
reconnectionOptions,
|
|
3416
|
+
status: "disconnected" /* DISCONNECTED */,
|
|
3417
|
+
stats: this.createInitialStats(name),
|
|
3418
|
+
reconnection: {
|
|
3419
|
+
attempts: 0,
|
|
3420
|
+
nextDelay: reconnectionOptions.initialDelay
|
|
3421
|
+
},
|
|
3422
|
+
everConnected: false,
|
|
3423
|
+
lastError: null
|
|
3424
|
+
};
|
|
3425
|
+
this.setupDriverEventHandlers(managedClient);
|
|
3426
|
+
this.clients.set(name, managedClient);
|
|
3427
|
+
this.emitEvent("manager:created" /* CREATED */, {
|
|
3428
|
+
name,
|
|
3429
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
3430
|
+
});
|
|
3431
|
+
return driver;
|
|
3432
|
+
}
|
|
3433
|
+
/**
|
|
3434
|
+
* Checks if client exists.
|
|
3435
|
+
*/
|
|
3436
|
+
hasClient(name) {
|
|
3437
|
+
return this.clients.has(name);
|
|
3438
|
+
}
|
|
3439
|
+
/**
|
|
3440
|
+
* Gets all registered client names.
|
|
3441
|
+
*/
|
|
3442
|
+
getClientNames() {
|
|
3443
|
+
return Array.from(this.clients.keys());
|
|
3444
|
+
}
|
|
3445
|
+
/**
|
|
3446
|
+
* Closes specific client connection.
|
|
3447
|
+
*/
|
|
3448
|
+
async closeClient(name) {
|
|
3449
|
+
const managedClient = this.clients.get(name);
|
|
3450
|
+
if (!managedClient) {
|
|
3451
|
+
throw new RedisXError(`Client "${name}" not found`, "CFG_INVALID" /* CFG_INVALID */, void 0, { clientName: name });
|
|
3452
|
+
}
|
|
3453
|
+
await this.disconnectClient(managedClient);
|
|
3454
|
+
this.clients.delete(name);
|
|
3455
|
+
this.emitEvent("manager:removed" /* REMOVED */, {
|
|
3456
|
+
name,
|
|
3457
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
3458
|
+
});
|
|
3459
|
+
}
|
|
3460
|
+
/**
|
|
3461
|
+
* Closes all client connections.
|
|
3462
|
+
*/
|
|
3463
|
+
async closeAll() {
|
|
3464
|
+
this.isShuttingDown = true;
|
|
3465
|
+
const closePromises = Array.from(this.clients.values()).map(
|
|
3466
|
+
(client) => this.disconnectClient(client).catch((error) => {
|
|
3467
|
+
this.logger.error(`Error closing client ${client.name}:`, error);
|
|
3468
|
+
})
|
|
3469
|
+
);
|
|
3470
|
+
await Promise.all(closePromises);
|
|
3471
|
+
this.clients.clear();
|
|
3472
|
+
}
|
|
3473
|
+
/**
|
|
3474
|
+
* Performs health check on client(s).
|
|
3475
|
+
*/
|
|
3476
|
+
async healthCheck(name) {
|
|
3477
|
+
if (name) {
|
|
3478
|
+
const managedClient = this.clients.get(name);
|
|
3479
|
+
if (!managedClient) {
|
|
3480
|
+
throw new RedisXError(`Client "${name}" not found`, "CFG_INVALID" /* CFG_INVALID */, void 0, { clientName: name });
|
|
3481
|
+
}
|
|
3482
|
+
return this.checkClientHealth(managedClient);
|
|
3483
|
+
} else {
|
|
3484
|
+
const healthChecks = await Promise.all(Array.from(this.clients.values()).map((client) => this.checkClientHealth(client)));
|
|
3485
|
+
return healthChecks;
|
|
3486
|
+
}
|
|
3487
|
+
}
|
|
3488
|
+
/**
|
|
3489
|
+
* Gets connection statistics.
|
|
3490
|
+
*/
|
|
3491
|
+
getStats() {
|
|
3492
|
+
const stats = {
|
|
3493
|
+
totalClients: this.clients.size,
|
|
3494
|
+
connectedClients: 0,
|
|
3495
|
+
disconnectedClients: 0,
|
|
3496
|
+
errorClients: 0,
|
|
3497
|
+
clients: {},
|
|
3498
|
+
collectedAt: /* @__PURE__ */ new Date()
|
|
3499
|
+
};
|
|
3500
|
+
for (const [name, client] of this.clients) {
|
|
3501
|
+
stats.clients[name] = { ...client.stats };
|
|
3502
|
+
if (client.status === "connected" /* CONNECTED */) {
|
|
3503
|
+
stats.connectedClients++;
|
|
3504
|
+
} else if (client.status === "error" /* ERROR */) {
|
|
3505
|
+
stats.errorClients++;
|
|
3506
|
+
} else {
|
|
3507
|
+
stats.disconnectedClients++;
|
|
3508
|
+
}
|
|
3509
|
+
}
|
|
3510
|
+
return stats;
|
|
3511
|
+
}
|
|
3512
|
+
/**
|
|
3513
|
+
* Gets client metadata.
|
|
3514
|
+
*/
|
|
3515
|
+
getMetadata(name) {
|
|
3516
|
+
const managedClient = this.clients.get(name);
|
|
3517
|
+
if (!managedClient) {
|
|
3518
|
+
throw new RedisXError(`Client "${name}" not found`, "CFG_INVALID" /* CFG_INVALID */, void 0, { clientName: name });
|
|
3519
|
+
}
|
|
3520
|
+
return { ...managedClient.metadata };
|
|
3521
|
+
}
|
|
3522
|
+
/**
|
|
3523
|
+
* Updates client metadata.
|
|
3524
|
+
*/
|
|
3525
|
+
updateMetadata(name, metadata) {
|
|
3526
|
+
const managedClient = this.clients.get(name);
|
|
3527
|
+
if (!managedClient) {
|
|
3528
|
+
throw new RedisXError(`Client "${name}" not found`, "CFG_INVALID" /* CFG_INVALID */, void 0, { clientName: name });
|
|
3529
|
+
}
|
|
3530
|
+
Object.assign(managedClient.metadata, metadata);
|
|
3531
|
+
}
|
|
3532
|
+
/**
|
|
3533
|
+
* Registers event listener.
|
|
3534
|
+
*/
|
|
3535
|
+
on(event, handler) {
|
|
3536
|
+
this.eventEmitter.on(event, handler);
|
|
3537
|
+
}
|
|
3538
|
+
/**
|
|
3539
|
+
* Unregisters event listener.
|
|
3540
|
+
*/
|
|
3541
|
+
off(event, handler) {
|
|
3542
|
+
this.eventEmitter.off(event, handler);
|
|
3543
|
+
}
|
|
3544
|
+
/**
|
|
3545
|
+
* NestJS lifecycle hook - cleanup on module destroy.
|
|
3546
|
+
*/
|
|
3547
|
+
async onModuleDestroy() {
|
|
3548
|
+
await this.closeAll();
|
|
3549
|
+
}
|
|
3550
|
+
/**
|
|
3551
|
+
* Connects client with error handling.
|
|
3552
|
+
*/
|
|
3553
|
+
async connectClient(managedClient) {
|
|
3554
|
+
try {
|
|
3555
|
+
managedClient.status = "connecting" /* CONNECTING */;
|
|
3556
|
+
await managedClient.driver.connect();
|
|
3557
|
+
managedClient.status = "connected" /* CONNECTED */;
|
|
3558
|
+
managedClient.everConnected = true;
|
|
3559
|
+
managedClient.stats.connectedAt = /* @__PURE__ */ new Date();
|
|
3560
|
+
managedClient.reconnection.attempts = 0;
|
|
3561
|
+
managedClient.reconnection.nextDelay = managedClient.reconnectionOptions.initialDelay;
|
|
3562
|
+
managedClient.lastError = null;
|
|
3563
|
+
this.emitEvent("manager:connected" /* CONNECTED */, {
|
|
3564
|
+
name: managedClient.name,
|
|
3565
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
3566
|
+
});
|
|
3567
|
+
} catch (error) {
|
|
3568
|
+
managedClient.status = "error" /* ERROR */;
|
|
3569
|
+
managedClient.stats.errors++;
|
|
3570
|
+
managedClient.lastError = error;
|
|
3571
|
+
this.emitEvent("manager:error" /* ERROR */, {
|
|
3572
|
+
name: managedClient.name,
|
|
3573
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
3574
|
+
error
|
|
3575
|
+
});
|
|
3576
|
+
throw error;
|
|
3577
|
+
}
|
|
3578
|
+
}
|
|
3579
|
+
/**
|
|
3580
|
+
* Disconnects client gracefully.
|
|
3581
|
+
*/
|
|
3582
|
+
async disconnectClient(managedClient) {
|
|
3583
|
+
if (managedClient.reconnection.timer) {
|
|
3584
|
+
clearTimeout(managedClient.reconnection.timer);
|
|
3585
|
+
managedClient.reconnection.timer = void 0;
|
|
3586
|
+
}
|
|
3587
|
+
if (managedClient.driver.isConnected()) {
|
|
3588
|
+
try {
|
|
3589
|
+
await managedClient.driver.disconnect();
|
|
3590
|
+
} catch (error) {
|
|
3591
|
+
this.logger.error(`Error disconnecting client ${managedClient.name}:`, error);
|
|
3592
|
+
}
|
|
3593
|
+
}
|
|
3594
|
+
managedClient.status = "disconnected" /* DISCONNECTED */;
|
|
3595
|
+
this.emitEvent("manager:disconnected" /* DISCONNECTED */, {
|
|
3596
|
+
name: managedClient.name,
|
|
3597
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
3598
|
+
});
|
|
3599
|
+
}
|
|
3600
|
+
/**
|
|
3601
|
+
* Sets up driver event handlers for reconnection.
|
|
3602
|
+
*/
|
|
3603
|
+
setupDriverEventHandlers(managedClient) {
|
|
3604
|
+
const { driver, name } = managedClient;
|
|
3605
|
+
driver.on("error" /* ERROR */, (error) => {
|
|
3606
|
+
managedClient.stats.errors++;
|
|
3607
|
+
managedClient.lastError = error;
|
|
3608
|
+
if (managedClient.status === "connected" /* CONNECTED */) {
|
|
3609
|
+
this.scheduleReconnection(managedClient);
|
|
3610
|
+
}
|
|
3611
|
+
});
|
|
3612
|
+
driver.on("close" /* CLOSE */, () => {
|
|
3613
|
+
if (managedClient.status === "connected" /* CONNECTED */ && !this.isShuttingDown) {
|
|
3614
|
+
managedClient.status = "disconnected" /* DISCONNECTED */;
|
|
3615
|
+
this.scheduleReconnection(managedClient);
|
|
3616
|
+
}
|
|
3617
|
+
});
|
|
3618
|
+
driver.on("end" /* END */, () => {
|
|
3619
|
+
if (managedClient.status === "connected" /* CONNECTED */ && !this.isShuttingDown) {
|
|
3620
|
+
managedClient.status = "disconnected" /* DISCONNECTED */;
|
|
3621
|
+
this.scheduleReconnection(managedClient);
|
|
3622
|
+
}
|
|
3623
|
+
});
|
|
3624
|
+
driver.on("ready" /* READY */, () => {
|
|
3625
|
+
if (managedClient.status === "reconnecting" /* RECONNECTING */) {
|
|
3626
|
+
managedClient.status = "connected" /* CONNECTED */;
|
|
3627
|
+
managedClient.reconnection.attempts = 0;
|
|
3628
|
+
managedClient.reconnection.nextDelay = managedClient.reconnectionOptions.initialDelay;
|
|
3629
|
+
this.emitEvent("manager:connected" /* CONNECTED */, {
|
|
3630
|
+
name,
|
|
3631
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
3632
|
+
});
|
|
3633
|
+
}
|
|
3634
|
+
});
|
|
3635
|
+
}
|
|
3636
|
+
/**
|
|
3637
|
+
* Schedules reconnection with exponential backoff.
|
|
3638
|
+
*/
|
|
3639
|
+
scheduleReconnection(managedClient) {
|
|
3640
|
+
const { reconnectionOptions, reconnection } = managedClient;
|
|
3641
|
+
if (reconnection.attempts >= reconnectionOptions.maxAttempts) {
|
|
3642
|
+
managedClient.status = "error" /* ERROR */;
|
|
3643
|
+
this.emitEvent("manager:error" /* ERROR */, {
|
|
3644
|
+
name: managedClient.name,
|
|
3645
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
3646
|
+
error: new Error(`Max reconnection attempts (${reconnectionOptions.maxAttempts}) exceeded`)
|
|
3647
|
+
});
|
|
3648
|
+
return;
|
|
3649
|
+
}
|
|
3650
|
+
if (reconnection.timer) {
|
|
3651
|
+
clearTimeout(reconnection.timer);
|
|
3652
|
+
}
|
|
3653
|
+
let delay = reconnection.nextDelay;
|
|
3654
|
+
if (reconnectionOptions.enableJitter) {
|
|
3655
|
+
delay = delay * (0.5 + Math.random() * 0.5);
|
|
3656
|
+
}
|
|
3657
|
+
delay = Math.min(delay, reconnectionOptions.maxDelay);
|
|
3658
|
+
managedClient.status = "reconnecting" /* RECONNECTING */;
|
|
3659
|
+
reconnection.attempts++;
|
|
3660
|
+
this.emitEvent("manager:reconnecting" /* RECONNECTING */, {
|
|
3661
|
+
name: managedClient.name,
|
|
3662
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
3663
|
+
metadata: {
|
|
3664
|
+
attempt: reconnection.attempts,
|
|
3665
|
+
delay
|
|
3666
|
+
}
|
|
3667
|
+
});
|
|
3668
|
+
reconnection.timer = setTimeout(async () => {
|
|
3669
|
+
try {
|
|
3670
|
+
await managedClient.driver.connect();
|
|
3671
|
+
managedClient.status = "connected" /* CONNECTED */;
|
|
3672
|
+
managedClient.stats.reconnections++;
|
|
3673
|
+
reconnection.attempts = 0;
|
|
3674
|
+
reconnection.nextDelay = reconnectionOptions.initialDelay;
|
|
3675
|
+
this.emitEvent("manager:connected" /* CONNECTED */, {
|
|
3676
|
+
name: managedClient.name,
|
|
3677
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
3678
|
+
});
|
|
3679
|
+
} catch {
|
|
3680
|
+
reconnection.nextDelay = Math.min(reconnection.nextDelay * reconnectionOptions.backoffMultiplier, reconnectionOptions.maxDelay);
|
|
3681
|
+
this.scheduleReconnection(managedClient);
|
|
3682
|
+
}
|
|
3683
|
+
}, delay);
|
|
3684
|
+
}
|
|
3685
|
+
/**
|
|
3686
|
+
* Performs health check on single client.
|
|
3687
|
+
*/
|
|
3688
|
+
async checkClientHealth(managedClient) {
|
|
3689
|
+
const startTime = Date.now();
|
|
3690
|
+
let latency = null;
|
|
3691
|
+
let healthy = false;
|
|
3692
|
+
try {
|
|
3693
|
+
if (managedClient.driver.isConnected()) {
|
|
3694
|
+
await managedClient.driver.ping();
|
|
3695
|
+
latency = Date.now() - startTime;
|
|
3696
|
+
healthy = true;
|
|
3697
|
+
managedClient.stats.lastActivityAt = /* @__PURE__ */ new Date();
|
|
3698
|
+
this.updateLatencyStats(managedClient, latency);
|
|
3699
|
+
}
|
|
3700
|
+
} catch (error) {
|
|
3701
|
+
managedClient.stats.errors++;
|
|
3702
|
+
managedClient.lastError = error;
|
|
3703
|
+
}
|
|
3704
|
+
return {
|
|
3705
|
+
name: managedClient.name,
|
|
3706
|
+
healthy,
|
|
3707
|
+
status: managedClient.status,
|
|
3708
|
+
latency,
|
|
3709
|
+
lastError: managedClient.lastError?.message ?? null,
|
|
3710
|
+
lastCheckAt: /* @__PURE__ */ new Date(),
|
|
3711
|
+
metadata: {
|
|
3712
|
+
driverType: this.defaultDriverType,
|
|
3713
|
+
connectionType: managedClient.config.type,
|
|
3714
|
+
reconnectAttempts: managedClient.reconnection.attempts,
|
|
3715
|
+
uptime: this.calculateUptime(managedClient)
|
|
3716
|
+
}
|
|
3717
|
+
};
|
|
3718
|
+
}
|
|
3719
|
+
/**
|
|
3720
|
+
* Creates initial statistics for new client.
|
|
3721
|
+
*/
|
|
3722
|
+
createInitialStats(name) {
|
|
3723
|
+
return {
|
|
3724
|
+
name,
|
|
3725
|
+
status: "disconnected" /* DISCONNECTED */,
|
|
3726
|
+
commandsExecuted: 0,
|
|
3727
|
+
errors: 0,
|
|
3728
|
+
reconnections: 0,
|
|
3729
|
+
averageLatency: 0,
|
|
3730
|
+
peakLatency: 0,
|
|
3731
|
+
lastActivityAt: null,
|
|
3732
|
+
connectedAt: null,
|
|
3733
|
+
uptime: 0
|
|
3734
|
+
};
|
|
3735
|
+
}
|
|
3736
|
+
/**
|
|
3737
|
+
* Updates latency statistics.
|
|
3738
|
+
*/
|
|
3739
|
+
updateLatencyStats(managedClient, latency) {
|
|
3740
|
+
const stats = managedClient.stats;
|
|
3741
|
+
if (latency > stats.peakLatency) {
|
|
3742
|
+
stats.peakLatency = latency;
|
|
3743
|
+
}
|
|
3744
|
+
if (stats.commandsExecuted === 0) {
|
|
3745
|
+
stats.averageLatency = latency;
|
|
3746
|
+
} else {
|
|
3747
|
+
stats.averageLatency = (stats.averageLatency * stats.commandsExecuted + latency) / (stats.commandsExecuted + 1);
|
|
3748
|
+
}
|
|
3749
|
+
stats.commandsExecuted++;
|
|
3750
|
+
}
|
|
3751
|
+
/**
|
|
3752
|
+
* Calculates client uptime in milliseconds.
|
|
3753
|
+
*/
|
|
3754
|
+
calculateUptime(managedClient) {
|
|
3755
|
+
if (!managedClient.stats.connectedAt) {
|
|
3756
|
+
return 0;
|
|
3757
|
+
}
|
|
3758
|
+
if (managedClient.status !== "connected" /* CONNECTED */) {
|
|
3759
|
+
return 0;
|
|
3760
|
+
}
|
|
3761
|
+
return Date.now() - managedClient.stats.connectedAt.getTime();
|
|
3762
|
+
}
|
|
3763
|
+
/**
|
|
3764
|
+
* Emits manager event.
|
|
3765
|
+
*/
|
|
3766
|
+
emitEvent(event, data) {
|
|
3767
|
+
this.eventEmitter.emit(event, data);
|
|
3768
|
+
}
|
|
3769
|
+
};
|
|
3770
|
+
RedisClientManager = __decorateClass([
|
|
3771
|
+
(0, import_common2.Injectable)()
|
|
3772
|
+
], RedisClientManager);
|
|
3773
|
+
|
|
3774
|
+
// src/application/redis.providers.ts
|
|
3775
|
+
function isISingleConnectionConfig(clients) {
|
|
3776
|
+
return "type" in clients || "host" in clients || "nodes" in clients || "sentinels" in clients;
|
|
3777
|
+
}
|
|
3778
|
+
function createRedisProviders(options) {
|
|
3779
|
+
return [
|
|
3780
|
+
// Module options provider
|
|
3781
|
+
{
|
|
3782
|
+
provide: REDIS_MODULE_OPTIONS,
|
|
3783
|
+
useValue: options
|
|
3784
|
+
},
|
|
3785
|
+
// Client manager provider
|
|
3786
|
+
{
|
|
3787
|
+
provide: CLIENT_MANAGER,
|
|
3788
|
+
useFactory: () => {
|
|
3789
|
+
return new RedisClientManager();
|
|
3790
|
+
}
|
|
3791
|
+
},
|
|
3792
|
+
// Client providers (one per connection)
|
|
3793
|
+
...createClientProviders(options),
|
|
3794
|
+
// REDIS_DRIVER alias to default client (for backwards compatibility and plugin usage)
|
|
3795
|
+
{
|
|
3796
|
+
provide: REDIS_DRIVER,
|
|
3797
|
+
useFactory: async (manager) => {
|
|
3798
|
+
return manager.getClient(DEFAULT_CLIENT_NAME);
|
|
3799
|
+
},
|
|
3800
|
+
inject: [CLIENT_MANAGER]
|
|
3801
|
+
}
|
|
3802
|
+
];
|
|
3803
|
+
}
|
|
3804
|
+
function createAsyncProviders(options) {
|
|
3805
|
+
const providers = [
|
|
3806
|
+
// Client manager provider
|
|
3807
|
+
{
|
|
3808
|
+
provide: CLIENT_MANAGER,
|
|
3809
|
+
useFactory: () => {
|
|
3810
|
+
return new RedisClientManager();
|
|
3811
|
+
}
|
|
3812
|
+
}
|
|
3813
|
+
];
|
|
3814
|
+
if (options.useExisting || options.useFactory) {
|
|
3815
|
+
providers.push(createAsyncOptionsProvider(options));
|
|
3816
|
+
}
|
|
3817
|
+
if (options.useClass) {
|
|
3818
|
+
providers.push(
|
|
3819
|
+
{
|
|
3820
|
+
provide: options.useClass,
|
|
3821
|
+
useClass: options.useClass
|
|
3822
|
+
},
|
|
3823
|
+
createAsyncOptionsProvider(options)
|
|
3824
|
+
);
|
|
3825
|
+
}
|
|
3826
|
+
providers.push(...createAsyncClientProviders(options));
|
|
3827
|
+
providers.push({
|
|
3828
|
+
provide: REDIS_DRIVER,
|
|
3829
|
+
useFactory: async (manager, _init) => {
|
|
3830
|
+
return manager.getClient(DEFAULT_CLIENT_NAME);
|
|
3831
|
+
},
|
|
3832
|
+
inject: [CLIENT_MANAGER, REDIS_CLIENTS_INITIALIZATION]
|
|
3833
|
+
});
|
|
3834
|
+
return providers;
|
|
3835
|
+
}
|
|
3836
|
+
function createAsyncOptionsProvider(options) {
|
|
3837
|
+
if (options.useFactory) {
|
|
3838
|
+
return {
|
|
3839
|
+
provide: REDIS_MODULE_OPTIONS,
|
|
3840
|
+
useFactory: options.useFactory,
|
|
3841
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3842
|
+
inject: options.inject || []
|
|
3843
|
+
};
|
|
3844
|
+
}
|
|
3845
|
+
if (options.useExisting) {
|
|
3846
|
+
return {
|
|
3847
|
+
provide: REDIS_MODULE_OPTIONS,
|
|
3848
|
+
useFactory: async (optionsFactory) => await optionsFactory.createRedisModuleOptions(),
|
|
3849
|
+
inject: [options.useExisting]
|
|
3850
|
+
};
|
|
3851
|
+
}
|
|
3852
|
+
if (options.useClass) {
|
|
3853
|
+
return {
|
|
3854
|
+
provide: REDIS_MODULE_OPTIONS,
|
|
3855
|
+
useFactory: async (optionsFactory) => await optionsFactory.createRedisModuleOptions(),
|
|
3856
|
+
inject: [options.useClass]
|
|
3857
|
+
};
|
|
3858
|
+
}
|
|
3859
|
+
throw new Error("Invalid async options configuration");
|
|
3860
|
+
}
|
|
3861
|
+
function createClientProviders(options) {
|
|
3862
|
+
const providers = [];
|
|
3863
|
+
const driverType = options.global?.driver ?? "ioredis";
|
|
3864
|
+
if (isISingleConnectionConfig(options.clients)) {
|
|
3865
|
+
providers.push({
|
|
3866
|
+
provide: getClientToken(DEFAULT_CLIENT_NAME),
|
|
3867
|
+
useFactory: async (manager) => {
|
|
3868
|
+
return manager.createClient(DEFAULT_CLIENT_NAME, options.clients, { driverType });
|
|
3869
|
+
},
|
|
3870
|
+
inject: [CLIENT_MANAGER]
|
|
3871
|
+
});
|
|
3872
|
+
} else {
|
|
3873
|
+
const configs = options.clients;
|
|
3874
|
+
for (const [name, config] of Object.entries(configs)) {
|
|
3875
|
+
providers.push({
|
|
3876
|
+
provide: getClientToken(name),
|
|
3877
|
+
useFactory: async (manager) => {
|
|
3878
|
+
return manager.createClient(name, config, { driverType });
|
|
3879
|
+
},
|
|
3880
|
+
inject: [CLIENT_MANAGER]
|
|
3881
|
+
});
|
|
3882
|
+
}
|
|
3883
|
+
}
|
|
3884
|
+
return providers;
|
|
3885
|
+
}
|
|
3886
|
+
function createAsyncClientProviders(_options) {
|
|
3887
|
+
return [
|
|
3888
|
+
{
|
|
3889
|
+
provide: REDIS_CLIENTS_INITIALIZATION,
|
|
3890
|
+
useFactory: async (moduleOptions, manager) => {
|
|
3891
|
+
const driverType = moduleOptions.global?.driver ?? "ioredis";
|
|
3892
|
+
if (isISingleConnectionConfig(moduleOptions.clients)) {
|
|
3893
|
+
await manager.createClient(DEFAULT_CLIENT_NAME, moduleOptions.clients, { driverType });
|
|
3894
|
+
} else {
|
|
3895
|
+
const configs = moduleOptions.clients;
|
|
3896
|
+
for (const [name, config] of Object.entries(configs)) {
|
|
3897
|
+
await manager.createClient(name, config, { driverType });
|
|
3898
|
+
}
|
|
3899
|
+
}
|
|
3900
|
+
},
|
|
3901
|
+
inject: [REDIS_MODULE_OPTIONS, CLIENT_MANAGER]
|
|
3902
|
+
},
|
|
3903
|
+
{
|
|
3904
|
+
provide: getClientToken(DEFAULT_CLIENT_NAME),
|
|
3905
|
+
useFactory: async (manager, _init) => {
|
|
3906
|
+
return manager.getClient(DEFAULT_CLIENT_NAME);
|
|
3907
|
+
},
|
|
3908
|
+
inject: [CLIENT_MANAGER, REDIS_CLIENTS_INITIALIZATION]
|
|
3909
|
+
}
|
|
3910
|
+
];
|
|
3911
|
+
}
|
|
3912
|
+
|
|
3913
|
+
// src/application/redis.service.ts
|
|
3914
|
+
var import_common3 = require("@nestjs/common");
|
|
3915
|
+
var RedisService = class {
|
|
3916
|
+
constructor(clientManager) {
|
|
3917
|
+
this.clientManager = clientManager;
|
|
3918
|
+
}
|
|
3919
|
+
/**
|
|
3920
|
+
* Default Redis driver instance.
|
|
3921
|
+
*/
|
|
3922
|
+
driver = null;
|
|
3923
|
+
/**
|
|
3924
|
+
* Gets default Redis driver.
|
|
3925
|
+
* Lazy loads on first access.
|
|
3926
|
+
*/
|
|
3927
|
+
async getDriver() {
|
|
3928
|
+
if (!this.driver) {
|
|
3929
|
+
this.driver = await this.clientManager.getClient(DEFAULT_CLIENT_NAME);
|
|
3930
|
+
}
|
|
3931
|
+
return this.driver;
|
|
3932
|
+
}
|
|
3933
|
+
/**
|
|
3934
|
+
* Gets a named Redis client.
|
|
3935
|
+
*
|
|
3936
|
+
* @param name - Client name
|
|
3937
|
+
* @returns Redis driver instance
|
|
3938
|
+
*
|
|
3939
|
+
* @example
|
|
3940
|
+
* ```typescript
|
|
3941
|
+
* const sessions = await this.redis.getClient('sessions');
|
|
3942
|
+
* await sessions.set('session:123', data);
|
|
3943
|
+
* ```
|
|
3944
|
+
*/
|
|
3945
|
+
async getClient(name = DEFAULT_CLIENT_NAME) {
|
|
3946
|
+
return this.clientManager.getClient(name);
|
|
3947
|
+
}
|
|
3948
|
+
/**
|
|
3949
|
+
* Checks if default client is connected.
|
|
3950
|
+
*/
|
|
3951
|
+
async isConnected() {
|
|
3952
|
+
const driver = await this.getDriver();
|
|
3953
|
+
return driver.isConnected();
|
|
3954
|
+
}
|
|
3955
|
+
/**
|
|
3956
|
+
* Pings Redis server.
|
|
3957
|
+
*/
|
|
3958
|
+
async ping(message) {
|
|
3959
|
+
const driver = await this.getDriver();
|
|
3960
|
+
return driver.ping(message);
|
|
3961
|
+
}
|
|
3962
|
+
/**
|
|
3963
|
+
* Selects database.
|
|
3964
|
+
*/
|
|
3965
|
+
async select(db) {
|
|
3966
|
+
const driver = await this.getDriver();
|
|
3967
|
+
return driver.select(db);
|
|
3968
|
+
}
|
|
3969
|
+
/**
|
|
3970
|
+
* Gets value by key.
|
|
3971
|
+
*/
|
|
3972
|
+
async get(key) {
|
|
3973
|
+
const driver = await this.getDriver();
|
|
3974
|
+
return driver.get(key);
|
|
3975
|
+
}
|
|
3976
|
+
/**
|
|
3977
|
+
* Sets key-value pair with optional TTL.
|
|
3978
|
+
*/
|
|
3979
|
+
async set(key, value, options) {
|
|
3980
|
+
const driver = await this.getDriver();
|
|
3981
|
+
return driver.set(key, value, options);
|
|
3982
|
+
}
|
|
3983
|
+
/**
|
|
3984
|
+
* Gets multiple values.
|
|
3985
|
+
*/
|
|
3986
|
+
async mget(...keys) {
|
|
3987
|
+
const driver = await this.getDriver();
|
|
3988
|
+
return driver.mget(...keys);
|
|
3989
|
+
}
|
|
3990
|
+
/**
|
|
3991
|
+
* Sets multiple key-value pairs.
|
|
3992
|
+
*/
|
|
3993
|
+
async mset(data) {
|
|
3994
|
+
const driver = await this.getDriver();
|
|
3995
|
+
return driver.mset(data);
|
|
3996
|
+
}
|
|
3997
|
+
/**
|
|
3998
|
+
* Sets value only if key doesn't exist.
|
|
3999
|
+
*/
|
|
4000
|
+
async setnx(key, value) {
|
|
4001
|
+
const driver = await this.getDriver();
|
|
4002
|
+
return driver.setnx(key, value);
|
|
4003
|
+
}
|
|
4004
|
+
/**
|
|
4005
|
+
* Sets value with expiration in seconds.
|
|
4006
|
+
*/
|
|
4007
|
+
async setex(key, seconds, value) {
|
|
4008
|
+
const driver = await this.getDriver();
|
|
4009
|
+
return driver.setex(key, seconds, value);
|
|
4010
|
+
}
|
|
4011
|
+
/**
|
|
4012
|
+
* Gets value and deletes key.
|
|
4013
|
+
*/
|
|
4014
|
+
async getdel(key) {
|
|
4015
|
+
const driver = await this.getDriver();
|
|
4016
|
+
return driver.getdel(key);
|
|
4017
|
+
}
|
|
4018
|
+
/**
|
|
4019
|
+
* Gets value and sets expiration.
|
|
4020
|
+
*/
|
|
4021
|
+
async getex(key, options) {
|
|
4022
|
+
const driver = await this.getDriver();
|
|
4023
|
+
return driver.getex(key, options);
|
|
4024
|
+
}
|
|
4025
|
+
/**
|
|
4026
|
+
* Increments integer value by 1.
|
|
4027
|
+
*/
|
|
4028
|
+
async incr(key) {
|
|
4029
|
+
const driver = await this.getDriver();
|
|
4030
|
+
return driver.incr(key);
|
|
4031
|
+
}
|
|
4032
|
+
/**
|
|
4033
|
+
* Increments integer value by amount.
|
|
4034
|
+
*/
|
|
4035
|
+
async incrby(key, increment) {
|
|
4036
|
+
const driver = await this.getDriver();
|
|
4037
|
+
return driver.incrby(key, increment);
|
|
4038
|
+
}
|
|
4039
|
+
/**
|
|
4040
|
+
* Decrements integer value by 1.
|
|
4041
|
+
*/
|
|
4042
|
+
async decr(key) {
|
|
4043
|
+
const driver = await this.getDriver();
|
|
4044
|
+
return driver.decr(key);
|
|
4045
|
+
}
|
|
4046
|
+
/**
|
|
4047
|
+
* Decrements integer value by amount.
|
|
4048
|
+
*/
|
|
4049
|
+
async decrby(key, decrement) {
|
|
4050
|
+
const driver = await this.getDriver();
|
|
4051
|
+
return driver.decrby(key, decrement);
|
|
4052
|
+
}
|
|
4053
|
+
/**
|
|
4054
|
+
* Appends value to key.
|
|
4055
|
+
*/
|
|
4056
|
+
async append(key, value) {
|
|
4057
|
+
const driver = await this.getDriver();
|
|
4058
|
+
return driver.append(key, value);
|
|
4059
|
+
}
|
|
4060
|
+
/**
|
|
4061
|
+
* Deletes one or more keys.
|
|
4062
|
+
*/
|
|
4063
|
+
async del(...keys) {
|
|
4064
|
+
const driver = await this.getDriver();
|
|
4065
|
+
return driver.del(...keys);
|
|
4066
|
+
}
|
|
4067
|
+
/**
|
|
4068
|
+
* Checks if keys exist.
|
|
4069
|
+
*/
|
|
4070
|
+
async exists(...keys) {
|
|
4071
|
+
const driver = await this.getDriver();
|
|
4072
|
+
return driver.exists(...keys);
|
|
4073
|
+
}
|
|
4074
|
+
/**
|
|
4075
|
+
* Sets TTL on key (seconds).
|
|
4076
|
+
*/
|
|
4077
|
+
async expire(key, seconds) {
|
|
4078
|
+
const driver = await this.getDriver();
|
|
4079
|
+
return driver.expire(key, seconds);
|
|
4080
|
+
}
|
|
4081
|
+
/**
|
|
4082
|
+
* Sets TTL on key (milliseconds).
|
|
4083
|
+
*/
|
|
4084
|
+
async pexpire(key, milliseconds) {
|
|
4085
|
+
const driver = await this.getDriver();
|
|
4086
|
+
return driver.pexpire(key, milliseconds);
|
|
4087
|
+
}
|
|
4088
|
+
/**
|
|
4089
|
+
* Sets absolute expiration time.
|
|
4090
|
+
*/
|
|
4091
|
+
async expireat(key, timestamp) {
|
|
4092
|
+
const driver = await this.getDriver();
|
|
4093
|
+
return driver.expireat(key, timestamp);
|
|
4094
|
+
}
|
|
4095
|
+
/**
|
|
4096
|
+
* Gets TTL of key (seconds).
|
|
4097
|
+
*/
|
|
4098
|
+
async ttl(key) {
|
|
4099
|
+
const driver = await this.getDriver();
|
|
4100
|
+
return driver.ttl(key);
|
|
4101
|
+
}
|
|
4102
|
+
/**
|
|
4103
|
+
* Gets TTL of key (milliseconds).
|
|
4104
|
+
*/
|
|
4105
|
+
async pttl(key) {
|
|
4106
|
+
const driver = await this.getDriver();
|
|
4107
|
+
return driver.pttl(key);
|
|
4108
|
+
}
|
|
4109
|
+
/**
|
|
4110
|
+
* Removes expiration from key.
|
|
4111
|
+
*/
|
|
4112
|
+
async persist(key) {
|
|
4113
|
+
const driver = await this.getDriver();
|
|
4114
|
+
return driver.persist(key);
|
|
4115
|
+
}
|
|
4116
|
+
/**
|
|
4117
|
+
* Renames key.
|
|
4118
|
+
*/
|
|
4119
|
+
async rename(key, newKey) {
|
|
4120
|
+
const driver = await this.getDriver();
|
|
4121
|
+
return driver.rename(key, newKey);
|
|
4122
|
+
}
|
|
4123
|
+
/**
|
|
4124
|
+
* Renames key only if new key doesn't exist.
|
|
4125
|
+
*/
|
|
4126
|
+
async renamenx(key, newKey) {
|
|
4127
|
+
const driver = await this.getDriver();
|
|
4128
|
+
return driver.renamenx(key, newKey);
|
|
4129
|
+
}
|
|
4130
|
+
/**
|
|
4131
|
+
* Gets type of value stored at key.
|
|
4132
|
+
*/
|
|
4133
|
+
async type(key) {
|
|
4134
|
+
const driver = await this.getDriver();
|
|
4135
|
+
return driver.type(key);
|
|
4136
|
+
}
|
|
4137
|
+
/**
|
|
4138
|
+
* Scans keys matching pattern.
|
|
4139
|
+
*/
|
|
4140
|
+
async scan(cursor, options) {
|
|
4141
|
+
const driver = await this.getDriver();
|
|
4142
|
+
return driver.scan(cursor, options);
|
|
4143
|
+
}
|
|
4144
|
+
/**
|
|
4145
|
+
* Gets hash field value.
|
|
4146
|
+
*/
|
|
4147
|
+
async hget(key, field) {
|
|
4148
|
+
const driver = await this.getDriver();
|
|
4149
|
+
return driver.hget(key, field);
|
|
4150
|
+
}
|
|
4151
|
+
/**
|
|
4152
|
+
* Sets hash field value.
|
|
4153
|
+
*/
|
|
4154
|
+
async hset(key, field, value) {
|
|
4155
|
+
const driver = await this.getDriver();
|
|
4156
|
+
return driver.hset(key, field, value);
|
|
4157
|
+
}
|
|
4158
|
+
/**
|
|
4159
|
+
* Sets multiple hash fields.
|
|
4160
|
+
*/
|
|
4161
|
+
async hmset(key, data) {
|
|
4162
|
+
const driver = await this.getDriver();
|
|
4163
|
+
return driver.hmset(key, data);
|
|
4164
|
+
}
|
|
4165
|
+
/**
|
|
4166
|
+
* Gets multiple hash field values.
|
|
4167
|
+
*/
|
|
4168
|
+
async hmget(key, ...fields) {
|
|
4169
|
+
const driver = await this.getDriver();
|
|
4170
|
+
return driver.hmget(key, ...fields);
|
|
4171
|
+
}
|
|
4172
|
+
/**
|
|
4173
|
+
* Gets all fields and values of hash.
|
|
4174
|
+
*/
|
|
4175
|
+
async hgetall(key) {
|
|
4176
|
+
const driver = await this.getDriver();
|
|
4177
|
+
return driver.hgetall(key);
|
|
4178
|
+
}
|
|
4179
|
+
/**
|
|
4180
|
+
* Deletes hash fields.
|
|
4181
|
+
*/
|
|
4182
|
+
async hdel(key, ...fields) {
|
|
4183
|
+
const driver = await this.getDriver();
|
|
4184
|
+
return driver.hdel(key, ...fields);
|
|
4185
|
+
}
|
|
4186
|
+
/**
|
|
4187
|
+
* Checks if hash field exists.
|
|
4188
|
+
*/
|
|
4189
|
+
async hexists(key, field) {
|
|
4190
|
+
const driver = await this.getDriver();
|
|
4191
|
+
return driver.hexists(key, field);
|
|
4192
|
+
}
|
|
4193
|
+
/**
|
|
4194
|
+
* Gets all field names in hash.
|
|
4195
|
+
*/
|
|
4196
|
+
async hkeys(key) {
|
|
4197
|
+
const driver = await this.getDriver();
|
|
4198
|
+
return driver.hkeys(key);
|
|
4199
|
+
}
|
|
4200
|
+
/**
|
|
4201
|
+
* Gets all values in hash.
|
|
4202
|
+
*/
|
|
4203
|
+
async hvals(key) {
|
|
4204
|
+
const driver = await this.getDriver();
|
|
4205
|
+
return driver.hvals(key);
|
|
4206
|
+
}
|
|
4207
|
+
/**
|
|
4208
|
+
* Gets number of fields in hash.
|
|
4209
|
+
*/
|
|
4210
|
+
async hlen(key) {
|
|
4211
|
+
const driver = await this.getDriver();
|
|
4212
|
+
return driver.hlen(key);
|
|
4213
|
+
}
|
|
4214
|
+
/**
|
|
4215
|
+
* Increments hash field by integer.
|
|
4216
|
+
*/
|
|
4217
|
+
async hincrby(key, field, increment) {
|
|
4218
|
+
const driver = await this.getDriver();
|
|
4219
|
+
return driver.hincrby(key, field, increment);
|
|
4220
|
+
}
|
|
4221
|
+
/**
|
|
4222
|
+
* Scans hash fields.
|
|
4223
|
+
*/
|
|
4224
|
+
async hscan(key, cursor, options) {
|
|
4225
|
+
const driver = await this.getDriver();
|
|
4226
|
+
return driver.hscan(key, cursor, options);
|
|
4227
|
+
}
|
|
4228
|
+
/**
|
|
4229
|
+
* Pushes values to left of list.
|
|
4230
|
+
*/
|
|
4231
|
+
async lpush(key, ...values) {
|
|
4232
|
+
const driver = await this.getDriver();
|
|
4233
|
+
return driver.lpush(key, ...values);
|
|
4234
|
+
}
|
|
4235
|
+
/**
|
|
4236
|
+
* Pushes values to right of list.
|
|
4237
|
+
*/
|
|
4238
|
+
async rpush(key, ...values) {
|
|
4239
|
+
const driver = await this.getDriver();
|
|
4240
|
+
return driver.rpush(key, ...values);
|
|
4241
|
+
}
|
|
4242
|
+
/**
|
|
4243
|
+
* Pops value from left of list.
|
|
4244
|
+
*/
|
|
4245
|
+
async lpop(key) {
|
|
4246
|
+
const driver = await this.getDriver();
|
|
4247
|
+
return driver.lpop(key);
|
|
4248
|
+
}
|
|
4249
|
+
/**
|
|
4250
|
+
* Pops value from right of list.
|
|
4251
|
+
*/
|
|
4252
|
+
async rpop(key) {
|
|
4253
|
+
const driver = await this.getDriver();
|
|
4254
|
+
return driver.rpop(key);
|
|
4255
|
+
}
|
|
4256
|
+
/**
|
|
4257
|
+
* Gets list length.
|
|
4258
|
+
*/
|
|
4259
|
+
async llen(key) {
|
|
4260
|
+
const driver = await this.getDriver();
|
|
4261
|
+
return driver.llen(key);
|
|
4262
|
+
}
|
|
4263
|
+
/**
|
|
4264
|
+
* Gets list range.
|
|
4265
|
+
*/
|
|
4266
|
+
async lrange(key, start, stop) {
|
|
4267
|
+
const driver = await this.getDriver();
|
|
4268
|
+
return driver.lrange(key, start, stop);
|
|
4269
|
+
}
|
|
4270
|
+
/**
|
|
4271
|
+
* Trims list to specified range.
|
|
4272
|
+
*/
|
|
4273
|
+
async ltrim(key, start, stop) {
|
|
4274
|
+
const driver = await this.getDriver();
|
|
4275
|
+
return driver.ltrim(key, start, stop);
|
|
4276
|
+
}
|
|
4277
|
+
/**
|
|
4278
|
+
* Gets element at index.
|
|
4279
|
+
*/
|
|
4280
|
+
async lindex(key, index) {
|
|
4281
|
+
const driver = await this.getDriver();
|
|
4282
|
+
return driver.lindex(key, index);
|
|
4283
|
+
}
|
|
4284
|
+
/**
|
|
4285
|
+
* Sets element at index.
|
|
4286
|
+
*/
|
|
4287
|
+
async lset(key, index, value) {
|
|
4288
|
+
const driver = await this.getDriver();
|
|
4289
|
+
return driver.lset(key, index, value);
|
|
4290
|
+
}
|
|
4291
|
+
/**
|
|
4292
|
+
* Adds members to set.
|
|
4293
|
+
*/
|
|
4294
|
+
async sadd(key, ...members) {
|
|
4295
|
+
const driver = await this.getDriver();
|
|
4296
|
+
return driver.sadd(key, ...members);
|
|
4297
|
+
}
|
|
4298
|
+
/**
|
|
4299
|
+
* Removes members from set.
|
|
4300
|
+
*/
|
|
4301
|
+
async srem(key, ...members) {
|
|
4302
|
+
const driver = await this.getDriver();
|
|
4303
|
+
return driver.srem(key, ...members);
|
|
4304
|
+
}
|
|
4305
|
+
/**
|
|
4306
|
+
* Gets all members of set.
|
|
4307
|
+
*/
|
|
4308
|
+
async smembers(key) {
|
|
4309
|
+
const driver = await this.getDriver();
|
|
4310
|
+
return driver.smembers(key);
|
|
4311
|
+
}
|
|
4312
|
+
/**
|
|
4313
|
+
* Checks if member is in set.
|
|
4314
|
+
*/
|
|
4315
|
+
async sismember(key, member) {
|
|
4316
|
+
const driver = await this.getDriver();
|
|
4317
|
+
return driver.sismember(key, member);
|
|
4318
|
+
}
|
|
4319
|
+
/**
|
|
4320
|
+
* Gets number of members in set.
|
|
4321
|
+
*/
|
|
4322
|
+
async scard(key) {
|
|
4323
|
+
const driver = await this.getDriver();
|
|
4324
|
+
return driver.scard(key);
|
|
4325
|
+
}
|
|
4326
|
+
/**
|
|
4327
|
+
* Gets random member from set.
|
|
4328
|
+
*/
|
|
4329
|
+
async srandmember(key, count) {
|
|
4330
|
+
const driver = await this.getDriver();
|
|
4331
|
+
return driver.srandmember(key, count);
|
|
4332
|
+
}
|
|
4333
|
+
/**
|
|
4334
|
+
* Pops random member from set.
|
|
4335
|
+
*/
|
|
4336
|
+
async spop(key, count) {
|
|
4337
|
+
const driver = await this.getDriver();
|
|
4338
|
+
return driver.spop(key, count);
|
|
4339
|
+
}
|
|
4340
|
+
/**
|
|
4341
|
+
* Scans set members.
|
|
4342
|
+
*/
|
|
4343
|
+
async sscan(key, cursor, options) {
|
|
4344
|
+
const driver = await this.getDriver();
|
|
4345
|
+
return driver.sscan(key, cursor, options);
|
|
4346
|
+
}
|
|
4347
|
+
/**
|
|
4348
|
+
* Adds members to sorted set with scores.
|
|
4349
|
+
*/
|
|
4350
|
+
async zadd(key, ...args) {
|
|
4351
|
+
const driver = await this.getDriver();
|
|
4352
|
+
return driver.zadd(key, ...args);
|
|
4353
|
+
}
|
|
4354
|
+
/**
|
|
4355
|
+
* Removes members from sorted set.
|
|
4356
|
+
*/
|
|
4357
|
+
async zrem(key, ...members) {
|
|
4358
|
+
const driver = await this.getDriver();
|
|
4359
|
+
return driver.zrem(key, ...members);
|
|
4360
|
+
}
|
|
4361
|
+
/**
|
|
4362
|
+
* Gets members in range by index.
|
|
4363
|
+
*/
|
|
4364
|
+
async zrange(key, start, stop, withScores) {
|
|
4365
|
+
const driver = await this.getDriver();
|
|
4366
|
+
return driver.zrange(key, start, stop, withScores);
|
|
4367
|
+
}
|
|
4368
|
+
/**
|
|
4369
|
+
* Gets members in range by score.
|
|
4370
|
+
*/
|
|
4371
|
+
async zrangebyscore(key, min, max, withScores) {
|
|
4372
|
+
const driver = await this.getDriver();
|
|
4373
|
+
return driver.zrangebyscore(key, min, max, withScores);
|
|
4374
|
+
}
|
|
4375
|
+
/**
|
|
4376
|
+
* Gets score of member.
|
|
4377
|
+
*/
|
|
4378
|
+
async zscore(key, member) {
|
|
4379
|
+
const driver = await this.getDriver();
|
|
4380
|
+
return driver.zscore(key, member);
|
|
4381
|
+
}
|
|
4382
|
+
/**
|
|
4383
|
+
* Gets number of members in sorted set.
|
|
4384
|
+
*/
|
|
4385
|
+
async zcard(key) {
|
|
4386
|
+
const driver = await this.getDriver();
|
|
4387
|
+
return driver.zcard(key);
|
|
4388
|
+
}
|
|
4389
|
+
/**
|
|
4390
|
+
* Gets rank of member.
|
|
4391
|
+
*/
|
|
4392
|
+
async zrank(key, member) {
|
|
4393
|
+
const driver = await this.getDriver();
|
|
4394
|
+
return driver.zrank(key, member);
|
|
4395
|
+
}
|
|
4396
|
+
/**
|
|
4397
|
+
* Increments score of member.
|
|
4398
|
+
*/
|
|
4399
|
+
async zincrby(key, increment, member) {
|
|
4400
|
+
const driver = await this.getDriver();
|
|
4401
|
+
return driver.zincrby(key, increment, member);
|
|
4402
|
+
}
|
|
4403
|
+
/**
|
|
4404
|
+
* Scans sorted set members.
|
|
4405
|
+
*/
|
|
4406
|
+
async zscan(key, cursor, options) {
|
|
4407
|
+
const driver = await this.getDriver();
|
|
4408
|
+
return driver.zscan(key, cursor, options);
|
|
4409
|
+
}
|
|
4410
|
+
/**
|
|
4411
|
+
* Publishes message to channel.
|
|
4412
|
+
*/
|
|
4413
|
+
async publish(channel, message) {
|
|
4414
|
+
const driver = await this.getDriver();
|
|
4415
|
+
return driver.publish(channel, message);
|
|
4416
|
+
}
|
|
4417
|
+
/**
|
|
4418
|
+
* Subscribes to channels.
|
|
4419
|
+
*/
|
|
4420
|
+
async subscribe(...channels) {
|
|
4421
|
+
const driver = await this.getDriver();
|
|
4422
|
+
return driver.subscribe(...channels);
|
|
4423
|
+
}
|
|
4424
|
+
/**
|
|
4425
|
+
* Unsubscribes from channels.
|
|
4426
|
+
*/
|
|
4427
|
+
async unsubscribe(...channels) {
|
|
4428
|
+
const driver = await this.getDriver();
|
|
4429
|
+
return driver.unsubscribe(...channels);
|
|
4430
|
+
}
|
|
4431
|
+
/**
|
|
4432
|
+
* Subscribes to channels by pattern.
|
|
4433
|
+
*/
|
|
4434
|
+
async psubscribe(...patterns) {
|
|
4435
|
+
const driver = await this.getDriver();
|
|
4436
|
+
return driver.psubscribe(...patterns);
|
|
4437
|
+
}
|
|
4438
|
+
/**
|
|
4439
|
+
* Unsubscribes from channel patterns.
|
|
4440
|
+
*/
|
|
4441
|
+
async punsubscribe(...patterns) {
|
|
4442
|
+
const driver = await this.getDriver();
|
|
4443
|
+
return driver.punsubscribe(...patterns);
|
|
4444
|
+
}
|
|
4445
|
+
/**
|
|
4446
|
+
* Creates a pipeline for batching commands.
|
|
4447
|
+
*/
|
|
4448
|
+
async pipeline() {
|
|
4449
|
+
const driver = await this.getDriver();
|
|
4450
|
+
return driver.pipeline();
|
|
4451
|
+
}
|
|
4452
|
+
/**
|
|
4453
|
+
* Creates a multi/exec transaction.
|
|
4454
|
+
*/
|
|
4455
|
+
async multi() {
|
|
4456
|
+
const driver = await this.getDriver();
|
|
4457
|
+
return driver.multi();
|
|
4458
|
+
}
|
|
4459
|
+
/**
|
|
4460
|
+
* Evaluates Lua script.
|
|
4461
|
+
*/
|
|
4462
|
+
async eval(script, keys, args) {
|
|
4463
|
+
const driver = await this.getDriver();
|
|
4464
|
+
return driver.eval(script, keys, args);
|
|
4465
|
+
}
|
|
4466
|
+
/**
|
|
4467
|
+
* Evaluates Lua script by SHA.
|
|
4468
|
+
*/
|
|
4469
|
+
async evalsha(sha, keys, args) {
|
|
4470
|
+
const driver = await this.getDriver();
|
|
4471
|
+
return driver.evalsha(sha, keys, args);
|
|
4472
|
+
}
|
|
4473
|
+
/**
|
|
4474
|
+
* Loads Lua script and returns SHA.
|
|
4475
|
+
*/
|
|
4476
|
+
async scriptLoad(script) {
|
|
4477
|
+
const driver = await this.getDriver();
|
|
4478
|
+
return driver.scriptLoad(script);
|
|
4479
|
+
}
|
|
4480
|
+
/**
|
|
4481
|
+
* Checks if scripts exist.
|
|
4482
|
+
*/
|
|
4483
|
+
async scriptExists(...shas) {
|
|
4484
|
+
const driver = await this.getDriver();
|
|
4485
|
+
return driver.scriptExists(...shas);
|
|
4486
|
+
}
|
|
4487
|
+
/**
|
|
4488
|
+
* Flushes all scripts from cache.
|
|
4489
|
+
*/
|
|
4490
|
+
async scriptFlush() {
|
|
4491
|
+
const driver = await this.getDriver();
|
|
4492
|
+
return driver.scriptFlush();
|
|
4493
|
+
}
|
|
4494
|
+
/**
|
|
4495
|
+
* Flushes all keys from current database.
|
|
4496
|
+
*/
|
|
4497
|
+
async flushdb() {
|
|
4498
|
+
const driver = await this.getDriver();
|
|
4499
|
+
return driver.flushdb();
|
|
4500
|
+
}
|
|
4501
|
+
/**
|
|
4502
|
+
* Flushes all keys from all databases.
|
|
4503
|
+
*/
|
|
4504
|
+
async flushall() {
|
|
4505
|
+
const driver = await this.getDriver();
|
|
4506
|
+
return driver.flushall();
|
|
4507
|
+
}
|
|
4508
|
+
/**
|
|
4509
|
+
* Gets server info.
|
|
4510
|
+
*/
|
|
4511
|
+
async info(section) {
|
|
4512
|
+
const driver = await this.getDriver();
|
|
4513
|
+
return driver.info(section);
|
|
4514
|
+
}
|
|
4515
|
+
/**
|
|
4516
|
+
* Gets database size (number of keys).
|
|
4517
|
+
*/
|
|
4518
|
+
async dbsize() {
|
|
4519
|
+
const driver = await this.getDriver();
|
|
4520
|
+
return driver.dbsize();
|
|
4521
|
+
}
|
|
4522
|
+
/**
|
|
4523
|
+
* NestJS lifecycle hook - cleanup on module destroy.
|
|
4524
|
+
*/
|
|
4525
|
+
async onModuleDestroy() {
|
|
4526
|
+
await this.clientManager.closeAll();
|
|
4527
|
+
}
|
|
4528
|
+
};
|
|
4529
|
+
RedisService = __decorateClass([
|
|
4530
|
+
(0, import_common3.Injectable)(),
|
|
4531
|
+
__decorateParam(0, (0, import_common3.Inject)(CLIENT_MANAGER))
|
|
4532
|
+
], RedisService);
|
|
4533
|
+
|
|
4534
|
+
// src/plugin/application/plugin-registry.service.ts
|
|
4535
|
+
var import_common4 = require("@nestjs/common");
|
|
4536
|
+
var PluginRegistryService = class {
|
|
4537
|
+
constructor(plugins, clientManager, options, moduleRef) {
|
|
4538
|
+
this.plugins = plugins;
|
|
4539
|
+
this.clientManager = clientManager;
|
|
4540
|
+
this.options = options;
|
|
4541
|
+
this.moduleRef = moduleRef;
|
|
4542
|
+
}
|
|
4543
|
+
logger = new import_common4.Logger(PluginRegistryService.name);
|
|
4544
|
+
async onModuleInit() {
|
|
4545
|
+
if (this.plugins.length === 0) {
|
|
4546
|
+
return;
|
|
4547
|
+
}
|
|
4548
|
+
const sorted = this.sortByDependencies(this.plugins);
|
|
4549
|
+
const context = this.createContext();
|
|
4550
|
+
for (const plugin of sorted) {
|
|
4551
|
+
if (plugin.onRegister) {
|
|
4552
|
+
this.logger.debug(`Calling onRegister for plugin "${plugin.name}"`);
|
|
4553
|
+
await plugin.onRegister(context);
|
|
4554
|
+
}
|
|
4555
|
+
}
|
|
4556
|
+
for (const plugin of sorted) {
|
|
4557
|
+
if (plugin.onModuleInit) {
|
|
4558
|
+
this.logger.debug(`Calling onModuleInit for plugin "${plugin.name}"`);
|
|
4559
|
+
await plugin.onModuleInit(context);
|
|
4560
|
+
}
|
|
4561
|
+
}
|
|
4562
|
+
}
|
|
4563
|
+
async onModuleDestroy() {
|
|
4564
|
+
if (this.plugins.length === 0) {
|
|
4565
|
+
return;
|
|
4566
|
+
}
|
|
4567
|
+
const sorted = this.sortByDependencies(this.plugins).reverse();
|
|
4568
|
+
const context = this.createContext();
|
|
4569
|
+
for (const plugin of sorted) {
|
|
4570
|
+
if (plugin.onModuleDestroy) {
|
|
4571
|
+
this.logger.debug(`Calling onModuleDestroy for plugin "${plugin.name}"`);
|
|
4572
|
+
await plugin.onModuleDestroy(context);
|
|
4573
|
+
}
|
|
4574
|
+
}
|
|
4575
|
+
}
|
|
4576
|
+
createContext() {
|
|
4577
|
+
const plugins = this.plugins;
|
|
4578
|
+
const clientManager = this.clientManager;
|
|
4579
|
+
const moduleRef = this.moduleRef;
|
|
4580
|
+
const pluginClientManager = {
|
|
4581
|
+
getClient(name) {
|
|
4582
|
+
return clientManager.getClient(name);
|
|
4583
|
+
},
|
|
4584
|
+
hasClient(name) {
|
|
4585
|
+
return clientManager.hasClient(name);
|
|
4586
|
+
},
|
|
4587
|
+
getClientNames() {
|
|
4588
|
+
return clientManager.getClientNames();
|
|
4589
|
+
}
|
|
4590
|
+
};
|
|
4591
|
+
const pluginLogger = {
|
|
4592
|
+
debug: (message, context) => {
|
|
4593
|
+
this.logger.debug(message, context ? JSON.stringify(context) : void 0);
|
|
4594
|
+
},
|
|
4595
|
+
info: (message, context) => {
|
|
4596
|
+
this.logger.log(message, context ? JSON.stringify(context) : void 0);
|
|
4597
|
+
},
|
|
4598
|
+
warn: (message, context) => {
|
|
4599
|
+
this.logger.warn(message, context ? JSON.stringify(context) : void 0);
|
|
4600
|
+
},
|
|
4601
|
+
error: (message, error, context) => {
|
|
4602
|
+
this.logger.error(message, error?.stack, context ? JSON.stringify(context) : void 0);
|
|
4603
|
+
}
|
|
4604
|
+
};
|
|
4605
|
+
const pluginConfig = {
|
|
4606
|
+
clients: {},
|
|
4607
|
+
plugins,
|
|
4608
|
+
global: {
|
|
4609
|
+
debug: this.options.global?.debug,
|
|
4610
|
+
defaultTtl: this.options.global?.defaultTtl,
|
|
4611
|
+
keyPrefix: this.options.global?.keyPrefix
|
|
4612
|
+
}
|
|
4613
|
+
};
|
|
4614
|
+
return {
|
|
4615
|
+
clientManager: pluginClientManager,
|
|
4616
|
+
config: pluginConfig,
|
|
4617
|
+
logger: pluginLogger,
|
|
4618
|
+
moduleRef,
|
|
4619
|
+
getPlugin: (name) => {
|
|
4620
|
+
return plugins.find((p) => p.name === name);
|
|
4621
|
+
},
|
|
4622
|
+
hasPlugin: (name) => {
|
|
4623
|
+
return plugins.some((p) => p.name === name);
|
|
4624
|
+
}
|
|
4625
|
+
};
|
|
4626
|
+
}
|
|
4627
|
+
/**
|
|
4628
|
+
* Topological sort of plugins by dependencies (Kahn's algorithm).
|
|
4629
|
+
* Plugins without dependencies maintain their original order.
|
|
4630
|
+
* Throws on circular dependencies.
|
|
4631
|
+
*/
|
|
4632
|
+
sortByDependencies(plugins) {
|
|
4633
|
+
if (plugins.length === 0) {
|
|
4634
|
+
return [];
|
|
4635
|
+
}
|
|
4636
|
+
const pluginMap = /* @__PURE__ */ new Map();
|
|
4637
|
+
const inDegree = /* @__PURE__ */ new Map();
|
|
4638
|
+
const adjacency = /* @__PURE__ */ new Map();
|
|
4639
|
+
for (const plugin of plugins) {
|
|
4640
|
+
pluginMap.set(plugin.name, plugin);
|
|
4641
|
+
inDegree.set(plugin.name, 0);
|
|
4642
|
+
adjacency.set(plugin.name, []);
|
|
4643
|
+
}
|
|
4644
|
+
for (const plugin of plugins) {
|
|
4645
|
+
const deps = plugin.dependencies || [];
|
|
4646
|
+
for (const dep of deps) {
|
|
4647
|
+
if (!pluginMap.has(dep)) {
|
|
4648
|
+
throw new RedisXError(`Plugin "${plugin.name}" depends on "${dep}" which is not registered`, "PLUGIN_DEPENDENCY_MISSING" /* PLUGIN_DEPENDENCY_MISSING */, void 0, { plugin: plugin.name, missingDependency: dep });
|
|
4649
|
+
}
|
|
4650
|
+
adjacency.get(dep).push(plugin.name);
|
|
4651
|
+
inDegree.set(plugin.name, (inDegree.get(plugin.name) || 0) + 1);
|
|
4652
|
+
}
|
|
4653
|
+
}
|
|
4654
|
+
const originalIndex = /* @__PURE__ */ new Map();
|
|
4655
|
+
plugins.forEach((p, i) => originalIndex.set(p.name, i));
|
|
4656
|
+
const queue = [];
|
|
4657
|
+
for (const plugin of plugins) {
|
|
4658
|
+
if (inDegree.get(plugin.name) === 0) {
|
|
4659
|
+
queue.push(plugin.name);
|
|
4660
|
+
}
|
|
4661
|
+
}
|
|
4662
|
+
queue.sort((a, b) => originalIndex.get(a) - originalIndex.get(b));
|
|
4663
|
+
const sorted = [];
|
|
4664
|
+
while (queue.length > 0) {
|
|
4665
|
+
const name = queue.shift();
|
|
4666
|
+
sorted.push(pluginMap.get(name));
|
|
4667
|
+
const neighbors = adjacency.get(name) || [];
|
|
4668
|
+
const newReady = [];
|
|
4669
|
+
for (const neighbor of neighbors) {
|
|
4670
|
+
const deg = inDegree.get(neighbor) - 1;
|
|
4671
|
+
inDegree.set(neighbor, deg);
|
|
4672
|
+
if (deg === 0) {
|
|
4673
|
+
newReady.push(neighbor);
|
|
4674
|
+
}
|
|
4675
|
+
}
|
|
4676
|
+
newReady.sort((a, b) => originalIndex.get(a) - originalIndex.get(b));
|
|
4677
|
+
queue.push(...newReady);
|
|
4678
|
+
}
|
|
4679
|
+
if (sorted.length !== plugins.length) {
|
|
4680
|
+
const remaining = plugins.filter((p) => !sorted.some((s) => s.name === p.name)).map((p) => p.name);
|
|
4681
|
+
throw new RedisXError(`Circular dependency detected among plugins: ${remaining.join(", ")}`, "PLUGIN_CIRCULAR_DEPENDENCY" /* PLUGIN_CIRCULAR_DEPENDENCY */, void 0, { circularPlugins: remaining });
|
|
4682
|
+
}
|
|
4683
|
+
return sorted;
|
|
4684
|
+
}
|
|
4685
|
+
};
|
|
4686
|
+
PluginRegistryService = __decorateClass([
|
|
4687
|
+
(0, import_common4.Injectable)(),
|
|
4688
|
+
__decorateParam(0, (0, import_common4.Inject)(REGISTERED_PLUGINS)),
|
|
4689
|
+
__decorateParam(1, (0, import_common4.Inject)(CLIENT_MANAGER)),
|
|
4690
|
+
__decorateParam(2, (0, import_common4.Inject)(REDIS_MODULE_OPTIONS))
|
|
4691
|
+
], PluginRegistryService);
|
|
4692
|
+
|
|
4693
|
+
// src/api/redis.module.ts
|
|
4694
|
+
var RedisModule = class {
|
|
4695
|
+
/**
|
|
4696
|
+
* Configures Redis module synchronously.
|
|
4697
|
+
*
|
|
4698
|
+
* Creates a global module with Redis clients and plugins.
|
|
4699
|
+
* All providers are available for injection everywhere.
|
|
4700
|
+
*
|
|
4701
|
+
* @param options - Redis module options
|
|
4702
|
+
* @returns Dynamic module
|
|
4703
|
+
*
|
|
4704
|
+
* @example
|
|
4705
|
+
* ```typescript
|
|
4706
|
+
* RedisModule.forRoot({
|
|
4707
|
+
* clients: {
|
|
4708
|
+
* type: 'single',
|
|
4709
|
+
* host: 'localhost',
|
|
4710
|
+
* port: 6379,
|
|
4711
|
+
* },
|
|
4712
|
+
* plugins: [new CachePlugin(), new LocksPlugin()],
|
|
4713
|
+
* })
|
|
4714
|
+
* ```
|
|
4715
|
+
*/
|
|
4716
|
+
static forRoot(options) {
|
|
4717
|
+
const providers = createRedisProviders(options);
|
|
4718
|
+
const pluginProviders = [];
|
|
4719
|
+
const pluginExports = [];
|
|
4720
|
+
const pluginControllers = [];
|
|
4721
|
+
if (options.plugins && options.plugins.length > 0) {
|
|
4722
|
+
options.plugins.forEach((plugin) => {
|
|
4723
|
+
if (plugin.getProviders) {
|
|
4724
|
+
const providers2 = plugin.getProviders();
|
|
4725
|
+
if (Array.isArray(providers2)) {
|
|
4726
|
+
pluginProviders.push(...providers2);
|
|
4727
|
+
}
|
|
4728
|
+
}
|
|
4729
|
+
if (plugin.getExports) {
|
|
4730
|
+
const exports2 = plugin.getExports();
|
|
4731
|
+
if (Array.isArray(exports2)) {
|
|
4732
|
+
pluginExports.push(...exports2);
|
|
4733
|
+
}
|
|
4734
|
+
}
|
|
4735
|
+
if (plugin.getControllers) {
|
|
4736
|
+
const controllers = plugin.getControllers();
|
|
4737
|
+
if (Array.isArray(controllers)) {
|
|
4738
|
+
pluginControllers.push(...controllers);
|
|
4739
|
+
}
|
|
4740
|
+
}
|
|
4741
|
+
});
|
|
4742
|
+
}
|
|
4743
|
+
const plugins = options.plugins || [];
|
|
4744
|
+
return {
|
|
4745
|
+
module: RedisModule,
|
|
4746
|
+
global: true,
|
|
4747
|
+
imports: [import_core.DiscoveryModule],
|
|
4748
|
+
// Required for plugins that scan providers (e.g., CachePlugin)
|
|
4749
|
+
providers: [...providers, ...pluginProviders, { provide: REGISTERED_PLUGINS, useValue: plugins }, PluginRegistryService, RedisService],
|
|
4750
|
+
controllers: pluginControllers,
|
|
4751
|
+
exports: [CLIENT_MANAGER, RedisService, ...pluginExports]
|
|
4752
|
+
};
|
|
4753
|
+
}
|
|
4754
|
+
/**
|
|
4755
|
+
* Configures Redis module asynchronously.
|
|
4756
|
+
*
|
|
4757
|
+
* Allows configuration to be loaded asynchronously using factory function,
|
|
4758
|
+
* existing provider, or class-based factory. Supports plugins.
|
|
4759
|
+
*
|
|
4760
|
+
* Note: Plugins must be provided in the async options (outside useFactory),
|
|
4761
|
+
* not in the factory result. This is a standard NestJS pattern — plugins
|
|
4762
|
+
* must be available at module construction time for NestJS DI.
|
|
4763
|
+
*
|
|
4764
|
+
* @param options - Async Redis module options
|
|
4765
|
+
* @returns Dynamic module
|
|
4766
|
+
*
|
|
4767
|
+
* @example
|
|
4768
|
+
* **Using factory with plugins:**
|
|
4769
|
+
* ```typescript
|
|
4770
|
+
* RedisModule.forRootAsync({
|
|
4771
|
+
* plugins: [new CachePlugin(), new LocksPlugin()],
|
|
4772
|
+
* useFactory: (config: ConfigService) => ({
|
|
4773
|
+
* clients: {
|
|
4774
|
+
* type: 'single',
|
|
4775
|
+
* host: config.get('REDIS_HOST'),
|
|
4776
|
+
* port: config.get('REDIS_PORT'),
|
|
4777
|
+
* },
|
|
4778
|
+
* }),
|
|
4779
|
+
* inject: [ConfigService],
|
|
4780
|
+
* })
|
|
4781
|
+
* ```
|
|
4782
|
+
*
|
|
4783
|
+
* @example
|
|
4784
|
+
* **Using class:**
|
|
4785
|
+
* ```typescript
|
|
4786
|
+
* @Injectable()
|
|
4787
|
+
* class RedisConfigService implements IRedisModuleOptionsFactory {
|
|
4788
|
+
* createRedisModuleOptions(): IRedisModuleOptions {
|
|
4789
|
+
* return {
|
|
4790
|
+
* clients: { type: 'single', host: 'localhost', port: 6379 },
|
|
4791
|
+
* };
|
|
4792
|
+
* }
|
|
4793
|
+
* }
|
|
4794
|
+
*
|
|
4795
|
+
* RedisModule.forRootAsync({
|
|
4796
|
+
* plugins: [new CachePlugin()],
|
|
4797
|
+
* useClass: RedisConfigService,
|
|
4798
|
+
* })
|
|
4799
|
+
* ```
|
|
4800
|
+
*/
|
|
4801
|
+
static forRootAsync(options) {
|
|
4802
|
+
const baseProviders = createAsyncProviders(options);
|
|
4803
|
+
const imports = options.imports || [];
|
|
4804
|
+
const plugins = options.plugins || [];
|
|
4805
|
+
const pluginProviders = [];
|
|
4806
|
+
const pluginExports = [];
|
|
4807
|
+
const pluginControllers = [];
|
|
4808
|
+
if (plugins.length > 0) {
|
|
4809
|
+
plugins.forEach((plugin) => {
|
|
4810
|
+
if (plugin.getProviders) {
|
|
4811
|
+
const providers = plugin.getProviders();
|
|
4812
|
+
if (Array.isArray(providers)) {
|
|
4813
|
+
pluginProviders.push(...providers);
|
|
4814
|
+
}
|
|
4815
|
+
}
|
|
4816
|
+
if (plugin.getExports) {
|
|
4817
|
+
const exports2 = plugin.getExports();
|
|
4818
|
+
if (Array.isArray(exports2)) {
|
|
4819
|
+
pluginExports.push(...exports2);
|
|
4820
|
+
}
|
|
4821
|
+
}
|
|
4822
|
+
if (plugin.getControllers) {
|
|
4823
|
+
const controllers = plugin.getControllers();
|
|
4824
|
+
if (Array.isArray(controllers)) {
|
|
4825
|
+
pluginControllers.push(...controllers);
|
|
4826
|
+
}
|
|
4827
|
+
}
|
|
4828
|
+
});
|
|
4829
|
+
}
|
|
4830
|
+
return {
|
|
4831
|
+
module: RedisModule,
|
|
4832
|
+
global: true,
|
|
4833
|
+
imports: [import_core.DiscoveryModule, ...imports],
|
|
4834
|
+
// DiscoveryModule required for plugins
|
|
4835
|
+
providers: [...baseProviders, ...pluginProviders, { provide: REGISTERED_PLUGINS, useValue: plugins }, PluginRegistryService, RedisService],
|
|
4836
|
+
controllers: pluginControllers,
|
|
4837
|
+
exports: [CLIENT_MANAGER, RedisService, ...pluginExports]
|
|
4838
|
+
};
|
|
4839
|
+
}
|
|
4840
|
+
};
|
|
4841
|
+
RedisModule = __decorateClass([
|
|
4842
|
+
(0, import_common5.Module)({})
|
|
4843
|
+
], RedisModule);
|
|
4844
|
+
|
|
4845
|
+
// src/api/decorators/inject-redis.decorator.ts
|
|
4846
|
+
var import_common6 = require("@nestjs/common");
|
|
4847
|
+
function InjectRedis(name = DEFAULT_CLIENT_NAME) {
|
|
4848
|
+
return (0, import_common6.Inject)(getClientToken(name));
|
|
4849
|
+
}
|
|
4850
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
4851
|
+
0 && (module.exports = {
|
|
4852
|
+
BaseRedisDriver,
|
|
4853
|
+
CLIENT_MANAGER,
|
|
4854
|
+
CommandError,
|
|
4855
|
+
ConnectionError,
|
|
4856
|
+
ConnectionStatus,
|
|
4857
|
+
DEFAULT_CLIENT_NAME,
|
|
4858
|
+
DEFAULT_COMMAND_TIMEOUT,
|
|
4859
|
+
DEFAULT_CONNECTION_TIMEOUT,
|
|
4860
|
+
DEFAULT_DRIVER_TYPE,
|
|
4861
|
+
DEFAULT_GRACEFUL_SHUTDOWN_TIMEOUT,
|
|
4862
|
+
DEFAULT_HEALTH_CHECK_INTERVAL,
|
|
4863
|
+
DriverError,
|
|
4864
|
+
DriverEvent,
|
|
4865
|
+
DriverFactory,
|
|
4866
|
+
ErrorCode,
|
|
4867
|
+
InjectRedis,
|
|
4868
|
+
IoRedisAdapter,
|
|
4869
|
+
MODULE_NAME,
|
|
4870
|
+
ManagerEvent,
|
|
4871
|
+
NodeRedisAdapter,
|
|
4872
|
+
PLUGIN_REGISTRY,
|
|
4873
|
+
PluginRegistryService,
|
|
4874
|
+
REDISX_CONFIG,
|
|
4875
|
+
REDISX_LOGGER,
|
|
4876
|
+
REDIS_CLIENT,
|
|
4877
|
+
REDIS_CLIENTS_INITIALIZATION,
|
|
4878
|
+
REDIS_CLIENTS_MAP,
|
|
4879
|
+
REDIS_DRIVER,
|
|
4880
|
+
REDIS_MODULE_OPTIONS,
|
|
4881
|
+
REGISTERED_PLUGINS,
|
|
4882
|
+
RedisAuthError,
|
|
4883
|
+
RedisClientManager,
|
|
4884
|
+
RedisClusterError,
|
|
4885
|
+
RedisConfigError,
|
|
4886
|
+
RedisConnectionError,
|
|
4887
|
+
RedisInvalidArgsError,
|
|
4888
|
+
RedisKeyNotFoundError,
|
|
4889
|
+
RedisMaxRetriesError,
|
|
4890
|
+
RedisModule,
|
|
4891
|
+
RedisNotConnectedError,
|
|
4892
|
+
RedisNotSupportedError,
|
|
4893
|
+
RedisOperationError,
|
|
4894
|
+
RedisOutOfMemoryError,
|
|
4895
|
+
RedisPipelineError,
|
|
4896
|
+
RedisPoolExhaustedError,
|
|
4897
|
+
RedisReadOnlyError,
|
|
4898
|
+
RedisScriptError,
|
|
4899
|
+
RedisSentinelError,
|
|
4900
|
+
RedisService,
|
|
4901
|
+
RedisTLSError,
|
|
4902
|
+
RedisTimeoutError,
|
|
4903
|
+
RedisTransactionError,
|
|
4904
|
+
RedisTypeMismatchError,
|
|
4905
|
+
RedisValidationError,
|
|
4906
|
+
RedisXError,
|
|
4907
|
+
TimeoutError,
|
|
4908
|
+
ValidationErrorCollector,
|
|
4909
|
+
createDriver,
|
|
4910
|
+
createDrivers,
|
|
4911
|
+
detectAvailableDriver,
|
|
4912
|
+
getClientToken,
|
|
4913
|
+
getDriverToken,
|
|
4914
|
+
getErrorDomain,
|
|
4915
|
+
getRecommendedDriver,
|
|
4916
|
+
hasClass,
|
|
4917
|
+
hasExisting,
|
|
4918
|
+
hasFactory,
|
|
4919
|
+
isClusterConnection,
|
|
4920
|
+
isErrorCode,
|
|
4921
|
+
isErrorDomain,
|
|
4922
|
+
isSentinelConnection,
|
|
4923
|
+
isSingleConnection
|
|
4924
|
+
});
|
|
4925
|
+
//# sourceMappingURL=index.js.map
|