Socket
Socket
Sign inDemoInstall

ioredis

Package Overview
Dependencies
Maintainers
2
Versions
228
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

ioredis - npm Package Compare versions

Comparing version 4.28.5 to 5.0.0-beta.1

built/autoPipelining.d.ts

16

built/autoPipelining.js
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const PromiseContainer = require("./promiseContainer");
exports.executeWithAutoPipelining = exports.getFirstValueInFlattenedArray = exports.shouldUseAutoPipelining = exports.notAllowedAutoPipelineCommands = exports.kCallbacks = exports.kExec = void 0;
const lodash_1 = require("./utils/lodash");

@@ -80,5 +80,2 @@ const calculateSlot = require("cluster-key-slot");

exports.shouldUseAutoPipelining = shouldUseAutoPipelining;
/**
* @private
*/
function getFirstValueInFlattenedArray(args) {

@@ -90,3 +87,3 @@ for (let i = 0; i < args.length; i++) {

}
else if (Array.isArray(arg) || lodash_1.isArguments(arg)) {
else if (Array.isArray(arg) || (0, lodash_1.isArguments)(arg)) {
if (arg.length === 0) {

@@ -97,3 +94,3 @@ continue;

}
const flattened = lodash_1.flatten([arg]);
const flattened = [arg].flat();
if (flattened.length > 0) {

@@ -107,3 +104,2 @@ return flattened[0];

function executeWithAutoPipelining(client, functionName, commandName, args, callback) {
const CustomPromise = PromiseContainer.get();
// On cluster mode let's wait for slots to be available

@@ -113,3 +109,3 @@ if (client.isCluster && !client.slots.length) {

client.connect().catch(lodash_1.noop);
return standard_as_callback_1.default(new CustomPromise(function (resolve, reject) {
return (0, standard_as_callback_1.default)(new Promise(function (resolve, reject) {
client.delayUntilReady((err) => {

@@ -152,3 +148,3 @@ if (err) {

// Create the promise which will execute the command in the pipeline.
const autoPipelinePromise = new CustomPromise(function (resolve, reject) {
const autoPipelinePromise = new Promise(function (resolve, reject) {
pipeline[exports.kCallbacks].push(function (err, value) {

@@ -163,4 +159,4 @@ if (err) {

});
return standard_as_callback_1.default(autoPipelinePromise, callback);
return (0, standard_as_callback_1.default)(autoPipelinePromise, callback);
}
exports.executeWithAutoPipelining = executeWithAutoPipelining;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DEFAULT_CLUSTER_OPTIONS = void 0;
const dns_1 = require("dns");

@@ -15,3 +16,2 @@ exports.DEFAULT_CLUSTER_OPTIONS = {

slotsRefreshTimeout: 1000,
slotsRefreshInterval: 5000,
useSRVRecords: false,

@@ -18,0 +18,0 @@ resolveSrv: dns_1.resolveSrv,

@@ -5,4 +5,4 @@ "use strict";

const utils_1 = require("../utils");
const redis_1 = require("../redis");
const debug = utils_1.Debug("cluster:subscriber");
const Redis_1 = require("../Redis");
const debug = (0, utils_1.Debug)("cluster:subscriber");
class ClusterSubscriber {

@@ -18,3 +18,3 @@ constructor(connectionPool, emitter) {

}
if (util_1.getNodeKey(this.subscriber.options) === key) {
if ((0, util_1.getNodeKey)(this.subscriber.options) === key) {
debug("subscriber has left, selecting a new one...");

@@ -35,2 +35,15 @@ this.selectSubscriber();

}
start() {
this.started = true;
this.selectSubscriber();
debug("started");
}
stop() {
this.started = false;
if (this.subscriber) {
this.subscriber.disconnect();
this.subscriber = null;
}
debug("stopped");
}
selectSubscriber() {

@@ -46,3 +59,3 @@ const lastActiveSubscriber = this.lastActiveSubscriber;

}
const sampleNode = utils_1.sample(this.connectionPool.getNodes());
const sampleNode = (0, utils_1.sample)(this.connectionPool.getNodes());
if (!sampleNode) {

@@ -64,3 +77,3 @@ debug("selecting subscriber failed since there is no node discovered in the cluster yet");

*/
this.subscriber = new redis_1.default({
this.subscriber = new Redis_1.default({
port: options.port,

@@ -71,3 +84,3 @@ host: options.host,

enableReadyCheck: true,
connectionName: util_1.getConnectionName("subscriber", options.connectionName),
connectionName: (0, util_1.getConnectionName)("subscriber", options.connectionName),
lazyConnect: true,

@@ -84,3 +97,4 @@ tls: options.tls,

previousChannels.subscribe = condition.subscriber.channels("subscribe");
previousChannels.psubscribe = condition.subscriber.channels("psubscribe");
previousChannels.psubscribe =
condition.subscriber.channels("psubscribe");
}

@@ -123,16 +137,3 @@ }

}
start() {
this.started = true;
this.selectSubscriber();
debug("started");
}
stop() {
this.started = false;
if (this.subscriber) {
this.subscriber.disconnect();
this.subscriber = null;
}
debug("stopped");
}
}
exports.default = ClusterSubscriber;

@@ -6,4 +6,4 @@ "use strict";

const util_1 = require("./util");
const redis_1 = require("../redis");
const debug = utils_1.Debug("cluster:connectionPool");
const Redis_1 = require("../Redis");
const debug = (0, utils_1.Debug)("cluster:connectionPool");
class ConnectionPool extends events_1.EventEmitter {

@@ -30,3 +30,3 @@ constructor(redisOptions) {

const keys = Object.keys(this.nodes[role]);
const sampleKey = utils_1.sample(keys);
const sampleKey = (0, utils_1.sample)(keys);
return this.nodes[role][sampleKey];

@@ -36,10 +36,5 @@ }

* Find or create a connection to the node
*
* @param {IRedisOptions} node
* @param {boolean} [readOnly=false]
* @returns {*}
* @memberof ConnectionPool
*/
findOrCreate(node, readOnly = false) {
const key = util_1.getNodeKey(node);
const key = (0, util_1.getNodeKey)(node);
readOnly = Boolean(readOnly);

@@ -71,3 +66,3 @@ if (this.specifiedOptions[key]) {

debug("Connecting to %s as %s", key, readOnly ? "slave" : "master");
redis = new redis_1.default(utils_1.defaults({
redis = new Redis_1.default((0, utils_1.defaults)({
// Never try to reconnect when a node is lose,

@@ -100,19 +95,4 @@ // instead, waiting for a `MOVED` error and

/**
* Remove a node from the pool.
*/
removeNode(key) {
const { nodes } = this;
if (nodes.all[key]) {
debug("Remove %s from the pool", key);
delete nodes.all[key];
}
delete nodes.master[key];
delete nodes.slave[key];
}
/**
* Reset the pool with a set of nodes.
* The old node will be removed.
*
* @param {(Array<string | number | object>)} nodes
* @memberof ConnectionPool
*/

@@ -123,3 +103,3 @@ reset(nodes) {

nodes.forEach((node) => {
const key = util_1.getNodeKey(node);
const key = (0, util_1.getNodeKey)(node);
// Don't override the existing (master) node

@@ -143,3 +123,15 @@ // when the current one is slave.

}
/**
* Remove a node from the pool.
*/
removeNode(key) {
const { nodes } = this;
if (nodes.all[key]) {
debug("Remove %s from the pool", key);
delete nodes.all[key];
}
delete nodes.master[key];
delete nodes.slave[key];
}
}
exports.default = ConnectionPool;

@@ -5,8 +5,5 @@ "use strict";

const Deque = require("denque");
const debug = utils_1.Debug("delayqueue");
const debug = (0, utils_1.Debug)("delayqueue");
/**
* Queue that runs items after specified duration
*
* @export
* @class DelayQueue
*/

@@ -21,6 +18,5 @@ class DelayQueue {

*
* @param {string} bucket bucket name
* @param {Function} item function that will run later
* @param {IDelayQueueOptions} options
* @memberof DelayQueue
* @param bucket bucket name
* @param item function that will run later
* @param options
*/

@@ -27,0 +23,0 @@ push(bucket, item, options) {

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const commands_1 = require("@ioredis/commands");
const events_1 = require("events");
const redis_errors_1 = require("redis-errors");
const standard_as_callback_1 = require("standard-as-callback");
const Command_1 = require("../Command");
const ClusterAllFailedError_1 = require("../errors/ClusterAllFailedError");
const Redis_1 = require("../Redis");
const ScanStream_1 = require("../ScanStream");
const transaction_1 = require("../transaction");
const utils_1 = require("../utils");
const applyMixin_1 = require("../utils/applyMixin");
const Commander_1 = require("../utils/Commander");
const ClusterOptions_1 = require("./ClusterOptions");
const ClusterSubscriber_1 = require("./ClusterSubscriber");
const ConnectionPool_1 = require("./ConnectionPool");
const DelayQueue_1 = require("./DelayQueue");
const util_1 = require("./util");
const ClusterSubscriber_1 = require("./ClusterSubscriber");
const DelayQueue_1 = require("./DelayQueue");
const ScanStream_1 = require("../ScanStream");
const redis_errors_1 = require("redis-errors");
const standard_as_callback_1 = require("standard-as-callback");
const PromiseContainer = require("../promiseContainer");
const ClusterOptions_1 = require("./ClusterOptions");
const utils_2 = require("../utils");
const commands = require("redis-commands");
const command_1 = require("../command");
const redis_1 = require("../redis");
const commander_1 = require("../commander");
const Deque = require("denque");
const debug = utils_1.Debug("cluster");
const debug = (0, utils_1.Debug)("cluster");
const REJECT_OVERWRITTEN_COMMANDS = new WeakSet();
/**
* Client for the official Redis Cluster
*
* @class Cluster
* @extends {EventEmitter}
*/
class Cluster extends events_1.EventEmitter {
class Cluster extends Commander_1.default {
/**
* Creates an instance of Cluster.
*
* @param {((string | number | object)[])} startupNodes
* @param {IClusterOptions} [options={}]
* @memberof Cluster
*/

@@ -39,2 +33,14 @@ constructor(startupNodes, options = {}) {

this.slots = [];
/**
* @ignore
*/
this._groupsIds = {};
/**
* @ignore
*/
this._groupsBySlot = Array(16384);
/**
* @ignore
*/
this.isCluster = true;
this.retryAttempts = 0;

@@ -44,9 +50,5 @@ this.delayQueue = new DelayQueue_1.default();

this.isRefreshing = false;
this.isCluster = true;
this._autoPipelines = new Map();
this._groupsIds = {};
this._groupsBySlot = Array(16384);
this._runningAutoPipelines = new Set();
this._readyDelayedCallbacks = [];
this._addedScriptHashes = {};
/**

@@ -57,11 +59,12 @@ * Every time Cluster#connect() is called, this value will be

* connection.
*
* @private
* @type {number}
* @memberof Cluster
*/
this.connectionEpoch = 0;
commander_1.default.call(this);
events_1.EventEmitter.call(this);
this.startupNodes = startupNodes;
this.options = utils_1.defaults({}, options, ClusterOptions_1.DEFAULT_CLUSTER_OPTIONS, this.options);
this.options = (0, utils_1.defaults)({}, options, ClusterOptions_1.DEFAULT_CLUSTER_OPTIONS, this.options);
if (this.options.redisOptions &&
this.options.redisOptions.keyPrefix &&
!this.options.keyPrefix) {
this.options.keyPrefix = this.options.redisOptions.keyPrefix;
}
// validate options

@@ -88,2 +91,7 @@ if (typeof this.options.scaleReads !== "function" &&

this.subscriber = new ClusterSubscriber_1.default(this.connectionPool, this);
if (this.options.scripts) {
Object.entries(this.options.scripts).forEach(([name, definition]) => {
this.defineCommand(name, definition);
});
}
if (this.options.lazyConnect) {

@@ -98,39 +106,6 @@ this.setStatus("wait");

}
resetOfflineQueue() {
this.offlineQueue = new Deque();
}
clearNodesRefreshInterval() {
if (this.slotsTimer) {
clearTimeout(this.slotsTimer);
this.slotsTimer = null;
}
}
clearAddedScriptHashesCleanInterval() {
if (this._addedScriptHashesCleanInterval) {
clearInterval(this._addedScriptHashesCleanInterval);
this._addedScriptHashesCleanInterval = null;
}
}
resetNodesRefreshInterval() {
if (this.slotsTimer) {
return;
}
const nextRound = () => {
this.slotsTimer = setTimeout(() => {
debug('refreshing slot caches... (triggered by "slotsRefreshInterval" option)');
this.refreshSlotsCache(() => {
nextRound();
});
}, this.options.slotsRefreshInterval);
};
nextRound();
}
/**
* Connect to a cluster
*
* @returns {Promise<void>}
* @memberof Cluster
*/
connect() {
const Promise = PromiseContainer.get();
return new Promise((resolve, reject) => {

@@ -143,8 +118,2 @@ if (this.status === "connecting" ||

}
// Make sure only one timer is active at a time
this.clearAddedScriptHashesCleanInterval();
// Start the script cache cleaning
this._addedScriptHashesCleanInterval = setInterval(() => {
this._addedScriptHashes = {};
}, this.options.maxScriptsCachingTime);
const epoch = ++this.connectionEpoch;

@@ -165,3 +134,3 @@ this.setStatus("connecting");

this.connectionPool.reset(nodes);
function readyHandler() {
const readyHandler = () => {
this.setStatus("ready");

@@ -172,3 +141,3 @@ this.retryAttempts = 0;

resolve();
}
};
let closeListener = undefined;

@@ -189,3 +158,3 @@ const refreshListener = () => {

else {
readyHandler.call(this);
readyHandler();
}

@@ -195,6 +164,6 @@ });

else {
readyHandler.call(this);
readyHandler();
}
};
closeListener = function () {
closeListener = () => {
const error = new Error("None of startup nodes is available");

@@ -208,8 +177,8 @@ this.removeListener("refresh", refreshListener);

this.once("close", this.handleCloseEvent.bind(this));
this.refreshSlotsCache(function (err) {
if (err && err.message === "Failed to refresh slots cache.") {
redis_1.default.prototype.silentEmit.call(this, "error", err);
this.refreshSlotsCache((err) => {
if (err && err.message === ClusterAllFailedError_1.default.defaultMessage) {
Redis_1.default.prototype.silentEmit.call(this, "error", err);
this.connectionPool.reset([]);
}
}.bind(this));
});
this.subscriber.start();

@@ -226,37 +195,3 @@ })

/**
* Called when closed to check whether a reconnection should be made
*
* @private
* @memberof Cluster
*/
handleCloseEvent(reason) {
if (reason) {
debug("closed because %s", reason);
}
this.clearAddedScriptHashesCleanInterval();
let retryDelay;
if (!this.manuallyClosing &&
typeof this.options.clusterRetryStrategy === "function") {
retryDelay = this.options.clusterRetryStrategy.call(this, ++this.retryAttempts, reason);
}
if (typeof retryDelay === "number") {
this.setStatus("reconnecting");
this.reconnectTimeout = setTimeout(function () {
this.reconnectTimeout = null;
debug("Cluster is disconnected. Retrying after %dms", retryDelay);
this.connect().catch(function (err) {
debug("Got error %s when reconnecting. Ignoring...", err);
});
}.bind(this), retryDelay);
}
else {
this.setStatus("end");
this.flushQueue(new Error("None of startup nodes is available"));
}
}
/**
* Disconnect from every node in the cluster.
*
* @param {boolean} [reconnect=false]
* @memberof Cluster
*/

@@ -266,3 +201,2 @@ disconnect(reconnect = false) {

this.setStatus("disconnecting");
this.clearAddedScriptHashesCleanInterval();
if (!reconnect) {

@@ -288,6 +222,2 @@ this.manuallyClosing = true;

* Quit the cluster gracefully.
*
* @param {CallbackFunction<'OK'>} [callback]
* @returns {Promise<'OK'>}
* @memberof Cluster
*/

@@ -297,3 +227,2 @@ quit(callback) {

this.setStatus("disconnecting");
this.clearAddedScriptHashesCleanInterval();
this.manuallyClosing = true;

@@ -306,5 +235,4 @@ if (this.reconnectTimeout) {

this.subscriber.stop();
const Promise = PromiseContainer.get();
if (status === "wait") {
const ret = standard_as_callback_1.default(Promise.resolve("OK"), callback);
const ret = (0, standard_as_callback_1.default)(Promise.resolve("OK"), callback);
// use setImmediate to make sure "close" event

@@ -318,6 +246,6 @@ // being emitted after quit() is returned

}
return standard_as_callback_1.default(Promise.all(this.nodes().map((node) => node.quit().catch((err) => {
return (0, standard_as_callback_1.default)(Promise.all(this.nodes().map((node) => node.quit().catch((err) => {
// Ignore the error caused by disconnecting since
// we're disconnecting...
if (err.message === utils_2.CONNECTION_CLOSED_ERROR_MSG) {
if (err.message === utils_1.CONNECTION_CLOSED_ERROR_MSG) {
return "OK";

@@ -336,7 +264,2 @@ }

* ```
*
* @public
* @param {((string | number | object)[])} [overrideStartupNodes=[]]
* @param {IClusterOptions} [overrideOptions={}]
* @memberof Cluster
*/

@@ -352,6 +275,2 @@ duplicate(overrideStartupNodes = [], overrideOptions = {}) {

* Get nodes with the specified role
*
* @param {NodeRole} [role='all']
* @returns {any[]}
* @memberof Cluster
*/

@@ -364,3 +283,7 @@ nodes(role = "all") {

}
// This is needed in order not to install a listener for each auto pipeline
/**
* This is needed in order not to install a listener for each auto pipeline
*
* @ignore
*/
delayUntilReady(callback) {

@@ -382,25 +305,9 @@ this._readyDelayedCallbacks.push(callback);

/**
* Change cluster instance's status
*
* @private
* @param {ClusterStatus} status
* @memberof Cluster
*/
setStatus(status) {
debug("status: %s -> %s", this.status || "[empty]", status);
this.status = status;
process.nextTick(() => {
this.emit(status);
});
}
/**
* Refresh the slot cache
*
* @private
* @param {CallbackFunction} [callback]
* @memberof Cluster
* @ignore
*/
refreshSlotsCache(callback) {
if (this.isRefreshing) {
if (typeof callback === "function") {
if (callback) {
process.nextTick(callback);

@@ -412,13 +319,13 @@ }

const _this = this;
const wrapper = function (error) {
_this.isRefreshing = false;
if (typeof callback === "function") {
const wrapper = (error) => {
this.isRefreshing = false;
if (callback) {
callback(error);
}
};
const nodes = utils_2.shuffle(this.connectionPool.getNodes());
const nodes = (0, utils_1.shuffle)(this.connectionPool.getNodes());
let lastNodeError = null;
function tryNode(index) {
if (index === nodes.length) {
const error = new ClusterAllFailedError_1.default("Failed to refresh slots cache.", lastNodeError);
const error = new ClusterAllFailedError_1.default(ClusterAllFailedError_1.default.defaultMessage, lastNodeError);
return wrapper(error);

@@ -451,40 +358,4 @@ }

/**
* Flush offline queue with error.
*
* @param {Error} error
* @memberof Cluster
* @ignore
*/
flushQueue(error) {
let item;
while (this.offlineQueue.length > 0) {
item = this.offlineQueue.shift();
item.command.reject(error);
}
}
executeOfflineCommands() {
if (this.offlineQueue.length) {
debug("send %d commands in offline queue", this.offlineQueue.length);
const offlineQueue = this.offlineQueue;
this.resetOfflineQueue();
while (offlineQueue.length > 0) {
const item = offlineQueue.shift();
this.sendCommand(item.command, item.stream, item.node);
}
}
}
natMapper(nodeKey) {
if (this.options.natMap && typeof this.options.natMap === "object") {
const key = typeof nodeKey === "string"
? nodeKey
: `${nodeKey.host}:${nodeKey.port}`;
const mapped = this.options.natMap[key];
if (mapped) {
debug("NAT mapping %s -> %O", key, mapped);
return Object.assign({}, mapped);
}
}
return typeof nodeKey === "string"
? util_1.nodeKeyToRedisOptions(nodeKey)
: nodeKey;
}
sendCommand(command, stream, node) {

@@ -495,3 +366,3 @@ if (this.status === "wait") {

if (this.status === "end") {
command.reject(new Error(utils_2.CONNECTION_CLOSED_ERROR_MSG));
command.reject(new Error(utils_1.CONNECTION_CLOSED_ERROR_MSG));
return command.promise;

@@ -502,4 +373,3 @@ }

const isCommandReadOnly = command.isReadOnly ||
(commands.exists(command.name) &&
commands.hasFlag(command.name, "readonly"));
((0, commands_1.exists)(command.name) && (0, commands_1.hasFlag)(command.name, "readonly"));
if (!isCommandReadOnly) {

@@ -512,5 +382,4 @@ to = "master";

const _this = this;
if (!node && !command.__is_reject_overwritten) {
// eslint-disable-next-line @typescript-eslint/camelcase
command.__is_reject_overwritten = true;
if (!node && !REJECT_OVERWRITTEN_COMMANDS.has(command)) {
REJECT_OVERWRITTEN_COMMANDS.add(command);
const reject = command.reject;

@@ -565,4 +434,4 @@ command.reject = function (err) {

}
else if (command_1.default.checkFlag("ENTER_SUBSCRIBER_MODE", command.name) ||
command_1.default.checkFlag("EXIT_SUBSCRIBER_MODE", command.name)) {
else if (Command_1.default.checkFlag("ENTER_SUBSCRIBER_MODE", command.name) ||
Command_1.default.checkFlag("EXIT_SUBSCRIBER_MODE", command.name)) {
redis = _this.subscriber.getInstance();

@@ -584,3 +453,3 @@ if (!redis) {

if (Array.isArray(redis)) {
redis = utils_2.sample(redis);
redis = (0, utils_1.sample)(redis);
}

@@ -594,6 +463,6 @@ if (!redis) {

if (to === "all") {
key = utils_2.sample(nodeKeys);
key = (0, utils_1.sample)(nodeKeys);
}
else if (to === "slave" && nodeKeys.length > 1) {
key = utils_2.sample(nodeKeys, 1);
key = (0, utils_1.sample)(nodeKeys, 1);
}

@@ -639,2 +508,23 @@ else {

}
sscanStream(key, options) {
return this.createScanStream("sscan", { key, options });
}
sscanBufferStream(key, options) {
return this.createScanStream("sscanBuffer", { key, options });
}
hscanStream(key, options) {
return this.createScanStream("hscan", { key, options });
}
hscanBufferStream(key, options) {
return this.createScanStream("hscanBuffer", { key, options });
}
zscanStream(key, options) {
return this.createScanStream("zscan", { key, options });
}
zscanBufferStream(key, options) {
return this.createScanStream("zscanBuffer", { key, options });
}
/**
* @ignore
*/
handleError(error, ttl, handlers) {

@@ -676,3 +566,3 @@ if (typeof ttl.value === "undefined") {

}
else if (error.message === utils_2.CONNECTION_CLOSED_ERROR_MSG &&
else if (error.message === utils_1.CONNECTION_CLOSED_ERROR_MSG &&
this.options.retryDelayOnFailover > 0 &&

@@ -689,2 +579,97 @@ this.status === "ready") {

}
resetOfflineQueue() {
this.offlineQueue = new Deque();
}
clearNodesRefreshInterval() {
if (this.slotsTimer) {
clearTimeout(this.slotsTimer);
this.slotsTimer = null;
}
}
resetNodesRefreshInterval() {
if (this.slotsTimer || !this.options.slotsRefreshInterval) {
return;
}
const nextRound = () => {
this.slotsTimer = setTimeout(() => {
debug('refreshing slot caches... (triggered by "slotsRefreshInterval" option)');
this.refreshSlotsCache(() => {
nextRound();
});
}, this.options.slotsRefreshInterval);
};
nextRound();
}
/**
* Change cluster instance's status
*/
setStatus(status) {
debug("status: %s -> %s", this.status || "[empty]", status);
this.status = status;
process.nextTick(() => {
this.emit(status);
});
}
/**
* Called when closed to check whether a reconnection should be made
*/
handleCloseEvent(reason) {
if (reason) {
debug("closed because %s", reason);
}
let retryDelay;
if (!this.manuallyClosing &&
typeof this.options.clusterRetryStrategy === "function") {
retryDelay = this.options.clusterRetryStrategy.call(this, ++this.retryAttempts, reason);
}
if (typeof retryDelay === "number") {
this.setStatus("reconnecting");
this.reconnectTimeout = setTimeout(() => {
this.reconnectTimeout = null;
debug("Cluster is disconnected. Retrying after %dms", retryDelay);
this.connect().catch(function (err) {
debug("Got error %s when reconnecting. Ignoring...", err);
});
}, retryDelay);
}
else {
this.setStatus("end");
this.flushQueue(new Error("None of startup nodes is available"));
}
}
/**
* Flush offline queue with error.
*/
flushQueue(error) {
let item;
while ((item = this.offlineQueue.shift())) {
item.command.reject(error);
}
}
executeOfflineCommands() {
if (this.offlineQueue.length) {
debug("send %d commands in offline queue", this.offlineQueue.length);
const offlineQueue = this.offlineQueue;
this.resetOfflineQueue();
let item;
while ((item = offlineQueue.shift())) {
this.sendCommand(item.command, item.stream, item.node);
}
}
}
natMapper(nodeKey) {
if (this.options.natMap && typeof this.options.natMap === "object") {
const key = typeof nodeKey === "string"
? nodeKey
: `${nodeKey.host}:${nodeKey.port}`;
const mapped = this.options.natMap[key];
if (mapped) {
debug("NAT mapping %s -> %O", key, mapped);
return Object.assign({}, mapped);
}
}
return typeof nodeKey === "string"
? (0, util_1.nodeKeyToRedisOptions)(nodeKey)
: nodeKey;
}
getInfoFromNode(redis, callback) {

@@ -701,3 +686,3 @@ if (!redis) {

retryStrategy: null,
connectionName: util_1.getConnectionName("refresher", this.options.redisOptions && this.options.redisOptions.connectionName),
connectionName: (0, util_1.getConnectionName)("refresher", this.options.redisOptions && this.options.redisOptions.connectionName),
});

@@ -707,3 +692,3 @@ // Ignore error events since we will handle

duplicatedConnection.on("error", utils_1.noop);
duplicatedConnection.cluster("slots", utils_2.timeout((err, result) => {
duplicatedConnection.cluster("SLOTS", (0, utils_1.timeout)((err, result) => {
duplicatedConnection.disconnect();

@@ -731,6 +716,9 @@ if (err) {

}
items[j] = this.natMapper({ host: items[j][0], port: items[j][1] });
items[j].readOnly = j !== 2;
nodes.push(items[j]);
keys.push(items[j].host + ":" + items[j].port);
const node = this.natMapper({
host: items[j][0],
port: items[j][1],
});
node.readOnly = j !== 2;
nodes.push(node);
keys.push(node.host + ":" + node.port);
}

@@ -768,8 +756,5 @@ debug("cluster slots result [%d]: slots %d~%d served by %s", i, slotRangeStart, slotRangeEnd, keys);

* Check whether Cluster is able to process commands
*
* @param {Function} callback
* @private
*/
readyCheck(callback) {
this.cluster("info", function (err, res) {
this.cluster("INFO", (err, res) => {
if (err) {

@@ -805,3 +790,3 @@ return callback(err);

}
const self = this, groupedRecords = util_1.groupSrvRecords(records), sortedKeys = Object.keys(groupedRecords).sort((a, b) => parseInt(a) - parseInt(b));
const self = this, groupedRecords = (0, util_1.groupSrvRecords)(records), sortedKeys = Object.keys(groupedRecords).sort((a, b) => parseInt(a) - parseInt(b));
function tryFirstOne(err) {

@@ -811,3 +796,3 @@ if (!sortedKeys.length) {

}
const key = sortedKeys[0], group = groupedRecords[key], record = util_1.weightSrvRecords(group);
const key = sortedKeys[0], group = groupedRecords[key], record = (0, util_1.weightSrvRecords)(group);
if (!group.records.length) {

@@ -844,48 +829,27 @@ sortedKeys.shift();

* #startupNodes and DNS records may chanage.
*
* @private
* @returns {Promise<IRedisOptions[]>}
*/
resolveStartupNodeHostnames() {
async resolveStartupNodeHostnames() {
if (!Array.isArray(this.startupNodes) || this.startupNodes.length === 0) {
return Promise.reject(new Error("`startupNodes` should contain at least one node."));
throw new Error("`startupNodes` should contain at least one node.");
}
const startupNodes = util_1.normalizeNodeOptions(this.startupNodes);
const hostnames = util_1.getUniqueHostnamesFromOptions(startupNodes);
const startupNodes = (0, util_1.normalizeNodeOptions)(this.startupNodes);
const hostnames = (0, util_1.getUniqueHostnamesFromOptions)(startupNodes);
if (hostnames.length === 0) {
return Promise.resolve(startupNodes);
return startupNodes;
}
return Promise.all(hostnames.map((this.options.useSRVRecords ? this.resolveSrv : this.dnsLookup).bind(this))).then((configs) => {
const hostnameToConfig = utils_2.zipMap(hostnames, configs);
return startupNodes.map((node) => {
const config = hostnameToConfig.get(node.host);
if (!config) {
return node;
}
else if (this.options.useSRVRecords) {
return Object.assign({}, node, config);
}
else {
return Object.assign({}, node, { host: config });
}
});
const configs = await Promise.all(hostnames.map((this.options.useSRVRecords ? this.resolveSrv : this.dnsLookup).bind(this)));
const hostnameToConfig = (0, utils_1.zipMap)(hostnames, configs);
return startupNodes.map((node) => {
const config = hostnameToConfig.get(node.host);
if (!config) {
return node;
}
if (this.options.useSRVRecords) {
return Object.assign({}, node, config);
}
return Object.assign({}, node, { host: config });
});
}
}
Object.getOwnPropertyNames(commander_1.default.prototype).forEach((name) => {
if (!Cluster.prototype.hasOwnProperty(name)) {
Cluster.prototype[name] = commander_1.default.prototype[name];
}
});
const scanCommands = [
"sscan",
"hscan",
"zscan",
"sscanBuffer",
"hscanBuffer",
"zscanBuffer",
];
scanCommands.forEach((command) => {
Cluster.prototype[command + "Stream"] = function (key, options) {
return new ScanStream_1.default(utils_1.defaults({
createScanStream(command, { key, options = {} }) {
return new ScanStream_1.default({
objectMode: true,

@@ -895,6 +859,8 @@ key: key,

command: command,
}, options));
};
});
require("../transaction").addTransactionSupport(Cluster.prototype);
...options,
});
}
}
(0, applyMixin_1.default)(Cluster, events_1.EventEmitter);
(0, transaction_1.addTransactionSupport)(Cluster.prototype);
exports.default = Cluster;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getConnectionName = exports.weightSrvRecords = exports.groupSrvRecords = exports.getUniqueHostnamesFromOptions = exports.normalizeNodeOptions = exports.nodeKeyToRedisOptions = exports.getNodeKey = void 0;
const utils_1 = require("../utils");

@@ -29,3 +30,3 @@ const net_1 = require("net");

else if (typeof node === "string") {
Object.assign(options, utils_1.parseURL(node));
Object.assign(options, (0, utils_1.parseURL)(node));
}

@@ -49,3 +50,3 @@ else if (typeof node === "number") {

}
return utils_1.resolveTLSProfile(options);
return (0, utils_1.resolveTLSProfile)(options);
});

@@ -59,3 +60,3 @@ }

});
return Object.keys(uniqueHostsMap).filter((host) => !net_1.isIP(host));
return Object.keys(uniqueHostsMap).filter((host) => !(0, net_1.isIP)(host));
}

@@ -62,0 +63,0 @@ exports.getUniqueHostnamesFromOptions = getUniqueHostnamesFromOptions;

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const utils_1 = require("../utils");
const debug = utils_1.Debug("AbstractConnector");
const debug = (0, utils_1.Debug)("AbstractConnector");
class AbstractConnector {

@@ -6,0 +6,0 @@ constructor(disconnectTimeout) {

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SentinelConnector = exports.StandaloneConnector = void 0;
const StandaloneConnector_1 = require("./StandaloneConnector");

@@ -4,0 +5,0 @@ exports.StandaloneConnector = StandaloneConnector_1.default;

"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.FailoverDetector = void 0;
const utils_1 = require("../../utils");
const debug = utils_1.Debug("FailoverDetector");
const debug = (0, utils_1.Debug)("FailoverDetector");
const CHANNEL_NAME = "+switch-master";

@@ -28,19 +20,17 @@ class FailoverDetector {

}
subscribe() {
return __awaiter(this, void 0, void 0, function* () {
debug("Starting FailoverDetector");
const promises = [];
for (const sentinel of this.sentinels) {
const promise = sentinel.client.subscribe(CHANNEL_NAME).catch((err) => {
debug("Failed to subscribe to failover messages on sentinel %s:%s (%s)", sentinel.address.host || "127.0.0.1", sentinel.address.port || 26739, err.message);
});
promises.push(promise);
sentinel.client.on("message", (channel) => {
if (!this.isDisconnected && channel === CHANNEL_NAME) {
this.disconnect();
}
});
}
yield Promise.all(promises);
});
async subscribe() {
debug("Starting FailoverDetector");
const promises = [];
for (const sentinel of this.sentinels) {
const promise = sentinel.client.subscribe(CHANNEL_NAME).catch((err) => {
debug("Failed to subscribe to failover messages on sentinel %s:%s (%s)", sentinel.address.host || "127.0.0.1", sentinel.address.port || 26739, err.message);
});
promises.push(promise);
sentinel.client.on("message", (channel) => {
if (!this.isDisconnected && channel === CHANNEL_NAME) {
this.disconnect();
}
});
}
await Promise.all(promises);
}

@@ -47,0 +37,0 @@ disconnect() {

"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.SentinelIterator = void 0;
const net_1 = require("net");
const utils_1 = require("../../utils");
const tls_1 = require("tls");
const StandaloneConnector_1 = require("../StandaloneConnector");
const SentinelIterator_1 = require("./SentinelIterator");
exports.SentinelIterator = SentinelIterator_1.default;
const AbstractConnector_1 = require("../AbstractConnector");
const redis_1 = require("../../redis");
const Redis_1 = require("../../Redis");
const FailoverDetector_1 = require("./FailoverDetector");
const debug = utils_1.Debug("SentinelConnector");
const debug = (0, utils_1.Debug)("SentinelConnector");
class SentinelConnector extends AbstractConnector_1.default {

@@ -26,4 +17,4 @@ constructor(options) {

this.options = options;
this.emitter = null;
this.failoverDetector = null;
this.emitter = null;
if (!this.options.sentinels.length) {

@@ -60,3 +51,3 @@ throw new Error("Requires at least one sentinel to connect to.");

let lastError;
const connectToNext = () => __awaiter(this, void 0, void 0, function* () {
const connectToNext = async () => {
const endpoint = this.sentinelIterator.next();

@@ -78,3 +69,3 @@ if (endpoint.done) {

eventEmitter("error", error);
yield new Promise((resolve) => setTimeout(resolve, retryDelay));
await new Promise((resolve) => setTimeout(resolve, retryDelay));
return connectToNext();

@@ -89,3 +80,3 @@ }

try {
resolved = yield this.resolve(endpoint.value);
resolved = await this.resolve(endpoint.value);
}

@@ -103,6 +94,6 @@ catch (error) {

Object.assign(resolved, this.options.tls);
this.stream = tls_1.connect(resolved);
this.stream = (0, tls_1.connect)(resolved);
}
else {
this.stream = net_1.createConnection(resolved);
this.stream = (0, net_1.createConnection)(resolved);
}

@@ -132,50 +123,44 @@ this.stream.once("connect", () => this.initFailoverDetector());

}
});
};
return connectToNext();
}
updateSentinels(client) {
return __awaiter(this, void 0, void 0, function* () {
if (!this.options.updateSentinels) {
return;
async updateSentinels(client) {
if (!this.options.updateSentinels) {
return;
}
const result = await client.sentinel("sentinels", this.options.name);
if (!Array.isArray(result)) {
return;
}
result
.map(utils_1.packObject)
.forEach((sentinel) => {
const flags = sentinel.flags ? sentinel.flags.split(",") : [];
if (flags.indexOf("disconnected") === -1 &&
sentinel.ip &&
sentinel.port) {
const endpoint = this.sentinelNatResolve(addressResponseToAddress(sentinel));
if (this.sentinelIterator.add(endpoint)) {
debug("adding sentinel %s:%s", endpoint.host, endpoint.port);
}
}
const result = yield client.sentinel("sentinels", this.options.name);
if (!Array.isArray(result)) {
return;
}
result
.map(utils_1.packObject)
.forEach((sentinel) => {
const flags = sentinel.flags ? sentinel.flags.split(",") : [];
if (flags.indexOf("disconnected") === -1 &&
sentinel.ip &&
sentinel.port) {
const endpoint = this.sentinelNatResolve(addressResponseToAddress(sentinel));
if (this.sentinelIterator.add(endpoint)) {
debug("adding sentinel %s:%s", endpoint.host, endpoint.port);
}
}
});
debug("Updated internal sentinels: %s", this.sentinelIterator);
});
debug("Updated internal sentinels: %s", this.sentinelIterator);
}
resolveMaster(client) {
return __awaiter(this, void 0, void 0, function* () {
const result = yield client.sentinel("get-master-addr-by-name", this.options.name);
yield this.updateSentinels(client);
return this.sentinelNatResolve(Array.isArray(result)
? { host: result[0], port: Number(result[1]) }
: null);
});
async resolveMaster(client) {
const result = await client.sentinel("get-master-addr-by-name", this.options.name);
await this.updateSentinels(client);
return this.sentinelNatResolve(Array.isArray(result)
? { host: result[0], port: Number(result[1]) }
: null);
}
resolveSlave(client) {
return __awaiter(this, void 0, void 0, function* () {
const result = yield client.sentinel("slaves", this.options.name);
if (!Array.isArray(result)) {
return null;
}
const availableSlaves = result
.map(utils_1.packObject)
.filter((slave) => slave.flags && !slave.flags.match(/(disconnected|s_down|o_down)/));
return this.sentinelNatResolve(selectPreferredSentinel(availableSlaves, this.options.preferredSlaves));
});
async resolveSlave(client) {
const result = await client.sentinel("slaves", this.options.name);
if (!Array.isArray(result)) {
return null;
}
const availableSlaves = result
.map(utils_1.packObject)
.filter((slave) => slave.flags && !slave.flags.match(/(disconnected|s_down|o_down)/));
return this.sentinelNatResolve(selectPreferredSentinel(availableSlaves, this.options.preferredSlaves));
}

@@ -188,61 +173,73 @@ sentinelNatResolve(item) {

connectToSentinel(endpoint, options) {
return new redis_1.default(Object.assign({ port: endpoint.port || 26379, host: endpoint.host, username: this.options.sentinelUsername || null, password: this.options.sentinelPassword || null, family: endpoint.family ||
(StandaloneConnector_1.isIIpcConnectionOptions(this.options)
const redis = new Redis_1.default({
port: endpoint.port || 26379,
host: endpoint.host,
username: this.options.sentinelUsername || null,
password: this.options.sentinelPassword || null,
family: endpoint.family ||
// @ts-expect-error
("path" in this.options && this.options.path
? undefined
: this.options.family), tls: this.options.sentinelTLS, retryStrategy: null, enableReadyCheck: false, connectTimeout: this.options.connectTimeout, commandTimeout: this.options.sentinelCommandTimeout, dropBufferSupport: true }, options));
: // @ts-expect-error
this.options.family),
tls: this.options.sentinelTLS,
retryStrategy: null,
enableReadyCheck: false,
connectTimeout: this.options.connectTimeout,
commandTimeout: this.options.sentinelCommandTimeout,
...options,
});
// @ts-expect-error
return redis;
}
resolve(endpoint) {
return __awaiter(this, void 0, void 0, function* () {
const client = this.connectToSentinel(endpoint);
// ignore the errors since resolve* methods will handle them
client.on("error", noop);
try {
if (this.options.role === "slave") {
return yield this.resolveSlave(client);
}
else {
return yield this.resolveMaster(client);
}
async resolve(endpoint) {
const client = this.connectToSentinel(endpoint);
// ignore the errors since resolve* methods will handle them
client.on("error", noop);
try {
if (this.options.role === "slave") {
return await this.resolveSlave(client);
}
finally {
client.disconnect();
else {
return await this.resolveMaster(client);
}
});
}
finally {
client.disconnect();
}
}
initFailoverDetector() {
async initFailoverDetector() {
var _a;
return __awaiter(this, void 0, void 0, function* () {
if (!this.options.failoverDetector) {
return;
if (!this.options.failoverDetector) {
return;
}
// Move the current sentinel to the first position
this.sentinelIterator.reset(true);
const sentinels = [];
// In case of a large amount of sentinels, limit the number of concurrent connections
while (sentinels.length < this.options.sentinelMaxConnections) {
const { done, value } = this.sentinelIterator.next();
if (done) {
break;
}
// Move the current sentinel to the first position
this.sentinelIterator.reset(true);
const sentinels = [];
// In case of a large amount of sentinels, limit the number of concurrent connections
while (sentinels.length < this.options.sentinelMaxConnections) {
const { done, value } = this.sentinelIterator.next();
if (done) {
break;
}
const client = this.connectToSentinel(value, {
lazyConnect: true,
retryStrategy: this.options.sentinelReconnectStrategy,
});
client.on("reconnecting", () => {
var _a;
// Tests listen to this event
(_a = this.emitter) === null || _a === void 0 ? void 0 : _a.emit("sentinelReconnecting");
});
sentinels.push({ address: value, client });
}
this.sentinelIterator.reset(false);
if (this.failoverDetector) {
// Clean up previous detector
this.failoverDetector.cleanup();
}
this.failoverDetector = new FailoverDetector_1.FailoverDetector(this, sentinels);
yield this.failoverDetector.subscribe();
// Tests listen to this event
(_a = this.emitter) === null || _a === void 0 ? void 0 : _a.emit("failoverSubscribed");
});
const client = this.connectToSentinel(value, {
lazyConnect: true,
retryStrategy: this.options.sentinelReconnectStrategy,
});
client.on("reconnecting", () => {
var _a;
// Tests listen to this event
(_a = this.emitter) === null || _a === void 0 ? void 0 : _a.emit("sentinelReconnecting");
});
sentinels.push({ address: value, client });
}
this.sentinelIterator.reset(false);
if (this.failoverDetector) {
// Clean up previous detector
this.failoverDetector.cleanup();
}
this.failoverDetector = new FailoverDetector_1.FailoverDetector(this, sentinels);
await this.failoverDetector.subscribe();
// Tests listen to this event
(_a = this.emitter) === null || _a === void 0 ? void 0 : _a.emit("failoverSubscribed");
}

@@ -299,3 +296,3 @@ }

if (!selectedSlave) {
selectedSlave = utils_1.sample(availableSlaves);
selectedSlave = (0, utils_1.sample)(availableSlaves);
}

@@ -302,0 +299,0 @@ return addressResponseToAddress(selectedSlave);

@@ -7,6 +7,2 @@ "use strict";

const AbstractConnector_1 = require("./AbstractConnector");
function isIIpcConnectionOptions(value) {
return value.path;
}
exports.isIIpcConnectionOptions = isIIpcConnectionOptions;
class StandaloneConnector extends AbstractConnector_1.default {

@@ -21,3 +17,3 @@ constructor(options) {

let connectionOptions;
if (isIIpcConnectionOptions(options)) {
if ("path" in options && options.path) {
connectionOptions = {

@@ -29,9 +25,9 @@ path: options.path,

connectionOptions = {};
if (options.port != null) {
if ("port" in options && options.port != null) {
connectionOptions.port = options.port;
}
if (options.host != null) {
if ("host" in options && options.host != null) {
connectionOptions.host = options.host;
}
if (options.family != null) {
if ("family" in options && options.family != null) {
connectionOptions.family = options.family;

@@ -58,6 +54,6 @@ }

if (options.tls) {
this.stream = tls_1.connect(connectionOptions);
this.stream = (0, tls_1.connect)(connectionOptions);
}
else {
this.stream = net_1.createConnection(connectionOptions);
this.stream = (0, net_1.createConnection)(connectionOptions);
}

@@ -64,0 +60,0 @@ }

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = {
const TLSProfiles = {
/**

@@ -104,1 +104,2 @@ * TLS settings for Redis.com Cloud Fixed plan. Updated on 2021-10-06.

};
exports.default = TLSProfiles;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const command_1 = require("./command");
const Command_1 = require("./Command");
const utils_1 = require("./utils");
const RedisParser = require("redis-parser");
const SubscriptionSet_1 = require("./SubscriptionSet");
const debug = utils_1.Debug("dataHandler");
const debug = (0, utils_1.Debug)("dataHandler");
class DataHandler {

@@ -13,3 +13,3 @@ constructor(redis, parserOptions) {

stringNumbers: parserOptions.stringNumbers,
returnBuffers: !parserOptions.dropBufferSupport,
returnBuffers: true,
returnError: (err) => {

@@ -55,3 +55,3 @@ this.returnError(err);

}
if (command_1.default.checkFlag("ENTER_SUBSCRIBER_MODE", item.command.name)) {
if (Command_1.default.checkFlag("ENTER_SUBSCRIBER_MODE", item.command.name)) {
this.redis.condition.subscriber = new SubscriptionSet_1.default();

@@ -63,3 +63,3 @@ this.redis.condition.subscriber.add(item.command.name, reply[1].toString());

}
else if (command_1.default.checkFlag("EXIT_SUBSCRIBER_MODE", item.command.name)) {
else if (Command_1.default.checkFlag("EXIT_SUBSCRIBER_MODE", item.command.name)) {
if (!fillUnsubCommand(item.command, reply[2])) {

@@ -83,3 +83,3 @@ this.redis.commandQueue.unshift(item);

// Check if there're listeners to avoid unnecessary `toString()`.
this.redis.emit("message", reply[1].toString(), reply[2] ? reply[2].toString() : '');
this.redis.emit("message", reply[1].toString(), reply[2] ? reply[2].toString() : "");
}

@@ -179,19 +179,23 @@ this.redis.emit("messageBuffer", reply[1], reply[2]);

exports.default = DataHandler;
const remainingRepliesMap = new WeakMap();
function fillSubCommand(command, count) {
// TODO: use WeakMap here
if (typeof command.remainReplies === "undefined") {
command.remainReplies = command.args.length;
}
if (--command.remainReplies === 0) {
let remainingReplies = remainingRepliesMap.has(command)
? remainingRepliesMap.get(command)
: command.args.length;
remainingReplies -= 1;
if (remainingReplies <= 0) {
command.resolve(count);
remainingRepliesMap.delete(command);
return true;
}
remainingRepliesMap.set(command, remainingReplies);
return false;
}
function fillUnsubCommand(command, count) {
if (typeof command.remainReplies === "undefined") {
command.remainReplies = command.args.length;
}
if (command.remainReplies === 0) {
let remainingReplies = remainingRepliesMap.has(command)
? remainingRepliesMap.get(command)
: command.args.length;
if (remainingReplies === 0) {
if (count === 0) {
remainingRepliesMap.delete(command);
command.resolve(count);

@@ -202,7 +206,9 @@ return true;

}
if (--command.remainReplies === 0) {
remainingReplies -= 1;
if (remainingReplies <= 0) {
command.resolve(count);
return true;
}
remainingRepliesMap.set(command, remainingReplies);
return false;
}

@@ -15,1 +15,2 @@ "use strict";

exports.default = ClusterAllFailedError;
ClusterAllFailedError.defaultMessage = "Failed to refresh slots cache.";
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MaxRetriesPerRequestError = void 0;
const MaxRetriesPerRequestError_1 = require("./MaxRetriesPerRequestError");
exports.MaxRetriesPerRequestError = MaxRetriesPerRequestError_1.default;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports = module.exports = require("./redis").default;
var redis_1 = require("./redis");
exports.default = redis_1.default;
exports.print = exports.ReplyError = exports.SentinelIterator = exports.SentinelConnector = exports.AbstractConnector = exports.Pipeline = exports.ScanStream = exports.Command = exports.Cluster = exports.default = void 0;
exports = module.exports = require("./Redis").default;
var Redis_1 = require("./Redis");
Object.defineProperty(exports, "default", { enumerable: true, get: function () { return Redis_1.default; } });
var cluster_1 = require("./cluster");
exports.Cluster = cluster_1.default;
var command_1 = require("./command");
exports.Command = command_1.default;
Object.defineProperty(exports, "Cluster", { enumerable: true, get: function () { return cluster_1.default; } });
/**
* @ignore
*/
var Command_1 = require("./Command");
Object.defineProperty(exports, "Command", { enumerable: true, get: function () { return Command_1.default; } });
/**
* @ignore
*/
var ScanStream_1 = require("./ScanStream");
exports.ScanStream = ScanStream_1.default;
var pipeline_1 = require("./pipeline");
exports.Pipeline = pipeline_1.default;
Object.defineProperty(exports, "ScanStream", { enumerable: true, get: function () { return ScanStream_1.default; } });
/**
* @ignore
*/
var Pipeline_1 = require("./Pipeline");
Object.defineProperty(exports, "Pipeline", { enumerable: true, get: function () { return Pipeline_1.default; } });
/**
* @ignore
*/
var AbstractConnector_1 = require("./connectors/AbstractConnector");
exports.AbstractConnector = AbstractConnector_1.default;
Object.defineProperty(exports, "AbstractConnector", { enumerable: true, get: function () { return AbstractConnector_1.default; } });
/**
* @ignore
*/
var SentinelConnector_1 = require("./connectors/SentinelConnector");
exports.SentinelConnector = SentinelConnector_1.default;
exports.SentinelIterator = SentinelConnector_1.SentinelIterator;
Object.defineProperty(exports, "SentinelConnector", { enumerable: true, get: function () { return SentinelConnector_1.default; } });
Object.defineProperty(exports, "SentinelIterator", { enumerable: true, get: function () { return SentinelConnector_1.SentinelIterator; } });
// No TS typings
exports.ReplyError = require("redis-errors").ReplyError;
const PromiseContainer = require("./promiseContainer");
/**
* @ignore
*/
Object.defineProperty(exports, "Promise", {
get() {
return PromiseContainer.get();
console.warn("ioredis v5 does not support plugging third-party Promise library anymore. Native Promise will be used.");
return Promise;
},
set(lib) {
PromiseContainer.set(lib);
console.warn("ioredis v5 does not support plugging third-party Promise library anymore. Native Promise will be used.");
},
});
/**
* @ignore
*/
function print(err, reply) {

@@ -31,0 +53,0 @@ if (err) {

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.readyHandler = exports.errorHandler = exports.closeHandler = exports.connectHandler = void 0;
const redis_errors_1 = require("redis-errors");
const command_1 = require("../command");
const Command_1 = require("../Command");
const errors_1 = require("../errors");
const utils_1 = require("../utils");
const DataHandler_1 = require("../DataHandler");
const debug = utils_1.Debug("connection");
const debug = (0, utils_1.Debug)("connection");
function connectHandler(self) {

@@ -55,3 +56,2 @@ return function () {

stringNumbers: self.options.stringNumbers,
dropBufferSupport: self.options.dropBufferSupport,
});

@@ -69,3 +69,2 @@ if (self.options.enableReadyCheck) {

else {
self.serverInfo = info;
if (self.connector.check(info)) {

@@ -149,3 +148,2 @@ exports.readyHandler(self)();

}
self.clearAddedScriptHashesCleanInterval();
if (self.manuallyClosing) {

@@ -206,3 +204,3 @@ self.manuallyClosing = false;

self.sendCommand = function (command) {
if (command_1.default.checkFlag("VALID_IN_MONITOR_MODE", command.name)) {
if (Command_1.default.checkFlag("VALID_IN_MONITOR_MODE", command.name)) {
return sendCommand.call(self, command);

@@ -209,0 +207,0 @@ }

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DEFAULT_REDIS_OPTIONS = void 0;
exports.DEFAULT_REDIS_OPTIONS = {

@@ -40,3 +41,2 @@ // Connection

// Others
dropBufferSupport: false,
enableOfflineQueue: true,

@@ -43,0 +43,0 @@ enableReadyCheck: true,

@@ -6,6 +6,2 @@ "use strict";

* Convenient class to convert the process of scaning keys to a readable stream.
*
* @export
* @class ScanStream
* @extends {Readable}
*/

@@ -12,0 +8,0 @@ class ScanStream extends stream_1.Readable {

@@ -5,5 +5,2 @@ "use strict";

* Tiny class to simplify dealing with subscription set
*
* @export
* @class SubscriptionSet
*/

@@ -10,0 +7,0 @@ class SubscriptionSet {

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.addTransactionSupport = void 0;
const utils_1 = require("./utils");
const standard_as_callback_1 = require("standard-as-callback");
const pipeline_1 = require("./pipeline");
const Pipeline_1 = require("./Pipeline");
function addTransactionSupport(redis) {
redis.pipeline = function (commands) {
const pipeline = new pipeline_1.default(this);
const pipeline = new Pipeline_1.default(this);
if (Array.isArray(commands)) {

@@ -23,3 +24,4 @@ pipeline.addBatch(commands);

}
const pipeline = new pipeline_1.default(this);
const pipeline = new Pipeline_1.default(this);
// @ts-expect-error
pipeline.multi();

@@ -35,3 +37,3 @@ if (Array.isArray(commands)) {

this.redis.connect().catch(utils_1.noop);
return standard_as_callback_1.default(new Promise((resolve, reject) => {
return (0, standard_as_callback_1.default)(new Promise((resolve, reject) => {
this.redis.delayUntilReady((err) => {

@@ -55,3 +57,3 @@ if (err) {

const promise = exec.call(pipeline);
return standard_as_callback_1.default(promise.then(function (result) {
return (0, standard_as_callback_1.default)(promise.then(function (result) {
const execResult = result[result.length - 1];

@@ -70,6 +72,8 @@ if (typeof execResult === "undefined") {

}
return utils_1.wrapMultiResult(execResult[1]);
return (0, utils_1.wrapMultiResult)(execResult[1]);
}), callback);
};
// @ts-expect-error
const { execBuffer } = pipeline;
// @ts-expect-error
pipeline.execBuffer = function (callback) {

@@ -85,5 +89,5 @@ if (this._transactions > 0) {

redis.exec = function (callback) {
return standard_as_callback_1.default(exec.call(this).then(function (results) {
return (0, standard_as_callback_1.default)(exec.call(this).then(function (results) {
if (Array.isArray(results)) {
results = utils_1.wrapMultiResult(results);
results = (0, utils_1.wrapMultiResult)(results);
}

@@ -90,0 +94,0 @@ return results;

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.genRedactedString = exports.getStringValue = exports.MAX_ARGUMENT_LENGTH = void 0;
const debug_1 = require("debug");

@@ -53,3 +54,3 @@ const MAX_ARGUMENT_LENGTH = 200;

function genDebugFunction(namespace) {
const fn = debug_1.default(`${NAMESPACE_PREFIX}:${namespace}`);
const fn = (0, debug_1.default)(`${NAMESPACE_PREFIX}:${namespace}`);
function wrappedDebug(...args) {

@@ -56,0 +57,0 @@ if (!fn.enabled) {

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.noop = exports.defaults = exports.Debug = exports.zipMap = exports.CONNECTION_CLOSED_ERROR_MSG = exports.shuffle = exports.sample = exports.resolveTLSProfile = exports.parseURL = exports.optimizeErrorStack = exports.toArg = exports.convertMapToArray = exports.convertObjectToArray = exports.timeout = exports.packObject = exports.isInt = exports.wrapMultiResult = exports.convertBufferToString = void 0;
const url_1 = require("url");
const lodash_1 = require("./lodash");
exports.defaults = lodash_1.defaults;
exports.noop = lodash_1.noop;
exports.flatten = lodash_1.flatten;
Object.defineProperty(exports, "defaults", { enumerable: true, get: function () { return lodash_1.defaults; } });
Object.defineProperty(exports, "noop", { enumerable: true, get: function () { return lodash_1.noop; } });
const debug_1 = require("./debug");

@@ -12,37 +12,10 @@ exports.Debug = debug_1.default;

/**
* Test if two buffers are equal
*
* @export
* @param {Buffer} a
* @param {Buffer} b
* @returns {boolean} Whether the two buffers are equal
*/
function bufferEqual(a, b) {
if (typeof a.equals === "function") {
return a.equals(b);
}
if (a.length !== b.length) {
return false;
}
for (let i = 0; i < a.length; ++i) {
if (a[i] !== b[i]) {
return false;
}
}
return true;
}
exports.bufferEqual = bufferEqual;
/**
* Convert a buffer to string, supports buffer array
*
* @param {*} value - The input value
* @param {string} encoding - string encoding
* @return {*} The result
* @example
* ```js
* var input = [Buffer.from('foo'), [Buffer.from('bar')]]
* var res = convertBufferToString(input, 'utf8')
* const input = [Buffer.from('foo'), [Buffer.from('bar')]]
* const res = convertBufferToString(input, 'utf8')
* expect(res).to.eql(['foo', ['bar']])
* ```
* @private
*/

@@ -70,11 +43,8 @@ function convertBufferToString(value, encoding) {

*
* @param {Array} arr - The input value
* @return {Array} The output value
* @example
* ```js
* var input = ['a', 'b', new Error('c'), 'd']
* var output = exports.wrapMultiResult(input)
* const input = ['a', 'b', new Error('c'), 'd']
* const output = exports.wrapMultiResult(input)
* expect(output).to.eql([[null, 'a'], [null, 'b'], [new Error('c')], [null, 'd'])
* ```
* @private
*/

@@ -103,5 +73,2 @@ function wrapMultiResult(arr) {

* Detect if the argument is a int
*
* @param {string} value
* @return {boolean} Whether the value is a int
* @example

@@ -120,3 +87,2 @@ * ```js

* ```
* @private
*/

@@ -131,4 +97,2 @@ function isInt(value) {

*
* @param {array} array
* @return {object}
* @example

@@ -151,9 +115,5 @@ * ```js

* Return a callback with timeout
*
* @param {function} callback
* @param {number} timeout
* @return {function}
*/
function timeout(callback, timeout) {
let timer;
let timer = null;
const run = function () {

@@ -172,5 +132,2 @@ if (timer) {

* Convert an object to an array
*
* @param {object} obj
* @return {array}
* @example

@@ -193,5 +150,2 @@ * ```js

* Convert a map to an array
*
* @param {Map} map
* @return {array}
* @example

@@ -216,5 +170,2 @@ * ```js

* Convert a non-string arg to a string
*
* @param {*} arg
* @return {string}
*/

@@ -231,5 +182,5 @@ function toArg(arg) {

*
* @param {Error} error - actually error
* @param {string} friendlyStack - the stack that more meaningful
* @param {string} filterPath - only show stacks with the specified path
* @param error actually error
* @param friendlyStack the stack that more meaningful
* @param filterPath only show stacks with the specified path
*/

@@ -255,5 +206,2 @@ function optimizeErrorStack(error, friendlyStack, filterPath) {

* Parse the redis protocol url
*
* @param {string} url - the redis protocol url
* @return {Object}
*/

@@ -264,17 +212,12 @@ function parseURL(url) {

}
let parsed = url_1.parse(url, true, true);
let parsed = (0, url_1.parse)(url, true, true);
if (!parsed.slashes && url[0] !== "/") {
url = "//" + url;
parsed = url_1.parse(url, true, true);
parsed = (0, url_1.parse)(url, true, true);
}
const options = parsed.query || {};
const allowUsernameInURI = options.allowUsernameInURI && options.allowUsernameInURI !== "false";
delete options.allowUsernameInURI;
const result = {};
if (parsed.auth) {
const index = parsed.auth.indexOf(":");
if (allowUsernameInURI) {
result.username =
index === -1 ? parsed.auth : parsed.auth.slice(0, index);
}
result.username = index === -1 ? parsed.auth : parsed.auth.slice(0, index);
result.password = index === -1 ? "" : parsed.auth.slice(index + 1);

@@ -298,3 +241,3 @@ }

}
lodash_1.defaults(result, options);
(0, lodash_1.defaults)(result, options);
return result;

@@ -305,5 +248,2 @@ }

* Resolve TLS profile shortcut in connection options
*
* @param {Object} options - the redis connection options
* @return {Object}
*/

@@ -325,8 +265,2 @@ function resolveTLSProfile(options) {

* Get a random element from `array`
*
* @export
* @template T
* @param {T[]} array the array
* @param {number} [from=0] start index
* @returns {T}
*/

@@ -344,7 +278,2 @@ function sample(array, from = 0) {

* This method will mutate the original array.
*
* @export
* @template T
* @param {T[]} array
* @returns {T[]}
*/

@@ -351,0 +280,0 @@ function shuffle(array) {

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isArguments = exports.defaults = exports.noop = void 0;
const defaults = require("lodash.defaults");
exports.defaults = defaults;
const flatten = require("lodash.flatten");
exports.flatten = flatten;
const isArguments = require("lodash.isarguments");

@@ -8,0 +7,0 @@ exports.isArguments = isArguments;

{
"name": "ioredis",
"version": "4.28.5",
"version": "5.0.0-beta.1",
"description": "A robust, performance-focused and full-featured Redis client for Node.js.",
"main": "built/index.js",
"main": "./built/index.js",
"types": "./built/index.d.ts",
"files": [

@@ -10,9 +11,13 @@ "built/"

"scripts": {
"test": "TS_NODE_TRANSPILE_ONLY=true TS_NODE_LOG_ERROR=true NODE_ENV=test mocha \"test/helpers/*.ts\" \"test/**/*.ts\"",
"test-single": "TS_NODE_TRANSPILE_ONLY=true TS_NODE_LOG_ERROR=true NODE_ENV=test mocha \"test/helpers/*.ts\" $1",
"lint": "eslint --ext .js,.ts .",
"test:tsd": "npm run build && tsd",
"test:js": "TS_NODE_TRANSPILE_ONLY=true NODE_ENV=test mocha \"test/helpers/*.ts\" \"test/**/*.ts\"",
"test:cov": "nyc npm run test:js",
"test:js:cluster": "TS_NODE_TRANSPILE_ONLY=true NODE_ENV=test mocha \"test-cluster/**/*.ts\"",
"test": "npm run test:js && npm run test:tsd",
"lint": "eslint --ext .js,.ts ./lib",
"docs": "npx typedoc --logLevel Error --excludeExternals --excludeProtected --excludePrivate --readme none lib/index.ts",
"format": "prettier --write \"{,!(node_modules)/**/}*.{js,ts}\"",
"format-check": "prettier --check \"{,!(node_modules)/**/}*.{js,ts}\"",
"build": "rm -rf built && tsc",
"prepublishOnly": "npm run build && npm test",
"prepublishOnly": "npm run build",
"semantic-release": "semantic-release"

@@ -30,3 +35,3 @@ },

],
"author": "luin <i@zihua.li> (http://zihua.li)",
"author": "Zihua Li <i@zihua.li> (http://zihua.li)",
"license": "MIT",

@@ -38,10 +43,8 @@ "funding": {

"dependencies": {
"@ioredis/commands": "^1.1.0",
"cluster-key-slot": "^1.1.0",
"debug": "^4.3.1",
"denque": "^1.1.0",
"debug": "^4.3.3",
"denque": "^2.0.1",
"lodash.defaults": "^4.2.0",
"lodash.flatten": "^4.4.0",
"lodash.isarguments": "^3.1.0",
"p-map": "^2.1.0",
"redis-commands": "1.7.0",
"redis-errors": "^1.2.0",

@@ -52,48 +55,40 @@ "redis-parser": "^3.0.0",

"devDependencies": {
"@semantic-release/changelog": "^5.0.1",
"@semantic-release/git": "^9.0.0",
"@types/bluebird": "^3.5.30",
"@types/chai": "^4.2.11",
"@types/chai-as-promised": "^7.1.3",
"@ioredis/interface-generator": "^1.1.0",
"@semantic-release/changelog": "^6.0.1",
"@semantic-release/commit-analyzer": "^9.0.2",
"@semantic-release/git": "^10.0.1",
"@types/chai": "^4.3.0",
"@types/debug": "^4.1.5",
"@types/lodash.defaults": "^4.2.6",
"@types/lodash.flatten": "^4.4.6",
"@types/lodash.isarguments": "^3.1.6",
"@types/mocha": "^7.0.2",
"@types/node": "^13.11.0",
"@types/redis-errors": "1.2.0",
"@types/sinon": "^9.0.0",
"@types/uuid": "^8.3.0",
"@typescript-eslint/eslint-plugin": "^1.13.0",
"@typescript-eslint/parser": "^2.26.0",
"bluebird": "^3.7.2",
"chai": "^4.2.0",
"@types/mocha": "^9.1.0",
"@types/node": "^17.0.18",
"@types/redis-errors": "^1.2.1",
"@types/sinon": "^10.0.11",
"@typescript-eslint/eslint-plugin": "^5.12.0",
"@typescript-eslint/parser": "^5.12.0",
"chai": "^4.3.6",
"chai-as-promised": "^7.1.1",
"cronometro": "^0.6.0",
"cz-conventional-changelog": "^3.1.0",
"eslint": "^5.16.0",
"eslint-config-prettier": "^6.10.1",
"husky": "^4.2.3",
"mocha": "^6.2.3",
"prettier": "^2.0.2",
"pretty-quick": "^2.0.1",
"eslint": "^8.9.0",
"eslint-config-prettier": "^8.4.0",
"mocha": "^9.2.1",
"nyc": "^15.1.0",
"prettier": "^2.5.1",
"semantic-release": "^19.0.2",
"server-destroy": "^1.0.1",
"sinon": "^9.0.1",
"ts-node": "^8.8.1",
"typescript": "3.8.3",
"sinon": "^13.0.1",
"ts-node": "^10.4.0",
"tsd": "^0.19.1",
"typedoc": "^0.22.12",
"typescript": "^4.5.5",
"uuid": "^8.3.0"
},
"nyc": {
"reporter": [
"lcov"
]
},
"engines": {
"node": ">=6"
"node": ">=12.22.0"
},
"config": {
"commitizen": {
"path": "./node_modules/cz-conventional-changelog"
}
},
"husky": {
"hooks": {
"pre-commit": "pretty-quick --staged"
}
},
"mocha": {

@@ -100,0 +95,0 @@ "exit": true,

[![ioredis](https://cdn.jsdelivr.net/gh/luin/ioredis@b5e8c74/logo.svg)](https://github.com/luin/ioredis)
[![Build Status](https://travis-ci.org/luin/ioredis.svg?branch=master)](https://travis-ci.org/luin/ioredis)
[![Build Status](https://github.com/luin/ioredis/actions/workflows/main.yml/badge.svg?branch=master)](https://github.com/luin/ioredis/actions/workflows/main.yml?query=branch%3Amaster)
[![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier)
[![Build Status](https://github.com/luin/ioredis/actions/workflows/release.yml/badge.svg?branch=main)](https://github.com/luin/ioredis/actions/workflows/release.yml?query=branch%3Amain)
[![Coverage Status](https://coveralls.io/repos/github/luin/ioredis/badge.svg?branch=main)](https://coveralls.io/github/luin/ioredis?branch=main)
[![Join the chat at https://gitter.im/luin/ioredis](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/luin/ioredis?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://commitizen.github.io/cz-cli/)
[![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)
[![npm latest version](https://img.shields.io/npm/v/ioredis/latest.svg)](https://www.npmjs.com/package/ioredis)
[![npm next version](https://img.shields.io/npm/v/ioredis/next.svg)](https://www.npmjs.com/package/ioredis)
<img alt="" src="">
A robust, performance-focused and full-featured [Redis](http://redis.io) client for [Node.js](https://nodejs.org).
Supports Redis >= 2.6.12 and (Node.js >= 6). Completely compatible with Redis 6.x.
Supports Redis >= 2.6.12 and (Node.js >= 12.22.0). Completely compatible with Redis 7.x.

@@ -22,21 +18,28 @@ # Features

0. Full-featured. It supports [Cluster](http://redis.io/topics/cluster-tutorial), [Sentinel](http://redis.io/topics/sentinel), [Streams](https://redis.io/topics/streams-intro), [Pipelining](http://redis.io/topics/pipelining) and of course [Lua scripting](http://redis.io/commands/eval) & [Pub/Sub](http://redis.io/topics/pubsub) (with the support of binary messages).
1. High performance.
2. Delightful API. It works with Node callbacks and Native promises.
0. Full-featured. It supports [Cluster](http://redis.io/topics/cluster-tutorial), [Sentinel](http://redis.io/topics/sentinel), [Streams](https://redis.io/topics/streams-intro), [Pipelining](http://redis.io/topics/pipelining), and of course [Lua scripting](http://redis.io/commands/eval), [Redis Functions](https://redis.io/topics/functions-intro), [Pub/Sub](http://redis.io/topics/pubsub) (with the support of binary messages).
1. High performance 🚀.
2. Delightful API 😄. It works with Node callbacks and Native promises.
3. Transformation of command arguments and replies.
4. Transparent key prefixing.
5. Abstraction for Lua scripting, allowing you to define custom commands.
6. Support for binary data.
7. Support for TLS 🔒.
8. Support for offline queue and ready checking.
9. Support for ES6 types, such as `Map` and `Set`.
10. Support for GEO commands 📍.
11. Support for Redis ACL.
5. Abstraction for Lua scripting, allowing you to [define custom commands](https://github.com/luin/ioredis#lua-scripting).
6. Supports [binary data](https://github.com/luin/ioredis#handle-binary-data).
7. Supports [TLS](https://github.com/luin/ioredis#tls-options) 🔒.
8. Supports offline queue and ready checking.
9. Supports ES6 types, such as `Map` and `Set`.
10. Supports GEO commands 📍.
11. Supports Redis ACL.
12. Sophisticated error handling strategy.
13. Support for NAT mapping.
14. Support for autopipelining
13. Supports NAT mapping.
14. Supports autopipelining
# Versions
| NPM Version | Branch | Node.js Version | Redis Version |
| ----------- | ------ | --------------- | --------------- |
| 5.x.x | main | >= 12 | 2.6.12 ~ latest |
| 4.x.x | v4 | >= 6 | 2.6.12 ~ 7 |
# Links
- [API Documentation](API.md)
- [API Documentation](http://luin.github.io/ioredis/) ([Redis](http://luin.github.io/ioredis/classes/default.html), [Cluster](http://luin.github.io/ioredis/classes/Cluster.html))
- [Changelog](Changelog.md)

@@ -102,17 +105,20 @@ - [Migrating from node_redis](https://github.com/luin/ioredis/wiki/Migrating-from-node_redis)

```javascript
// Import ioredis.
// You can also use `import Redis from "ioredis"`
// if your project is an ESM module or a TypeScript project.
const Redis = require("ioredis");
const redis = new Redis(); // uses defaults unless given configuration object
// ioredis supports all Redis commands:
redis.set("foo", "bar"); // returns promise which resolves to string, "OK"
// Create a Redis instance.
// By default, it will connect to localhost:6379.
// We are going to cover how to specify connection options soon.
const redis = new Redis();
// the format is: redis[SOME_REDIS_COMMAND_IN_LOWERCASE](ARGUMENTS_ARE_JOINED_INTO_COMMAND_STRING)
// the js: ` redis.set("mykey", "Hello") ` is equivalent to the cli: ` redis> SET mykey "Hello" `
redis.set("mykey", "value"); // Returns a promise which resolves to "OK" when the command succeeds.
// ioredis supports the node.js callback style
redis.get("foo", function (err, result) {
redis.get("mykey", (err, result) => {
if (err) {
console.error(err);
} else {
console.log(result); // Promise resolves to "bar"
console.log(result); // Prints "value"
}

@@ -122,16 +128,29 @@ });

// Or ioredis returns a promise if the last argument isn't a function
redis.get("foo").then(function (result) {
console.log(result); // Prints "bar"
redis.get("mykey").then((result) => {
console.log(result); // Prints "value"
});
// Most responses are strings, or arrays of strings
redis.zadd("sortedSet", 1, "one", 2, "dos", 4, "quatro", 3, "three");
redis.zrange("sortedSet", 0, 2, "WITHSCORES").then((res) => console.log(res)); // Promise resolves to ["one", "1", "dos", "2", "three", "3"] as if the command was ` redis> ZRANGE sortedSet 0 2 WITHSCORES `
redis.zrange("sortedSet", 0, 2, "WITHSCORES").then((elements) => {
// ["one", "1", "dos", "2", "three", "3"] as if the command was `redis> ZRANGE sortedSet 0 2 WITHSCORES`
console.log(elements);
});
// All arguments are passed directly to the redis server:
redis.set("key", 100, "EX", 10);
// All arguments are passed directly to the redis server,
// so technically ioredis supports all Redis commands.
// The format is: redis[SOME_REDIS_COMMAND_IN_LOWERCASE](ARGUMENTS_ARE_JOINED_INTO_COMMAND_STRING)
// so the following statement is equivalent to the CLI: `redis> SET mykey hello EX 10`
redis.set("mykey", "hello", "EX", 10);
```
See the `examples/` folder for more examples.
See the `examples/` folder for more examples. For example:
* [Strings](examples/string.js)
* [Hashes](examples/hash.js)
* [Lists](examples/list.js)
* [Sets](examples/set.js)
* [Sorted Sets](examples/zset.js)
* [Streams](examples/stream.js)
* [Redis Modules](examples/module.js) e.g. RedisJSON
## Connect to Redis

@@ -151,5 +170,5 @@

host: "127.0.0.1", // Redis host
family: 4, // 4 (IPv4) or 6 (IPv6)
password: "auth",
db: 0,
username: "default", // needs Redis >= 6
password: "my-top-secret",
db: 0, // Defaults to 0
});

@@ -165,7 +184,3 @@ ```

// Username can also be passed via URI.
// It's worth to noticing that for compatibility reasons `allowUsernameInURI`
// need to be provided, otherwise the username part will be ignored.
new Redis(
"redis://username:authpassword@127.0.0.1:6380/4?allowUsernameInURI=true"
);
new Redis("redis://username:authpassword@127.0.0.1:6380/4");
```

@@ -230,3 +245,3 @@

It worth noticing that a connection (aka `Redis` instance) can't play both roles together. More specifically, when a client issues `subscribe()` or `psubscribe()`, it enters the "subscriber" mode. From that point, only commands that modify the subscription set are valid. Namely, they are: `subscribe`, `psubscribe`, `unsubscribe`, `punsubscribe`, `ping`, and `quit`. When the subscription set is empty (via `unsubscribe`/`punsubscribe`), the connection is put back into the regular mode.
It's worth noticing that a connection (aka a `Redis` instance) can't play both roles at the same time. More specifically, when a client issues `subscribe()` or `psubscribe()`, it enters the "subscriber" mode. From that point, only commands that modify the subscription set are valid. Namely, they are: `subscribe`, `psubscribe`, `unsubscribe`, `punsubscribe`, `ping`, and `quit`. When the subscription set is empty (via `unsubscribe`/`punsubscribe`), the connection is put back into the regular mode.

@@ -473,4 +488,4 @@ If you want to do pub/sub in the same file/process, you should create a separate connection:

// This will define a command echo:
redis.defineCommand("echo", {
// This will define a command myecho:
redis.defineCommand("myecho", {
numberOfKeys: 2,

@@ -480,10 +495,10 @@ lua: "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}",

// Now `echo` can be used just like any other ordinary command,
// Now `myecho` can be used just like any other ordinary command,
// and ioredis will try to use `EVALSHA` internally when possible for better performance.
redis.echo("k1", "k2", "a1", "a2", (err, result) => {
redis.myecho("k1", "k2", "a1", "a2", (err, result) => {
// result === ['k1', 'k2', 'a1', 'a2']
});
// `echoBuffer` is also defined automatically to return buffers instead of strings:
redis.echoBuffer("k1", "k2", "a1", "a2", (err, result) => {
// `myechoBuffer` is also defined automatically to return buffers instead of strings:
redis.myechoBuffer("k1", "k2", "a1", "a2", (err, result) => {
// result[0] equals to Buffer.from('k1');

@@ -493,5 +508,7 @@ });

// And of course it works with pipeline:
redis.pipeline().set("foo", "bar").echo("k1", "k2", "a1", "a2").exec();
redis.pipeline().set("foo", "bar").myecho("k1", "k2", "a1", "a2").exec();
```
### Dynamic Keys
If the number of keys can't be determined when defining a command, you can

@@ -513,2 +530,21 @@ omit the `numberOfKeys` property and pass the number of keys as the first argument

### As Constructor Options
Besides `defineCommand()`, you can also define custom commands with the `scripts` constructor option:
```javascript
const redis = new Redis({
scripts: {
myecho: {
numberOfKeys: 2,
lua: "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}",
},
},
});
```
### TypeScript Usages
You can refer to [the example](examples/typescript/scripts.ts) for how to declare your custom commands.
## Transparent Key Prefixing

@@ -527,3 +563,3 @@

fooRedis.defineCommand("echo", {
fooRedis.defineCommand("myecho", {
numberOfKeys: 2,

@@ -540,3 +576,3 @@ lua: "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}",

// Sends EVALSHA xxx foo:k1 foo:k2 a1 a2
.echo("k1", "k2", "a1", "a2")
.myecho("k1", "k2", "a1", "a2")
.exec();

@@ -566,8 +602,8 @@ ```

if (args.length === 2) {
if (typeof Map !== "undefined" && args[1] instanceof Map) {
if (args[1] instanceof Map) {
// utils is a internal module of ioredis
return [args[0]].concat(utils.convertMapToArray(args[1]));
return [args[0], ...utils.convertMapToArray(args[1])];
}
if (typeof args[1] === "object" && args[1] !== null) {
return [args[0]].concat(utils.convertObjectToArray(args[1]));
return [args[0], ...utils.convertObjectToArray(args[1])];
}

@@ -774,3 +810,3 @@ }

### Reconnect on error
### Reconnect on Error

@@ -1026,3 +1062,3 @@ Besides auto-reconnect when the connection is closed, ioredis supports reconnecting on certain Redis errors using the `reconnectOnError` option. Here's an example that will reconnect when receiving `READONLY` error:

### Read-write splitting
### Read-Write Splitting

@@ -1056,3 +1092,3 @@ A typical redis cluster contains three or more masters and several slaves for each master. It's possible to scale out redis cluster by sending read queries to slaves and write queries to masters by setting the `scaleReads` option.

### Running commands to multiple nodes
### Running Commands to Multiple Nodes

@@ -1107,3 +1143,3 @@ Every command will be sent to exactly one node. For commands containing keys, (e.g. `GET`, `SET` and `HGETALL`), ioredis sends them to the node that serving the keys, and for other commands not containing keys, (e.g. `INFO`, `KEYS` and `FLUSHDB`), ioredis sends them to a random node.

### Transaction and pipeline in Cluster mode
### Transaction and Pipeline in Cluster Mode

@@ -1187,3 +1223,3 @@ Almost all features that are supported by `Redis` are also supported by `Redis.Cluster`, e.g. custom commands, transaction and pipeline.

### Special note: AWS ElastiCache Clusters with TLS
### Special Note: Aws Elasticache Clusters with TLS

@@ -1233,3 +1269,3 @@ AWS ElastiCache for Redis (Clustered Mode) supports TLS encryption. If you use

### Example of automatic pipeline enqueuing
### Example of Automatic Pipeline Enqueuing

@@ -1353,20 +1389,2 @@ This sample code uses ioredis with automatic pipeline enabled.

# Plugging in your own Promises Library
If you're an advanced user, you may want to plug in your own promise library like [bluebird](https://www.npmjs.com/package/bluebird). Just set Redis.Promise to your favorite ES6-style promise constructor and ioredis will use it.
```javascript
const Redis = require("ioredis");
Redis.Promise = require("bluebird");
const redis = new Redis();
// Use bluebird
assert.equal(redis.get().constructor, require("bluebird"));
// You can change the Promise implementation at any time:
Redis.Promise = global.Promise;
assert.equal(redis.get().constructor, global.Promise);
```
# Running tests

@@ -1373,0 +1391,0 @@

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc