ioredis 4.27.6 → 4.27.10

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/Changelog.md CHANGED
@@ -1,3 +1,32 @@
1
+ ## [4.27.10](https://github.com/luin/ioredis/compare/v4.27.9...v4.27.10) (2021-10-04)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * **cluster:** lazyConnect with pipeline ([#1408](https://github.com/luin/ioredis/issues/1408)) ([b798107](https://github.com/luin/ioredis/commit/b798107e4123d0027ef1bdb3319cd00516221f3b))
7
+
8
+ ## [4.27.9](https://github.com/luin/ioredis/compare/v4.27.8...v4.27.9) (2021-08-30)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * Fix undefined property warning in executeAutoPipeline ([#1425](https://github.com/luin/ioredis/issues/1425)) ([f898672](https://github.com/luin/ioredis/commit/f898672a29753774eeb6e166c28ed6f548533517))
14
+ * improve proto checking for hgetall [skip ci] ([#1418](https://github.com/luin/ioredis/issues/1418)) ([cba83cb](https://github.com/luin/ioredis/commit/cba83cba2dba25e59ad87c85d740f15f78e45e14))
15
+
16
+ ## [4.27.8](https://github.com/luin/ioredis/compare/v4.27.7...v4.27.8) (2021-08-18)
17
+
18
+
19
+ ### Bug Fixes
20
+
21
+ * handle malicious keys for hgetall ([#1416](https://github.com/luin/ioredis/issues/1416)) ([7d73b9d](https://github.com/luin/ioredis/commit/7d73b9d07b52ec077f235292aa15c7aca203bba9)), closes [#1267](https://github.com/luin/ioredis/issues/1267)
22
+
23
+ ## [4.27.7](https://github.com/luin/ioredis/compare/v4.27.6...v4.27.7) (2021-08-01)
24
+
25
+
26
+ ### Bug Fixes
27
+
28
+ * **cluster:** fix autopipeline with keyPrefix or arg array ([#1391](https://github.com/luin/ioredis/issues/1391)) ([d7477aa](https://github.com/luin/ioredis/commit/d7477aa5853388b51037210542372131919ddfb2)), closes [#1264](https://github.com/luin/ioredis/issues/1264) [#1248](https://github.com/luin/ioredis/issues/1248) [#1392](https://github.com/luin/ioredis/issues/1392)
29
+
1
30
  ## [4.27.6](https://github.com/luin/ioredis/compare/v4.27.5...v4.27.6) (2021-06-13)
2
31
 
3
32
 
package/README.md CHANGED
@@ -578,7 +578,7 @@ redis.get("k3", (err, result) => {
578
578
  });
579
579
  ```
580
580
 
581
- Another useful example of a reply transformer is one that changes `hgetall` to return array of arrays instead of objects which avoids a unwanted conversation of hash keys to strings when dealing with binary hash keys:
581
+ Another useful example of a reply transformer is one that changes `hgetall` to return array of arrays instead of objects which avoids an unwanted conversation of hash keys to strings when dealing with binary hash keys:
582
582
 
583
583
  ```javascript
584
584
  Redis.Command.setReplyTransformer("hgetall", (result) => {
@@ -755,7 +755,7 @@ const redis = new Redis({
755
755
 
756
756
  This feature is useful when using Amazon ElastiCache instances with Auto-failover disabled. On these instances, test your `reconnectOnError` handler by manually promoting the replica node to the primary role using the AWS console. The following writes fail with the error `READONLY`. Using `reconnectOnError`, we can force the connection to reconnect on this error in order to connect to the new master. Furthermore, if the `reconnectOnError` returns `2`, ioredis will resend the failed command after reconnecting.
757
757
 
758
- On ElastiCache insances with Auto-failover enabled, `reconnectOnError` does not execute. Instead of returning a Redis error, AWS closes all connections to the master endpoint until the new primary node is ready. ioredis reconnects via `retryStrategy` instead of `reconnectOnError` after about a minute. On ElastiCache insances with Auto-failover enabled, test failover events with the `Failover primary` option in the AWS console.
758
+ On ElastiCache instances with Auto-failover enabled, `reconnectOnError` does not execute. Instead of returning a Redis error, AWS closes all connections to the master endpoint until the new primary node is ready. ioredis reconnects via `retryStrategy` instead of `reconnectOnError` after about a minute. On ElastiCache instances with Auto-failover enabled, test failover events with the `Failover primary` option in the AWS console.
759
759
 
760
760
  ## Connection Events
761
761
 
@@ -1,6 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.executeWithAutoPipelining = exports.getFirstValueInFlattenedArray = exports.shouldUseAutoPipelining = exports.notAllowedAutoPipelineCommands = exports.kCallbacks = exports.kExec = void 0;
3
4
  const PromiseContainer = require("./promiseContainer");
5
+ const lodash_1 = require("./utils/lodash");
4
6
  const calculateSlot = require("cluster-key-slot");
5
7
  const standard_as_callback_1 = require("standard-as-callback");
6
8
  exports.kExec = Symbol("exec");
@@ -26,6 +28,16 @@ function executeAutoPipeline(client, slotKey) {
26
28
  if (client._runningAutoPipelines.has(slotKey)) {
27
29
  return;
28
30
  }
31
+ if (!client._autoPipelines.has(slotKey)) {
32
+ /*
33
+ Rare edge case. Somehow, something has deleted this running autopipeline in an immediate
34
+ call to executeAutoPipeline.
35
+
36
+ Maybe the callback in the pipeline.exec is sometimes called in the same tick,
37
+ e.g. if redis is disconnected?
38
+ */
39
+ return;
40
+ }
29
41
  client._runningAutoPipelines.add(slotKey);
30
42
  // Get the pipeline and immediately delete it so that new commands are queued on a new pipeline
31
43
  const pipeline = client._autoPipelines.get(slotKey);
@@ -62,10 +74,35 @@ function shouldUseAutoPipelining(client, functionName, commandName) {
62
74
  !client.options.autoPipeliningIgnoredCommands.includes(commandName));
63
75
  }
64
76
  exports.shouldUseAutoPipelining = shouldUseAutoPipelining;
77
+ /**
78
+ * @private
79
+ */
80
+ function getFirstValueInFlattenedArray(args) {
81
+ for (let i = 0; i < args.length; i++) {
82
+ const arg = args[i];
83
+ if (typeof arg === "string") {
84
+ return arg;
85
+ }
86
+ else if (Array.isArray(arg) || lodash_1.isArguments(arg)) {
87
+ if (arg.length === 0) {
88
+ continue;
89
+ }
90
+ return arg[0];
91
+ }
92
+ const flattened = lodash_1.flatten([arg]);
93
+ if (flattened.length > 0) {
94
+ return flattened[0];
95
+ }
96
+ }
97
+ return undefined;
98
+ }
99
+ exports.getFirstValueInFlattenedArray = getFirstValueInFlattenedArray;
65
100
  function executeWithAutoPipelining(client, functionName, commandName, args, callback) {
66
101
  const CustomPromise = PromiseContainer.get();
67
102
  // On cluster mode let's wait for slots to be available
68
103
  if (client.isCluster && !client.slots.length) {
104
+ if (client.status === "wait")
105
+ client.connect().catch(lodash_1.noop);
69
106
  return new CustomPromise(function (resolve, reject) {
70
107
  client.delayUntilReady((err) => {
71
108
  if (err) {
@@ -77,7 +114,12 @@ function executeWithAutoPipelining(client, functionName, commandName, args, call
77
114
  });
78
115
  }
79
116
  // If we have slot information, we can improve routing by grouping slots served by the same subset of nodes
80
- const slotKey = client.isCluster ? client.slots[calculateSlot(args[0])].join(",") : 'main';
117
+ // Note that the first value in args may be a (possibly empty) array.
118
+ // ioredis will only flatten one level of the array, in the Command constructor.
119
+ const prefix = client.options.keyPrefix || "";
120
+ const slotKey = client.isCluster
121
+ ? client.slots[calculateSlot(`${prefix}${getFirstValueInFlattenedArray(args)}`)].join(",")
122
+ : "main";
81
123
  if (!client._autoPipelines.has(slotKey)) {
82
124
  const pipeline = client.pipeline();
83
125
  pipeline[exports.kExec] = false;
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_CLUSTER_OPTIONS = void 0;
3
4
  const dns_1 = require("dns");
4
5
  exports.DEFAULT_CLUSTER_OPTIONS = {
5
6
  clusterRetryStrategy: (times) => Math.min(100 + times * 2, 2000),
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getConnectionName = exports.weightSrvRecords = exports.groupSrvRecords = exports.getUniqueHostnamesFromOptions = exports.normalizeNodeOptions = exports.nodeKeyToRedisOptions = exports.getNodeKey = void 0;
3
4
  const utils_1 = require("../utils");
4
5
  const net_1 = require("net");
5
6
  function getNodeKey(node) {
package/built/command.js CHANGED
@@ -313,7 +313,21 @@ Command.setReplyTransformer("hgetall", function (result) {
313
313
  if (Array.isArray(result)) {
314
314
  const obj = {};
315
315
  for (let i = 0; i < result.length; i += 2) {
316
- obj[result[i]] = result[i + 1];
316
+ const key = result[i];
317
+ const value = result[i + 1];
318
+ if (key in obj) {
319
+ // can only be truthy if the property is special somehow, like '__proto__' or 'constructor'
320
+ // https://github.com/luin/ioredis/issues/1267
321
+ Object.defineProperty(obj, key, {
322
+ value,
323
+ configurable: true,
324
+ enumerable: true,
325
+ writable: true,
326
+ });
327
+ }
328
+ else {
329
+ obj[key] = value;
330
+ }
317
331
  }
318
332
  return obj;
319
333
  }
@@ -9,6 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  });
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.FailoverDetector = void 0;
12
13
  const utils_1 = require("../../utils");
13
14
  const debug = utils_1.Debug("FailoverDetector");
14
15
  const CHANNEL_NAME = "+switch-master";
@@ -9,6 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  });
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.SentinelIterator = void 0;
12
13
  const net_1 = require("net");
13
14
  const utils_1 = require("../../utils");
14
15
  const tls_1 = require("tls");
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isIIpcConnectionOptions = void 0;
3
4
  const net_1 = require("net");
4
5
  const tls_1 = require("tls");
5
6
  const utils_1 = require("../utils");
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SentinelConnector = exports.StandaloneConnector = void 0;
3
4
  const StandaloneConnector_1 = require("./StandaloneConnector");
4
5
  exports.StandaloneConnector = StandaloneConnector_1.default;
5
6
  const SentinelConnector_1 = require("./SentinelConnector");
@@ -1,4 +1,5 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MaxRetriesPerRequestError = void 0;
3
4
  const MaxRetriesPerRequestError_1 = require("./MaxRetriesPerRequestError");
4
5
  exports.MaxRetriesPerRequestError = MaxRetriesPerRequestError_1.default;
package/built/index.js CHANGED
@@ -1,21 +1,22 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.print = exports.ReplyError = void 0;
3
4
  exports = module.exports = require("./redis").default;
4
5
  var redis_1 = require("./redis");
5
- exports.default = redis_1.default;
6
+ Object.defineProperty(exports, "default", { enumerable: true, get: function () { return redis_1.default; } });
6
7
  var cluster_1 = require("./cluster");
7
- exports.Cluster = cluster_1.default;
8
+ Object.defineProperty(exports, "Cluster", { enumerable: true, get: function () { return cluster_1.default; } });
8
9
  var command_1 = require("./command");
9
- exports.Command = command_1.default;
10
+ Object.defineProperty(exports, "Command", { enumerable: true, get: function () { return command_1.default; } });
10
11
  var ScanStream_1 = require("./ScanStream");
11
- exports.ScanStream = ScanStream_1.default;
12
+ Object.defineProperty(exports, "ScanStream", { enumerable: true, get: function () { return ScanStream_1.default; } });
12
13
  var pipeline_1 = require("./pipeline");
13
- exports.Pipeline = pipeline_1.default;
14
+ Object.defineProperty(exports, "Pipeline", { enumerable: true, get: function () { return pipeline_1.default; } });
14
15
  var AbstractConnector_1 = require("./connectors/AbstractConnector");
15
- exports.AbstractConnector = AbstractConnector_1.default;
16
+ Object.defineProperty(exports, "AbstractConnector", { enumerable: true, get: function () { return AbstractConnector_1.default; } });
16
17
  var SentinelConnector_1 = require("./connectors/SentinelConnector");
17
- exports.SentinelConnector = SentinelConnector_1.default;
18
- exports.SentinelIterator = SentinelConnector_1.SentinelIterator;
18
+ Object.defineProperty(exports, "SentinelConnector", { enumerable: true, get: function () { return SentinelConnector_1.default; } });
19
+ Object.defineProperty(exports, "SentinelIterator", { enumerable: true, get: function () { return SentinelConnector_1.SentinelIterator; } });
19
20
  // No TS typings
20
21
  exports.ReplyError = require("redis-errors").ReplyError;
21
22
  const PromiseContainer = require("./promiseContainer");
package/built/pipeline.js CHANGED
@@ -8,6 +8,7 @@ const calculateSlot = require("cluster-key-slot");
8
8
  const pMap = require("p-map");
9
9
  const PromiseContainer = require("./promiseContainer");
10
10
  const commander_1 = require("./commander");
11
+ const utils_1 = require("./utils");
11
12
  /*
12
13
  This function derives from the cluster-key-slot implementation.
13
14
  Instead of checking that all keys have the same slot, it checks that all slots are served by the same set of nodes.
@@ -140,7 +141,8 @@ Pipeline.prototype.fillResult = function (value, position) {
140
141
  moved: function (slot, key) {
141
142
  _this.preferKey = key;
142
143
  _this.redis.slots[errv[1]] = [key];
143
- _this.redis._groupsBySlot[errv[1]] = _this.redis._groupsIds[_this.redis.slots[errv[1]].join(";")];
144
+ _this.redis._groupsBySlot[errv[1]] =
145
+ _this.redis._groupsIds[_this.redis.slots[errv[1]].join(";")];
144
146
  _this.redis.refreshSlotsCache();
145
147
  _this.exec();
146
148
  },
@@ -214,6 +216,8 @@ Pipeline.prototype.execBuffer = util_1.deprecate(function () {
214
216
  Pipeline.prototype.exec = function (callback) {
215
217
  // Wait for the cluster to be connected, since we need nodes information before continuing
216
218
  if (this.isCluster && !this.redis.slots.length) {
219
+ if (this.redis.status === "wait")
220
+ this.redis.connect().catch(utils_1.noop);
217
221
  this.redis.delayUntilReady((err) => {
218
222
  if (err) {
219
223
  callback(err);
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.set = exports.get = exports.isPromise = void 0;
3
4
  function isPromise(obj) {
4
5
  return (!!obj &&
5
6
  (typeof obj === "object" || typeof obj === "function") &&
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_REDIS_OPTIONS = void 0;
3
4
  exports.DEFAULT_REDIS_OPTIONS = {
4
5
  // Connection
5
6
  port: 6379,
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.readyHandler = exports.errorHandler = exports.closeHandler = exports.connectHandler = void 0;
3
4
  const redis_errors_1 = require("redis-errors");
4
5
  const command_1 = require("../command");
5
6
  const errors_1 = require("../errors");
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.addTransactionSupport = void 0;
3
4
  const utils_1 = require("./utils");
4
5
  const standard_as_callback_1 = require("standard-as-callback");
5
6
  const pipeline_1 = require("./pipeline");
@@ -29,6 +30,8 @@ function addTransactionSupport(redis) {
29
30
  pipeline.exec = function (callback) {
30
31
  // Wait for the cluster to be connected, since we need nodes information before continuing
31
32
  if (this.isCluster && !this.redis.slots.length) {
33
+ if (this.redis.status === "wait")
34
+ this.redis.connect().catch(utils_1.noop);
32
35
  return standard_as_callback_1.default(new Promise((resolve, reject) => {
33
36
  this.redis.delayUntilReady((err) => {
34
37
  if (err) {
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.genRedactedString = exports.getStringValue = exports.MAX_ARGUMENT_LENGTH = void 0;
3
4
  const debug_1 = require("debug");
4
5
  const MAX_ARGUMENT_LENGTH = 200;
5
6
  exports.MAX_ARGUMENT_LENGTH = MAX_ARGUMENT_LENGTH;
@@ -1,10 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.flatten = exports.noop = exports.defaults = exports.Debug = exports.zipMap = exports.CONNECTION_CLOSED_ERROR_MSG = exports.shuffle = exports.sample = exports.parseURL = exports.optimizeErrorStack = exports.toArg = exports.convertMapToArray = exports.convertObjectToArray = exports.timeout = exports.packObject = exports.isInt = exports.wrapMultiResult = exports.convertBufferToString = exports.bufferEqual = void 0;
3
4
  const url_1 = require("url");
4
5
  const lodash_1 = require("./lodash");
5
- exports.defaults = lodash_1.defaults;
6
- exports.noop = lodash_1.noop;
7
- exports.flatten = lodash_1.flatten;
6
+ Object.defineProperty(exports, "defaults", { enumerable: true, get: function () { return lodash_1.defaults; } });
7
+ Object.defineProperty(exports, "noop", { enumerable: true, get: function () { return lodash_1.noop; } });
8
+ Object.defineProperty(exports, "flatten", { enumerable: true, get: function () { return lodash_1.flatten; } });
8
9
  const debug_1 = require("./debug");
9
10
  exports.Debug = debug_1.default;
10
11
  /**
@@ -1,8 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isArguments = exports.flatten = exports.defaults = exports.noop = void 0;
3
4
  const defaults = require("lodash.defaults");
4
5
  exports.defaults = defaults;
5
6
  const flatten = require("lodash.flatten");
6
7
  exports.flatten = flatten;
8
+ const isArguments = require("lodash.isarguments");
9
+ exports.isArguments = isArguments;
7
10
  function noop() { }
8
11
  exports.noop = noop;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ioredis",
3
- "version": "4.27.6",
3
+ "version": "4.27.10",
4
4
  "description": "A robust, performance-focused and full-featured Redis client for Node.js.",
5
5
  "main": "built/index.js",
6
6
  "files": [
@@ -38,6 +38,7 @@
38
38
  "denque": "^1.1.0",
39
39
  "lodash.defaults": "^4.2.0",
40
40
  "lodash.flatten": "^4.4.0",
41
+ "lodash.isarguments": "^3.1.0",
41
42
  "p-map": "^2.1.0",
42
43
  "redis-commands": "1.7.0",
43
44
  "redis-errors": "^1.2.0",
@@ -53,6 +54,7 @@
53
54
  "@types/debug": "^4.1.5",
54
55
  "@types/lodash.defaults": "^4.2.6",
55
56
  "@types/lodash.flatten": "^4.4.6",
57
+ "@types/lodash.isarguments": "^3.1.6",
56
58
  "@types/mocha": "^7.0.2",
57
59
  "@types/node": "^13.11.0",
58
60
  "@types/redis-errors": "1.2.0",