ioredis-om 5.10.2

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.
Files changed (85) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1571 -0
  3. package/built/Command.d.ts +166 -0
  4. package/built/Command.js +450 -0
  5. package/built/DataHandler.d.ts +37 -0
  6. package/built/DataHandler.js +224 -0
  7. package/built/Pipeline.d.ts +31 -0
  8. package/built/Pipeline.js +342 -0
  9. package/built/Redis.d.ts +243 -0
  10. package/built/Redis.js +800 -0
  11. package/built/ScanStream.d.ts +23 -0
  12. package/built/ScanStream.js +51 -0
  13. package/built/Script.d.ts +11 -0
  14. package/built/Script.js +62 -0
  15. package/built/SubscriptionSet.d.ts +14 -0
  16. package/built/SubscriptionSet.js +41 -0
  17. package/built/autoPipelining.d.ts +8 -0
  18. package/built/autoPipelining.js +167 -0
  19. package/built/cluster/ClusterOptions.d.ts +172 -0
  20. package/built/cluster/ClusterOptions.js +22 -0
  21. package/built/cluster/ClusterSubscriber.d.ts +29 -0
  22. package/built/cluster/ClusterSubscriber.js +223 -0
  23. package/built/cluster/ClusterSubscriberGroup.d.ts +108 -0
  24. package/built/cluster/ClusterSubscriberGroup.js +373 -0
  25. package/built/cluster/ConnectionPool.d.ts +37 -0
  26. package/built/cluster/ConnectionPool.js +154 -0
  27. package/built/cluster/DelayQueue.d.ts +20 -0
  28. package/built/cluster/DelayQueue.js +53 -0
  29. package/built/cluster/ShardedSubscriber.d.ts +36 -0
  30. package/built/cluster/ShardedSubscriber.js +147 -0
  31. package/built/cluster/index.d.ts +163 -0
  32. package/built/cluster/index.js +937 -0
  33. package/built/cluster/util.d.ts +25 -0
  34. package/built/cluster/util.js +100 -0
  35. package/built/connectors/AbstractConnector.d.ts +12 -0
  36. package/built/connectors/AbstractConnector.js +26 -0
  37. package/built/connectors/ConnectorConstructor.d.ts +5 -0
  38. package/built/connectors/ConnectorConstructor.js +2 -0
  39. package/built/connectors/SentinelConnector/FailoverDetector.d.ts +11 -0
  40. package/built/connectors/SentinelConnector/FailoverDetector.js +45 -0
  41. package/built/connectors/SentinelConnector/SentinelIterator.d.ts +13 -0
  42. package/built/connectors/SentinelConnector/SentinelIterator.js +37 -0
  43. package/built/connectors/SentinelConnector/index.d.ts +72 -0
  44. package/built/connectors/SentinelConnector/index.js +305 -0
  45. package/built/connectors/SentinelConnector/types.d.ts +21 -0
  46. package/built/connectors/SentinelConnector/types.js +2 -0
  47. package/built/connectors/StandaloneConnector.d.ts +17 -0
  48. package/built/connectors/StandaloneConnector.js +69 -0
  49. package/built/connectors/index.d.ts +3 -0
  50. package/built/connectors/index.js +7 -0
  51. package/built/constants/TLSProfiles.d.ts +9 -0
  52. package/built/constants/TLSProfiles.js +149 -0
  53. package/built/errors/ClusterAllFailedError.d.ts +7 -0
  54. package/built/errors/ClusterAllFailedError.js +15 -0
  55. package/built/errors/MaxRetriesPerRequestError.d.ts +5 -0
  56. package/built/errors/MaxRetriesPerRequestError.js +14 -0
  57. package/built/errors/index.d.ts +2 -0
  58. package/built/errors/index.js +5 -0
  59. package/built/index.d.ts +44 -0
  60. package/built/index.js +62 -0
  61. package/built/redis/RedisOptions.d.ts +197 -0
  62. package/built/redis/RedisOptions.js +58 -0
  63. package/built/redis/event_handler.d.ts +4 -0
  64. package/built/redis/event_handler.js +315 -0
  65. package/built/tracing.d.ts +26 -0
  66. package/built/tracing.js +96 -0
  67. package/built/transaction.d.ts +13 -0
  68. package/built/transaction.js +100 -0
  69. package/built/types.d.ts +33 -0
  70. package/built/types.js +2 -0
  71. package/built/utils/Commander.d.ts +50 -0
  72. package/built/utils/Commander.js +117 -0
  73. package/built/utils/RedisCommander.d.ts +8950 -0
  74. package/built/utils/RedisCommander.js +7 -0
  75. package/built/utils/applyMixin.d.ts +3 -0
  76. package/built/utils/applyMixin.js +8 -0
  77. package/built/utils/argumentParsers.d.ts +14 -0
  78. package/built/utils/argumentParsers.js +74 -0
  79. package/built/utils/debug.d.ts +16 -0
  80. package/built/utils/debug.js +95 -0
  81. package/built/utils/index.d.ts +124 -0
  82. package/built/utils/index.js +332 -0
  83. package/built/utils/lodash.d.ts +4 -0
  84. package/built/utils/lodash.js +9 -0
  85. package/package.json +103 -0
package/built/Redis.js ADDED
@@ -0,0 +1,800 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const commands_1 = require("@ioredis/commands");
4
+ const events_1 = require("events");
5
+ const standard_as_callback_1 = require("standard-as-callback");
6
+ const cluster_1 = require("./cluster");
7
+ const Command_1 = require("./Command");
8
+ const connectors_1 = require("./connectors");
9
+ const SentinelConnector_1 = require("./connectors/SentinelConnector");
10
+ const eventHandler = require("./redis/event_handler");
11
+ const RedisOptions_1 = require("./redis/RedisOptions");
12
+ const ScanStream_1 = require("./ScanStream");
13
+ const transaction_1 = require("./transaction");
14
+ const utils_1 = require("./utils");
15
+ const tracing_1 = require("./tracing");
16
+ const applyMixin_1 = require("./utils/applyMixin");
17
+ const Commander_1 = require("./utils/Commander");
18
+ const lodash_1 = require("./utils/lodash");
19
+ const Deque = require("denque");
20
+ const debug = (0, utils_1.Debug)("redis");
21
+ /**
22
+ * This is the major component of ioredis-om.
23
+ * Use it to connect to a standalone Redis server or Sentinels.
24
+ *
25
+ * ```typescript
26
+ * const redis = new Redis(); // Default port is 6379
27
+ * async function main() {
28
+ * redis.set("foo", "bar");
29
+ * redis.get("foo", (err, result) => {
30
+ * // `result` should be "bar"
31
+ * console.log(err, result);
32
+ * });
33
+ * // Or use Promise
34
+ * const result = await redis.get("foo");
35
+ * }
36
+ * ```
37
+ */
38
+ class Redis extends Commander_1.default {
39
+ constructor(arg1, arg2, arg3) {
40
+ super();
41
+ this.status = "wait";
42
+ /**
43
+ * @ignore
44
+ */
45
+ this.isCluster = false;
46
+ this.reconnectTimeout = null;
47
+ this.connectionEpoch = 0;
48
+ this.retryAttempts = 0;
49
+ this.manuallyClosing = false;
50
+ // Prepare autopipelines structures
51
+ this._autoPipelines = new Map();
52
+ this._runningAutoPipelines = new Set();
53
+ this.parseOptions(arg1, arg2, arg3);
54
+ events_1.EventEmitter.call(this);
55
+ this.resetCommandQueue();
56
+ this.resetOfflineQueue();
57
+ if (this.options.Connector) {
58
+ this.connector = new this.options.Connector(this.options);
59
+ }
60
+ else if (this.options.sentinels) {
61
+ const sentinelConnector = new SentinelConnector_1.default(this.options);
62
+ sentinelConnector.emitter = this;
63
+ this.connector = sentinelConnector;
64
+ }
65
+ else {
66
+ this.connector = new connectors_1.StandaloneConnector(this.options);
67
+ }
68
+ if (this.options.scripts) {
69
+ Object.entries(this.options.scripts).forEach(([name, definition]) => {
70
+ this.defineCommand(name, definition);
71
+ });
72
+ }
73
+ // end(or wait) -> connecting -> connect -> ready -> end
74
+ if (this.options.lazyConnect) {
75
+ this.setStatus("wait");
76
+ }
77
+ else {
78
+ this.connect().catch(lodash_1.noop);
79
+ }
80
+ }
81
+ /**
82
+ * Create a Redis instance.
83
+ * This is the same as `new Redis()` but is included for compatibility with node-redis.
84
+ */
85
+ static createClient(...args) {
86
+ return new Redis(...args);
87
+ }
88
+ get autoPipelineQueueSize() {
89
+ let queued = 0;
90
+ for (const pipeline of this._autoPipelines.values()) {
91
+ queued += pipeline.length;
92
+ }
93
+ return queued;
94
+ }
95
+ /**
96
+ * Create a connection to Redis.
97
+ * This method will be invoked automatically when creating a new Redis instance
98
+ * unless `lazyConnect: true` is passed.
99
+ *
100
+ * When calling this method manually, a Promise is returned, which will
101
+ * be resolved when the connection status is ready. The promise can reject
102
+ * if the connection fails, times out, or if Redis is already connecting/connected.
103
+ */
104
+ connect(callback) {
105
+ const promise = (0, tracing_1.traceConnect)(() => this._connect(), () => {
106
+ const { address, port } = this._getServerAddress();
107
+ return {
108
+ serverAddress: address,
109
+ serverPort: port,
110
+ connectionEpoch: this.connectionEpoch,
111
+ };
112
+ });
113
+ return (0, standard_as_callback_1.default)(promise, callback);
114
+ }
115
+ _connect() {
116
+ return new Promise((resolve, reject) => {
117
+ if (this.status === "connecting" ||
118
+ this.status === "connect" ||
119
+ this.status === "ready") {
120
+ reject(new Error("Redis is already connecting/connected"));
121
+ return;
122
+ }
123
+ this.connectionEpoch += 1;
124
+ this.setStatus("connecting");
125
+ const { options } = this;
126
+ this.condition = {
127
+ select: options.db,
128
+ auth: options.username
129
+ ? [options.username, options.password]
130
+ : options.password,
131
+ subscriber: false,
132
+ };
133
+ const _this = this;
134
+ (0, standard_as_callback_1.default)(this.connector.connect(function (type, err) {
135
+ _this.silentEmit(type, err);
136
+ }), function (err, stream) {
137
+ if (err) {
138
+ _this.flushQueue(err);
139
+ _this.silentEmit("error", err);
140
+ reject(err);
141
+ _this.setStatus("end");
142
+ return;
143
+ }
144
+ let CONNECT_EVENT = options.tls ? "secureConnect" : "connect";
145
+ if ("sentinels" in options &&
146
+ options.sentinels &&
147
+ !options.enableTLSForSentinelMode) {
148
+ CONNECT_EVENT = "connect";
149
+ }
150
+ _this.stream = stream;
151
+ if (options.noDelay) {
152
+ stream.setNoDelay(true);
153
+ }
154
+ // Node ignores setKeepAlive before connect, therefore we wait for the event:
155
+ // https://github.com/nodejs/node/issues/31663
156
+ if (typeof options.keepAlive === "number") {
157
+ if (stream.connecting) {
158
+ stream.once(CONNECT_EVENT, () => {
159
+ stream.setKeepAlive(true, options.keepAlive);
160
+ });
161
+ }
162
+ else {
163
+ stream.setKeepAlive(true, options.keepAlive);
164
+ }
165
+ }
166
+ if (stream.connecting) {
167
+ stream.once(CONNECT_EVENT, eventHandler.connectHandler(_this));
168
+ if (options.connectTimeout) {
169
+ /*
170
+ * Typically, Socket#setTimeout(0) will clear the timer
171
+ * set before. However, in some platforms (Electron 3.x~4.x),
172
+ * the timer will not be cleared. So we introduce a variable here.
173
+ *
174
+ * See https://github.com/electron/electron/issues/14915
175
+ */
176
+ let connectTimeoutCleared = false;
177
+ stream.setTimeout(options.connectTimeout, function () {
178
+ if (connectTimeoutCleared) {
179
+ return;
180
+ }
181
+ stream.setTimeout(0);
182
+ stream.destroy();
183
+ const err = new Error("connect ETIMEDOUT");
184
+ // @ts-expect-error
185
+ err.errorno = "ETIMEDOUT";
186
+ // @ts-expect-error
187
+ err.code = "ETIMEDOUT";
188
+ // @ts-expect-error
189
+ err.syscall = "connect";
190
+ eventHandler.errorHandler(_this)(err);
191
+ });
192
+ stream.once(CONNECT_EVENT, function () {
193
+ connectTimeoutCleared = true;
194
+ stream.setTimeout(0);
195
+ });
196
+ }
197
+ }
198
+ else if (stream.destroyed) {
199
+ const firstError = _this.connector.firstError;
200
+ if (firstError) {
201
+ process.nextTick(() => {
202
+ eventHandler.errorHandler(_this)(firstError);
203
+ });
204
+ }
205
+ process.nextTick(eventHandler.closeHandler(_this));
206
+ }
207
+ else {
208
+ process.nextTick(eventHandler.connectHandler(_this));
209
+ }
210
+ if (!stream.destroyed) {
211
+ stream.once("error", eventHandler.errorHandler(_this));
212
+ stream.once("close", eventHandler.closeHandler(_this));
213
+ }
214
+ const connectionReadyHandler = function () {
215
+ _this.removeListener("close", connectionCloseHandler);
216
+ resolve();
217
+ };
218
+ var connectionCloseHandler = function () {
219
+ _this.removeListener("ready", connectionReadyHandler);
220
+ reject(new Error(utils_1.CONNECTION_CLOSED_ERROR_MSG));
221
+ };
222
+ _this.once("ready", connectionReadyHandler);
223
+ _this.once("close", connectionCloseHandler);
224
+ });
225
+ });
226
+ }
227
+ /**
228
+ * Disconnect from Redis.
229
+ *
230
+ * This method closes the connection immediately,
231
+ * and may lose some pending replies that haven't written to client.
232
+ * If you want to wait for the pending replies, use Redis#quit instead.
233
+ */
234
+ disconnect(reconnect = false) {
235
+ if (!reconnect) {
236
+ this.manuallyClosing = true;
237
+ }
238
+ if (this.reconnectTimeout && !reconnect) {
239
+ clearTimeout(this.reconnectTimeout);
240
+ this.reconnectTimeout = null;
241
+ }
242
+ if (this.status === "wait") {
243
+ eventHandler.closeHandler(this)();
244
+ }
245
+ else {
246
+ this.connector.disconnect();
247
+ }
248
+ }
249
+ /**
250
+ * Disconnect from Redis.
251
+ *
252
+ * @deprecated
253
+ */
254
+ end() {
255
+ this.disconnect();
256
+ }
257
+ /**
258
+ * Create a new instance with the same options as the current one.
259
+ *
260
+ * @example
261
+ * ```js
262
+ * var redis = new Redis(6380);
263
+ * var anotherRedis = redis.duplicate();
264
+ * ```
265
+ */
266
+ duplicate(override) {
267
+ return new Redis({ ...this.options, ...override });
268
+ }
269
+ /**
270
+ * Mode of the connection.
271
+ *
272
+ * One of `"normal"`, `"subscriber"`, or `"monitor"`. When the connection is
273
+ * not in `"normal"` mode, certain commands are not allowed.
274
+ */
275
+ get mode() {
276
+ var _a;
277
+ return this.options.monitor
278
+ ? "monitor"
279
+ : ((_a = this.condition) === null || _a === void 0 ? void 0 : _a.subscriber)
280
+ ? "subscriber"
281
+ : "normal";
282
+ }
283
+ /**
284
+ * Listen for all requests received by the server in real time.
285
+ *
286
+ * This command will create a new connection to Redis and send a
287
+ * MONITOR command via the new connection in order to avoid disturbing
288
+ * the current connection.
289
+ *
290
+ * @param callback The callback function. If omit, a promise will be returned.
291
+ * @example
292
+ * ```js
293
+ * var redis = new Redis();
294
+ * redis.monitor(function (err, monitor) {
295
+ * // Entering monitoring mode.
296
+ * monitor.on('monitor', function (time, args, source, database) {
297
+ * console.log(time + ": " + util.inspect(args));
298
+ * });
299
+ * });
300
+ *
301
+ * // supports promise as well as other commands
302
+ * redis.monitor().then(function (monitor) {
303
+ * monitor.on('monitor', function (time, args, source, database) {
304
+ * console.log(time + ": " + util.inspect(args));
305
+ * });
306
+ * });
307
+ * ```
308
+ */
309
+ monitor(callback) {
310
+ const monitorInstance = this.duplicate({
311
+ monitor: true,
312
+ lazyConnect: false,
313
+ });
314
+ return (0, standard_as_callback_1.default)(new Promise(function (resolve, reject) {
315
+ monitorInstance.once("error", reject);
316
+ monitorInstance.once("monitoring", function () {
317
+ resolve(monitorInstance);
318
+ });
319
+ }), callback);
320
+ }
321
+ /**
322
+ * Send a command to Redis
323
+ *
324
+ * This method is used internally and in most cases you should not
325
+ * use it directly. If you need to send a command that is not supported
326
+ * by the library, you can use the `call` method:
327
+ *
328
+ * ```js
329
+ * const redis = new Redis();
330
+ *
331
+ * redis.call('set', 'foo', 'bar');
332
+ * // or
333
+ * redis.call(['set', 'foo', 'bar']);
334
+ * ```
335
+ *
336
+ * @ignore
337
+ */
338
+ sendCommand(command, stream) {
339
+ var _a, _b;
340
+ if (this.status === "wait") {
341
+ this.connect().catch(lodash_1.noop);
342
+ }
343
+ if (this.status === "end") {
344
+ command.reject(new Error(utils_1.CONNECTION_CLOSED_ERROR_MSG));
345
+ return command.promise;
346
+ }
347
+ if (((_a = this.condition) === null || _a === void 0 ? void 0 : _a.subscriber) &&
348
+ !Command_1.default.checkFlag("VALID_IN_SUBSCRIBER_MODE", command.name)) {
349
+ command.reject(new Error("Connection in subscriber mode, only subscriber commands may be used"));
350
+ return command.promise;
351
+ }
352
+ if (typeof this.options.commandTimeout === "number") {
353
+ command.setTimeout(this.options.commandTimeout);
354
+ }
355
+ const blockingTimeout = this.getBlockingTimeoutInMs(command);
356
+ let writable = this.status === "ready" ||
357
+ (!stream &&
358
+ this.status === "connect" &&
359
+ (0, commands_1.exists)(command.name, { caseInsensitive: true }) &&
360
+ ((0, commands_1.hasFlag)(command.name, "loading", { nameCaseInsensitive: true }) ||
361
+ Command_1.default.checkFlag("HANDSHAKE_COMMANDS", command.name)));
362
+ if (!this.stream) {
363
+ writable = false;
364
+ }
365
+ else if (!this.stream.writable) {
366
+ writable = false;
367
+ // @ts-expect-error
368
+ }
369
+ else if (this.stream._writableState && this.stream._writableState.ended) {
370
+ // TODO: We should be able to remove this as the PR has already been merged.
371
+ // https://github.com/iojs/io.js/pull/1217
372
+ writable = false;
373
+ }
374
+ if (!writable) {
375
+ if (!this.options.enableOfflineQueue) {
376
+ command.reject(new Error("Stream isn't writeable and enableOfflineQueue options is false"));
377
+ return command.promise;
378
+ }
379
+ if (command.name === "quit" && this.offlineQueue.length === 0) {
380
+ this.disconnect();
381
+ command.resolve(Buffer.from("OK"));
382
+ return command.promise;
383
+ }
384
+ // @ts-expect-error
385
+ if (debug.enabled) {
386
+ debug("queue command[%s]: %d -> %s(%o)", this._getDescription(), this.condition.select, command.name, command.args);
387
+ }
388
+ this.offlineQueue.push({
389
+ command: command,
390
+ stream: stream,
391
+ select: this.condition.select,
392
+ });
393
+ // For blocking commands in the offline queue, arm a client-side timeout
394
+ // only when blockingTimeout is configured. Without this option, queued
395
+ // blocking commands may wait indefinitely on a dead connection.
396
+ if (Command_1.default.checkFlag("BLOCKING_COMMANDS", command.name)) {
397
+ const offlineTimeout = this.getConfiguredBlockingTimeout();
398
+ if (offlineTimeout !== undefined) {
399
+ command.setBlockingTimeout(offlineTimeout);
400
+ }
401
+ }
402
+ }
403
+ else {
404
+ // @ts-expect-error
405
+ if (debug.enabled) {
406
+ debug("write command[%s]: %d -> %s(%o)", this._getDescription(), (_b = this.condition) === null || _b === void 0 ? void 0 : _b.select, command.name, command.args);
407
+ }
408
+ if (stream) {
409
+ if ("isPipeline" in stream && stream.isPipeline) {
410
+ stream.write(command.toWritable(stream.destination.redis.stream));
411
+ }
412
+ else {
413
+ stream.write(command.toWritable(stream));
414
+ }
415
+ }
416
+ else {
417
+ this.stream.write(command.toWritable(this.stream));
418
+ }
419
+ this.commandQueue.push({
420
+ command: command,
421
+ stream: stream,
422
+ select: this.condition.select,
423
+ });
424
+ if (blockingTimeout !== undefined) {
425
+ command.setBlockingTimeout(blockingTimeout);
426
+ }
427
+ if (Command_1.default.checkFlag("WILL_DISCONNECT", command.name)) {
428
+ this.manuallyClosing = true;
429
+ }
430
+ if (this.options.socketTimeout !== undefined &&
431
+ this.socketTimeoutTimer === undefined) {
432
+ this.setSocketTimeout();
433
+ }
434
+ }
435
+ if (command.name === "select" && (0, utils_1.isInt)(command.args[0])) {
436
+ const db = parseInt(command.args[0], 10);
437
+ if (this.condition.select !== db) {
438
+ this.condition.select = db;
439
+ this.emit("select", db);
440
+ debug("switch to db [%d]", this.condition.select);
441
+ }
442
+ }
443
+ if (!writable || command.isTraced) {
444
+ return command.promise;
445
+ }
446
+ // Trace on the write path only, and only once per command. Commands may
447
+ // pass through sendCommand multiple times (offline queue flush,
448
+ // prevCommandQueue resend after reconnect). The isTraced flag ensures
449
+ // we don't emit duplicate trace events.
450
+ command.isTraced = true;
451
+ return (0, tracing_1.traceCommand)(() => command.promise, () => this._buildCommandContext(command));
452
+ }
453
+ getBlockingTimeoutInMs(command) {
454
+ var _a;
455
+ if (!Command_1.default.checkFlag("BLOCKING_COMMANDS", command.name)) {
456
+ return undefined;
457
+ }
458
+ // Feature is opt-in: only enabled when blockingTimeout is set to a positive number
459
+ const configuredTimeout = this.getConfiguredBlockingTimeout();
460
+ if (configuredTimeout === undefined) {
461
+ return undefined;
462
+ }
463
+ const timeout = command.extractBlockingTimeout();
464
+ if (typeof timeout === "number") {
465
+ if (timeout > 0) {
466
+ // Finite timeout from command args - add grace period
467
+ return (timeout +
468
+ ((_a = this.options.blockingTimeoutGrace) !== null && _a !== void 0 ? _a : RedisOptions_1.DEFAULT_REDIS_OPTIONS.blockingTimeoutGrace));
469
+ }
470
+ // Command has timeout=0 (block forever), use blockingTimeout option as safety net
471
+ return configuredTimeout;
472
+ }
473
+ if (timeout === null) {
474
+ // No BLOCK option found (e.g., XREAD without BLOCK), use blockingTimeout as safety net
475
+ return configuredTimeout;
476
+ }
477
+ return undefined;
478
+ }
479
+ getConfiguredBlockingTimeout() {
480
+ if (typeof this.options.blockingTimeout === "number" &&
481
+ this.options.blockingTimeout > 0) {
482
+ return this.options.blockingTimeout;
483
+ }
484
+ return undefined;
485
+ }
486
+ setSocketTimeout() {
487
+ this.socketTimeoutTimer = setTimeout(() => {
488
+ this.stream.destroy(new Error(`Socket timeout. Expecting data, but didn't receive any in ${this.options.socketTimeout}ms.`));
489
+ this.socketTimeoutTimer = undefined;
490
+ }, this.options.socketTimeout);
491
+ // this handler must run after the "data" handler in "DataHandler"
492
+ // so that `this.commandQueue.length` will be updated
493
+ this.stream.once("data", () => {
494
+ clearTimeout(this.socketTimeoutTimer);
495
+ this.socketTimeoutTimer = undefined;
496
+ if (this.commandQueue.length === 0)
497
+ return;
498
+ this.setSocketTimeout();
499
+ });
500
+ }
501
+ scanStream(options) {
502
+ return this.createScanStream("scan", { options });
503
+ }
504
+ scanBufferStream(options) {
505
+ return this.createScanStream("scanBuffer", { options });
506
+ }
507
+ sscanStream(key, options) {
508
+ return this.createScanStream("sscan", { key, options });
509
+ }
510
+ sscanBufferStream(key, options) {
511
+ return this.createScanStream("sscanBuffer", { key, options });
512
+ }
513
+ hscanStream(key, options) {
514
+ return this.createScanStream("hscan", { key, options });
515
+ }
516
+ hscanBufferStream(key, options) {
517
+ return this.createScanStream("hscanBuffer", { key, options });
518
+ }
519
+ zscanStream(key, options) {
520
+ return this.createScanStream("zscan", { key, options });
521
+ }
522
+ zscanBufferStream(key, options) {
523
+ return this.createScanStream("zscanBuffer", { key, options });
524
+ }
525
+ /**
526
+ * Emit only when there's at least one listener.
527
+ *
528
+ * @ignore
529
+ */
530
+ silentEmit(eventName, arg) {
531
+ let error;
532
+ if (eventName === "error") {
533
+ error = arg;
534
+ if (this.status === "end") {
535
+ return;
536
+ }
537
+ if (this.manuallyClosing) {
538
+ // ignore connection related errors when manually disconnecting
539
+ if (error instanceof Error &&
540
+ (error.message === utils_1.CONNECTION_CLOSED_ERROR_MSG ||
541
+ // @ts-expect-error
542
+ error.syscall === "connect" ||
543
+ // @ts-expect-error
544
+ error.syscall === "read")) {
545
+ return;
546
+ }
547
+ }
548
+ }
549
+ if (this.listeners(eventName).length > 0) {
550
+ return this.emit.apply(this, arguments);
551
+ }
552
+ if (error && error instanceof Error) {
553
+ console.error("[ioredis-om] Unhandled error event:", error.stack);
554
+ }
555
+ return false;
556
+ }
557
+ /**
558
+ * @ignore
559
+ */
560
+ recoverFromFatalError(_commandError, err, options) {
561
+ this.flushQueue(err, options);
562
+ this.silentEmit("error", err);
563
+ this.disconnect(true);
564
+ }
565
+ /**
566
+ * @ignore
567
+ */
568
+ handleReconnection(err, item) {
569
+ var _a;
570
+ let needReconnect = false;
571
+ if (this.options.reconnectOnError &&
572
+ !Command_1.default.checkFlag("IGNORE_RECONNECT_ON_ERROR", item.command.name)) {
573
+ needReconnect = this.options.reconnectOnError(err);
574
+ }
575
+ switch (needReconnect) {
576
+ case 1:
577
+ case true:
578
+ if (this.status !== "reconnecting") {
579
+ this.disconnect(true);
580
+ }
581
+ item.command.reject(err);
582
+ break;
583
+ case 2:
584
+ if (this.status !== "reconnecting") {
585
+ this.disconnect(true);
586
+ }
587
+ if (((_a = this.condition) === null || _a === void 0 ? void 0 : _a.select) !== item.select &&
588
+ item.command.name !== "select") {
589
+ this.select(item.select);
590
+ }
591
+ // TODO
592
+ // @ts-expect-error
593
+ this.sendCommand(item.command);
594
+ break;
595
+ default:
596
+ item.command.reject(err);
597
+ }
598
+ }
599
+ /**
600
+ * @ignore
601
+ */
602
+ _getServerAddress() {
603
+ if ("path" in this.options && this.options.path) {
604
+ return { address: this.options.path, port: undefined };
605
+ }
606
+ return {
607
+ address: ("host" in this.options && this.options.host) || "localhost",
608
+ port: ("port" in this.options && this.options.port) || 6379,
609
+ };
610
+ }
611
+ _buildCommandContext(command) {
612
+ var _a, _b, _c;
613
+ const { address, port } = this._getServerAddress();
614
+ return {
615
+ command: command.name,
616
+ args: (0, tracing_1.sanitizeArgs)(command.name, command.args),
617
+ database: (_c = (_b = (_a = this.condition) === null || _a === void 0 ? void 0 : _a.select) !== null && _b !== void 0 ? _b : this.options.db) !== null && _c !== void 0 ? _c : 0,
618
+ serverAddress: address,
619
+ serverPort: port,
620
+ };
621
+ }
622
+ _buildBatchContext(batchSize) {
623
+ var _a, _b, _c;
624
+ const { address, port } = this._getServerAddress();
625
+ return {
626
+ batchMode: "MULTI",
627
+ batchSize,
628
+ database: (_c = (_b = (_a = this.condition) === null || _a === void 0 ? void 0 : _a.select) !== null && _b !== void 0 ? _b : this.options.db) !== null && _c !== void 0 ? _c : 0,
629
+ serverAddress: address,
630
+ serverPort: port,
631
+ };
632
+ }
633
+ /**
634
+ * Get description of the connection. Used for debugging.
635
+ */
636
+ _getDescription() {
637
+ let description;
638
+ if ("path" in this.options && this.options.path) {
639
+ description = this.options.path;
640
+ }
641
+ else if (this.stream &&
642
+ this.stream.remoteAddress &&
643
+ this.stream.remotePort) {
644
+ description = this.stream.remoteAddress + ":" + this.stream.remotePort;
645
+ }
646
+ else if ("host" in this.options && this.options.host) {
647
+ description = this.options.host + ":" + this.options.port;
648
+ }
649
+ else {
650
+ // Unexpected
651
+ description = "";
652
+ }
653
+ if (this.options.connectionName) {
654
+ description += ` (${this.options.connectionName})`;
655
+ }
656
+ return description;
657
+ }
658
+ resetCommandQueue() {
659
+ this.commandQueue = new Deque();
660
+ }
661
+ resetOfflineQueue() {
662
+ this.offlineQueue = new Deque();
663
+ }
664
+ parseOptions(...args) {
665
+ const options = {};
666
+ let isTls = false;
667
+ for (let i = 0; i < args.length; ++i) {
668
+ const arg = args[i];
669
+ if (arg === null || typeof arg === "undefined") {
670
+ continue;
671
+ }
672
+ if (typeof arg === "object") {
673
+ (0, lodash_1.defaults)(options, arg);
674
+ }
675
+ else if (typeof arg === "string") {
676
+ (0, lodash_1.defaults)(options, (0, utils_1.parseURL)(arg));
677
+ if (arg.startsWith("rediss://")) {
678
+ isTls = true;
679
+ }
680
+ }
681
+ else if (typeof arg === "number") {
682
+ options.port = arg;
683
+ }
684
+ else {
685
+ throw new Error("Invalid argument " + arg);
686
+ }
687
+ }
688
+ if (isTls) {
689
+ (0, lodash_1.defaults)(options, { tls: true });
690
+ }
691
+ (0, lodash_1.defaults)(options, Redis.defaultOptions);
692
+ if (typeof options.port === "string") {
693
+ options.port = parseInt(options.port, 10);
694
+ }
695
+ if (typeof options.db === "string") {
696
+ options.db = parseInt(options.db, 10);
697
+ }
698
+ // @ts-expect-error
699
+ this.options = (0, utils_1.resolveTLSProfile)(options);
700
+ }
701
+ /**
702
+ * Change instance's status
703
+ */
704
+ setStatus(status, arg) {
705
+ // @ts-expect-error
706
+ if (debug.enabled) {
707
+ debug("status[%s]: %s -> %s", this._getDescription(), this.status || "[empty]", status);
708
+ }
709
+ this.status = status;
710
+ process.nextTick(this.emit.bind(this, status, arg));
711
+ }
712
+ createScanStream(command, { key, options = {} }) {
713
+ return new ScanStream_1.default({
714
+ objectMode: true,
715
+ key: key,
716
+ redis: this,
717
+ command: command,
718
+ ...options,
719
+ });
720
+ }
721
+ /**
722
+ * Flush offline queue and command queue with error.
723
+ *
724
+ * @param error The error object to send to the commands
725
+ * @param options options
726
+ */
727
+ flushQueue(error, options) {
728
+ options = (0, lodash_1.defaults)({}, options, {
729
+ offlineQueue: true,
730
+ commandQueue: true,
731
+ });
732
+ let item;
733
+ if (options.offlineQueue) {
734
+ while ((item = this.offlineQueue.shift())) {
735
+ item.command.reject(error);
736
+ }
737
+ }
738
+ if (options.commandQueue) {
739
+ if (this.commandQueue.length > 0) {
740
+ if (this.stream) {
741
+ this.stream.removeAllListeners("data");
742
+ }
743
+ while ((item = this.commandQueue.shift())) {
744
+ item.command.reject(error);
745
+ }
746
+ }
747
+ }
748
+ }
749
+ /**
750
+ * Check whether Redis has finished loading the persistent data and is able to
751
+ * process commands.
752
+ */
753
+ _readyCheck(callback) {
754
+ const _this = this;
755
+ this.info(function (err, res) {
756
+ if (err) {
757
+ if (err.message && err.message.includes("NOPERM")) {
758
+ console.warn(`Skipping the ready check because INFO command fails: "${err.message}". You can disable ready check with "enableReadyCheck". More: https://github.com/luin/ioredis/wiki/Disable-ready-check.`);
759
+ return callback(null, {});
760
+ }
761
+ return callback(err);
762
+ }
763
+ if (typeof res !== "string") {
764
+ return callback(null, res);
765
+ }
766
+ const info = {};
767
+ const lines = res.split("\r\n");
768
+ for (let i = 0; i < lines.length; ++i) {
769
+ const [fieldName, ...fieldValueParts] = lines[i].split(":");
770
+ const fieldValue = fieldValueParts.join(":");
771
+ if (fieldValue) {
772
+ info[fieldName] = fieldValue;
773
+ }
774
+ }
775
+ if (!info.loading || info.loading === "0") {
776
+ callback(null, info);
777
+ }
778
+ else {
779
+ const loadingEtaMs = (info.loading_eta_seconds || 1) * 1000;
780
+ const retryTime = _this.options.maxLoadingRetryTime &&
781
+ _this.options.maxLoadingRetryTime < loadingEtaMs
782
+ ? _this.options.maxLoadingRetryTime
783
+ : loadingEtaMs;
784
+ debug("Redis server still loading, trying again in " + retryTime + "ms");
785
+ setTimeout(function () {
786
+ _this._readyCheck(callback);
787
+ }, retryTime);
788
+ }
789
+ }).catch(lodash_1.noop);
790
+ }
791
+ }
792
+ Redis.Cluster = cluster_1.default;
793
+ Redis.Command = Command_1.default;
794
+ /**
795
+ * Default options
796
+ */
797
+ Redis.defaultOptions = RedisOptions_1.DEFAULT_REDIS_OPTIONS;
798
+ (0, applyMixin_1.default)(Redis, events_1.EventEmitter);
799
+ (0, transaction_1.addTransactionSupport)(Redis.prototype);
800
+ exports.default = Redis;