Review funkt
This commit is contained in:
179
node_modules/ioredis/built/Command.d.ts
generated
vendored
Normal file
179
node_modules/ioredis/built/Command.d.ts
generated
vendored
Normal file
@@ -0,0 +1,179 @@
|
||||
/// <reference types="node" />
|
||||
/// <reference types="node" />
|
||||
import { Callback, Respondable, CommandParameter } from "./types";
|
||||
import { ReplyContext, ReplyTransformer } from "./replyTransformers";
|
||||
export type ArgumentType = string | Buffer | number | (string | Buffer | number | any[])[];
|
||||
interface CommandOptions {
|
||||
/**
|
||||
* Set the encoding of the reply, by default buffer will be returned.
|
||||
*/
|
||||
replyEncoding?: BufferEncoding | null;
|
||||
errorStack?: Error;
|
||||
keyPrefix?: string;
|
||||
/**
|
||||
* Force the command to be readOnly so it will also execute on slaves
|
||||
*/
|
||||
readOnly?: boolean;
|
||||
}
|
||||
type ArgumentTransformer = (args: any[]) => any[];
|
||||
export interface CommandNameFlags {
|
||||
VALID_IN_SUBSCRIBER_MODE: [
|
||||
"subscribe",
|
||||
"psubscribe",
|
||||
"unsubscribe",
|
||||
"punsubscribe",
|
||||
"ssubscribe",
|
||||
"sunsubscribe",
|
||||
"ping",
|
||||
"quit"
|
||||
];
|
||||
VALID_IN_MONITOR_MODE: ["monitor", "auth"];
|
||||
ENTER_SUBSCRIBER_MODE: ["subscribe", "psubscribe", "ssubscribe"];
|
||||
EXIT_SUBSCRIBER_MODE: ["unsubscribe", "punsubscribe", "sunsubscribe"];
|
||||
WILL_DISCONNECT: ["quit"];
|
||||
HANDSHAKE_COMMANDS: [
|
||||
"auth",
|
||||
"hello",
|
||||
"select",
|
||||
"client",
|
||||
"readonly",
|
||||
"info"
|
||||
];
|
||||
IGNORE_RECONNECT_ON_ERROR: ["client"];
|
||||
BLOCKING_COMMANDS: [
|
||||
"blpop",
|
||||
"brpop",
|
||||
"brpoplpush",
|
||||
"blmove",
|
||||
"blmovem",
|
||||
"bzpopmin",
|
||||
"bzpopmax",
|
||||
"bzmpop",
|
||||
"blmpop",
|
||||
"xread",
|
||||
"xreadgroup"
|
||||
];
|
||||
LAST_ARG_TIMEOUT_COMMANDS: [
|
||||
"blpop",
|
||||
"brpop",
|
||||
"brpoplpush",
|
||||
"blmove",
|
||||
"bzpopmin",
|
||||
"bzpopmax"
|
||||
];
|
||||
FIRST_ARG_TIMEOUT_COMMANDS: ["bzmpop", "blmpop"];
|
||||
BLOCK_OPTION_COMMANDS: ["xread", "xreadgroup"];
|
||||
}
|
||||
/**
|
||||
* Command instance
|
||||
*
|
||||
* It's rare that you need to create a Command instance yourself.
|
||||
*
|
||||
* ```js
|
||||
* var infoCommand = new Command('info', null, function (err, result) {
|
||||
* console.log('result', result);
|
||||
* });
|
||||
*
|
||||
* redis.sendCommand(infoCommand);
|
||||
*
|
||||
* // When no callback provided, Command instance will have a `promise` property,
|
||||
* // which will resolve/reject with the result of the command.
|
||||
* var getCommand = new Command('get', ['foo']);
|
||||
* getCommand.promise.then(function (result) {
|
||||
* console.log('result', result);
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
export default class Command implements Respondable {
|
||||
name: string;
|
||||
static FLAGS: {
|
||||
[key in keyof CommandNameFlags]: CommandNameFlags[key];
|
||||
};
|
||||
private static flagMap?;
|
||||
private static _transformer;
|
||||
/**
|
||||
* Check whether the command has the flag
|
||||
*/
|
||||
static checkFlag<T extends keyof CommandNameFlags>(flagName: T, commandName: string): commandName is CommandNameFlags[T][number];
|
||||
static setArgumentTransformer(name: string, func: ArgumentTransformer): void;
|
||||
static setReplyTransformer(name: string, func: ReplyTransformer): void;
|
||||
private static getFlagMap;
|
||||
ignore?: boolean;
|
||||
isReadOnly?: boolean;
|
||||
args: CommandParameter[];
|
||||
inTransaction: boolean;
|
||||
pipelineIndex?: number;
|
||||
isTraced: boolean;
|
||||
isResolved: boolean;
|
||||
isSettled: boolean;
|
||||
reject: (err: Error) => void;
|
||||
resolve: (result: any) => void;
|
||||
promise: Promise<any>;
|
||||
private replyEncoding;
|
||||
private protocol;
|
||||
private replyMapping;
|
||||
private errorStack;
|
||||
private bufferMode;
|
||||
private callback;
|
||||
private transformed;
|
||||
private _commandTimeoutTimer?;
|
||||
private _blockingTimeoutTimer?;
|
||||
private _blockingDeadline?;
|
||||
private slot?;
|
||||
private keys?;
|
||||
/**
|
||||
* Creates an instance of Command.
|
||||
* @param name Command name
|
||||
* @param args An array of command arguments
|
||||
* @param options
|
||||
* @param callback The callback that handles the response.
|
||||
* If omit, the response will be handled via Promise
|
||||
*/
|
||||
constructor(name: string, args?: Array<ArgumentType>, options?: CommandOptions, callback?: Callback);
|
||||
getSlot(): number;
|
||||
getKeys(): Array<string | Buffer>;
|
||||
setReplyContext(context: ReplyContext | null | undefined): void;
|
||||
/**
|
||||
* Convert command to writable buffer or string
|
||||
*/
|
||||
toWritable(_socket: object): string | Buffer;
|
||||
stringifyArguments(): void;
|
||||
/**
|
||||
* Convert buffer/buffer[] to string/string[],
|
||||
* and apply reply transformer.
|
||||
*/
|
||||
transformReply(result: Buffer | Buffer[]): string | string[] | Buffer | Buffer[];
|
||||
/**
|
||||
* Set the wait time before terminating the attempt to execute a command
|
||||
* and generating an error.
|
||||
*/
|
||||
setTimeout(ms: number): void;
|
||||
/**
|
||||
* Set a timeout for blocking commands.
|
||||
* When the timeout expires, the command resolves with null (matching Redis behavior).
|
||||
* This handles the case of undetectable network failures (e.g., docker network disconnect)
|
||||
* where the TCP connection becomes a zombie and no close event fires.
|
||||
*/
|
||||
setBlockingTimeout(ms: number): void;
|
||||
/**
|
||||
* Extract the blocking timeout from the command arguments.
|
||||
*
|
||||
* @returns The timeout in seconds, null for indefinite blocking (timeout of 0),
|
||||
* or undefined if this is not a blocking command
|
||||
*/
|
||||
extractBlockingTimeout(): number | null | undefined;
|
||||
/**
|
||||
* Clear the command and blocking timers
|
||||
*/
|
||||
private _clearTimers;
|
||||
private initPromise;
|
||||
/**
|
||||
* Iterate through the command arguments that are considered keys.
|
||||
*/
|
||||
private _iterateKeys;
|
||||
/**
|
||||
* Convert the value from buffer to the target encoding.
|
||||
*/
|
||||
private _convertValue;
|
||||
}
|
||||
export {};
|
||||
485
node_modules/ioredis/built/Command.js
generated
vendored
Normal file
485
node_modules/ioredis/built/Command.js
generated
vendored
Normal file
@@ -0,0 +1,485 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const commands_1 = require("@ioredis/commands");
|
||||
const calculateSlot = require("cluster-key-slot");
|
||||
const standard_as_callback_1 = require("standard-as-callback");
|
||||
const utils_1 = require("./utils");
|
||||
const argumentParsers_1 = require("./utils/argumentParsers");
|
||||
const replyTransformers_1 = require("./replyTransformers");
|
||||
/**
|
||||
* Command instance
|
||||
*
|
||||
* It's rare that you need to create a Command instance yourself.
|
||||
*
|
||||
* ```js
|
||||
* var infoCommand = new Command('info', null, function (err, result) {
|
||||
* console.log('result', result);
|
||||
* });
|
||||
*
|
||||
* redis.sendCommand(infoCommand);
|
||||
*
|
||||
* // When no callback provided, Command instance will have a `promise` property,
|
||||
* // which will resolve/reject with the result of the command.
|
||||
* var getCommand = new Command('get', ['foo']);
|
||||
* getCommand.promise.then(function (result) {
|
||||
* console.log('result', result);
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
class Command {
|
||||
/**
|
||||
* Check whether the command has the flag
|
||||
*/
|
||||
static checkFlag(flagName, commandName) {
|
||||
commandName = commandName.toLowerCase();
|
||||
return !!this.getFlagMap()[flagName][commandName];
|
||||
}
|
||||
static setArgumentTransformer(name, func) {
|
||||
this._transformer.argument[name] = func;
|
||||
}
|
||||
static setReplyTransformer(name, func) {
|
||||
this._transformer.reply[name] = func;
|
||||
}
|
||||
static getFlagMap() {
|
||||
if (!this.flagMap) {
|
||||
this.flagMap = Object.keys(Command.FLAGS).reduce((map, flagName) => {
|
||||
map[flagName] = {};
|
||||
Command.FLAGS[flagName].forEach((commandName) => {
|
||||
map[flagName][commandName] = true;
|
||||
});
|
||||
return map;
|
||||
}, {});
|
||||
}
|
||||
return this.flagMap;
|
||||
}
|
||||
/**
|
||||
* Creates an instance of Command.
|
||||
* @param name Command name
|
||||
* @param args An array of command arguments
|
||||
* @param options
|
||||
* @param callback The callback that handles the response.
|
||||
* If omit, the response will be handled via Promise
|
||||
*/
|
||||
constructor(name, args = [], options = {}, callback) {
|
||||
this.name = name;
|
||||
this.inTransaction = false;
|
||||
this.isTraced = false;
|
||||
this.isResolved = false;
|
||||
this.isSettled = false;
|
||||
this.transformed = false;
|
||||
this.replyEncoding = options.replyEncoding;
|
||||
this.protocol = 2;
|
||||
this.replyMapping = "legacy";
|
||||
this.errorStack = options.errorStack;
|
||||
this.args = args.flat();
|
||||
this.callback = callback;
|
||||
this.initPromise();
|
||||
if (options.keyPrefix) {
|
||||
// @ts-expect-error
|
||||
const isBufferKeyPrefix = options.keyPrefix instanceof Buffer;
|
||||
// @ts-expect-error
|
||||
let keyPrefixBuffer = isBufferKeyPrefix
|
||||
? options.keyPrefix
|
||||
: null;
|
||||
this._iterateKeys((key) => {
|
||||
if (key instanceof Buffer) {
|
||||
if (keyPrefixBuffer === null) {
|
||||
keyPrefixBuffer = Buffer.from(options.keyPrefix);
|
||||
}
|
||||
return Buffer.concat([keyPrefixBuffer, key]);
|
||||
}
|
||||
else if (isBufferKeyPrefix) {
|
||||
// @ts-expect-error
|
||||
return Buffer.concat([options.keyPrefix, Buffer.from(String(key))]);
|
||||
}
|
||||
return options.keyPrefix + key;
|
||||
});
|
||||
}
|
||||
if (options.readOnly) {
|
||||
this.isReadOnly = true;
|
||||
}
|
||||
}
|
||||
getSlot() {
|
||||
if (typeof this.slot === "undefined") {
|
||||
const key = this.getKeys()[0];
|
||||
this.slot = key == null ? null : calculateSlot(key);
|
||||
}
|
||||
return this.slot;
|
||||
}
|
||||
getKeys() {
|
||||
return this._iterateKeys();
|
||||
}
|
||||
setReplyContext(context) {
|
||||
this.protocol = context?.protocol ?? 2;
|
||||
this.replyMapping = context?.replyMapping ?? "legacy";
|
||||
}
|
||||
/**
|
||||
* Convert command to writable buffer or string
|
||||
*/
|
||||
toWritable(_socket) {
|
||||
let result;
|
||||
const commandStr = "*" +
|
||||
(this.args.length + 1) +
|
||||
"\r\n$" +
|
||||
Buffer.byteLength(this.name) +
|
||||
"\r\n" +
|
||||
this.name +
|
||||
"\r\n";
|
||||
if (this.bufferMode) {
|
||||
const buffers = new MixedBuffers();
|
||||
buffers.push(commandStr);
|
||||
for (let i = 0; i < this.args.length; ++i) {
|
||||
const arg = this.args[i];
|
||||
if (arg instanceof Buffer) {
|
||||
if (arg.length === 0) {
|
||||
buffers.push("$0\r\n\r\n");
|
||||
}
|
||||
else {
|
||||
buffers.push("$" + arg.length + "\r\n");
|
||||
buffers.push(arg);
|
||||
buffers.push("\r\n");
|
||||
}
|
||||
}
|
||||
else {
|
||||
buffers.push("$" +
|
||||
Buffer.byteLength(arg) +
|
||||
"\r\n" +
|
||||
arg +
|
||||
"\r\n");
|
||||
}
|
||||
}
|
||||
result = buffers.toBuffer();
|
||||
}
|
||||
else {
|
||||
result = commandStr;
|
||||
for (let i = 0; i < this.args.length; ++i) {
|
||||
const arg = this.args[i];
|
||||
result +=
|
||||
"$" +
|
||||
Buffer.byteLength(arg) +
|
||||
"\r\n" +
|
||||
arg +
|
||||
"\r\n";
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
stringifyArguments() {
|
||||
for (let i = 0; i < this.args.length; ++i) {
|
||||
const arg = this.args[i];
|
||||
if (typeof arg === "string") {
|
||||
// buffers and strings don't need any transformation
|
||||
}
|
||||
else if (arg instanceof Buffer) {
|
||||
this.bufferMode = true;
|
||||
}
|
||||
else {
|
||||
this.args[i] = (0, utils_1.toArg)(arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Convert buffer/buffer[] to string/string[],
|
||||
* and apply reply transformer.
|
||||
*/
|
||||
transformReply(result) {
|
||||
if (this.replyEncoding) {
|
||||
result = (0, utils_1.convertBufferToString)(result, this.replyEncoding);
|
||||
}
|
||||
const transformer = Command._transformer.reply[this.name];
|
||||
if (transformer) {
|
||||
result = transformer(result, {
|
||||
commandName: this.name,
|
||||
protocol: this.protocol,
|
||||
replyMapping: this.replyMapping,
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* Set the wait time before terminating the attempt to execute a command
|
||||
* and generating an error.
|
||||
*/
|
||||
setTimeout(ms) {
|
||||
if (!this._commandTimeoutTimer) {
|
||||
this._commandTimeoutTimer = setTimeout(() => {
|
||||
if (!this.isSettled) {
|
||||
this.reject(new Error("Command timed out"));
|
||||
}
|
||||
}, ms);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Set a timeout for blocking commands.
|
||||
* When the timeout expires, the command resolves with null (matching Redis behavior).
|
||||
* This handles the case of undetectable network failures (e.g., docker network disconnect)
|
||||
* where the TCP connection becomes a zombie and no close event fires.
|
||||
*/
|
||||
setBlockingTimeout(ms) {
|
||||
if (ms <= 0) {
|
||||
return;
|
||||
}
|
||||
// Clear existing timer if any (can happen when command moves from offline to command queue)
|
||||
if (this._blockingTimeoutTimer) {
|
||||
clearTimeout(this._blockingTimeoutTimer);
|
||||
this._blockingTimeoutTimer = undefined;
|
||||
}
|
||||
const now = Date.now();
|
||||
// First call: establish absolute deadline
|
||||
if (this._blockingDeadline === undefined) {
|
||||
this._blockingDeadline = now + ms;
|
||||
}
|
||||
// Check if we've already exceeded the deadline
|
||||
const remaining = this._blockingDeadline - now;
|
||||
if (remaining <= 0) {
|
||||
// Resolve with null to indicate timeout (same as Redis behavior)
|
||||
this.resolve(null);
|
||||
return;
|
||||
}
|
||||
this._blockingTimeoutTimer = setTimeout(() => {
|
||||
if (this.isSettled) {
|
||||
this._blockingTimeoutTimer = undefined;
|
||||
return;
|
||||
}
|
||||
this._blockingTimeoutTimer = undefined;
|
||||
// Timeout expired - resolve with null (same as Redis behavior when blocking command times out)
|
||||
this.resolve(null);
|
||||
}, remaining);
|
||||
}
|
||||
/**
|
||||
* Extract the blocking timeout from the command arguments.
|
||||
*
|
||||
* @returns The timeout in seconds, null for indefinite blocking (timeout of 0),
|
||||
* or undefined if this is not a blocking command
|
||||
*/
|
||||
extractBlockingTimeout() {
|
||||
const args = this.args;
|
||||
if (!args || args.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
const name = this.name.toLowerCase();
|
||||
// BLMOVEM may have trailing options, but its timeout is always the fifth argument.
|
||||
if (name === "blmovem") {
|
||||
return (0, argumentParsers_1.parseSecondsArgument)(args[4]);
|
||||
}
|
||||
if (Command.checkFlag("LAST_ARG_TIMEOUT_COMMANDS", name)) {
|
||||
return (0, argumentParsers_1.parseSecondsArgument)(args[args.length - 1]);
|
||||
}
|
||||
if (Command.checkFlag("FIRST_ARG_TIMEOUT_COMMANDS", name)) {
|
||||
return (0, argumentParsers_1.parseSecondsArgument)(args[0]);
|
||||
}
|
||||
if (Command.checkFlag("BLOCK_OPTION_COMMANDS", name)) {
|
||||
return (0, argumentParsers_1.parseBlockOption)(args);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
/**
|
||||
* Clear the command and blocking timers
|
||||
*/
|
||||
_clearTimers() {
|
||||
const existingTimer = this._commandTimeoutTimer;
|
||||
if (existingTimer) {
|
||||
clearTimeout(existingTimer);
|
||||
delete this._commandTimeoutTimer;
|
||||
}
|
||||
const blockingTimer = this._blockingTimeoutTimer;
|
||||
if (blockingTimer) {
|
||||
clearTimeout(blockingTimer);
|
||||
delete this._blockingTimeoutTimer;
|
||||
}
|
||||
}
|
||||
initPromise() {
|
||||
this.isResolved = false;
|
||||
this.isSettled = false;
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
if (!this.transformed) {
|
||||
this.transformed = true;
|
||||
const transformer = Command._transformer.argument[this.name];
|
||||
if (transformer) {
|
||||
this.args = transformer(this.args);
|
||||
}
|
||||
this.stringifyArguments();
|
||||
}
|
||||
this.resolve = this._convertValue(resolve);
|
||||
this.reject = (err) => {
|
||||
this._clearTimers();
|
||||
this.isSettled = true;
|
||||
if (this.errorStack) {
|
||||
reject((0, utils_1.optimizeErrorStack)(err, this.errorStack.stack, __dirname));
|
||||
}
|
||||
else {
|
||||
reject(err);
|
||||
}
|
||||
};
|
||||
});
|
||||
this.promise = (0, standard_as_callback_1.default)(promise, this.callback);
|
||||
}
|
||||
/**
|
||||
* Iterate through the command arguments that are considered keys.
|
||||
*/
|
||||
_iterateKeys(transform = (key) => key) {
|
||||
if (typeof this.keys === "undefined") {
|
||||
this.keys = [];
|
||||
if ((0, commands_1.exists)(this.name, { caseInsensitive: true })) {
|
||||
// @ts-expect-error
|
||||
const keyIndexes = (0, commands_1.getKeyIndexes)(this.name, this.args, {
|
||||
nameCaseInsensitive: true,
|
||||
});
|
||||
for (const index of keyIndexes) {
|
||||
this.args[index] = transform(this.args[index]);
|
||||
this.keys.push(this.args[index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return this.keys;
|
||||
}
|
||||
/**
|
||||
* Convert the value from buffer to the target encoding.
|
||||
*/
|
||||
_convertValue(resolve) {
|
||||
return (value) => {
|
||||
try {
|
||||
this._clearTimers();
|
||||
resolve(this.transformReply(value));
|
||||
this.isResolved = true;
|
||||
this.isSettled = true;
|
||||
}
|
||||
catch (err) {
|
||||
this.reject(err);
|
||||
}
|
||||
return this.promise;
|
||||
};
|
||||
}
|
||||
}
|
||||
Command.FLAGS = {
|
||||
VALID_IN_SUBSCRIBER_MODE: [
|
||||
"subscribe",
|
||||
"psubscribe",
|
||||
"unsubscribe",
|
||||
"punsubscribe",
|
||||
"ssubscribe",
|
||||
"sunsubscribe",
|
||||
"ping",
|
||||
"quit",
|
||||
],
|
||||
VALID_IN_MONITOR_MODE: ["monitor", "auth"],
|
||||
ENTER_SUBSCRIBER_MODE: ["subscribe", "psubscribe", "ssubscribe"],
|
||||
EXIT_SUBSCRIBER_MODE: ["unsubscribe", "punsubscribe", "sunsubscribe"],
|
||||
WILL_DISCONNECT: ["quit"],
|
||||
HANDSHAKE_COMMANDS: [
|
||||
"auth",
|
||||
"hello",
|
||||
"select",
|
||||
"client",
|
||||
"readonly",
|
||||
"info",
|
||||
],
|
||||
IGNORE_RECONNECT_ON_ERROR: ["client"],
|
||||
BLOCKING_COMMANDS: [
|
||||
"blpop",
|
||||
"brpop",
|
||||
"brpoplpush",
|
||||
"blmove",
|
||||
"blmovem",
|
||||
"bzpopmin",
|
||||
"bzpopmax",
|
||||
"bzmpop",
|
||||
"blmpop",
|
||||
"xread",
|
||||
"xreadgroup",
|
||||
],
|
||||
LAST_ARG_TIMEOUT_COMMANDS: [
|
||||
"blpop",
|
||||
"brpop",
|
||||
"brpoplpush",
|
||||
"blmove",
|
||||
"bzpopmin",
|
||||
"bzpopmax",
|
||||
],
|
||||
FIRST_ARG_TIMEOUT_COMMANDS: ["bzmpop", "blmpop"],
|
||||
BLOCK_OPTION_COMMANDS: ["xread", "xreadgroup"],
|
||||
};
|
||||
Command._transformer = {
|
||||
argument: {},
|
||||
reply: {},
|
||||
};
|
||||
exports.default = Command;
|
||||
const msetArgumentTransformer = function (args) {
|
||||
if (args.length === 1) {
|
||||
if (args[0] instanceof Map) {
|
||||
return (0, utils_1.convertMapToArray)(args[0]);
|
||||
}
|
||||
if (typeof args[0] === "object" && args[0] !== null) {
|
||||
return (0, utils_1.convertObjectToArray)(args[0]);
|
||||
}
|
||||
}
|
||||
return args;
|
||||
};
|
||||
const hsetArgumentTransformer = function (args) {
|
||||
if (args.length === 2) {
|
||||
if (args[1] instanceof Map) {
|
||||
return [args[0]].concat((0, utils_1.convertMapToArray)(args[1]));
|
||||
}
|
||||
if (typeof args[1] === "object" && args[1] !== null) {
|
||||
return [args[0]].concat((0, utils_1.convertObjectToArray)(args[1]));
|
||||
}
|
||||
}
|
||||
return args;
|
||||
};
|
||||
Command.setArgumentTransformer("mset", msetArgumentTransformer);
|
||||
Command.setArgumentTransformer("msetnx", msetArgumentTransformer);
|
||||
Command.setArgumentTransformer("hset", hsetArgumentTransformer);
|
||||
Command.setArgumentTransformer("hmset", hsetArgumentTransformer);
|
||||
Command.setReplyTransformer("hgetall", function (result) {
|
||||
if (Array.isArray(result)) {
|
||||
const obj = {};
|
||||
for (let i = 0; i < result.length; i += 2) {
|
||||
const key = result[i];
|
||||
const value = result[i + 1];
|
||||
if (key in obj) {
|
||||
// can only be truthy if the property is special somehow, like '__proto__' or 'constructor'
|
||||
// https://github.com/luin/ioredis/issues/1267
|
||||
Object.defineProperty(obj, key, {
|
||||
value,
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
});
|
||||
}
|
||||
else {
|
||||
obj[key] = value;
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
return result;
|
||||
});
|
||||
Command.setReplyTransformer("vsim", replyTransformers_1.transformVsimReply);
|
||||
Command.setReplyTransformer("hrandfield", replyTransformers_1.transformPairReply);
|
||||
Command.setReplyTransformer("xread", replyTransformers_1.transformStreamReadReply);
|
||||
Command.setReplyTransformer("xreadgroup", replyTransformers_1.transformStreamReadReply);
|
||||
for (const command of replyTransformers_1.sortedSetWithScorePairCommands) {
|
||||
Command.setReplyTransformer(command, replyTransformers_1.transformPairReply);
|
||||
}
|
||||
class MixedBuffers {
|
||||
constructor() {
|
||||
this.length = 0;
|
||||
this.items = [];
|
||||
}
|
||||
push(x) {
|
||||
this.length += Buffer.byteLength(x);
|
||||
this.items.push(x);
|
||||
}
|
||||
toBuffer() {
|
||||
const result = Buffer.allocUnsafe(this.length);
|
||||
let offset = 0;
|
||||
for (const item of this.items) {
|
||||
const length = Buffer.byteLength(item);
|
||||
Buffer.isBuffer(item)
|
||||
? item.copy(result, offset)
|
||||
: result.write(item, offset, length);
|
||||
offset += length;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
44
node_modules/ioredis/built/DataHandler.d.ts
generated
vendored
Normal file
44
node_modules/ioredis/built/DataHandler.d.ts
generated
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
/// <reference types="node" />
|
||||
import { NetStream, CommandItem, ProtocolVersion, ReplyMappingMode } from "./types";
|
||||
import Deque = require("denque");
|
||||
import { EventEmitter } from "events";
|
||||
import SubscriptionSet from "./SubscriptionSet";
|
||||
export interface Condition {
|
||||
select: number;
|
||||
auth?: string | [string, string];
|
||||
subscriber: false | SubscriptionSet;
|
||||
protocol: ProtocolVersion;
|
||||
replyMapping: ReplyMappingMode;
|
||||
handshake: boolean;
|
||||
}
|
||||
export type FlushQueueOptions = {
|
||||
offlineQueue?: boolean;
|
||||
commandQueue?: boolean;
|
||||
};
|
||||
export interface DataHandledable extends EventEmitter {
|
||||
stream: NetStream;
|
||||
status: string;
|
||||
condition: Condition | null;
|
||||
commandQueue: Deque<CommandItem>;
|
||||
disconnect(reconnect: boolean): void;
|
||||
recoverFromFatalError(commandError: Error, err: Error, options: FlushQueueOptions): void;
|
||||
handleReconnection(err: Error, item: CommandItem): void;
|
||||
}
|
||||
interface ParserOptions {
|
||||
stringNumbers: boolean;
|
||||
replyMapping: ReplyMappingMode;
|
||||
}
|
||||
export default class DataHandler {
|
||||
private redis;
|
||||
constructor(redis: DataHandledable, parserOptions: ParserOptions);
|
||||
private dispatch;
|
||||
private returnFatalError;
|
||||
private returnError;
|
||||
private returnReply;
|
||||
private returnPush;
|
||||
private handleUnsolicitedUnsubscribe;
|
||||
private handleSubscriberReply;
|
||||
private handleMonitorReply;
|
||||
private shiftCommand;
|
||||
}
|
||||
export {};
|
||||
373
node_modules/ioredis/built/DataHandler.js
generated
vendored
Normal file
373
node_modules/ioredis/built/DataHandler.js
generated
vendored
Normal file
@@ -0,0 +1,373 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const Command_1 = require("./Command");
|
||||
const utils_1 = require("./utils");
|
||||
const SubscriptionSet_1 = require("./SubscriptionSet");
|
||||
const decoder_1 = require("./resp/decoder");
|
||||
const debug = (0, utils_1.Debug)("dataHandler");
|
||||
class DataHandler {
|
||||
constructor(redis, parserOptions) {
|
||||
this.redis = redis;
|
||||
// Parser options can't change over the lifetime of a connection, so the
|
||||
// mapping is resolved once instead of per reply.
|
||||
const typeMapping = getParserTypeMapping(parserOptions);
|
||||
const decoder = new decoder_1.Decoder({
|
||||
getTypeMapping: () => typeMapping,
|
||||
onReply: (reply) => {
|
||||
this.dispatch(() => this.returnReply(reply));
|
||||
},
|
||||
onErrorReply: (err) => {
|
||||
this.dispatch(() => this.returnError(err));
|
||||
},
|
||||
onPush: (reply) => {
|
||||
this.dispatch(() => this.returnPush(reply));
|
||||
},
|
||||
});
|
||||
// prependListener ensures the parser receives and processes data before socket timeout checks are performed
|
||||
redis.stream.prependListener("data", (data) => {
|
||||
try {
|
||||
decoder.write(data);
|
||||
}
|
||||
catch (err) {
|
||||
this.returnFatalError(err);
|
||||
}
|
||||
});
|
||||
// prependListener() doesn't enable flowing mode automatically - we need to resume the stream manually
|
||||
redis.stream.resume();
|
||||
}
|
||||
// Rethrows user listener/transformer exceptions asynchronously so they
|
||||
// surface as uncaught exceptions (as with redis-parser in v5) instead of
|
||||
// unwinding the decoder mid-parse and tearing down the connection as a
|
||||
// fatal protocol error.
|
||||
dispatch(fn) {
|
||||
try {
|
||||
fn();
|
||||
}
|
||||
catch (err) {
|
||||
process.nextTick(() => {
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
}
|
||||
returnFatalError(err) {
|
||||
err.message += ". Please report this.";
|
||||
this.redis.recoverFromFatalError(err, err, { offlineQueue: false });
|
||||
}
|
||||
returnError(err) {
|
||||
const item = this.shiftCommand(err);
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
err.command = {
|
||||
name: item.command.name,
|
||||
args: item.command.args,
|
||||
};
|
||||
// A MOVED reply to SSUBSCRIBE means the shard channel's slot migrated.
|
||||
// Signal the subscriber layer to refresh its topology, but still settle
|
||||
// the command through normal error handling so the caller's promise
|
||||
// rejects instead of staying pending forever.
|
||||
const isMovedSsubscribe = item.command.name === "ssubscribe" && err.message.startsWith("MOVED ");
|
||||
if (isMovedSsubscribe) {
|
||||
this.redis.emit("moved");
|
||||
}
|
||||
this.redis.handleReconnection(err, item);
|
||||
}
|
||||
returnReply(reply) {
|
||||
if (this.handleMonitorReply(reply)) {
|
||||
return;
|
||||
}
|
||||
// Under RESP3, pub/sub traffic arrives exclusively as push frames
|
||||
// (returnPush), so a normal reply on a subscribed connection is always a
|
||||
// command response. Routing it by content would misinterpret replies that
|
||||
// merely look like pub/sub messages (e.g. an LRANGE result starting with
|
||||
// "message") and desync the command queue.
|
||||
if (this.redis.condition.protocol !== 3 && this.handleSubscriberReply(reply)) {
|
||||
return;
|
||||
}
|
||||
const item = this.shiftCommand(reply);
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
if (Command_1.default.checkFlag("ENTER_SUBSCRIBER_MODE", item.command.name)) {
|
||||
this.redis.condition.subscriber = new SubscriptionSet_1.default();
|
||||
this.redis.condition.subscriber.add(item.command.name, reply[1].toString());
|
||||
if (!fillSubCommand(item.command, reply[2])) {
|
||||
this.redis.commandQueue.unshift(item);
|
||||
}
|
||||
}
|
||||
else if (Command_1.default.checkFlag("EXIT_SUBSCRIBER_MODE", item.command.name)) {
|
||||
if (!fillUnsubCommand(item.command, reply[2])) {
|
||||
this.redis.commandQueue.unshift(item);
|
||||
}
|
||||
}
|
||||
else {
|
||||
item.command.resolve(reply);
|
||||
}
|
||||
}
|
||||
returnPush(reply) {
|
||||
if (!Array.isArray(reply) || reply.length === 0) {
|
||||
return;
|
||||
}
|
||||
const replyType = reply[0].toString();
|
||||
debug('receive push "%s"', replyType);
|
||||
switch (replyType) {
|
||||
case "message":
|
||||
case "pmessage":
|
||||
case "smessage":
|
||||
this.handleSubscriberReply(reply);
|
||||
break;
|
||||
case "ssubscribe":
|
||||
case "subscribe":
|
||||
case "psubscribe": {
|
||||
if (!this.redis.condition.subscriber) {
|
||||
this.redis.condition.subscriber = new SubscriptionSet_1.default();
|
||||
}
|
||||
const channel = reply[1].toString();
|
||||
this.redis.condition.subscriber.add(replyType, channel);
|
||||
const item = this.shiftCommand(reply);
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
if (!fillSubCommand(item.command, reply[2])) {
|
||||
this.redis.commandQueue.unshift(item);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "sunsubscribe":
|
||||
case "unsubscribe":
|
||||
case "punsubscribe": {
|
||||
if (this.redis.condition.subscriber) {
|
||||
const channel = reply[1] ? reply[1].toString() : null;
|
||||
if (channel) {
|
||||
this.redis.condition.subscriber.del(replyType, channel);
|
||||
}
|
||||
}
|
||||
const count = reply[2];
|
||||
if (Number(count) === 0) {
|
||||
this.redis.condition.subscriber = false;
|
||||
}
|
||||
if (this.handleUnsolicitedUnsubscribe(replyType)) {
|
||||
return;
|
||||
}
|
||||
const item = this.shiftCommand(reply);
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
if (!fillUnsubCommand(item.command, count)) {
|
||||
this.redis.commandQueue.unshift(item);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Cluster sends unsolicited `sunsubscribe` pushes on slot migration; those
|
||||
// answer no queued command, so shifting the queue head would resolve an
|
||||
// unrelated in-flight command. Detected via a non-matching queue head, and
|
||||
// `sunsubscribe` reuses the ssubscribe-MOVED recovery path.
|
||||
handleUnsolicitedUnsubscribe(replyType) {
|
||||
const head = this.redis.commandQueue.peekFront();
|
||||
// Command names keep the user's casing (e.g. `call("UNSUBSCRIBE")`), while
|
||||
// reply types from the server are lowercase — compare case-insensitively
|
||||
// or an uppercase unsubscribe would be treated as unsolicited and its
|
||||
// reply dropped, desyncing the queue.
|
||||
if (head && head.command.name.toLowerCase() === replyType) {
|
||||
return false;
|
||||
}
|
||||
if (replyType === "sunsubscribe") {
|
||||
this.redis.emit("moved");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
handleSubscriberReply(reply) {
|
||||
if (!this.redis.condition.subscriber) {
|
||||
return false;
|
||||
}
|
||||
const replyType = Array.isArray(reply) ? reply[0].toString() : null;
|
||||
debug('receive reply "%s" in subscriber mode', replyType);
|
||||
switch (replyType) {
|
||||
case "message":
|
||||
if (this.redis.listeners("message").length > 0) {
|
||||
// Check if there're listeners to avoid unnecessary `toString()`.
|
||||
this.redis.emit("message", reply[1].toString(), reply[2] ? reply[2].toString() : "");
|
||||
}
|
||||
this.redis.emit("messageBuffer", reply[1], reply[2]);
|
||||
break;
|
||||
case "pmessage": {
|
||||
const pattern = reply[1].toString();
|
||||
if (this.redis.listeners("pmessage").length > 0) {
|
||||
this.redis.emit("pmessage", pattern, reply[2].toString(), reply[3].toString());
|
||||
}
|
||||
this.redis.emit("pmessageBuffer", pattern, reply[2], reply[3]);
|
||||
break;
|
||||
}
|
||||
case "smessage": {
|
||||
if (this.redis.listeners("smessage").length > 0) {
|
||||
this.redis.emit("smessage", reply[1].toString(), reply[2] ? reply[2].toString() : "");
|
||||
}
|
||||
this.redis.emit("smessageBuffer", reply[1], reply[2]);
|
||||
break;
|
||||
}
|
||||
case "ssubscribe":
|
||||
case "subscribe":
|
||||
case "psubscribe": {
|
||||
const channel = reply[1].toString();
|
||||
this.redis.condition.subscriber.add(replyType, channel);
|
||||
const item = this.shiftCommand(reply);
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
if (!fillSubCommand(item.command, reply[2])) {
|
||||
this.redis.commandQueue.unshift(item);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "sunsubscribe":
|
||||
case "unsubscribe":
|
||||
case "punsubscribe": {
|
||||
const channel = reply[1] ? reply[1].toString() : null;
|
||||
if (channel) {
|
||||
this.redis.condition.subscriber.del(replyType, channel);
|
||||
}
|
||||
const count = reply[2];
|
||||
if (Number(count) === 0) {
|
||||
this.redis.condition.subscriber = false;
|
||||
}
|
||||
if (this.handleUnsolicitedUnsubscribe(replyType)) {
|
||||
break;
|
||||
}
|
||||
const item = this.shiftCommand(reply);
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
if (!fillUnsubCommand(item.command, count)) {
|
||||
this.redis.commandQueue.unshift(item);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
const item = this.shiftCommand(reply);
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
item.command.resolve(reply);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
handleMonitorReply(reply) {
|
||||
if (this.redis.status !== "monitoring") {
|
||||
return false;
|
||||
}
|
||||
const replyStr = reply.toString();
|
||||
if (replyStr === "OK") {
|
||||
// Valid commands in the monitoring mode are AUTH and MONITOR,
|
||||
// both of which always reply with 'OK'.
|
||||
// So if we got an 'OK', we can make certain that
|
||||
// the reply is made to AUTH & MONITOR.
|
||||
return false;
|
||||
}
|
||||
// Since commands sent in the monitoring mode will trigger an exception,
|
||||
// any replies we received in the monitoring mode should consider to be
|
||||
// realtime monitor data instead of result of commands.
|
||||
const len = replyStr.indexOf(" ");
|
||||
const timestamp = replyStr.slice(0, len);
|
||||
const argIndex = replyStr.indexOf('"');
|
||||
const args = replyStr
|
||||
.slice(argIndex + 1, -1)
|
||||
.split('" "')
|
||||
.map((elem) => elem.replace(/\\"/g, '"'));
|
||||
const dbAndSource = replyStr.slice(len + 2, argIndex - 2).split(" ");
|
||||
this.redis.emit("monitor", timestamp, args, dbAndSource[1], dbAndSource[0]);
|
||||
return true;
|
||||
}
|
||||
shiftCommand(reply) {
|
||||
const item = this.redis.commandQueue.shift();
|
||||
if (!item) {
|
||||
const message = "Command queue state error. If you can reproduce this, please report it.";
|
||||
const error = new Error(message +
|
||||
(reply instanceof Error
|
||||
? ` Last error: ${reply.message}`
|
||||
: ` Last reply: ${reply.toString()}`));
|
||||
this.redis.emit("error", error);
|
||||
return null;
|
||||
}
|
||||
return item;
|
||||
}
|
||||
}
|
||||
exports.default = DataHandler;
|
||||
// All string RESP types (simple, blob, verbatim) decode to Buffer rather than
|
||||
// utf8 strings. This keeps the parser encoding-agnostic: ioredis decides
|
||||
// utf8-vs-Buffer per command afterwards via `replyEncoding`. Decoding to utf8
|
||||
// here would be irreversible and corrupt binary-safe values (e.g. DUMP output,
|
||||
// serialized blobs), whereas bytes -> string stays lossless and deferred. This
|
||||
// mirrors the old `redis-parser` running with `returnBuffers: true`.
|
||||
// RESP2-compatible shapes: RESP3-only container and numeric types are
|
||||
// flattened so replies are identical across both protocols. Strings stay
|
||||
// buffers; utf8 conversion is decided per command by `replyEncoding`.
|
||||
const legacyTypeMapping = {
|
||||
[decoder_1.RESP_TYPES.SIMPLE_STRING]: Buffer,
|
||||
[decoder_1.RESP_TYPES.BLOB_STRING]: Buffer,
|
||||
[decoder_1.RESP_TYPES.VERBATIM_STRING]: Buffer,
|
||||
[decoder_1.RESP_TYPES.BIG_NUMBER]: String,
|
||||
// RESP2 represented doubles as bulk strings and booleans as `1`/`0`
|
||||
// integers. Decode doubles to Buffer (like blob strings, so `replyEncoding`
|
||||
// still chooses string-vs-buffer per command) and booleans to numbers, so
|
||||
// RESP3 replies match the classic RESP2 shape.
|
||||
[decoder_1.RESP_TYPES.DOUBLE]: Buffer,
|
||||
[decoder_1.RESP_TYPES.BOOLEAN]: Number,
|
||||
[decoder_1.RESP_TYPES.MAP]: Array,
|
||||
[decoder_1.RESP_TYPES.SET]: Array,
|
||||
};
|
||||
// Leaving MAP and DOUBLE unmapped makes the decoder produce plain objects
|
||||
// (with string keys) and numbers respectively.
|
||||
const resp3TypeMapping = {
|
||||
[decoder_1.RESP_TYPES.SIMPLE_STRING]: Buffer,
|
||||
[decoder_1.RESP_TYPES.BLOB_STRING]: Buffer,
|
||||
[decoder_1.RESP_TYPES.VERBATIM_STRING]: Buffer,
|
||||
[decoder_1.RESP_TYPES.BIG_NUMBER]: String,
|
||||
[decoder_1.RESP_TYPES.SET]: Array,
|
||||
};
|
||||
function getParserTypeMapping(parserOptions) {
|
||||
const base = parserOptions.replyMapping === "resp3"
|
||||
? resp3TypeMapping
|
||||
: legacyTypeMapping;
|
||||
// `stringNumbers` means "all numerics as strings", so it wins over the
|
||||
// preset for DOUBLE as well.
|
||||
return parserOptions.stringNumbers
|
||||
? { ...base, [decoder_1.RESP_TYPES.NUMBER]: String, [decoder_1.RESP_TYPES.DOUBLE]: String }
|
||||
: base;
|
||||
}
|
||||
const remainingRepliesMap = new WeakMap();
|
||||
function fillSubCommand(command, count) {
|
||||
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) {
|
||||
let remainingReplies = remainingRepliesMap.has(command)
|
||||
? remainingRepliesMap.get(command)
|
||||
: command.args.length;
|
||||
if (remainingReplies === 0) {
|
||||
if (Number(count) === 0) {
|
||||
remainingRepliesMap.delete(command);
|
||||
command.resolve(count);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
remainingReplies -= 1;
|
||||
if (remainingReplies <= 0) {
|
||||
command.resolve(count);
|
||||
return true;
|
||||
}
|
||||
remainingRepliesMap.set(command, remainingReplies);
|
||||
return false;
|
||||
}
|
||||
31
node_modules/ioredis/built/Pipeline.d.ts
generated
vendored
Normal file
31
node_modules/ioredis/built/Pipeline.d.ts
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
import Redis from "./Redis";
|
||||
import Cluster from "./cluster";
|
||||
import Command from "./Command";
|
||||
import Commander from "./utils/Commander";
|
||||
declare class Pipeline extends Commander<{
|
||||
type: "pipeline";
|
||||
}> {
|
||||
redis: Redis | Cluster;
|
||||
isCluster: boolean;
|
||||
isPipeline: boolean;
|
||||
leftRedirections: {
|
||||
value?: number;
|
||||
};
|
||||
promise: Promise<unknown>;
|
||||
resolve: (result: unknown) => void;
|
||||
reject: (error: Error) => void;
|
||||
private replyPending;
|
||||
private _queue;
|
||||
private _result;
|
||||
private _transactions;
|
||||
private _shaToScript;
|
||||
private preferKey;
|
||||
constructor(redis: Redis | Cluster);
|
||||
fillResult(value: unknown[], position: number): void;
|
||||
sendCommand(command: Command): unknown;
|
||||
addBatch(commands: any): this;
|
||||
}
|
||||
export default Pipeline;
|
||||
interface Pipeline {
|
||||
length: number;
|
||||
}
|
||||
371
node_modules/ioredis/built/Pipeline.js
generated
vendored
Normal file
371
node_modules/ioredis/built/Pipeline.js
generated
vendored
Normal file
@@ -0,0 +1,371 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const calculateSlot = require("cluster-key-slot");
|
||||
const commands_1 = require("@ioredis/commands");
|
||||
const standard_as_callback_1 = require("standard-as-callback");
|
||||
const util_1 = require("util");
|
||||
const Command_1 = require("./Command");
|
||||
const buffer_1 = require("buffer");
|
||||
const utils_1 = require("./utils");
|
||||
const Commander_1 = require("./utils/Commander");
|
||||
const HimportCoordinator_1 = require("./himport/HimportCoordinator");
|
||||
/*
|
||||
This function derives from the cluster-key-slot implementation.
|
||||
Instead of checking that all keys have the same slot, it checks that all slots are served by the same set of nodes.
|
||||
If this is satisfied, it returns the first key's slot.
|
||||
*/
|
||||
function generateMultiWithNodes(redis, keys) {
|
||||
const slot = calculateSlot(keys[0]);
|
||||
const target = redis._groupsBySlot[slot];
|
||||
for (let i = 1; i < keys.length; i++) {
|
||||
if (redis._groupsBySlot[calculateSlot(keys[i])] !== target) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return slot;
|
||||
}
|
||||
class Pipeline extends Commander_1.default {
|
||||
constructor(redis) {
|
||||
super();
|
||||
this.redis = redis;
|
||||
this.isPipeline = true;
|
||||
this.replyPending = 0;
|
||||
this._queue = [];
|
||||
this._result = [];
|
||||
this._transactions = 0;
|
||||
this._shaToScript = {};
|
||||
this.isCluster =
|
||||
this.redis.constructor.name === "Cluster" || this.redis.isCluster;
|
||||
this.options = redis.options;
|
||||
Object.keys(redis.scriptsSet).forEach((name) => {
|
||||
const script = redis.scriptsSet[name];
|
||||
this._shaToScript[script.sha] = script;
|
||||
this[name] = redis[name];
|
||||
this[name + "Buffer"] = redis[name + "Buffer"];
|
||||
});
|
||||
redis.addedBuiltinSet.forEach((name) => {
|
||||
this[name] = redis[name];
|
||||
this[name + "Buffer"] = redis[name + "Buffer"];
|
||||
});
|
||||
this.promise = new Promise((resolve, reject) => {
|
||||
this.resolve = resolve;
|
||||
this.reject = reject;
|
||||
});
|
||||
const _this = this;
|
||||
Object.defineProperty(this, "length", {
|
||||
get: function () {
|
||||
return _this._queue.length;
|
||||
},
|
||||
});
|
||||
}
|
||||
fillResult(value, position) {
|
||||
if (this._queue[position].name === "exec" && Array.isArray(value[1])) {
|
||||
const execLength = value[1].length;
|
||||
for (let i = 0; i < execLength; i++) {
|
||||
if (value[1][i] instanceof Error) {
|
||||
continue;
|
||||
}
|
||||
const cmd = this._queue[position - (execLength - i)];
|
||||
try {
|
||||
value[1][i] = cmd.transformReply(value[1][i]);
|
||||
}
|
||||
catch (err) {
|
||||
value[1][i] = err;
|
||||
}
|
||||
}
|
||||
}
|
||||
this._result[position] = value;
|
||||
if (--this.replyPending) {
|
||||
return;
|
||||
}
|
||||
if (this.isCluster) {
|
||||
let retriable = true;
|
||||
let commonError;
|
||||
for (let i = 0; i < this._result.length; ++i) {
|
||||
const error = this._result[i][0];
|
||||
const command = this._queue[i];
|
||||
if (error) {
|
||||
if (command.name === "exec" &&
|
||||
error.message ===
|
||||
"EXECABORT Transaction discarded because of previous errors.") {
|
||||
continue;
|
||||
}
|
||||
if (!commonError) {
|
||||
commonError = {
|
||||
name: error.name,
|
||||
message: error.message,
|
||||
};
|
||||
}
|
||||
else if (commonError.name !== error.name ||
|
||||
commonError.message !== error.message) {
|
||||
retriable = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (!command.inTransaction) {
|
||||
const isReadOnly = (0, commands_1.exists)(command.name, { caseInsensitive: true }) &&
|
||||
(0, commands_1.hasFlag)(command.name, "readonly", { nameCaseInsensitive: true });
|
||||
if (!isReadOnly) {
|
||||
retriable = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (commonError && retriable) {
|
||||
const _this = this;
|
||||
const errv = commonError.message.split(" ");
|
||||
const queue = this._queue;
|
||||
let inTransaction = false;
|
||||
this._queue = [];
|
||||
for (let i = 0; i < queue.length; ++i) {
|
||||
if (errv[0] === "ASK" &&
|
||||
!inTransaction &&
|
||||
queue[i].name !== "asking" &&
|
||||
(!queue[i - 1] || queue[i - 1].name !== "asking")) {
|
||||
const asking = new Command_1.default("asking");
|
||||
asking.ignore = true;
|
||||
this.sendCommand(asking);
|
||||
}
|
||||
queue[i].initPromise();
|
||||
this.sendCommand(queue[i]);
|
||||
inTransaction = queue[i].inTransaction;
|
||||
}
|
||||
let matched = true;
|
||||
if (typeof this.leftRedirections === "undefined") {
|
||||
this.leftRedirections = {};
|
||||
}
|
||||
const exec = function () {
|
||||
_this.exec();
|
||||
};
|
||||
const cluster = this.redis;
|
||||
cluster.handleError(commonError, this.leftRedirections, {
|
||||
moved: function (slot, key) {
|
||||
_this.preferKey = key;
|
||||
if (cluster.slots[slot]) {
|
||||
if (cluster.slots[slot][0] !== key) {
|
||||
cluster.slots[slot] = [key];
|
||||
}
|
||||
}
|
||||
else {
|
||||
cluster.slots[slot] = [key];
|
||||
}
|
||||
cluster._groupsBySlot[slot] =
|
||||
cluster._groupsIds[cluster.slots[slot].join(";")];
|
||||
cluster.refreshSlotsCache();
|
||||
_this.exec();
|
||||
},
|
||||
ask: function (_slot, key) {
|
||||
_this.preferKey = key;
|
||||
_this.exec();
|
||||
},
|
||||
tryagain: exec,
|
||||
clusterDown: exec,
|
||||
connectionClosed: exec,
|
||||
maxRedirections: () => {
|
||||
matched = false;
|
||||
},
|
||||
defaults: () => {
|
||||
matched = false;
|
||||
},
|
||||
});
|
||||
if (matched) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
let ignoredCount = 0;
|
||||
for (let i = 0; i < this._queue.length - ignoredCount; ++i) {
|
||||
if (this._queue[i + ignoredCount].ignore) {
|
||||
ignoredCount += 1;
|
||||
}
|
||||
this._result[i] = this._result[i + ignoredCount];
|
||||
}
|
||||
this.resolve(this._result.slice(0, this._result.length - ignoredCount));
|
||||
}
|
||||
sendCommand(command) {
|
||||
if (this._transactions > 0) {
|
||||
command.inTransaction = true;
|
||||
}
|
||||
const position = this._queue.length;
|
||||
command.pipelineIndex = position;
|
||||
command.promise
|
||||
.then((result) => {
|
||||
this.fillResult([null, result], position);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.fillResult([error], position);
|
||||
});
|
||||
this._queue.push(command);
|
||||
return this;
|
||||
}
|
||||
addBatch(commands) {
|
||||
let command, commandName, args;
|
||||
for (let i = 0; i < commands.length; ++i) {
|
||||
command = commands[i];
|
||||
commandName = command[0];
|
||||
args = command.slice(1);
|
||||
this[commandName].apply(this, args);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
exports.default = Pipeline;
|
||||
// @ts-expect-error
|
||||
const multi = Pipeline.prototype.multi;
|
||||
// @ts-expect-error
|
||||
Pipeline.prototype.multi = function () {
|
||||
this._transactions += 1;
|
||||
return multi.apply(this, arguments);
|
||||
};
|
||||
// @ts-expect-error
|
||||
const execBuffer = Pipeline.prototype.execBuffer;
|
||||
// @ts-expect-error
|
||||
Pipeline.prototype.execBuffer = (0, util_1.deprecate)(function () {
|
||||
if (this._transactions > 0) {
|
||||
this._transactions -= 1;
|
||||
}
|
||||
return execBuffer.apply(this, arguments);
|
||||
}, "Pipeline#execBuffer: Use Pipeline#exec instead");
|
||||
// NOTE: To avoid an unhandled promise rejection, this will unconditionally always return this.promise,
|
||||
// which always has the rejection handled by standard-as-callback
|
||||
// adding the provided rejection callback.
|
||||
//
|
||||
// If a different promise instance were returned, that promise would cause its own unhandled promise rejection
|
||||
// errors, even if that promise unconditionally resolved to **the resolved value of** this.promise.
|
||||
Pipeline.prototype.exec = function (callback) {
|
||||
// Wait for the cluster to be connected, since we need nodes information before continuing
|
||||
if (this.isCluster && !this.redis.slots.length) {
|
||||
if (this.redis.status === "wait")
|
||||
this.redis.connect().catch(utils_1.noop);
|
||||
if (callback && !this.nodeifiedPromise) {
|
||||
this.nodeifiedPromise = true;
|
||||
(0, standard_as_callback_1.default)(this.promise, callback);
|
||||
}
|
||||
this.redis.delayUntilReady((err) => {
|
||||
if (err) {
|
||||
this.reject(err);
|
||||
return;
|
||||
}
|
||||
this.exec(callback);
|
||||
});
|
||||
return this.promise;
|
||||
}
|
||||
if (this._transactions > 0) {
|
||||
this._transactions -= 1;
|
||||
return execBuffer.apply(this, arguments);
|
||||
}
|
||||
if (!this.nodeifiedPromise) {
|
||||
this.nodeifiedPromise = true;
|
||||
(0, standard_as_callback_1.default)(this.promise, callback);
|
||||
}
|
||||
if (!this._queue.length) {
|
||||
this.resolve([]);
|
||||
}
|
||||
let pipelineSlot;
|
||||
if (this.isCluster) {
|
||||
// List of the first key for each command
|
||||
const sampleKeys = [];
|
||||
for (let i = 0; i < this._queue.length; i++) {
|
||||
const keys = this._queue[i].getKeys();
|
||||
if (keys.length) {
|
||||
sampleKeys.push(keys[0]);
|
||||
}
|
||||
// For each command, check that the keys belong to the same slot
|
||||
if (keys.length && calculateSlot.generateMulti(keys) < 0) {
|
||||
this.reject(new Error("All the keys in a pipeline command should belong to the same slot"));
|
||||
return this.promise;
|
||||
}
|
||||
}
|
||||
if (sampleKeys.length) {
|
||||
pipelineSlot = generateMultiWithNodes(this.redis, sampleKeys);
|
||||
if (pipelineSlot < 0) {
|
||||
this.reject(new Error("All keys in the pipeline should belong to the same slots allocation group"));
|
||||
return this.promise;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Send the pipeline to a random node
|
||||
pipelineSlot = (Math.random() * 16384) | 0;
|
||||
}
|
||||
}
|
||||
const _this = this;
|
||||
let node;
|
||||
const himportIntercepted = (0, HimportCoordinator_1.interceptHimportPipeline)({
|
||||
owner: _this.redis,
|
||||
commands: _this._queue,
|
||||
slot: pipelineSlot,
|
||||
preferredNodeKey: _this.preferKey,
|
||||
setDestination(connection) {
|
||||
node = {
|
||||
slot: pipelineSlot,
|
||||
redis: connection,
|
||||
};
|
||||
},
|
||||
resume: execPipeline,
|
||||
reject(error) {
|
||||
_this.reject(error);
|
||||
},
|
||||
});
|
||||
if (!himportIntercepted) {
|
||||
execPipeline();
|
||||
}
|
||||
return this.promise;
|
||||
function execPipeline() {
|
||||
let writePending = (_this.replyPending = _this._queue.length);
|
||||
if (_this.isCluster && !node) {
|
||||
node = {
|
||||
slot: pipelineSlot,
|
||||
redis: _this.redis.connectionPool.nodes.all[_this.preferKey],
|
||||
};
|
||||
}
|
||||
let data = "";
|
||||
let buffers;
|
||||
const stream = {
|
||||
isPipeline: true,
|
||||
destination: _this.isCluster ? node : { redis: _this.redis },
|
||||
write(writable) {
|
||||
if (typeof writable !== "string") {
|
||||
if (!buffers) {
|
||||
buffers = [];
|
||||
}
|
||||
if (data) {
|
||||
buffers.push(Buffer.from(data, "utf8"));
|
||||
data = "";
|
||||
}
|
||||
buffers.push(writable);
|
||||
}
|
||||
else {
|
||||
if (data.length + writable.length >= buffer_1.constants.MAX_STRING_LENGTH) {
|
||||
if (!buffers) {
|
||||
buffers = [];
|
||||
}
|
||||
if (data) {
|
||||
buffers.push(Buffer.from(data, "utf8"));
|
||||
data = "";
|
||||
}
|
||||
}
|
||||
data += writable;
|
||||
}
|
||||
if (!--writePending) {
|
||||
if (buffers) {
|
||||
if (data) {
|
||||
buffers.push(Buffer.from(data, "utf8"));
|
||||
}
|
||||
stream.destination.redis.stream.write(Buffer.concat(buffers));
|
||||
}
|
||||
else {
|
||||
stream.destination.redis.stream.write(data);
|
||||
}
|
||||
// Reset writePending for resending
|
||||
writePending = _this._queue.length;
|
||||
data = "";
|
||||
buffers = undefined;
|
||||
}
|
||||
},
|
||||
};
|
||||
for (let i = 0; i < _this._queue.length; ++i) {
|
||||
_this.redis.sendCommand(_this._queue[i], stream, node);
|
||||
}
|
||||
return _this.promise;
|
||||
}
|
||||
};
|
||||
258
node_modules/ioredis/built/Redis.d.ts
generated
vendored
Normal file
258
node_modules/ioredis/built/Redis.d.ts
generated
vendored
Normal file
@@ -0,0 +1,258 @@
|
||||
/// <reference types="node" />
|
||||
/// <reference types="node" />
|
||||
/// <reference types="node" />
|
||||
import { EventEmitter } from "events";
|
||||
import Cluster from "./cluster";
|
||||
import Command from "./Command";
|
||||
import { DataHandledable, FlushQueueOptions, Condition } from "./DataHandler";
|
||||
import { RedisOptions } from "./redis/RedisOptions";
|
||||
import ScanStream from "./ScanStream";
|
||||
import { Transaction } from "./transaction";
|
||||
import { Callback, CommandItem, NetStream, ReplyMappingFromOptions, ReplyMappingMode, ScanStreamOptions, WriteableStream } from "./types";
|
||||
import { type BatchOperationContext } from "./tracing";
|
||||
import Commander from "./utils/Commander";
|
||||
import { hasHimportCoordinator } from "./himport/HimportCoordinator";
|
||||
import Deque = require("denque");
|
||||
export type RedisStatus = "wait" | "reconnecting" | "connecting" | "connect" | "ready" | "close" | "end";
|
||||
/**
|
||||
* This is the major component of ioredis.
|
||||
* Use it to connect to a standalone Redis server or Sentinels.
|
||||
*
|
||||
* ```typescript
|
||||
* const redis = new Redis(); // Default port is 6379
|
||||
* async function main() {
|
||||
* redis.set("foo", "bar");
|
||||
* redis.get("foo", (err, result) => {
|
||||
* // `result` should be "bar"
|
||||
* console.log(err, result);
|
||||
* });
|
||||
* // Or use Promise
|
||||
* const result = await redis.get("foo");
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
declare class Redis<ReplyMapping extends ReplyMappingMode = "legacy"> extends Commander<{
|
||||
type: "default";
|
||||
mapping: ReplyMapping extends "resp3" ? "resp3" : "resp2";
|
||||
}> implements DataHandledable {
|
||||
static Cluster: typeof Cluster;
|
||||
static Command: typeof Command;
|
||||
/**
|
||||
* Default options
|
||||
*/
|
||||
private static defaultOptions;
|
||||
/**
|
||||
* Create a Redis instance.
|
||||
* This is the same as `new Redis()` but is included for compatibility with node-redis.
|
||||
*/
|
||||
static createClient(...args: ConstructorParameters<typeof Redis>): Redis;
|
||||
options: RedisOptions;
|
||||
status: RedisStatus;
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
stream: NetStream;
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
isCluster: boolean;
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
condition: Condition | null;
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
commandQueue: Deque<CommandItem>;
|
||||
private connector;
|
||||
private reconnectTimeout;
|
||||
private offlineQueue;
|
||||
private connectionEpoch;
|
||||
private retryAttempts;
|
||||
private manuallyClosing;
|
||||
private socketTimeoutTimer;
|
||||
private [hasHimportCoordinator];
|
||||
private _autoPipelines;
|
||||
private _runningAutoPipelines;
|
||||
constructor(port: number, host: string, options: RedisOptions & {
|
||||
replyMapping?: ReplyMapping;
|
||||
});
|
||||
constructor(path: string, options: RedisOptions & {
|
||||
replyMapping?: ReplyMapping;
|
||||
});
|
||||
constructor(port: number, options: RedisOptions & {
|
||||
replyMapping?: ReplyMapping;
|
||||
});
|
||||
constructor(port: number, host: string);
|
||||
constructor(options: RedisOptions & {
|
||||
replyMapping?: ReplyMapping;
|
||||
});
|
||||
constructor(port: number);
|
||||
constructor(path: string);
|
||||
constructor();
|
||||
get autoPipelineQueueSize(): number;
|
||||
/**
|
||||
* Create a connection to Redis.
|
||||
* This method will be invoked automatically when creating a new Redis instance
|
||||
* unless `lazyConnect: true` is passed.
|
||||
*
|
||||
* When calling this method manually, a Promise is returned, which will
|
||||
* be resolved when the connection status is ready. The promise can reject
|
||||
* if the connection fails, times out, or if Redis is already connecting/connected.
|
||||
*/
|
||||
connect(callback?: Callback<void>): Promise<void>;
|
||||
private _connect;
|
||||
/**
|
||||
* Disconnect from Redis.
|
||||
*
|
||||
* This method closes the connection immediately,
|
||||
* and may lose some pending replies that haven't written to client.
|
||||
* If you want to wait for the pending replies, use Redis#quit instead.
|
||||
*/
|
||||
disconnect(reconnect?: boolean): void;
|
||||
/**
|
||||
* Disconnect from Redis.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
end(): void;
|
||||
/**
|
||||
* Create a new instance with the same options as the current one.
|
||||
*
|
||||
* @example
|
||||
* ```js
|
||||
* var redis = new Redis(6380);
|
||||
* var anotherRedis = redis.duplicate();
|
||||
* ```
|
||||
*/
|
||||
duplicate<Override extends Partial<RedisOptions> | undefined = undefined>(override?: Override): Redis<ReplyMappingFromOptions<ReplyMapping, Override>>;
|
||||
/**
|
||||
* Mode of the connection.
|
||||
*
|
||||
* One of `"normal"`, `"subscriber"`, or `"monitor"`. When the connection is
|
||||
* not in `"normal"` mode, certain commands are not allowed.
|
||||
*/
|
||||
get mode(): "normal" | "subscriber" | "monitor";
|
||||
/**
|
||||
* Listen for all requests received by the server in real time.
|
||||
*
|
||||
* This command will create a new connection to Redis and send a
|
||||
* MONITOR command via the new connection in order to avoid disturbing
|
||||
* the current connection.
|
||||
*
|
||||
* @param callback The callback function. If omit, a promise will be returned.
|
||||
* @example
|
||||
* ```js
|
||||
* var redis = new Redis();
|
||||
* redis.monitor(function (err, monitor) {
|
||||
* // Entering monitoring mode.
|
||||
* monitor.on('monitor', function (time, args, source, database) {
|
||||
* console.log(time + ": " + util.inspect(args));
|
||||
* });
|
||||
* });
|
||||
*
|
||||
* // supports promise as well as other commands
|
||||
* redis.monitor().then(function (monitor) {
|
||||
* monitor.on('monitor', function (time, args, source, database) {
|
||||
* console.log(time + ": " + util.inspect(args));
|
||||
* });
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
monitor(callback?: Callback<Redis>): Promise<Redis>;
|
||||
/**
|
||||
* Send a command to Redis
|
||||
*
|
||||
* This method is used internally and in most cases you should not
|
||||
* use it directly. If you need to send a command that is not supported
|
||||
* by the library, you can use the `call` method:
|
||||
*
|
||||
* ```js
|
||||
* const redis = new Redis();
|
||||
*
|
||||
* redis.call('set', 'foo', 'bar');
|
||||
* // or
|
||||
* redis.call(['set', 'foo', 'bar']);
|
||||
* ```
|
||||
*
|
||||
* @ignore
|
||||
*/
|
||||
sendCommand(command: Command, stream?: WriteableStream): unknown;
|
||||
private getBlockingTimeoutInMs;
|
||||
private getConfiguredBlockingTimeout;
|
||||
private setSocketTimeout;
|
||||
scanStream(options?: ScanStreamOptions): ScanStream;
|
||||
scanBufferStream(options?: ScanStreamOptions): ScanStream;
|
||||
sscanStream(key: string, options?: ScanStreamOptions): ScanStream;
|
||||
sscanBufferStream(key: string, options?: ScanStreamOptions): ScanStream;
|
||||
hscanStream(key: string, options?: ScanStreamOptions): ScanStream;
|
||||
hscanBufferStream(key: string, options?: ScanStreamOptions): ScanStream;
|
||||
zscanStream(key: string, options?: ScanStreamOptions): ScanStream;
|
||||
zscanBufferStream(key: string, options?: ScanStreamOptions): ScanStream;
|
||||
/**
|
||||
* Emit only when there's at least one listener.
|
||||
*
|
||||
* @ignore
|
||||
*/
|
||||
silentEmit(eventName: string, arg?: unknown): boolean;
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
recoverFromFatalError(_commandError: Error, err: Error, options: FlushQueueOptions): void;
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
handleReconnection(err: Error, item: CommandItem): void;
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
_getServerAddress(): {
|
||||
address: string;
|
||||
port: number | undefined;
|
||||
};
|
||||
private _buildCommandContext;
|
||||
_buildBatchContext(batchSize: number): BatchOperationContext;
|
||||
/**
|
||||
* Get description of the connection. Used for debugging.
|
||||
*/
|
||||
private _getDescription;
|
||||
private resetCommandQueue;
|
||||
private resetOfflineQueue;
|
||||
private parseOptions;
|
||||
/**
|
||||
* Change instance's status
|
||||
*/
|
||||
private setStatus;
|
||||
private createScanStream;
|
||||
/**
|
||||
* Flush offline queue and command queue with error.
|
||||
*
|
||||
* @param error The error object to send to the commands
|
||||
* @param options options
|
||||
*/
|
||||
private flushQueue;
|
||||
/**
|
||||
* Check whether Redis has finished loading the persistent data and is able to
|
||||
* process commands.
|
||||
*/
|
||||
private _readyCheck;
|
||||
}
|
||||
interface Redis<ReplyMapping extends "legacy" | "resp3" = "legacy"> extends EventEmitter {
|
||||
on(event: "message", cb: (channel: string, message: string) => void): this;
|
||||
once(event: "message", cb: (channel: string, message: string) => void): this;
|
||||
on(event: "messageBuffer", cb: (channel: Buffer, message: Buffer) => void): this;
|
||||
once(event: "messageBuffer", cb: (channel: Buffer, message: Buffer) => void): this;
|
||||
on(event: "pmessage", cb: (pattern: string, channel: string, message: string) => void): this;
|
||||
once(event: "pmessage", cb: (pattern: string, channel: string, message: string) => void): this;
|
||||
on(event: "pmessageBuffer", cb: (pattern: string, channel: Buffer, message: Buffer) => void): this;
|
||||
once(event: "pmessageBuffer", cb: (pattern: string, channel: Buffer, message: Buffer) => void): this;
|
||||
on(event: "error", cb: (error: Error) => void): this;
|
||||
once(event: "error", cb: (error: Error) => void): this;
|
||||
on(event: RedisStatus, cb: () => void): this;
|
||||
once(event: RedisStatus, cb: () => void): this;
|
||||
on(event: string | symbol, listener: (...args: any[]) => void): this;
|
||||
once(event: string | symbol, listener: (...args: any[]) => void): this;
|
||||
}
|
||||
interface Redis<ReplyMapping extends "legacy" | "resp3" = "legacy"> extends Transaction<ReplyMapping extends "resp3" ? "resp3" : "resp2"> {
|
||||
}
|
||||
export default Redis;
|
||||
834
node_modules/ioredis/built/Redis.js
generated
vendored
Normal file
834
node_modules/ioredis/built/Redis.js
generated
vendored
Normal file
@@ -0,0 +1,834 @@
|
||||
"use strict";
|
||||
var _a;
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const commands_1 = require("@ioredis/commands");
|
||||
const events_1 = require("events");
|
||||
const standard_as_callback_1 = require("standard-as-callback");
|
||||
const cluster_1 = require("./cluster");
|
||||
const Command_1 = require("./Command");
|
||||
const connectors_1 = require("./connectors");
|
||||
const SentinelConnector_1 = require("./connectors/SentinelConnector");
|
||||
const eventHandler = require("./redis/event_handler");
|
||||
const RedisOptions_1 = require("./redis/RedisOptions");
|
||||
const ScanStream_1 = require("./ScanStream");
|
||||
const transaction_1 = require("./transaction");
|
||||
const utils_1 = require("./utils");
|
||||
const tracing_1 = require("./tracing");
|
||||
const applyMixin_1 = require("./utils/applyMixin");
|
||||
const Commander_1 = require("./utils/Commander");
|
||||
const lodash_1 = require("./utils/lodash");
|
||||
const HimportCoordinator_1 = require("./himport/HimportCoordinator");
|
||||
const HimportCoordinator_2 = require("./himport/HimportCoordinator");
|
||||
const Deque = require("denque");
|
||||
const debug = (0, utils_1.Debug)("redis");
|
||||
/**
|
||||
* This is the major component of ioredis.
|
||||
* Use it to connect to a standalone Redis server or Sentinels.
|
||||
*
|
||||
* ```typescript
|
||||
* const redis = new Redis(); // Default port is 6379
|
||||
* async function main() {
|
||||
* redis.set("foo", "bar");
|
||||
* redis.get("foo", (err, result) => {
|
||||
* // `result` should be "bar"
|
||||
* console.log(err, result);
|
||||
* });
|
||||
* // Or use Promise
|
||||
* const result = await redis.get("foo");
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class Redis extends Commander_1.default {
|
||||
/**
|
||||
* Create a Redis instance.
|
||||
* This is the same as `new Redis()` but is included for compatibility with node-redis.
|
||||
*/
|
||||
static createClient(...args) {
|
||||
return new Redis(...args);
|
||||
}
|
||||
constructor(arg1, arg2, arg3) {
|
||||
super();
|
||||
this.status = "wait";
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
this.isCluster = false;
|
||||
this.reconnectTimeout = null;
|
||||
this.connectionEpoch = 0;
|
||||
this.retryAttempts = 0;
|
||||
this.manuallyClosing = false;
|
||||
this[_a] = false;
|
||||
// Prepare autopipelines structures
|
||||
this._autoPipelines = new Map();
|
||||
this._runningAutoPipelines = new Set();
|
||||
this.parseOptions(arg1, arg2, arg3);
|
||||
events_1.EventEmitter.call(this);
|
||||
this.options.himportFieldsets = (0, HimportCoordinator_2.cloneHimportFieldsets)(this.options.himportFieldsets);
|
||||
if (this.options.himportFieldsets?.length) {
|
||||
(0, HimportCoordinator_1.bindHimportCoordinator)(this, new HimportCoordinator_1.default(this.options.himportFieldsets), "standalone");
|
||||
}
|
||||
this.resetCommandQueue();
|
||||
this.resetOfflineQueue();
|
||||
if (this.options.Connector) {
|
||||
this.connector = new this.options.Connector(this.options);
|
||||
}
|
||||
else if (this.options.sentinels) {
|
||||
const sentinelConnector = new SentinelConnector_1.default(this.options);
|
||||
sentinelConnector.emitter = this;
|
||||
this.connector = sentinelConnector;
|
||||
}
|
||||
else {
|
||||
this.connector = new connectors_1.StandaloneConnector(this.options);
|
||||
}
|
||||
if (this.options.scripts) {
|
||||
Object.entries(this.options.scripts).forEach(([name, definition]) => {
|
||||
this.defineCommand(name, definition);
|
||||
});
|
||||
}
|
||||
// end(or wait) -> connecting -> connect -> ready -> end
|
||||
if (this.options.lazyConnect) {
|
||||
this.setStatus("wait");
|
||||
}
|
||||
else {
|
||||
this.connect().catch(lodash_1.noop);
|
||||
}
|
||||
}
|
||||
get autoPipelineQueueSize() {
|
||||
let queued = 0;
|
||||
for (const pipeline of this._autoPipelines.values()) {
|
||||
queued += pipeline.length;
|
||||
}
|
||||
return queued;
|
||||
}
|
||||
/**
|
||||
* Create a connection to Redis.
|
||||
* This method will be invoked automatically when creating a new Redis instance
|
||||
* unless `lazyConnect: true` is passed.
|
||||
*
|
||||
* When calling this method manually, a Promise is returned, which will
|
||||
* be resolved when the connection status is ready. The promise can reject
|
||||
* if the connection fails, times out, or if Redis is already connecting/connected.
|
||||
*/
|
||||
connect(callback) {
|
||||
const promise = (0, tracing_1.traceConnect)(() => this._connect(), () => {
|
||||
const { address, port } = this._getServerAddress();
|
||||
return {
|
||||
serverAddress: address,
|
||||
serverPort: port,
|
||||
connectionEpoch: this.connectionEpoch,
|
||||
};
|
||||
});
|
||||
return (0, standard_as_callback_1.default)(promise, callback);
|
||||
}
|
||||
_connect() {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (this.status === "connecting" ||
|
||||
this.status === "connect" ||
|
||||
this.status === "ready") {
|
||||
reject(new Error("Redis is already connecting/connected"));
|
||||
return;
|
||||
}
|
||||
this.connectionEpoch += 1;
|
||||
this.setStatus("connecting");
|
||||
const { options } = this;
|
||||
this.condition = {
|
||||
select: options.db,
|
||||
auth: options.username
|
||||
? [options.username, options.password]
|
||||
: options.password,
|
||||
subscriber: false,
|
||||
protocol: options.protocol,
|
||||
replyMapping: options.protocol === 3 && options.replyMapping === "resp3"
|
||||
? "resp3"
|
||||
: "legacy",
|
||||
handshake: false,
|
||||
};
|
||||
const _this = this;
|
||||
(0, standard_as_callback_1.default)(this.connector.connect(function (type, err) {
|
||||
_this.silentEmit(type, err);
|
||||
}), function (err, stream) {
|
||||
if (err) {
|
||||
_this.flushQueue(err);
|
||||
_this.silentEmit("error", err);
|
||||
reject(err);
|
||||
_this.setStatus("end");
|
||||
return;
|
||||
}
|
||||
let CONNECT_EVENT = options.tls ? "secureConnect" : "connect";
|
||||
if ("sentinels" in options &&
|
||||
options.sentinels &&
|
||||
!options.enableTLSForSentinelMode) {
|
||||
CONNECT_EVENT = "connect";
|
||||
}
|
||||
_this.stream = stream;
|
||||
if (options.noDelay) {
|
||||
stream.setNoDelay(true);
|
||||
}
|
||||
// Node ignores setKeepAlive before connect, therefore we wait for the event:
|
||||
// https://github.com/nodejs/node/issues/31663
|
||||
if (typeof options.keepAlive === "number") {
|
||||
if (stream.connecting) {
|
||||
stream.once(CONNECT_EVENT, () => {
|
||||
stream.setKeepAlive(true, options.keepAlive);
|
||||
});
|
||||
}
|
||||
else {
|
||||
stream.setKeepAlive(true, options.keepAlive);
|
||||
}
|
||||
}
|
||||
if (stream.connecting) {
|
||||
stream.once(CONNECT_EVENT, eventHandler.connectHandler(_this));
|
||||
if (options.connectTimeout) {
|
||||
/*
|
||||
* Typically, Socket#setTimeout(0) will clear the timer
|
||||
* set before. However, in some platforms (Electron 3.x~4.x),
|
||||
* the timer will not be cleared. So we introduce a variable here.
|
||||
*
|
||||
* See https://github.com/electron/electron/issues/14915
|
||||
*/
|
||||
let connectTimeoutCleared = false;
|
||||
stream.setTimeout(options.connectTimeout, function () {
|
||||
if (connectTimeoutCleared) {
|
||||
return;
|
||||
}
|
||||
stream.setTimeout(0);
|
||||
stream.destroy();
|
||||
const err = new Error("connect ETIMEDOUT");
|
||||
// @ts-expect-error
|
||||
err.errorno = "ETIMEDOUT";
|
||||
// @ts-expect-error
|
||||
err.code = "ETIMEDOUT";
|
||||
// @ts-expect-error
|
||||
err.syscall = "connect";
|
||||
eventHandler.errorHandler(_this)(err);
|
||||
});
|
||||
stream.once(CONNECT_EVENT, function () {
|
||||
connectTimeoutCleared = true;
|
||||
stream.setTimeout(0);
|
||||
});
|
||||
}
|
||||
}
|
||||
else if (stream.destroyed) {
|
||||
const firstError = _this.connector.firstError;
|
||||
if (firstError) {
|
||||
process.nextTick(() => {
|
||||
eventHandler.errorHandler(_this)(firstError);
|
||||
});
|
||||
}
|
||||
process.nextTick(eventHandler.closeHandler(_this));
|
||||
}
|
||||
else {
|
||||
process.nextTick(eventHandler.connectHandler(_this));
|
||||
}
|
||||
if (!stream.destroyed) {
|
||||
stream.once("error", eventHandler.errorHandler(_this));
|
||||
stream.once("close", eventHandler.closeHandler(_this));
|
||||
}
|
||||
const connectionReadyHandler = function () {
|
||||
_this.removeListener("close", connectionCloseHandler);
|
||||
resolve();
|
||||
};
|
||||
var connectionCloseHandler = function () {
|
||||
_this.removeListener("ready", connectionReadyHandler);
|
||||
reject(new Error(utils_1.CONNECTION_CLOSED_ERROR_MSG));
|
||||
};
|
||||
_this.once("ready", connectionReadyHandler);
|
||||
_this.once("close", connectionCloseHandler);
|
||||
});
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Disconnect from Redis.
|
||||
*
|
||||
* This method closes the connection immediately,
|
||||
* and may lose some pending replies that haven't written to client.
|
||||
* If you want to wait for the pending replies, use Redis#quit instead.
|
||||
*/
|
||||
disconnect(reconnect = false) {
|
||||
if (!reconnect) {
|
||||
this.manuallyClosing = true;
|
||||
}
|
||||
if (this.reconnectTimeout && !reconnect) {
|
||||
clearTimeout(this.reconnectTimeout);
|
||||
this.reconnectTimeout = null;
|
||||
}
|
||||
if (this.status === "wait") {
|
||||
eventHandler.closeHandler(this)();
|
||||
}
|
||||
else {
|
||||
this.connector.disconnect();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Disconnect from Redis.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
end() {
|
||||
this.disconnect();
|
||||
}
|
||||
/**
|
||||
* Create a new instance with the same options as the current one.
|
||||
*
|
||||
* @example
|
||||
* ```js
|
||||
* var redis = new Redis(6380);
|
||||
* var anotherRedis = redis.duplicate();
|
||||
* ```
|
||||
*/
|
||||
duplicate(override) {
|
||||
return new Redis({
|
||||
...this.options,
|
||||
...(override ?? {}),
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Mode of the connection.
|
||||
*
|
||||
* One of `"normal"`, `"subscriber"`, or `"monitor"`. When the connection is
|
||||
* not in `"normal"` mode, certain commands are not allowed.
|
||||
*/
|
||||
get mode() {
|
||||
return this.options.monitor
|
||||
? "monitor"
|
||||
: (0, utils_1.isResp2SubscriberMode)(this.condition)
|
||||
? "subscriber"
|
||||
: "normal";
|
||||
}
|
||||
/**
|
||||
* Listen for all requests received by the server in real time.
|
||||
*
|
||||
* This command will create a new connection to Redis and send a
|
||||
* MONITOR command via the new connection in order to avoid disturbing
|
||||
* the current connection.
|
||||
*
|
||||
* @param callback The callback function. If omit, a promise will be returned.
|
||||
* @example
|
||||
* ```js
|
||||
* var redis = new Redis();
|
||||
* redis.monitor(function (err, monitor) {
|
||||
* // Entering monitoring mode.
|
||||
* monitor.on('monitor', function (time, args, source, database) {
|
||||
* console.log(time + ": " + util.inspect(args));
|
||||
* });
|
||||
* });
|
||||
*
|
||||
* // supports promise as well as other commands
|
||||
* redis.monitor().then(function (monitor) {
|
||||
* monitor.on('monitor', function (time, args, source, database) {
|
||||
* console.log(time + ": " + util.inspect(args));
|
||||
* });
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
monitor(callback) {
|
||||
const monitorInstance = this.duplicate({
|
||||
monitor: true,
|
||||
lazyConnect: false,
|
||||
himportFieldsets: undefined,
|
||||
});
|
||||
return (0, standard_as_callback_1.default)(new Promise(function (resolve, reject) {
|
||||
monitorInstance.once("error", reject);
|
||||
monitorInstance.once("monitoring", function () {
|
||||
resolve(monitorInstance);
|
||||
});
|
||||
}), callback);
|
||||
}
|
||||
/**
|
||||
* Send a command to Redis
|
||||
*
|
||||
* This method is used internally and in most cases you should not
|
||||
* use it directly. If you need to send a command that is not supported
|
||||
* by the library, you can use the `call` method:
|
||||
*
|
||||
* ```js
|
||||
* const redis = new Redis();
|
||||
*
|
||||
* redis.call('set', 'foo', 'bar');
|
||||
* // or
|
||||
* redis.call(['set', 'foo', 'bar']);
|
||||
* ```
|
||||
*
|
||||
* @ignore
|
||||
*/
|
||||
sendCommand(command, stream) {
|
||||
command.setReplyContext(this.condition ?? this.options);
|
||||
if (this.status === "wait") {
|
||||
this.connect().catch(lodash_1.noop);
|
||||
}
|
||||
if (this.status === "end") {
|
||||
command.reject(new Error(utils_1.CONNECTION_CLOSED_ERROR_MSG));
|
||||
return command.promise;
|
||||
}
|
||||
if ((0, utils_1.isResp2SubscriberMode)(this.condition) &&
|
||||
!Command_1.default.checkFlag("VALID_IN_SUBSCRIBER_MODE", command.name)) {
|
||||
command.reject(new Error("Connection in subscriber mode, only subscriber commands may be used"));
|
||||
return command.promise;
|
||||
}
|
||||
if (typeof this.options.commandTimeout === "number") {
|
||||
command.setTimeout(this.options.commandTimeout);
|
||||
}
|
||||
if (!stream &&
|
||||
this[HimportCoordinator_1.hasHimportCoordinator] &&
|
||||
(0, HimportCoordinator_1.interceptHimportCommand)(this, command, this.status === "ready", () => {
|
||||
this.sendCommand(command);
|
||||
})) {
|
||||
return command.promise;
|
||||
}
|
||||
const blockingTimeout = this.getBlockingTimeoutInMs(command);
|
||||
let writable = this.status === "ready" ||
|
||||
// During handshake, only internal handshake commands may bypass the queue.
|
||||
(!stream &&
|
||||
this.status === "connect" &&
|
||||
this.condition?.handshake &&
|
||||
(Command_1.default.checkFlag("HANDSHAKE_COMMANDS", command.name) ||
|
||||
(0, HimportCoordinator_1.isInternalHimportCommand)(command))) ||
|
||||
// Before ready, loading-safe commands remain writable after handshake.
|
||||
(!stream &&
|
||||
this.status === "connect" &&
|
||||
!this.condition?.handshake &&
|
||||
(0, commands_1.exists)(command.name, { caseInsensitive: true }) &&
|
||||
(0, commands_1.hasFlag)(command.name, "loading", { nameCaseInsensitive: true }));
|
||||
if (!this.stream) {
|
||||
writable = false;
|
||||
}
|
||||
else if (!this.stream.writable) {
|
||||
writable = false;
|
||||
// @ts-expect-error
|
||||
}
|
||||
else if (this.stream._writableState && this.stream._writableState.ended) {
|
||||
// TODO: We should be able to remove this as the PR has already been merged.
|
||||
// https://github.com/iojs/io.js/pull/1217
|
||||
writable = false;
|
||||
}
|
||||
if (!writable) {
|
||||
if (!this.options.enableOfflineQueue) {
|
||||
command.reject(new Error("Stream isn't writeable and enableOfflineQueue options is false"));
|
||||
return command.promise;
|
||||
}
|
||||
if (command.name === "quit" && this.offlineQueue.length === 0) {
|
||||
this.disconnect();
|
||||
command.resolve(Buffer.from("OK"));
|
||||
return command.promise;
|
||||
}
|
||||
// @ts-expect-error
|
||||
if (debug.enabled) {
|
||||
debug("queue command[%s]: %d -> %s(%o)", this._getDescription(), this.condition.select, command.name, command.args);
|
||||
}
|
||||
this.offlineQueue.push({
|
||||
command: command,
|
||||
stream: stream,
|
||||
select: this.condition.select,
|
||||
});
|
||||
// For blocking commands in the offline queue, arm a client-side timeout
|
||||
// only when blockingTimeout is configured. Without this option, queued
|
||||
// blocking commands may wait indefinitely on a dead connection.
|
||||
if (Command_1.default.checkFlag("BLOCKING_COMMANDS", command.name)) {
|
||||
const offlineTimeout = this.getConfiguredBlockingTimeout();
|
||||
if (offlineTimeout !== undefined) {
|
||||
command.setBlockingTimeout(offlineTimeout);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// @ts-expect-error
|
||||
if (debug.enabled) {
|
||||
debug("write command[%s]: %d -> %s(%o)", this._getDescription(), this.condition?.select, command.name, command.args);
|
||||
}
|
||||
if (stream) {
|
||||
if ("isPipeline" in stream && stream.isPipeline) {
|
||||
stream.write(command.toWritable(stream.destination.redis.stream));
|
||||
}
|
||||
else {
|
||||
stream.write(command.toWritable(stream));
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.stream.write(command.toWritable(this.stream));
|
||||
}
|
||||
this.commandQueue.push({
|
||||
command: command,
|
||||
stream: stream,
|
||||
select: this.condition.select,
|
||||
});
|
||||
if (blockingTimeout !== undefined) {
|
||||
command.setBlockingTimeout(blockingTimeout);
|
||||
}
|
||||
if (Command_1.default.checkFlag("WILL_DISCONNECT", command.name)) {
|
||||
this.manuallyClosing = true;
|
||||
}
|
||||
if (this.options.socketTimeout !== undefined &&
|
||||
this.socketTimeoutTimer === undefined) {
|
||||
this.setSocketTimeout();
|
||||
}
|
||||
}
|
||||
if (command.name === "select" && (0, utils_1.isInt)(command.args[0])) {
|
||||
const db = parseInt(command.args[0], 10);
|
||||
if (this.condition.select !== db) {
|
||||
this.condition.select = db;
|
||||
this.emit("select", db);
|
||||
debug("switch to db [%d]", this.condition.select);
|
||||
}
|
||||
}
|
||||
if (!writable || command.isTraced) {
|
||||
return command.promise;
|
||||
}
|
||||
// Trace on the write path only, and only once per command. Commands may
|
||||
// pass through sendCommand multiple times (offline queue flush,
|
||||
// prevCommandQueue resend after reconnect). The isTraced flag ensures
|
||||
// we don't emit duplicate trace events.
|
||||
command.isTraced = true;
|
||||
return (0, tracing_1.traceCommand)(() => command.promise, () => this._buildCommandContext(command));
|
||||
}
|
||||
getBlockingTimeoutInMs(command) {
|
||||
if (!Command_1.default.checkFlag("BLOCKING_COMMANDS", command.name)) {
|
||||
return undefined;
|
||||
}
|
||||
// Feature is opt-in: only enabled when blockingTimeout is set to a positive number
|
||||
const configuredTimeout = this.getConfiguredBlockingTimeout();
|
||||
if (configuredTimeout === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
const timeout = command.extractBlockingTimeout();
|
||||
if (typeof timeout === "number") {
|
||||
if (timeout > 0) {
|
||||
// Finite timeout from command args - add grace period
|
||||
return (timeout +
|
||||
(this.options.blockingTimeoutGrace ??
|
||||
RedisOptions_1.DEFAULT_REDIS_OPTIONS.blockingTimeoutGrace));
|
||||
}
|
||||
// Command has timeout=0 (block forever), use blockingTimeout option as safety net
|
||||
return configuredTimeout;
|
||||
}
|
||||
if (timeout === null) {
|
||||
// No BLOCK option found (e.g., XREAD without BLOCK), use blockingTimeout as safety net
|
||||
return configuredTimeout;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
getConfiguredBlockingTimeout() {
|
||||
if (typeof this.options.blockingTimeout === "number" &&
|
||||
this.options.blockingTimeout > 0) {
|
||||
return this.options.blockingTimeout;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
setSocketTimeout() {
|
||||
const stream = this.stream;
|
||||
this.socketTimeoutTimer = setTimeout(() => {
|
||||
stream.destroy(new Error(`Socket timeout. Expecting data, but didn't receive any in ${this.options.socketTimeout}ms.`));
|
||||
this.socketTimeoutTimer = undefined;
|
||||
}, this.options.socketTimeout);
|
||||
// this handler must run after the "data" handler in "DataHandler"
|
||||
// so that `this.commandQueue.length` will be updated
|
||||
stream.once("data", () => {
|
||||
clearTimeout(this.socketTimeoutTimer);
|
||||
this.socketTimeoutTimer = undefined;
|
||||
if (this.commandQueue.length === 0)
|
||||
return;
|
||||
this.setSocketTimeout();
|
||||
});
|
||||
}
|
||||
scanStream(options) {
|
||||
return this.createScanStream("scan", { options });
|
||||
}
|
||||
scanBufferStream(options) {
|
||||
return this.createScanStream("scanBuffer", { options });
|
||||
}
|
||||
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 });
|
||||
}
|
||||
/**
|
||||
* Emit only when there's at least one listener.
|
||||
*
|
||||
* @ignore
|
||||
*/
|
||||
silentEmit(eventName, arg) {
|
||||
let error;
|
||||
if (eventName === "error") {
|
||||
error = arg;
|
||||
if (this.status === "end") {
|
||||
return;
|
||||
}
|
||||
if (this.manuallyClosing) {
|
||||
// ignore connection related errors when manually disconnecting
|
||||
if (error instanceof Error &&
|
||||
(error.message === utils_1.CONNECTION_CLOSED_ERROR_MSG ||
|
||||
// @ts-expect-error
|
||||
error.syscall === "connect" ||
|
||||
// @ts-expect-error
|
||||
error.syscall === "read")) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.listeners(eventName).length > 0) {
|
||||
return this.emit.apply(this, arguments);
|
||||
}
|
||||
if (error && error instanceof Error) {
|
||||
console.error("[ioredis] Unhandled error event:", error.stack);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
recoverFromFatalError(_commandError, err, options) {
|
||||
this.flushQueue(err, options);
|
||||
this.silentEmit("error", err);
|
||||
this.disconnect(true);
|
||||
}
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
handleReconnection(err, item) {
|
||||
let needReconnect = false;
|
||||
const ignoreReconnectOnError = Command_1.default.checkFlag("IGNORE_RECONNECT_ON_ERROR", item.command.name) ||
|
||||
(this.condition?.handshake &&
|
||||
Command_1.default.checkFlag("HANDSHAKE_COMMANDS", item.command.name));
|
||||
if (this.options.reconnectOnError && !ignoreReconnectOnError) {
|
||||
needReconnect = this.options.reconnectOnError(err);
|
||||
}
|
||||
switch (needReconnect) {
|
||||
case 1:
|
||||
case true:
|
||||
if (this.status !== "reconnecting") {
|
||||
this.disconnect(true);
|
||||
}
|
||||
item.command.reject(err);
|
||||
break;
|
||||
case 2:
|
||||
if (this.status !== "reconnecting") {
|
||||
this.disconnect(true);
|
||||
}
|
||||
if (this.condition?.select !== item.select &&
|
||||
item.command.name !== "select") {
|
||||
this.select(item.select);
|
||||
}
|
||||
// TODO
|
||||
// @ts-expect-error
|
||||
this.sendCommand(item.command);
|
||||
break;
|
||||
default:
|
||||
item.command.reject(err);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
_getServerAddress() {
|
||||
if ("path" in this.options && this.options.path) {
|
||||
return { address: this.options.path, port: undefined };
|
||||
}
|
||||
return {
|
||||
address: ("host" in this.options && this.options.host) || "localhost",
|
||||
port: ("port" in this.options && this.options.port) || 6379,
|
||||
};
|
||||
}
|
||||
_buildCommandContext(command) {
|
||||
const { address, port } = this._getServerAddress();
|
||||
return {
|
||||
command: command.name,
|
||||
args: (0, tracing_1.sanitizeArgs)(command.name, command.args),
|
||||
database: this.condition?.select ?? this.options.db ?? 0,
|
||||
serverAddress: address,
|
||||
serverPort: port,
|
||||
};
|
||||
}
|
||||
_buildBatchContext(batchSize) {
|
||||
const { address, port } = this._getServerAddress();
|
||||
return {
|
||||
batchMode: "MULTI",
|
||||
batchSize,
|
||||
database: this.condition?.select ?? this.options.db ?? 0,
|
||||
serverAddress: address,
|
||||
serverPort: port,
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Get description of the connection. Used for debugging.
|
||||
*/
|
||||
_getDescription() {
|
||||
let description;
|
||||
if ("path" in this.options && this.options.path) {
|
||||
description = this.options.path;
|
||||
}
|
||||
else if (this.stream &&
|
||||
this.stream.remoteAddress &&
|
||||
this.stream.remotePort) {
|
||||
description = this.stream.remoteAddress + ":" + this.stream.remotePort;
|
||||
}
|
||||
else if ("host" in this.options && this.options.host) {
|
||||
description = this.options.host + ":" + this.options.port;
|
||||
}
|
||||
else {
|
||||
// Unexpected
|
||||
description = "";
|
||||
}
|
||||
if (this.options.connectionName) {
|
||||
description += ` (${this.options.connectionName})`;
|
||||
}
|
||||
return description;
|
||||
}
|
||||
resetCommandQueue() {
|
||||
this.commandQueue = new Deque();
|
||||
}
|
||||
resetOfflineQueue() {
|
||||
this.offlineQueue = new Deque();
|
||||
}
|
||||
parseOptions(...args) {
|
||||
const options = {};
|
||||
let isTls = false;
|
||||
for (let i = 0; i < args.length; ++i) {
|
||||
const arg = args[i];
|
||||
if (arg === null || typeof arg === "undefined") {
|
||||
continue;
|
||||
}
|
||||
if (typeof arg === "object") {
|
||||
(0, lodash_1.defaults)(options, arg);
|
||||
}
|
||||
else if (typeof arg === "string") {
|
||||
(0, lodash_1.defaults)(options, (0, utils_1.parseURL)(arg));
|
||||
if (arg.startsWith("rediss://")) {
|
||||
isTls = true;
|
||||
}
|
||||
}
|
||||
else if (typeof arg === "number") {
|
||||
options.port = arg;
|
||||
}
|
||||
else {
|
||||
throw new Error("Invalid argument " + arg);
|
||||
}
|
||||
}
|
||||
if (isTls) {
|
||||
(0, lodash_1.defaults)(options, { tls: true });
|
||||
}
|
||||
(0, lodash_1.defaults)(options, Redis.defaultOptions);
|
||||
if (typeof options.port === "string") {
|
||||
options.port = parseInt(options.port, 10);
|
||||
}
|
||||
if (typeof options.db === "string") {
|
||||
options.db = parseInt(options.db, 10);
|
||||
}
|
||||
if (options.replyMapping === "resp3" && options.protocol !== 3) {
|
||||
throw new Error('The "resp3" replyMapping is only supported with protocol 3');
|
||||
}
|
||||
// @ts-expect-error
|
||||
this.options = (0, utils_1.resolveTLSProfile)(options);
|
||||
}
|
||||
/**
|
||||
* Change instance's status
|
||||
*/
|
||||
setStatus(status, arg) {
|
||||
// @ts-expect-error
|
||||
if (debug.enabled) {
|
||||
debug("status[%s]: %s -> %s", this._getDescription(), this.status || "[empty]", status);
|
||||
}
|
||||
this.status = status;
|
||||
process.nextTick(this.emit.bind(this, status, arg));
|
||||
}
|
||||
createScanStream(command, { key, options = {} }) {
|
||||
return new ScanStream_1.default({
|
||||
objectMode: true,
|
||||
key: key,
|
||||
redis: this,
|
||||
command: command,
|
||||
...options,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Flush offline queue and command queue with error.
|
||||
*
|
||||
* @param error The error object to send to the commands
|
||||
* @param options options
|
||||
*/
|
||||
flushQueue(error, options) {
|
||||
options = (0, lodash_1.defaults)({}, options, {
|
||||
offlineQueue: true,
|
||||
commandQueue: true,
|
||||
});
|
||||
let item;
|
||||
if (options.offlineQueue) {
|
||||
while ((item = this.offlineQueue.shift())) {
|
||||
item.command.reject(error);
|
||||
}
|
||||
}
|
||||
if (options.commandQueue) {
|
||||
if (this.commandQueue.length > 0) {
|
||||
if (this.stream) {
|
||||
this.stream.removeAllListeners("data");
|
||||
}
|
||||
while ((item = this.commandQueue.shift())) {
|
||||
item.command.reject(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Check whether Redis has finished loading the persistent data and is able to
|
||||
* process commands.
|
||||
*/
|
||||
_readyCheck(callback) {
|
||||
const _this = this;
|
||||
this.info(function (err, res) {
|
||||
if (err) {
|
||||
if (err.message && err.message.includes("NOPERM")) {
|
||||
console.warn(`Skipping the ready check because INFO command fails: "${err.message}". You can disable ready check with "enableReadyCheck". More: https://github.com/luin/ioredis/wiki/Disable-ready-check.`);
|
||||
return callback(null, {});
|
||||
}
|
||||
return callback(err);
|
||||
}
|
||||
if (typeof res !== "string") {
|
||||
return callback(null, res);
|
||||
}
|
||||
const info = {};
|
||||
const lines = res.split("\r\n");
|
||||
for (let i = 0; i < lines.length; ++i) {
|
||||
const [fieldName, ...fieldValueParts] = lines[i].split(":");
|
||||
const fieldValue = fieldValueParts.join(":");
|
||||
if (fieldValue) {
|
||||
info[fieldName] = fieldValue;
|
||||
}
|
||||
}
|
||||
if (!info.loading || info.loading === "0") {
|
||||
callback(null, info);
|
||||
}
|
||||
else {
|
||||
const loadingEtaMs = (info.loading_eta_seconds || 1) * 1000;
|
||||
const retryTime = _this.options.maxLoadingRetryTime &&
|
||||
_this.options.maxLoadingRetryTime < loadingEtaMs
|
||||
? _this.options.maxLoadingRetryTime
|
||||
: loadingEtaMs;
|
||||
debug("Redis server still loading, trying again in " + retryTime + "ms");
|
||||
setTimeout(function () {
|
||||
_this._readyCheck(callback);
|
||||
}, retryTime);
|
||||
}
|
||||
}).catch(lodash_1.noop);
|
||||
}
|
||||
}
|
||||
_a = HimportCoordinator_1.hasHimportCoordinator;
|
||||
Redis.Cluster = cluster_1.default;
|
||||
Redis.Command = Command_1.default;
|
||||
/**
|
||||
* Default options
|
||||
*/
|
||||
Redis.defaultOptions = RedisOptions_1.DEFAULT_REDIS_OPTIONS;
|
||||
(0, applyMixin_1.default)(Redis, events_1.EventEmitter);
|
||||
(0, transaction_1.addTransactionSupport)(Redis.prototype);
|
||||
exports.default = Redis;
|
||||
23
node_modules/ioredis/built/ScanStream.d.ts
generated
vendored
Normal file
23
node_modules/ioredis/built/ScanStream.d.ts
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
/// <reference types="node" />
|
||||
import { Readable, ReadableOptions } from "stream";
|
||||
interface Options extends ReadableOptions {
|
||||
key?: string;
|
||||
match?: string;
|
||||
type?: string;
|
||||
command: string;
|
||||
redis: any;
|
||||
count?: string | number;
|
||||
noValues?: boolean;
|
||||
}
|
||||
/**
|
||||
* Convenient class to convert the process of scanning keys to a readable stream.
|
||||
*/
|
||||
export default class ScanStream extends Readable {
|
||||
private opt;
|
||||
private _redisCursor;
|
||||
private _redisDrained;
|
||||
constructor(opt: Options);
|
||||
_read(): void;
|
||||
close(): void;
|
||||
}
|
||||
export {};
|
||||
51
node_modules/ioredis/built/ScanStream.js
generated
vendored
Normal file
51
node_modules/ioredis/built/ScanStream.js
generated
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const stream_1 = require("stream");
|
||||
/**
|
||||
* Convenient class to convert the process of scanning keys to a readable stream.
|
||||
*/
|
||||
class ScanStream extends stream_1.Readable {
|
||||
constructor(opt) {
|
||||
super(opt);
|
||||
this.opt = opt;
|
||||
this._redisCursor = "0";
|
||||
this._redisDrained = false;
|
||||
}
|
||||
_read() {
|
||||
if (this._redisDrained) {
|
||||
this.push(null);
|
||||
return;
|
||||
}
|
||||
const args = [this._redisCursor];
|
||||
if (this.opt.key) {
|
||||
args.unshift(this.opt.key);
|
||||
}
|
||||
if (this.opt.match) {
|
||||
args.push("MATCH", this.opt.match);
|
||||
}
|
||||
if (this.opt.type) {
|
||||
args.push("TYPE", this.opt.type);
|
||||
}
|
||||
if (this.opt.count) {
|
||||
args.push("COUNT", String(this.opt.count));
|
||||
}
|
||||
if (this.opt.noValues) {
|
||||
args.push("NOVALUES");
|
||||
}
|
||||
this.opt.redis[this.opt.command](args, (err, res) => {
|
||||
if (err) {
|
||||
this.emit("error", err);
|
||||
return;
|
||||
}
|
||||
this._redisCursor = res[0] instanceof Buffer ? res[0].toString() : res[0];
|
||||
if (this._redisCursor === "0") {
|
||||
this._redisDrained = true;
|
||||
}
|
||||
this.push(res[1]);
|
||||
});
|
||||
}
|
||||
close() {
|
||||
this._redisDrained = true;
|
||||
}
|
||||
}
|
||||
exports.default = ScanStream;
|
||||
11
node_modules/ioredis/built/Script.d.ts
generated
vendored
Normal file
11
node_modules/ioredis/built/Script.d.ts
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
import { Callback } from "./types";
|
||||
export default class Script {
|
||||
private lua;
|
||||
private numberOfKeys;
|
||||
private keyPrefix;
|
||||
private readOnly;
|
||||
private sha;
|
||||
private Command;
|
||||
constructor(lua: string, numberOfKeys?: number | null, keyPrefix?: string, readOnly?: boolean);
|
||||
execute(container: any, args: any[], options: any, callback?: Callback): any;
|
||||
}
|
||||
62
node_modules/ioredis/built/Script.js
generated
vendored
Normal file
62
node_modules/ioredis/built/Script.js
generated
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const crypto_1 = require("crypto");
|
||||
const Command_1 = require("./Command");
|
||||
const standard_as_callback_1 = require("standard-as-callback");
|
||||
class Script {
|
||||
constructor(lua, numberOfKeys = null, keyPrefix = "", readOnly = false) {
|
||||
this.lua = lua;
|
||||
this.numberOfKeys = numberOfKeys;
|
||||
this.keyPrefix = keyPrefix;
|
||||
this.readOnly = readOnly;
|
||||
this.sha = (0, crypto_1.createHash)("sha1").update(lua).digest("hex");
|
||||
const sha = this.sha;
|
||||
const socketHasScriptLoaded = new WeakSet();
|
||||
this.Command = class CustomScriptCommand extends Command_1.default {
|
||||
toWritable(socket) {
|
||||
const origReject = this.reject;
|
||||
this.reject = (err) => {
|
||||
if (err.message.indexOf("NOSCRIPT") !== -1) {
|
||||
socketHasScriptLoaded.delete(socket);
|
||||
}
|
||||
origReject.call(this, err);
|
||||
};
|
||||
if (!socketHasScriptLoaded.has(socket)) {
|
||||
socketHasScriptLoaded.add(socket);
|
||||
this.name = "eval";
|
||||
this.args[0] = lua;
|
||||
}
|
||||
else if (this.name === "eval") {
|
||||
this.name = "evalsha";
|
||||
this.args[0] = sha;
|
||||
}
|
||||
return super.toWritable(socket);
|
||||
}
|
||||
};
|
||||
}
|
||||
execute(container, args, options, callback) {
|
||||
if (typeof this.numberOfKeys === "number") {
|
||||
args.unshift(this.numberOfKeys);
|
||||
}
|
||||
if (this.keyPrefix) {
|
||||
options.keyPrefix = this.keyPrefix;
|
||||
}
|
||||
if (this.readOnly) {
|
||||
options.readOnly = true;
|
||||
}
|
||||
const evalsha = new this.Command("evalsha", [this.sha, ...args], options);
|
||||
evalsha.promise = evalsha.promise.catch((err) => {
|
||||
if (err.message.indexOf("NOSCRIPT") === -1) {
|
||||
throw err;
|
||||
}
|
||||
// Resend the same custom evalsha command that gets transformed
|
||||
// to an eval in case it's not loaded yet on the connection.
|
||||
const resend = new this.Command("evalsha", [this.sha, ...args], options);
|
||||
const client = container.isPipeline ? container.redis : container;
|
||||
return client.sendCommand(resend);
|
||||
});
|
||||
(0, standard_as_callback_1.default)(evalsha.promise, callback);
|
||||
return container.sendCommand(evalsha);
|
||||
}
|
||||
}
|
||||
exports.default = Script;
|
||||
14
node_modules/ioredis/built/SubscriptionSet.d.ts
generated
vendored
Normal file
14
node_modules/ioredis/built/SubscriptionSet.d.ts
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
import { CommandNameFlags } from "./Command";
|
||||
type AddSet = CommandNameFlags["ENTER_SUBSCRIBER_MODE"][number];
|
||||
type DelSet = CommandNameFlags["EXIT_SUBSCRIBER_MODE"][number];
|
||||
/**
|
||||
* Tiny class to simplify dealing with subscription set
|
||||
*/
|
||||
export default class SubscriptionSet {
|
||||
private set;
|
||||
add(set: AddSet, channel: string): void;
|
||||
del(set: DelSet, channel: string): void;
|
||||
channels(set: AddSet | DelSet): string[];
|
||||
isEmpty(): boolean;
|
||||
}
|
||||
export {};
|
||||
41
node_modules/ioredis/built/SubscriptionSet.js
generated
vendored
Normal file
41
node_modules/ioredis/built/SubscriptionSet.js
generated
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
/**
|
||||
* Tiny class to simplify dealing with subscription set
|
||||
*/
|
||||
class SubscriptionSet {
|
||||
constructor() {
|
||||
this.set = {
|
||||
subscribe: {},
|
||||
psubscribe: {},
|
||||
ssubscribe: {},
|
||||
};
|
||||
}
|
||||
add(set, channel) {
|
||||
this.set[mapSet(set)][channel] = true;
|
||||
}
|
||||
del(set, channel) {
|
||||
delete this.set[mapSet(set)][channel];
|
||||
}
|
||||
channels(set) {
|
||||
return Object.keys(this.set[mapSet(set)]);
|
||||
}
|
||||
isEmpty() {
|
||||
return (this.channels("subscribe").length === 0 &&
|
||||
this.channels("psubscribe").length === 0 &&
|
||||
this.channels("ssubscribe").length === 0);
|
||||
}
|
||||
}
|
||||
exports.default = SubscriptionSet;
|
||||
function mapSet(set) {
|
||||
if (set === "unsubscribe") {
|
||||
return "subscribe";
|
||||
}
|
||||
if (set === "punsubscribe") {
|
||||
return "psubscribe";
|
||||
}
|
||||
if (set === "sunsubscribe") {
|
||||
return "ssubscribe";
|
||||
}
|
||||
return set;
|
||||
}
|
||||
9
node_modules/ioredis/built/autoPipelining.d.ts
generated
vendored
Normal file
9
node_modules/ioredis/built/autoPipelining.d.ts
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
/// <reference types="node" />
|
||||
/// <reference types="node" />
|
||||
import { ArgumentType } from "./Command";
|
||||
export declare const kExec: unique symbol;
|
||||
export declare const kCallbacks: unique symbol;
|
||||
export declare const notAllowedAutoPipelineCommands: string[];
|
||||
export declare function shouldUseAutoPipelining(client: any, functionName: string, commandName: string): boolean;
|
||||
export declare function getFirstValueInFlattenedArray(args: ArgumentType[]): string | Buffer | number | null | undefined;
|
||||
export declare function executeWithAutoPipelining(client: any, functionName: string, commandName: string, args: ArgumentType[], callback: any): Promise<unknown>;
|
||||
180
node_modules/ioredis/built/autoPipelining.js
generated
vendored
Normal file
180
node_modules/ioredis/built/autoPipelining.js
generated
vendored
Normal file
@@ -0,0 +1,180 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.executeWithAutoPipelining = exports.getFirstValueInFlattenedArray = exports.shouldUseAutoPipelining = exports.notAllowedAutoPipelineCommands = exports.kCallbacks = exports.kExec = void 0;
|
||||
const lodash_1 = require("./utils/lodash");
|
||||
const calculateSlot = require("cluster-key-slot");
|
||||
const standard_as_callback_1 = require("standard-as-callback");
|
||||
const commands_1 = require("@ioredis/commands");
|
||||
exports.kExec = Symbol("exec");
|
||||
exports.kCallbacks = Symbol("callbacks");
|
||||
exports.notAllowedAutoPipelineCommands = [
|
||||
"auth",
|
||||
"info",
|
||||
"script",
|
||||
"quit",
|
||||
"cluster",
|
||||
"pipeline",
|
||||
"multi",
|
||||
"subscribe",
|
||||
"psubscribe",
|
||||
"unsubscribe",
|
||||
"unpsubscribe",
|
||||
"select",
|
||||
"client",
|
||||
"hello",
|
||||
"readonly",
|
||||
"himport",
|
||||
];
|
||||
function executeAutoPipeline(client, slotKey) {
|
||||
/*
|
||||
If a pipeline is already executing, keep queueing up commands
|
||||
since ioredis won't serve two pipelines at the same time
|
||||
*/
|
||||
if (client._runningAutoPipelines.has(slotKey)) {
|
||||
return;
|
||||
}
|
||||
if (!client._autoPipelines.has(slotKey)) {
|
||||
/*
|
||||
Rare edge case. Somehow, something has deleted this running autopipeline in an immediate
|
||||
call to executeAutoPipeline.
|
||||
|
||||
Maybe the callback in the pipeline.exec is sometimes called in the same tick,
|
||||
e.g. if redis is disconnected?
|
||||
*/
|
||||
return;
|
||||
}
|
||||
client._runningAutoPipelines.add(slotKey);
|
||||
// Get the pipeline and immediately delete it so that new commands are queued on a new pipeline
|
||||
const pipeline = client._autoPipelines.get(slotKey);
|
||||
client._autoPipelines.delete(slotKey);
|
||||
const callbacks = pipeline[exports.kCallbacks];
|
||||
// Stop keeping a reference to callbacks immediately after the callbacks stop being used.
|
||||
// This allows the GC to reclaim objects referenced by callbacks, especially with 16384 slots
|
||||
// in Redis.Cluster
|
||||
pipeline[exports.kCallbacks] = null;
|
||||
// Perform the call
|
||||
pipeline.exec(function (err, results) {
|
||||
client._runningAutoPipelines.delete(slotKey);
|
||||
/*
|
||||
Invoke all callback in nextTick so the stack is cleared
|
||||
and callbacks can throw errors without affecting other callbacks.
|
||||
*/
|
||||
if (err) {
|
||||
for (let i = 0; i < callbacks.length; i++) {
|
||||
process.nextTick(callbacks[i], err);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (let i = 0; i < callbacks.length; i++) {
|
||||
process.nextTick(callbacks[i], ...results[i]);
|
||||
}
|
||||
}
|
||||
// If there is another pipeline on the same node, immediately execute it without waiting for nextTick
|
||||
if (client._autoPipelines.has(slotKey)) {
|
||||
executeAutoPipeline(client, slotKey);
|
||||
}
|
||||
});
|
||||
}
|
||||
function shouldUseAutoPipelining(client, functionName, commandName) {
|
||||
return (functionName &&
|
||||
client.options.enableAutoPipelining &&
|
||||
!client.isPipeline &&
|
||||
!exports.notAllowedAutoPipelineCommands.includes(commandName) &&
|
||||
!client.options.autoPipeliningIgnoredCommands.includes(commandName));
|
||||
}
|
||||
exports.shouldUseAutoPipelining = shouldUseAutoPipelining;
|
||||
function getFirstValueInFlattenedArray(args) {
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
const arg = args[i];
|
||||
if (typeof arg === "string") {
|
||||
return arg;
|
||||
}
|
||||
else if (Array.isArray(arg) || (0, lodash_1.isArguments)(arg)) {
|
||||
if (arg.length === 0) {
|
||||
continue;
|
||||
}
|
||||
return arg[0];
|
||||
}
|
||||
const flattened = [arg].flat();
|
||||
if (flattened.length > 0) {
|
||||
return flattened[0];
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
exports.getFirstValueInFlattenedArray = getFirstValueInFlattenedArray;
|
||||
function getFirstKeyForCommand(commandName, args) {
|
||||
if ((0, commands_1.exists)(commandName, { caseInsensitive: true })) {
|
||||
const flattenedArgs = args.flat();
|
||||
const keyIndexes = (0, commands_1.getKeyIndexes)(commandName, flattenedArgs, {
|
||||
nameCaseInsensitive: true,
|
||||
});
|
||||
if (keyIndexes.length) {
|
||||
return flattenedArgs[keyIndexes[0]];
|
||||
}
|
||||
}
|
||||
return getFirstValueInFlattenedArray(args);
|
||||
}
|
||||
function executeWithAutoPipelining(client, functionName, commandName, args, callback) {
|
||||
// On cluster mode let's wait for slots to be available
|
||||
if (client.isCluster && !client.slots.length) {
|
||||
if (client.status === "wait")
|
||||
client.connect().catch(lodash_1.noop);
|
||||
return (0, standard_as_callback_1.default)(new Promise(function (resolve, reject) {
|
||||
client.delayUntilReady((err) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
executeWithAutoPipelining(client, functionName, commandName, args, null).then(resolve, reject);
|
||||
});
|
||||
}), callback);
|
||||
}
|
||||
// If we have slot information, we can improve routing by grouping slots served by the same subset of nodes
|
||||
const prefix = client.options.keyPrefix || "";
|
||||
let slotKey = client.isCluster
|
||||
? client.slots[calculateSlot(`${prefix}${getFirstKeyForCommand(commandName, args)}`)].join(",")
|
||||
: "main";
|
||||
// When scaleReads is enabled, separate read and write commands into different pipelines
|
||||
// so they can be routed to replicas and masters respectively
|
||||
if (client.isCluster && client.options.scaleReads !== "master") {
|
||||
const isReadOnly = (0, commands_1.exists)(commandName) && (0, commands_1.hasFlag)(commandName, "readonly");
|
||||
slotKey += isReadOnly ? ":read" : ":write";
|
||||
}
|
||||
if (!client._autoPipelines.has(slotKey)) {
|
||||
const pipeline = client.pipeline();
|
||||
pipeline[exports.kExec] = false;
|
||||
pipeline[exports.kCallbacks] = [];
|
||||
client._autoPipelines.set(slotKey, pipeline);
|
||||
}
|
||||
const pipeline = client._autoPipelines.get(slotKey);
|
||||
/*
|
||||
Mark the pipeline as scheduled.
|
||||
The symbol will make sure that the pipeline is only scheduled once per tick.
|
||||
New commands are appended to an already scheduled pipeline.
|
||||
*/
|
||||
if (!pipeline[exports.kExec]) {
|
||||
pipeline[exports.kExec] = true;
|
||||
/*
|
||||
Deferring with setImmediate so we have a chance to capture multiple
|
||||
commands that can be scheduled by I/O events already in the event loop queue.
|
||||
*/
|
||||
setImmediate(executeAutoPipeline, client, slotKey);
|
||||
}
|
||||
// Create the promise which will execute the command in the pipeline.
|
||||
const autoPipelinePromise = new Promise(function (resolve, reject) {
|
||||
pipeline[exports.kCallbacks].push(function (err, value) {
|
||||
if (err) {
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
resolve(value);
|
||||
});
|
||||
if (functionName === "call") {
|
||||
args.unshift(commandName);
|
||||
}
|
||||
pipeline[functionName](...args);
|
||||
});
|
||||
return (0, standard_as_callback_1.default)(autoPipelinePromise, callback);
|
||||
}
|
||||
exports.executeWithAutoPipelining = executeWithAutoPipelining;
|
||||
227
node_modules/ioredis/built/cluster/ClusterOptions.d.ts
generated
vendored
Normal file
227
node_modules/ioredis/built/cluster/ClusterOptions.d.ts
generated
vendored
Normal file
@@ -0,0 +1,227 @@
|
||||
/// <reference types="node" />
|
||||
/// <reference types="node" />
|
||||
import { SrvRecord } from "dns";
|
||||
import { RedisOptions, RetryStrategy } from "../redis/RedisOptions";
|
||||
import { ReplyMappingMode } from "../types";
|
||||
import { CommanderOptions } from "../utils/Commander";
|
||||
import { NodeRole } from "./util";
|
||||
import type { HimportFieldset } from "../himport/types";
|
||||
export type DNSResolveSrvFunction = (hostname: string, callback: (err: NodeJS.ErrnoException | null | undefined, records?: SrvRecord[]) => void) => void;
|
||||
export type DNSLookupFunction = (hostname: string, callback: (err: NodeJS.ErrnoException | null | undefined, address: string, family?: number) => void) => void;
|
||||
export type NatMapFunction = (key: string) => {
|
||||
host: string;
|
||||
port: number;
|
||||
} | null;
|
||||
export type NatMap = {
|
||||
[key: string]: {
|
||||
host: string;
|
||||
port: number;
|
||||
};
|
||||
} | NatMapFunction;
|
||||
export type ClusterNodeRetryStrategy = RetryStrategy;
|
||||
/**
|
||||
* Options for Cluster constructor
|
||||
*/
|
||||
export interface ClusterOptions extends CommanderOptions {
|
||||
/**
|
||||
* See "Quick Start" section.
|
||||
*
|
||||
* @default (times) => Math.min(100 + times * 2, 2000)
|
||||
*/
|
||||
clusterRetryStrategy?: ((times: number, reason?: Error) => number | void | null) | null | undefined;
|
||||
/**
|
||||
* See Redis class.
|
||||
*
|
||||
* @default true
|
||||
*/
|
||||
enableOfflineQueue?: boolean | undefined;
|
||||
/**
|
||||
* When enabled, ioredis only emits "ready" event when `CLUSTER INFO`
|
||||
* command reporting the cluster is ready for handling commands.
|
||||
*
|
||||
* @default true
|
||||
*/
|
||||
enableReadyCheck?: boolean | undefined;
|
||||
/**
|
||||
* Scale reads to the node with the specified role.
|
||||
*
|
||||
* @default "master"
|
||||
*/
|
||||
scaleReads?: NodeRole | Function | undefined;
|
||||
/**
|
||||
* When a MOVED or ASK error is received, client will redirect the
|
||||
* command to another node.
|
||||
* This option limits the max redirections allowed to send a command.
|
||||
*
|
||||
* @default 16
|
||||
*/
|
||||
maxRedirections?: number | undefined;
|
||||
/**
|
||||
* When an error is received when sending a command (e.g.
|
||||
* "Connection is closed." when the target Redis node is down), client will retry
|
||||
* if `retryDelayOnFailover` is valid delay time (in ms).
|
||||
*
|
||||
* @default 100
|
||||
*/
|
||||
retryDelayOnFailover?: number | undefined;
|
||||
/**
|
||||
* When a CLUSTERDOWN error is received, client will retry
|
||||
* if `retryDelayOnClusterDown` is valid delay time (in ms).
|
||||
*
|
||||
* @default 100
|
||||
*/
|
||||
retryDelayOnClusterDown?: number | undefined;
|
||||
/**
|
||||
* When a TRYAGAIN error is received, client will retry
|
||||
* if `retryDelayOnTryAgain` is valid delay time (in ms).
|
||||
*
|
||||
* @default 100
|
||||
*/
|
||||
retryDelayOnTryAgain?: number | undefined;
|
||||
/**
|
||||
* By default, this value is 0, which means when a `MOVED` error is received,
|
||||
* the client will resend the command instantly to the node returned together with
|
||||
* the `MOVED` error. However, sometimes it takes time for a cluster to become
|
||||
* state stabilized after a failover, so adding a delay before resending can
|
||||
* prevent a ping pong effect.
|
||||
*
|
||||
* @default 0
|
||||
*/
|
||||
retryDelayOnMoved?: number | undefined;
|
||||
/**
|
||||
* The milliseconds before a timeout occurs while refreshing
|
||||
* slots from the cluster.
|
||||
*
|
||||
* @default 1000
|
||||
*/
|
||||
slotsRefreshTimeout?: number | undefined;
|
||||
/**
|
||||
* The milliseconds between every automatic slots refresh.
|
||||
*
|
||||
* @default 5000
|
||||
*/
|
||||
slotsRefreshInterval?: number | undefined;
|
||||
/**
|
||||
* Use sharded subscribers instead of a single subscriber.
|
||||
*
|
||||
* If sharded subscribers are used, then one additional subscriber connection per master node
|
||||
* is established. If you don't plan to use SPUBLISH/SSUBSCRIBE, then this should be disabled.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
shardedSubscribers?: boolean | undefined;
|
||||
/**
|
||||
* When a cluster node connection is closed, this function will be called
|
||||
* to determine the retry delay (in ms). Returning `null` or a non-number
|
||||
* disables reconnection for that node.
|
||||
*
|
||||
* By default this is `null`, meaning cluster nodes will NOT automatically
|
||||
* reconnect — the cluster relies on `MOVED` errors to refresh topology.
|
||||
* Set this to enable reconnection, e.g. for replica nodes that restart
|
||||
* without any slot changes.
|
||||
*
|
||||
* @example
|
||||
* clusterNodeRetryStrategy: (times) => Math.min(times * 100, 3000)
|
||||
*
|
||||
* @default null
|
||||
*/
|
||||
clusterNodeRetryStrategy?: ClusterNodeRetryStrategy;
|
||||
/**
|
||||
* Passed to the constructor of `Redis`
|
||||
*
|
||||
* @default null
|
||||
*/
|
||||
redisOptions?: Omit<RedisOptions, "port" | "host" | "path" | "sentinels" | "retryStrategy" | "enableOfflineQueue" | "readOnly" | "himportFieldsets"> | undefined;
|
||||
/**
|
||||
* By default, When a new Cluster instance is created,
|
||||
* it will connect to the Redis cluster automatically.
|
||||
* If you want to keep the instance disconnected until the first command is called,
|
||||
* set this option to `true`.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
lazyConnect?: boolean | undefined;
|
||||
/**
|
||||
* Discover nodes using SRV records
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
useSRVRecords?: boolean | undefined;
|
||||
/**
|
||||
* SRV records will be resolved via this function.
|
||||
*
|
||||
* You may provide a custom `resolveSrv` function when you want to customize
|
||||
* the cache behavior of the default function.
|
||||
*
|
||||
* @default require('dns').resolveSrv
|
||||
*/
|
||||
resolveSrv?: DNSResolveSrvFunction | undefined;
|
||||
/**
|
||||
* Hostnames will be resolved to IP addresses via this function.
|
||||
* This is needed when the addresses of startup nodes are hostnames instead
|
||||
* of IPs.
|
||||
*
|
||||
* You may provide a custom `lookup` function when you want to customize
|
||||
* the cache behavior of the default function.
|
||||
*
|
||||
* @default require('dns').lookup
|
||||
*/
|
||||
dnsLookup?: DNSLookupFunction | undefined;
|
||||
natMap?: NatMap | undefined;
|
||||
/**
|
||||
* See Redis class.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
enableAutoPipelining?: boolean | undefined;
|
||||
/**
|
||||
* See Redis class.
|
||||
*
|
||||
* @default []
|
||||
*/
|
||||
autoPipeliningIgnoredCommands?: string[] | undefined;
|
||||
/**
|
||||
* Custom LUA commands
|
||||
*/
|
||||
scripts?: Record<string, {
|
||||
lua: string;
|
||||
numberOfKeys?: number;
|
||||
readOnly?: boolean;
|
||||
}> | undefined;
|
||||
/**
|
||||
* Managed-fieldset support is experimental and requires Redis 8.10 or newer.
|
||||
*
|
||||
* Long-lived HIMPORT fieldsets managed across all current and future master
|
||||
* connections for the lifetime of this Cluster client.
|
||||
* Configure this option at the top level, not under `redisOptions`.
|
||||
*
|
||||
* When a managed `HIMPORT SET` needs fieldset preparation or recovery,
|
||||
* later commands issued on this Cluster client may be sent before that SET
|
||||
* resumes. Await the SET before issuing commands that depend on its write.
|
||||
*
|
||||
* Explicit pipelines containing a managed `HIMPORT SET` wait for required
|
||||
* fieldset preparation on the selected master before the batch is sent.
|
||||
*
|
||||
* Background preparation failures do not prevent the connection from
|
||||
* becoming ready and are reported through the `node error` event. A
|
||||
* dependent managed `HIMPORT SET` retries preparation and rejects if
|
||||
* recovery fails.
|
||||
*
|
||||
* Direct `HIMPORT PREPARE`, `DISCARD`, and `DISCARDALL` calls fan out to all
|
||||
* current masters. Within an explicit pipeline, these commands remain
|
||||
* connection-affine and are not managed.
|
||||
*
|
||||
* Use explicit HIMPORT commands on a separate unconfigured client for
|
||||
* bounded, manually managed batches.
|
||||
*
|
||||
* @default undefined
|
||||
* @experimental
|
||||
*/
|
||||
himportFieldsets?: readonly HimportFieldset[] | undefined;
|
||||
}
|
||||
export type ClusterOptionsWithReplyMapping<Mapping extends ReplyMappingMode> = ClusterOptions & {
|
||||
redisOptions?: ClusterOptions["redisOptions"] & {
|
||||
replyMapping?: Mapping;
|
||||
};
|
||||
};
|
||||
export declare const DEFAULT_CLUSTER_OPTIONS: ClusterOptions;
|
||||
23
node_modules/ioredis/built/cluster/ClusterOptions.js
generated
vendored
Normal file
23
node_modules/ioredis/built/cluster/ClusterOptions.js
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.DEFAULT_CLUSTER_OPTIONS = void 0;
|
||||
const dns_1 = require("dns");
|
||||
exports.DEFAULT_CLUSTER_OPTIONS = {
|
||||
clusterRetryStrategy: (times) => Math.min(100 + times * 2, 2000),
|
||||
clusterNodeRetryStrategy: null,
|
||||
enableOfflineQueue: true,
|
||||
enableReadyCheck: true,
|
||||
scaleReads: "master",
|
||||
maxRedirections: 16,
|
||||
retryDelayOnMoved: 0,
|
||||
retryDelayOnFailover: 100,
|
||||
retryDelayOnClusterDown: 100,
|
||||
retryDelayOnTryAgain: 100,
|
||||
slotsRefreshTimeout: 1000,
|
||||
useSRVRecords: false,
|
||||
resolveSrv: dns_1.resolveSrv,
|
||||
dnsLookup: dns_1.lookup,
|
||||
enableAutoPipelining: false,
|
||||
autoPipeliningIgnoredCommands: [],
|
||||
shardedSubscribers: false,
|
||||
};
|
||||
29
node_modules/ioredis/built/cluster/ClusterSubscriber.d.ts
generated
vendored
Normal file
29
node_modules/ioredis/built/cluster/ClusterSubscriber.d.ts
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
/// <reference types="node" />
|
||||
import { EventEmitter } from "events";
|
||||
import ConnectionPool from "./ConnectionPool";
|
||||
export default class ClusterSubscriber {
|
||||
private connectionPool;
|
||||
private emitter;
|
||||
private isSharded;
|
||||
private started;
|
||||
private subscriber;
|
||||
private lastActiveSubscriber;
|
||||
private slotRange;
|
||||
constructor(connectionPool: ConnectionPool, emitter: EventEmitter, isSharded?: boolean);
|
||||
getInstance(): any;
|
||||
/**
|
||||
* Associate this subscriber to a specific slot range.
|
||||
*
|
||||
* Returns the range or an empty array if the slot range couldn't be associated.
|
||||
*
|
||||
* BTW: This is more for debugging and testing purposes.
|
||||
*
|
||||
* @param range
|
||||
*/
|
||||
associateSlotRange(range: number[]): number[];
|
||||
start(): void;
|
||||
stop(): void;
|
||||
isStarted(): boolean;
|
||||
private onSubscriberEnd;
|
||||
private selectSubscriber;
|
||||
}
|
||||
225
node_modules/ioredis/built/cluster/ClusterSubscriber.js
generated
vendored
Normal file
225
node_modules/ioredis/built/cluster/ClusterSubscriber.js
generated
vendored
Normal file
@@ -0,0 +1,225 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const util_1 = require("./util");
|
||||
const utils_1 = require("../utils");
|
||||
const Redis_1 = require("../Redis");
|
||||
const debug = (0, utils_1.Debug)("cluster:subscriber");
|
||||
class ClusterSubscriber {
|
||||
constructor(connectionPool, emitter, isSharded = false) {
|
||||
this.connectionPool = connectionPool;
|
||||
this.emitter = emitter;
|
||||
this.isSharded = isSharded;
|
||||
this.started = false;
|
||||
//There is only one connection for the entire pool
|
||||
this.subscriber = null;
|
||||
//The slot range for which this subscriber is responsible
|
||||
this.slotRange = [];
|
||||
this.onSubscriberEnd = () => {
|
||||
if (!this.started) {
|
||||
debug("subscriber has disconnected, but ClusterSubscriber is not started, so not reconnecting.");
|
||||
return;
|
||||
}
|
||||
// If the subscriber closes whilst it's still the active connection,
|
||||
// we might as well try to connecting to a new node if possible to
|
||||
// minimise the number of missed publishes.
|
||||
debug("subscriber has disconnected, selecting a new one...");
|
||||
this.selectSubscriber();
|
||||
};
|
||||
// If the current node we're using as the subscriber disappears
|
||||
// from the node pool for some reason, we will select a new one
|
||||
// to connect to.
|
||||
// Note that this event is only triggered if the connection to
|
||||
// the node has been used; cluster subscriptions are setup with
|
||||
// lazyConnect = true. It's possible for the subscriber node to
|
||||
// disappear without this method being called!
|
||||
// See https://github.com/luin/ioredis/pull/1589
|
||||
this.connectionPool.on("-node", (_, key) => {
|
||||
if (!this.started || !this.subscriber) {
|
||||
return;
|
||||
}
|
||||
if ((0, util_1.getNodeKey)(this.subscriber.options) === key) {
|
||||
debug("subscriber has left, selecting a new one...");
|
||||
this.selectSubscriber();
|
||||
}
|
||||
});
|
||||
this.connectionPool.on("+node", () => {
|
||||
if (!this.started || this.subscriber) {
|
||||
return;
|
||||
}
|
||||
debug("a new node is discovered and there is no subscriber, selecting a new one...");
|
||||
this.selectSubscriber();
|
||||
});
|
||||
}
|
||||
getInstance() {
|
||||
return this.subscriber;
|
||||
}
|
||||
/**
|
||||
* Associate this subscriber to a specific slot range.
|
||||
*
|
||||
* Returns the range or an empty array if the slot range couldn't be associated.
|
||||
*
|
||||
* BTW: This is more for debugging and testing purposes.
|
||||
*
|
||||
* @param range
|
||||
*/
|
||||
associateSlotRange(range) {
|
||||
if (this.isSharded) {
|
||||
this.slotRange = range;
|
||||
}
|
||||
return this.slotRange;
|
||||
}
|
||||
start() {
|
||||
this.started = true;
|
||||
this.selectSubscriber();
|
||||
debug("started");
|
||||
}
|
||||
stop() {
|
||||
this.started = false;
|
||||
if (this.subscriber) {
|
||||
this.subscriber.disconnect();
|
||||
this.subscriber = null;
|
||||
}
|
||||
}
|
||||
isStarted() {
|
||||
return this.started;
|
||||
}
|
||||
selectSubscriber() {
|
||||
const lastActiveSubscriber = this.lastActiveSubscriber;
|
||||
// Disconnect the previous subscriber even if there
|
||||
// will not be a new one.
|
||||
if (lastActiveSubscriber) {
|
||||
lastActiveSubscriber.off("end", this.onSubscriberEnd);
|
||||
lastActiveSubscriber.disconnect();
|
||||
}
|
||||
if (this.subscriber) {
|
||||
this.subscriber.off("end", this.onSubscriberEnd);
|
||||
this.subscriber.disconnect();
|
||||
}
|
||||
const sampleNode = (0, utils_1.sample)(this.connectionPool.getNodes());
|
||||
if (!sampleNode) {
|
||||
debug("selecting subscriber failed since there is no node discovered in the cluster yet");
|
||||
this.subscriber = null;
|
||||
return;
|
||||
}
|
||||
const { options } = sampleNode;
|
||||
debug("selected a subscriber %s:%s", options.host, options.port);
|
||||
/*
|
||||
* Create a specialized Redis connection for the subscription.
|
||||
* Note that auto reconnection is enabled here.
|
||||
*
|
||||
* `enableReadyCheck` is also enabled because although subscription is allowed
|
||||
* while redis is loading data from the disk, we can check if the password
|
||||
* provided for the subscriber is correct, and if not, the current subscriber
|
||||
* will be disconnected and a new subscriber will be selected.
|
||||
*/
|
||||
let connectionPrefix = "subscriber";
|
||||
if (this.isSharded)
|
||||
connectionPrefix = "ssubscriber";
|
||||
this.subscriber = new Redis_1.default({
|
||||
port: options.port,
|
||||
host: options.host,
|
||||
username: options.username,
|
||||
password: options.password,
|
||||
enableReadyCheck: true,
|
||||
connectionName: (0, util_1.getConnectionName)(connectionPrefix, options.connectionName),
|
||||
lazyConnect: true,
|
||||
tls: options.tls,
|
||||
protocol: options.protocol,
|
||||
replyMapping: options.replyMapping,
|
||||
// Don't try to reconnect the subscriber connection. If the connection fails
|
||||
// we will get an end event (handled below), at which point we'll pick a new
|
||||
// node from the pool and try to connect to that as the subscriber connection.
|
||||
retryStrategy: null,
|
||||
});
|
||||
// Ignore the errors since they're handled in the connection pool.
|
||||
this.subscriber.on("error", utils_1.noop);
|
||||
this.subscriber.on("moved", () => {
|
||||
this.emitter.emit("forceRefresh");
|
||||
});
|
||||
// The node we lost connection to may not come back up in a
|
||||
// reasonable amount of time (e.g. a slave that's taken down
|
||||
// for maintainence), we could potentially miss many published
|
||||
// messages so we should reconnect as quickly as possible, to
|
||||
// a different node if needed.
|
||||
this.subscriber.once("end", this.onSubscriberEnd);
|
||||
// Re-subscribe previous channels
|
||||
const previousChannels = { subscribe: [], psubscribe: [], ssubscribe: [] };
|
||||
if (lastActiveSubscriber) {
|
||||
const condition = lastActiveSubscriber.condition || lastActiveSubscriber.prevCondition;
|
||||
if (condition && condition.subscriber) {
|
||||
previousChannels.subscribe = condition.subscriber.channels("subscribe");
|
||||
previousChannels.psubscribe =
|
||||
condition.subscriber.channels("psubscribe");
|
||||
previousChannels.ssubscribe =
|
||||
condition.subscriber.channels("ssubscribe");
|
||||
}
|
||||
}
|
||||
if (previousChannels.subscribe.length ||
|
||||
previousChannels.psubscribe.length ||
|
||||
previousChannels.ssubscribe.length) {
|
||||
let pending = 0;
|
||||
for (const type of ["subscribe", "psubscribe", "ssubscribe"]) {
|
||||
const channels = previousChannels[type];
|
||||
if (channels.length == 0) {
|
||||
continue;
|
||||
}
|
||||
debug("%s %d channels", type, channels.length);
|
||||
if (type === "ssubscribe") {
|
||||
for (const channel of channels) {
|
||||
pending += 1;
|
||||
this.subscriber[type](channel)
|
||||
.then(() => {
|
||||
if (!--pending) {
|
||||
this.lastActiveSubscriber = this.subscriber;
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
// TODO: should probably disconnect the subscriber and try again.
|
||||
debug("failed to ssubscribe to channel: %s", channel);
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
pending += 1;
|
||||
this.subscriber[type](channels)
|
||||
.then(() => {
|
||||
if (!--pending) {
|
||||
this.lastActiveSubscriber = this.subscriber;
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
// TODO: should probably disconnect the subscriber and try again.
|
||||
debug("failed to %s %d channels", type, channels.length);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.lastActiveSubscriber = this.subscriber;
|
||||
}
|
||||
for (const event of [
|
||||
"message",
|
||||
"messageBuffer",
|
||||
]) {
|
||||
this.subscriber.on(event, (arg1, arg2) => {
|
||||
this.emitter.emit(event, arg1, arg2);
|
||||
});
|
||||
}
|
||||
for (const event of ["pmessage", "pmessageBuffer"]) {
|
||||
this.subscriber.on(event, (arg1, arg2, arg3) => {
|
||||
this.emitter.emit(event, arg1, arg2, arg3);
|
||||
});
|
||||
}
|
||||
if (this.isSharded == true) {
|
||||
for (const event of [
|
||||
"smessage",
|
||||
"smessageBuffer",
|
||||
]) {
|
||||
this.subscriber.on(event, (arg1, arg2) => {
|
||||
this.emitter.emit(event, arg1, arg2);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.default = ClusterSubscriber;
|
||||
110
node_modules/ioredis/built/cluster/ClusterSubscriberGroup.d.ts
generated
vendored
Normal file
110
node_modules/ioredis/built/cluster/ClusterSubscriberGroup.d.ts
generated
vendored
Normal file
@@ -0,0 +1,110 @@
|
||||
/// <reference types="node" />
|
||||
/// <reference types="node" />
|
||||
/// <reference types="node" />
|
||||
import * as EventEmitter from "events";
|
||||
import ShardedSubscriber from "./ShardedSubscriber";
|
||||
import { ClusterOptions } from "./ClusterOptions";
|
||||
/**
|
||||
* Redis distinguishes between "normal" and sharded PubSub. When using the normal PubSub feature,
|
||||
* exactly one subscriber exists per cluster instance because the Redis cluster bus forwards
|
||||
* messages between shards. Sharded PubSub removes this limitation by making each shard
|
||||
* responsible for its own messages.
|
||||
*
|
||||
* This class coordinates one ShardedSubscriber per master node in the cluster, providing
|
||||
* sharded PubSub support while keeping the public API backward compatible.
|
||||
*/
|
||||
export default class ClusterSubscriberGroup {
|
||||
private readonly subscriberGroupEmitter;
|
||||
private readonly options;
|
||||
private shardedSubscribers;
|
||||
private clusterSlots;
|
||||
private subscriberToSlotsIndex;
|
||||
private channels;
|
||||
private failedAttemptsByNode;
|
||||
private isResetting;
|
||||
private pendingReset;
|
||||
private static readonly MAX_RETRY_ATTEMPTS;
|
||||
private static readonly MAX_BACKOFF_MS;
|
||||
private static readonly BASE_BACKOFF_MS;
|
||||
/**
|
||||
* Register callbacks
|
||||
*
|
||||
* @param cluster
|
||||
*/
|
||||
constructor(subscriberGroupEmitter: EventEmitter, options: ClusterOptions);
|
||||
/**
|
||||
* Get the responsible subscriber.
|
||||
*
|
||||
* @param slot
|
||||
*/
|
||||
getResponsibleSubscriber(slot: number): ShardedSubscriber | undefined;
|
||||
/**
|
||||
* Adds a channel for which this subscriber group is responsible
|
||||
*
|
||||
* @param channels
|
||||
*/
|
||||
addChannels(channels: (string | Buffer)[]): number;
|
||||
/**
|
||||
* Removes channels for which the subscriber group is responsible by optionally unsubscribing
|
||||
* @param channels
|
||||
*/
|
||||
removeChannels(channels: (string | Buffer)[]): number;
|
||||
/**
|
||||
* Disconnect all subscribers and clear some of the internal state.
|
||||
*/
|
||||
stop(): void;
|
||||
/**
|
||||
* Start all not yet started subscribers
|
||||
*/
|
||||
start(): Promise<any[]>;
|
||||
/**
|
||||
* Resets the subscriber group by disconnecting all subscribers that are no longer needed and connecting new ones.
|
||||
*/
|
||||
reset(clusterSlots: string[][], clusterNodes: any[]): Promise<void>;
|
||||
/**
|
||||
* Refreshes the subscriber-related slot ranges
|
||||
*
|
||||
* Returns false if no refresh was needed
|
||||
*
|
||||
* @param targetSlots
|
||||
*/
|
||||
private _refreshSlots;
|
||||
/**
|
||||
* Resubscribes to the previous channels
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
private _resubscribe;
|
||||
/**
|
||||
* Deep equality of the cluster slots objects
|
||||
*
|
||||
* @param other
|
||||
* @private
|
||||
*/
|
||||
private _slotsAreEqual;
|
||||
/**
|
||||
* Checks if any subscribers are in an unhealthy state.
|
||||
*
|
||||
* A subscriber is considered unhealthy if:
|
||||
* - It exists but is not started (failed/disconnected)
|
||||
* - It's missing entirely for a node that should have one
|
||||
*
|
||||
* @returns true if any subscribers need to be recreated
|
||||
*/
|
||||
private hasUnhealthySubscribers;
|
||||
/**
|
||||
* Handles failed subscriber connections by emitting an event to refresh the slots cache
|
||||
* after a backoff period.
|
||||
*
|
||||
* @param error
|
||||
* @param nodeKey
|
||||
*/
|
||||
private handleSubscriberConnectFailed;
|
||||
/**
|
||||
* Handles successful subscriber connections by resetting the failed attempts counter.
|
||||
*
|
||||
* @param nodeKey
|
||||
*/
|
||||
private handleSubscriberConnectSucceeded;
|
||||
private shouldStartSubscriber;
|
||||
}
|
||||
373
node_modules/ioredis/built/cluster/ClusterSubscriberGroup.js
generated
vendored
Normal file
373
node_modules/ioredis/built/cluster/ClusterSubscriberGroup.js
generated
vendored
Normal file
@@ -0,0 +1,373 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const utils_1 = require("../utils");
|
||||
const util_1 = require("./util");
|
||||
const calculateSlot = require("cluster-key-slot");
|
||||
const ShardedSubscriber_1 = require("./ShardedSubscriber");
|
||||
const debug = (0, utils_1.Debug)("cluster:subscriberGroup");
|
||||
/**
|
||||
* Redis distinguishes between "normal" and sharded PubSub. When using the normal PubSub feature,
|
||||
* exactly one subscriber exists per cluster instance because the Redis cluster bus forwards
|
||||
* messages between shards. Sharded PubSub removes this limitation by making each shard
|
||||
* responsible for its own messages.
|
||||
*
|
||||
* This class coordinates one ShardedSubscriber per master node in the cluster, providing
|
||||
* sharded PubSub support while keeping the public API backward compatible.
|
||||
*/
|
||||
class ClusterSubscriberGroup {
|
||||
/**
|
||||
* Register callbacks
|
||||
*
|
||||
* @param cluster
|
||||
*/
|
||||
constructor(subscriberGroupEmitter, options) {
|
||||
this.subscriberGroupEmitter = subscriberGroupEmitter;
|
||||
this.options = options;
|
||||
this.shardedSubscribers = new Map();
|
||||
this.clusterSlots = [];
|
||||
// Simple [min, max] slot ranges aren't enough because you can migrate single slots
|
||||
this.subscriberToSlotsIndex = new Map();
|
||||
this.channels = new Map();
|
||||
this.failedAttemptsByNode = new Map();
|
||||
// Only latest pending reset kept; throttled by refreshSlotsCache's isRefreshing + backoff delay
|
||||
this.isResetting = false;
|
||||
this.pendingReset = null;
|
||||
/**
|
||||
* Handles failed subscriber connections by emitting an event to refresh the slots cache
|
||||
* after a backoff period.
|
||||
*
|
||||
* @param error
|
||||
* @param nodeKey
|
||||
*/
|
||||
this.handleSubscriberConnectFailed = (error, nodeKey) => {
|
||||
const currentAttempts = this.failedAttemptsByNode.get(nodeKey) || 0;
|
||||
const failedAttempts = currentAttempts + 1;
|
||||
this.failedAttemptsByNode.set(nodeKey, failedAttempts);
|
||||
const attempts = Math.min(failedAttempts, ClusterSubscriberGroup.MAX_RETRY_ATTEMPTS);
|
||||
const backoff = Math.min(ClusterSubscriberGroup.BASE_BACKOFF_MS * 2 ** attempts, ClusterSubscriberGroup.MAX_BACKOFF_MS);
|
||||
const jitter = Math.floor((Math.random() - 0.5) * (backoff * 0.5));
|
||||
const delay = Math.max(0, backoff + jitter);
|
||||
debug("Failed to connect subscriber for %s. Refreshing slots in %dms", nodeKey, delay);
|
||||
this.subscriberGroupEmitter.emit("subscriberConnectFailed", {
|
||||
delay,
|
||||
error,
|
||||
});
|
||||
};
|
||||
/**
|
||||
* Handles successful subscriber connections by resetting the failed attempts counter.
|
||||
*
|
||||
* @param nodeKey
|
||||
*/
|
||||
this.handleSubscriberConnectSucceeded = (nodeKey) => {
|
||||
this.failedAttemptsByNode.delete(nodeKey);
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Get the responsible subscriber.
|
||||
*
|
||||
* @param slot
|
||||
*/
|
||||
getResponsibleSubscriber(slot) {
|
||||
const nodeKey = this.clusterSlots[slot][0];
|
||||
const sub = this.shardedSubscribers.get(nodeKey);
|
||||
if (sub && sub.subscriberStatus === "idle") {
|
||||
sub
|
||||
.start()
|
||||
.then(() => {
|
||||
this.handleSubscriberConnectSucceeded(sub.getNodeKey());
|
||||
})
|
||||
.catch((err) => {
|
||||
this.handleSubscriberConnectFailed(err, sub.getNodeKey());
|
||||
});
|
||||
}
|
||||
return sub;
|
||||
}
|
||||
/**
|
||||
* Adds a channel for which this subscriber group is responsible
|
||||
*
|
||||
* @param channels
|
||||
*/
|
||||
addChannels(channels) {
|
||||
const slot = calculateSlot(channels[0]);
|
||||
// Check if the all channels belong to the same slot and otherwise reject the operation
|
||||
for (const c of channels) {
|
||||
if (calculateSlot(c) !== slot) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
const currChannels = this.channels.get(slot);
|
||||
if (!currChannels) {
|
||||
this.channels.set(slot, channels);
|
||||
}
|
||||
else {
|
||||
this.channels.set(slot, currChannels.concat(channels));
|
||||
}
|
||||
return Array.from(this.channels.values()).reduce((sum, array) => sum + array.length, 0);
|
||||
}
|
||||
/**
|
||||
* Removes channels for which the subscriber group is responsible by optionally unsubscribing
|
||||
* @param channels
|
||||
*/
|
||||
removeChannels(channels) {
|
||||
const slot = calculateSlot(channels[0]);
|
||||
// Check if the all channels belong to the same slot and otherwise reject the operation
|
||||
for (const c of channels) {
|
||||
if (calculateSlot(c) !== slot) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
const slotChannels = this.channels.get(slot);
|
||||
if (slotChannels) {
|
||||
const updatedChannels = slotChannels.filter((c) => !channels.includes(c));
|
||||
this.channels.set(slot, updatedChannels);
|
||||
}
|
||||
return Array.from(this.channels.values()).reduce((sum, array) => sum + array.length, 0);
|
||||
}
|
||||
/**
|
||||
* Disconnect all subscribers and clear some of the internal state.
|
||||
*/
|
||||
stop() {
|
||||
for (const s of this.shardedSubscribers.values()) {
|
||||
s.stop();
|
||||
}
|
||||
// Clear subscriber instances and pending operations.
|
||||
// Channels are preserved for resubscription on reconnect.
|
||||
this.pendingReset = null;
|
||||
this.shardedSubscribers.clear();
|
||||
this.subscriberToSlotsIndex.clear();
|
||||
}
|
||||
/**
|
||||
* Start all not yet started subscribers
|
||||
*/
|
||||
start() {
|
||||
const startPromises = [];
|
||||
for (const s of this.shardedSubscribers.values()) {
|
||||
if (this.shouldStartSubscriber(s)) {
|
||||
startPromises.push(s
|
||||
.start()
|
||||
.then(() => {
|
||||
this.handleSubscriberConnectSucceeded(s.getNodeKey());
|
||||
})
|
||||
.catch((err) => {
|
||||
this.handleSubscriberConnectFailed(err, s.getNodeKey());
|
||||
}));
|
||||
this.subscriberGroupEmitter.emit("+subscriber");
|
||||
}
|
||||
}
|
||||
return Promise.all(startPromises);
|
||||
}
|
||||
/**
|
||||
* Resets the subscriber group by disconnecting all subscribers that are no longer needed and connecting new ones.
|
||||
*/
|
||||
async reset(clusterSlots, clusterNodes) {
|
||||
if (this.isResetting) {
|
||||
this.pendingReset = { slots: clusterSlots, nodes: clusterNodes };
|
||||
return;
|
||||
}
|
||||
this.isResetting = true;
|
||||
try {
|
||||
const hasTopologyChanged = this._refreshSlots(clusterSlots);
|
||||
const hasFailedSubscribers = this.hasUnhealthySubscribers();
|
||||
if (!hasTopologyChanged && !hasFailedSubscribers) {
|
||||
debug("No topology change detected or failed subscribers. Skipping reset.");
|
||||
return;
|
||||
}
|
||||
// For each of the sharded subscribers
|
||||
for (const [nodeKey, shardedSubscriber] of this.shardedSubscribers) {
|
||||
if (
|
||||
// If the subscriber is still responsible for a slot range and is healthy then keep it
|
||||
this.subscriberToSlotsIndex.has(nodeKey) &&
|
||||
shardedSubscriber.isHealthy()) {
|
||||
debug("Skipping deleting subscriber for %s", nodeKey);
|
||||
continue;
|
||||
}
|
||||
debug("Removing subscriber for %s", nodeKey);
|
||||
// Otherwise stop the subscriber and remove it
|
||||
shardedSubscriber.stop();
|
||||
this.shardedSubscribers.delete(nodeKey);
|
||||
this.subscriberGroupEmitter.emit("-subscriber");
|
||||
}
|
||||
const startPromises = [];
|
||||
// For each node in slots cache
|
||||
for (const [nodeKey, _] of this.subscriberToSlotsIndex) {
|
||||
const existingSubscriber = this.shardedSubscribers.get(nodeKey);
|
||||
// If we already have a subscriber for this node, only ensure it is healthy
|
||||
// when it now owns slots with active channel subscriptions.
|
||||
if (existingSubscriber && existingSubscriber.isHealthy()) {
|
||||
debug("Skipping creating new subscriber for %s", nodeKey);
|
||||
if (!existingSubscriber.isStarted() &&
|
||||
this.shouldStartSubscriber(existingSubscriber)) {
|
||||
startPromises.push(existingSubscriber
|
||||
.start()
|
||||
.then(() => {
|
||||
this.handleSubscriberConnectSucceeded(nodeKey);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.handleSubscriberConnectFailed(error, nodeKey);
|
||||
}));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// If we have an existing subscriber but it is not healthy, stop it
|
||||
if (existingSubscriber && !existingSubscriber.isHealthy()) {
|
||||
debug("Replacing subscriber for %s", nodeKey);
|
||||
existingSubscriber.stop();
|
||||
this.shardedSubscribers.delete(nodeKey);
|
||||
this.subscriberGroupEmitter.emit("-subscriber");
|
||||
}
|
||||
debug("Creating new subscriber for %s", nodeKey);
|
||||
// Otherwise create a new subscriber
|
||||
const redis = clusterNodes.find((node) => {
|
||||
return (0, util_1.getNodeKey)(node.options) === nodeKey;
|
||||
});
|
||||
if (!redis) {
|
||||
debug("Failed to find node for key %s", nodeKey);
|
||||
continue;
|
||||
}
|
||||
const sub = new ShardedSubscriber_1.default(this.subscriberGroupEmitter, redis.options, this.options.redisOptions);
|
||||
this.shardedSubscribers.set(nodeKey, sub);
|
||||
if (this.shouldStartSubscriber(sub)) {
|
||||
startPromises.push(sub
|
||||
.start()
|
||||
.then(() => {
|
||||
this.handleSubscriberConnectSucceeded(nodeKey);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.handleSubscriberConnectFailed(error, nodeKey);
|
||||
}));
|
||||
}
|
||||
this.subscriberGroupEmitter.emit("+subscriber");
|
||||
}
|
||||
// It's vital to await the start promises before resubscribing
|
||||
// Otherwise we might try to resubscribe to a subscriber that is not yet connected
|
||||
// This can cause a race condition
|
||||
await Promise.all(startPromises);
|
||||
this._resubscribe();
|
||||
this.subscriberGroupEmitter.emit("subscribersReady");
|
||||
}
|
||||
finally {
|
||||
this.isResetting = false;
|
||||
if (this.pendingReset) {
|
||||
const { slots, nodes } = this.pendingReset;
|
||||
this.pendingReset = null;
|
||||
await this.reset(slots, nodes);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Refreshes the subscriber-related slot ranges
|
||||
*
|
||||
* Returns false if no refresh was needed
|
||||
*
|
||||
* @param targetSlots
|
||||
*/
|
||||
_refreshSlots(targetSlots) {
|
||||
//If there was an actual change, then reassign the slot ranges
|
||||
// Also rebuild if subscriberToSlotsIndex is empty (e.g., after stop() was called)
|
||||
if (this._slotsAreEqual(targetSlots) && this.subscriberToSlotsIndex.size > 0) {
|
||||
debug("Nothing to refresh because the new cluster map is equal to the previous one.");
|
||||
return false;
|
||||
}
|
||||
debug("Refreshing the slots of the subscriber group.");
|
||||
//Rebuild the slots index
|
||||
this.subscriberToSlotsIndex = new Map();
|
||||
for (let slot = 0; slot < targetSlots.length; slot++) {
|
||||
const node = targetSlots[slot][0];
|
||||
if (!this.subscriberToSlotsIndex.has(node)) {
|
||||
this.subscriberToSlotsIndex.set(node, []);
|
||||
}
|
||||
this.subscriberToSlotsIndex.get(node).push(Number(slot));
|
||||
}
|
||||
//Update the cached slots map
|
||||
this.clusterSlots = JSON.parse(JSON.stringify(targetSlots));
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Resubscribes to the previous channels
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_resubscribe() {
|
||||
if (this.shardedSubscribers) {
|
||||
this.shardedSubscribers.forEach((s, nodeKey) => {
|
||||
const subscriberSlots = this.subscriberToSlotsIndex.get(nodeKey);
|
||||
if (subscriberSlots) {
|
||||
//Resubscribe on the underlying connection
|
||||
subscriberSlots.forEach((ss) => {
|
||||
//Might return null if being disconnected
|
||||
const redis = s.getInstance();
|
||||
const channels = this.channels.get(ss);
|
||||
if (channels && channels.length > 0) {
|
||||
if (!redis || redis.status === "end") {
|
||||
return;
|
||||
}
|
||||
if (redis.status === "ready") {
|
||||
redis.ssubscribe(...channels).catch((err) => {
|
||||
// TODO: Should we emit an error event here?
|
||||
debug("Failed to ssubscribe on node %s: %s", nodeKey, err);
|
||||
});
|
||||
}
|
||||
else {
|
||||
redis.once("ready", () => {
|
||||
redis.ssubscribe(...channels).catch((err) => {
|
||||
// TODO: Should we emit an error event here?
|
||||
debug("Failed to ssubscribe on node %s: %s", nodeKey, err);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Deep equality of the cluster slots objects
|
||||
*
|
||||
* @param other
|
||||
* @private
|
||||
*/
|
||||
_slotsAreEqual(other) {
|
||||
if (this.clusterSlots === undefined) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
return JSON.stringify(this.clusterSlots) === JSON.stringify(other);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Checks if any subscribers are in an unhealthy state.
|
||||
*
|
||||
* A subscriber is considered unhealthy if:
|
||||
* - It exists but is not started (failed/disconnected)
|
||||
* - It's missing entirely for a node that should have one
|
||||
*
|
||||
* @returns true if any subscribers need to be recreated
|
||||
*/
|
||||
hasUnhealthySubscribers() {
|
||||
const hasFailedSubscribers = Array.from(this.shardedSubscribers.values()).some((sub) => !sub.isHealthy());
|
||||
const hasMissingSubscribers = Array.from(this.subscriberToSlotsIndex.keys()).some((nodeKey) => !this.shardedSubscribers.has(nodeKey));
|
||||
return hasFailedSubscribers || hasMissingSubscribers;
|
||||
}
|
||||
shouldStartSubscriber(sub) {
|
||||
if (sub.isStarted()) {
|
||||
return false;
|
||||
}
|
||||
if (!sub.isLazyConnect()) {
|
||||
return true;
|
||||
}
|
||||
const subscriberSlots = this.subscriberToSlotsIndex.get(sub.getNodeKey());
|
||||
if (!subscriberSlots) {
|
||||
return false;
|
||||
}
|
||||
return subscriberSlots.some((slot) => {
|
||||
const channels = this.channels.get(slot);
|
||||
return Boolean(channels && channels.length > 0);
|
||||
});
|
||||
}
|
||||
}
|
||||
// Retry strategy
|
||||
ClusterSubscriberGroup.MAX_RETRY_ATTEMPTS = 10;
|
||||
ClusterSubscriberGroup.MAX_BACKOFF_MS = 2000;
|
||||
ClusterSubscriberGroup.BASE_BACKOFF_MS = 100;
|
||||
exports.default = ClusterSubscriberGroup;
|
||||
51
node_modules/ioredis/built/cluster/ConnectionPool.d.ts
generated
vendored
Normal file
51
node_modules/ioredis/built/cluster/ConnectionPool.d.ts
generated
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
/// <reference types="node" />
|
||||
import { EventEmitter } from "events";
|
||||
import { RedisOptions, NodeKey, NodeRole } from "./util";
|
||||
import { ClusterOptions, ClusterNodeRetryStrategy } from "./ClusterOptions";
|
||||
import Redis from "../Redis";
|
||||
export interface ConnectionPoolHooks {
|
||||
onCreate?: (redis: Redis, readOnly: boolean) => void;
|
||||
onRoleChange?: (redis: Redis, key: NodeKey, readOnly: boolean) => void;
|
||||
onRemove?: (redis: Redis) => void;
|
||||
}
|
||||
export default class ConnectionPool extends EventEmitter {
|
||||
private redisOptions;
|
||||
private clusterNodeRetryStrategy;
|
||||
private hooks;
|
||||
private nodes;
|
||||
private specifiedOptions;
|
||||
constructor(redisOptions: NonNullable<ClusterOptions["redisOptions"]>, clusterNodeRetryStrategy?: ClusterNodeRetryStrategy, hooks?: ConnectionPoolHooks);
|
||||
getNodes(role?: NodeRole): Redis[];
|
||||
getInstanceByKey(key: NodeKey): Redis;
|
||||
getSampleInstance(role: NodeRole): Redis;
|
||||
/**
|
||||
* Add a master node to the pool
|
||||
* @param node
|
||||
*/
|
||||
addMasterNode(node: RedisOptions): boolean;
|
||||
/**
|
||||
* Creates a Redis connection instance from the node options
|
||||
* @param node
|
||||
* @param readOnly
|
||||
*/
|
||||
createRedisFromOptions(node: RedisOptions, readOnly: boolean): Redis<import("../types").ReplyMappingMode>;
|
||||
/**
|
||||
* Find or create a connection to the node
|
||||
*/
|
||||
findOrCreate(node: RedisOptions, readOnly?: boolean): Redis;
|
||||
/**
|
||||
* Replace the connection to the node with a fresh one. Used when the
|
||||
* existing connection is known to be stale, e.g. after a MOVED redirect
|
||||
* that points back at the node it came from.
|
||||
*/
|
||||
recreate(node: RedisOptions, readOnly?: boolean): Redis;
|
||||
/**
|
||||
* Reset the pool with a set of nodes.
|
||||
* The old node will be removed.
|
||||
*/
|
||||
reset(nodes: RedisOptions[]): void;
|
||||
/**
|
||||
* Remove a node from the pool.
|
||||
*/
|
||||
private removeNode;
|
||||
}
|
||||
190
node_modules/ioredis/built/cluster/ConnectionPool.js
generated
vendored
Normal file
190
node_modules/ioredis/built/cluster/ConnectionPool.js
generated
vendored
Normal file
@@ -0,0 +1,190 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const events_1 = require("events");
|
||||
const utils_1 = require("../utils");
|
||||
const util_1 = require("./util");
|
||||
const Redis_1 = require("../Redis");
|
||||
const debug = (0, utils_1.Debug)("cluster:connectionPool");
|
||||
class ConnectionPool extends events_1.EventEmitter {
|
||||
constructor(redisOptions, clusterNodeRetryStrategy = null, hooks = {}) {
|
||||
super();
|
||||
this.redisOptions = redisOptions;
|
||||
this.clusterNodeRetryStrategy = clusterNodeRetryStrategy;
|
||||
this.hooks = hooks;
|
||||
// master + slave = all
|
||||
this.nodes = {
|
||||
all: {},
|
||||
master: {},
|
||||
slave: {},
|
||||
};
|
||||
this.specifiedOptions = {};
|
||||
}
|
||||
getNodes(role = "all") {
|
||||
const nodes = this.nodes[role];
|
||||
return Object.keys(nodes).map((key) => nodes[key]);
|
||||
}
|
||||
getInstanceByKey(key) {
|
||||
return this.nodes.all[key];
|
||||
}
|
||||
getSampleInstance(role) {
|
||||
const keys = Object.keys(this.nodes[role]);
|
||||
const sampleKey = (0, utils_1.sample)(keys);
|
||||
return this.nodes[role][sampleKey];
|
||||
}
|
||||
/**
|
||||
* Add a master node to the pool
|
||||
* @param node
|
||||
*/
|
||||
addMasterNode(node) {
|
||||
const key = (0, util_1.getNodeKey)(node.options);
|
||||
const redis = this.createRedisFromOptions(node, node.options.readOnly);
|
||||
//Master nodes aren't read-only
|
||||
if (!node.options.readOnly) {
|
||||
this.nodes.all[key] = redis;
|
||||
this.nodes.master[key] = redis;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Creates a Redis connection instance from the node options
|
||||
* @param node
|
||||
* @param readOnly
|
||||
*/
|
||||
createRedisFromOptions(node, readOnly) {
|
||||
const redis = new Redis_1.default((0, utils_1.defaults)({
|
||||
// By default, never try to reconnect when a node is lost,
|
||||
// instead, waiting for a `MOVED` error and fetching slots again.
|
||||
// When `clusterNodeRetryStrategy` is set, use it to allow
|
||||
// reconnection (e.g. for replica nodes that restart without
|
||||
// any slot changes).
|
||||
retryStrategy: typeof this.clusterNodeRetryStrategy === "function"
|
||||
? this.clusterNodeRetryStrategy
|
||||
: null,
|
||||
// Offline queue should be enabled so that
|
||||
// we don't need to wait for the `ready` event
|
||||
// before sending commands to the node.
|
||||
enableOfflineQueue: true,
|
||||
readOnly: readOnly,
|
||||
}, node, this.redisOptions, { lazyConnect: true }));
|
||||
this.hooks.onCreate?.(redis, readOnly);
|
||||
return redis;
|
||||
}
|
||||
/**
|
||||
* Find or create a connection to the node
|
||||
*/
|
||||
findOrCreate(node, readOnly = false) {
|
||||
const key = (0, util_1.getNodeKey)(node);
|
||||
readOnly = Boolean(readOnly);
|
||||
if (this.specifiedOptions[key]) {
|
||||
Object.assign(node, this.specifiedOptions[key]);
|
||||
}
|
||||
else {
|
||||
this.specifiedOptions[key] = node;
|
||||
}
|
||||
let redis;
|
||||
if (this.nodes.all[key]) {
|
||||
redis = this.nodes.all[key];
|
||||
if (redis.options.readOnly !== readOnly) {
|
||||
redis.options.readOnly = readOnly;
|
||||
debug("Change role of %s to %s", key, readOnly ? "slave" : "master");
|
||||
redis[readOnly ? "readonly" : "readwrite"]().catch(utils_1.noop);
|
||||
if (readOnly) {
|
||||
delete this.nodes.master[key];
|
||||
this.nodes.slave[key] = redis;
|
||||
}
|
||||
else {
|
||||
delete this.nodes.slave[key];
|
||||
this.nodes.master[key] = redis;
|
||||
}
|
||||
this.hooks.onRoleChange?.(redis, key, readOnly);
|
||||
}
|
||||
}
|
||||
else {
|
||||
debug("Connecting to %s as %s", key, readOnly ? "slave" : "master");
|
||||
redis = this.createRedisFromOptions(node, readOnly);
|
||||
this.nodes.all[key] = redis;
|
||||
this.nodes[readOnly ? "slave" : "master"][key] = redis;
|
||||
redis.once("end", () => {
|
||||
if (this.nodes.all[key] && this.nodes.all[key] !== redis) {
|
||||
// The node was recreated (see `recreate`); leave the new one alone.
|
||||
return;
|
||||
}
|
||||
this.removeNode(key);
|
||||
this.emit("-node", redis, key);
|
||||
if (!Object.keys(this.nodes.all).length) {
|
||||
this.emit("drain");
|
||||
}
|
||||
});
|
||||
this.emit("+node", redis, key);
|
||||
redis.on("error", (error) => {
|
||||
this.emit("nodeError", error, key);
|
||||
});
|
||||
}
|
||||
return redis;
|
||||
}
|
||||
/**
|
||||
* Replace the connection to the node with a fresh one. Used when the
|
||||
* existing connection is known to be stale, e.g. after a MOVED redirect
|
||||
* that points back at the node it came from.
|
||||
*/
|
||||
recreate(node, readOnly = false) {
|
||||
const key = (0, util_1.getNodeKey)(node);
|
||||
const existing = this.nodes.all[key];
|
||||
if (existing) {
|
||||
debug("Recreating connection to %s", key);
|
||||
// Remove before disconnecting so the "end" event of the stale
|
||||
// instance doesn't tear down the replacement.
|
||||
this.removeNode(key);
|
||||
existing.disconnect();
|
||||
}
|
||||
const redis = this.findOrCreate(node, readOnly);
|
||||
if (existing) {
|
||||
// Emit after the replacement is in place so listeners (e.g.
|
||||
// ClusterSubscriber) never observe the pool without the node.
|
||||
this.emit("-node", existing, key);
|
||||
}
|
||||
return redis;
|
||||
}
|
||||
/**
|
||||
* Reset the pool with a set of nodes.
|
||||
* The old node will be removed.
|
||||
*/
|
||||
reset(nodes) {
|
||||
debug("Reset with %O", nodes);
|
||||
const newNodes = {};
|
||||
nodes.forEach((node) => {
|
||||
const key = (0, util_1.getNodeKey)(node);
|
||||
// Don't override the existing (master) node
|
||||
// when the current one is slave.
|
||||
if (!(node.readOnly && newNodes[key])) {
|
||||
newNodes[key] = node;
|
||||
}
|
||||
});
|
||||
Object.keys(this.nodes.all).forEach((key) => {
|
||||
if (!newNodes[key]) {
|
||||
debug("Disconnect %s because the node does not hold any slot", key);
|
||||
this.nodes.all[key].disconnect();
|
||||
this.removeNode(key);
|
||||
}
|
||||
});
|
||||
Object.keys(newNodes).forEach((key) => {
|
||||
const node = newNodes[key];
|
||||
this.findOrCreate(node, node.readOnly);
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Remove a node from the pool.
|
||||
*/
|
||||
removeNode(key) {
|
||||
const { nodes } = this;
|
||||
if (nodes.all[key]) {
|
||||
debug("Remove %s from the pool", key);
|
||||
this.hooks.onRemove?.(nodes.all[key]);
|
||||
delete nodes.all[key];
|
||||
}
|
||||
delete nodes.master[key];
|
||||
delete nodes.slave[key];
|
||||
}
|
||||
}
|
||||
exports.default = ConnectionPool;
|
||||
20
node_modules/ioredis/built/cluster/DelayQueue.d.ts
generated
vendored
Normal file
20
node_modules/ioredis/built/cluster/DelayQueue.d.ts
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
export interface DelayQueueOptions {
|
||||
callback?: Function;
|
||||
timeout: number;
|
||||
}
|
||||
/**
|
||||
* Queue that runs items after specified duration
|
||||
*/
|
||||
export default class DelayQueue {
|
||||
private queues;
|
||||
private timeouts;
|
||||
/**
|
||||
* Add a new item to the queue
|
||||
*
|
||||
* @param bucket bucket name
|
||||
* @param item function that will run later
|
||||
* @param options
|
||||
*/
|
||||
push(bucket: string, item: Function, options: DelayQueueOptions): void;
|
||||
private execute;
|
||||
}
|
||||
53
node_modules/ioredis/built/cluster/DelayQueue.js
generated
vendored
Normal file
53
node_modules/ioredis/built/cluster/DelayQueue.js
generated
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const utils_1 = require("../utils");
|
||||
const Deque = require("denque");
|
||||
const debug = (0, utils_1.Debug)("delayqueue");
|
||||
/**
|
||||
* Queue that runs items after specified duration
|
||||
*/
|
||||
class DelayQueue {
|
||||
constructor() {
|
||||
this.queues = {};
|
||||
this.timeouts = {};
|
||||
}
|
||||
/**
|
||||
* Add a new item to the queue
|
||||
*
|
||||
* @param bucket bucket name
|
||||
* @param item function that will run later
|
||||
* @param options
|
||||
*/
|
||||
push(bucket, item, options) {
|
||||
const callback = options.callback || process.nextTick;
|
||||
if (!this.queues[bucket]) {
|
||||
this.queues[bucket] = new Deque();
|
||||
}
|
||||
const queue = this.queues[bucket];
|
||||
queue.push(item);
|
||||
if (!this.timeouts[bucket]) {
|
||||
this.timeouts[bucket] = setTimeout(() => {
|
||||
callback(() => {
|
||||
this.timeouts[bucket] = null;
|
||||
this.execute(bucket);
|
||||
});
|
||||
}, options.timeout);
|
||||
}
|
||||
}
|
||||
execute(bucket) {
|
||||
const queue = this.queues[bucket];
|
||||
if (!queue) {
|
||||
return;
|
||||
}
|
||||
const { length } = queue;
|
||||
if (!length) {
|
||||
return;
|
||||
}
|
||||
debug("send %d commands in %s queue", length, bucket);
|
||||
this.queues[bucket] = null;
|
||||
while (queue.length > 0) {
|
||||
queue.shift()();
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.default = DelayQueue;
|
||||
36
node_modules/ioredis/built/cluster/ShardedSubscriber.d.ts
generated
vendored
Normal file
36
node_modules/ioredis/built/cluster/ShardedSubscriber.d.ts
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
/// <reference types="node" />
|
||||
import EventEmitter = require("events");
|
||||
import { RedisOptions } from "./util";
|
||||
import Redis from "../Redis";
|
||||
import { ClusterOptions } from "./ClusterOptions";
|
||||
declare const SubscriberStatus: {
|
||||
readonly IDLE: "idle";
|
||||
readonly STARTING: "starting";
|
||||
readonly CONNECTED: "connected";
|
||||
readonly STOPPING: "stopping";
|
||||
readonly ENDED: "ended";
|
||||
};
|
||||
type SubscriberStatus = typeof SubscriberStatus[keyof typeof SubscriberStatus];
|
||||
export default class ShardedSubscriber {
|
||||
private readonly emitter;
|
||||
private readonly nodeKey;
|
||||
private status;
|
||||
private instance;
|
||||
private connectPromise;
|
||||
private lazyConnect;
|
||||
private readonly messageListeners;
|
||||
constructor(emitter: EventEmitter, options: RedisOptions, redisOptions?: ClusterOptions["redisOptions"]);
|
||||
start(): Promise<void>;
|
||||
stop(): void;
|
||||
isStarted(): boolean;
|
||||
get subscriberStatus(): SubscriberStatus;
|
||||
isHealthy(): boolean;
|
||||
getInstance(): Redis | null;
|
||||
getNodeKey(): string;
|
||||
isLazyConnect(): boolean;
|
||||
private onEnd;
|
||||
private onError;
|
||||
private onMoved;
|
||||
private updateStatus;
|
||||
}
|
||||
export {};
|
||||
146
node_modules/ioredis/built/cluster/ShardedSubscriber.js
generated
vendored
Normal file
146
node_modules/ioredis/built/cluster/ShardedSubscriber.js
generated
vendored
Normal file
@@ -0,0 +1,146 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const util_1 = require("./util");
|
||||
const utils_1 = require("../utils");
|
||||
const Redis_1 = require("../Redis");
|
||||
const debug = (0, utils_1.Debug)("cluster:subscriberGroup:shardedSubscriber");
|
||||
const SubscriberStatus = {
|
||||
IDLE: "idle",
|
||||
STARTING: "starting",
|
||||
CONNECTED: "connected",
|
||||
STOPPING: "stopping",
|
||||
ENDED: "ended",
|
||||
};
|
||||
const ALLOWED_STATUS_UPDATES = {
|
||||
[SubscriberStatus.IDLE]: [
|
||||
SubscriberStatus.STARTING,
|
||||
SubscriberStatus.STOPPING,
|
||||
SubscriberStatus.ENDED,
|
||||
],
|
||||
[SubscriberStatus.STARTING]: [
|
||||
SubscriberStatus.CONNECTED,
|
||||
SubscriberStatus.STOPPING,
|
||||
SubscriberStatus.ENDED,
|
||||
],
|
||||
[SubscriberStatus.CONNECTED]: [
|
||||
SubscriberStatus.STOPPING,
|
||||
SubscriberStatus.ENDED,
|
||||
],
|
||||
[SubscriberStatus.STOPPING]: [SubscriberStatus.ENDED],
|
||||
[SubscriberStatus.ENDED]: [],
|
||||
};
|
||||
class ShardedSubscriber {
|
||||
constructor(emitter, options, redisOptions) {
|
||||
this.emitter = emitter;
|
||||
this.status = SubscriberStatus.IDLE;
|
||||
this.instance = null;
|
||||
this.connectPromise = null;
|
||||
// Store listener references for cleanup
|
||||
this.messageListeners = new Map();
|
||||
this.onEnd = () => {
|
||||
this.updateStatus(SubscriberStatus.ENDED);
|
||||
this.emitter.emit("-node", this.instance, this.nodeKey);
|
||||
};
|
||||
this.onError = (error) => {
|
||||
this.emitter.emit("nodeError", error, this.nodeKey);
|
||||
};
|
||||
this.onMoved = () => {
|
||||
this.emitter.emit("moved");
|
||||
};
|
||||
this.instance = new Redis_1.default((0, utils_1.defaults)({
|
||||
enableReadyCheck: false,
|
||||
enableOfflineQueue: true,
|
||||
connectionName: (0, util_1.getConnectionName)("ssubscriber", options.connectionName),
|
||||
/**
|
||||
* Disable auto reconnection for subscribers.
|
||||
* The ClusterSubscriberGroup will handle the reconnection.
|
||||
*/
|
||||
retryStrategy: null,
|
||||
lazyConnect: true,
|
||||
}, options, redisOptions));
|
||||
this.lazyConnect = redisOptions?.lazyConnect ?? true;
|
||||
this.nodeKey = (0, util_1.getNodeKey)(options);
|
||||
// Register listeners
|
||||
this.instance.on("end", this.onEnd);
|
||||
this.instance.on("error", this.onError);
|
||||
this.instance.on("moved", this.onMoved);
|
||||
for (const event of ["smessage", "smessageBuffer"]) {
|
||||
const listener = (...args) => {
|
||||
this.emitter.emit(event, ...args);
|
||||
};
|
||||
this.messageListeners.set(event, listener);
|
||||
this.instance.on(event, listener);
|
||||
}
|
||||
}
|
||||
async start() {
|
||||
if (this.connectPromise) {
|
||||
return this.connectPromise;
|
||||
}
|
||||
if (this.status === SubscriberStatus.STARTING ||
|
||||
this.status === SubscriberStatus.CONNECTED) {
|
||||
return;
|
||||
}
|
||||
if (this.status === SubscriberStatus.ENDED || !this.instance) {
|
||||
throw new Error(`Sharded subscriber ${this.nodeKey} cannot be restarted once ended.`);
|
||||
}
|
||||
this.updateStatus(SubscriberStatus.STARTING);
|
||||
this.connectPromise = this.instance.connect();
|
||||
try {
|
||||
await this.connectPromise;
|
||||
this.updateStatus(SubscriberStatus.CONNECTED);
|
||||
}
|
||||
catch (err) {
|
||||
this.updateStatus(SubscriberStatus.ENDED);
|
||||
throw err;
|
||||
}
|
||||
finally {
|
||||
this.connectPromise = null;
|
||||
}
|
||||
}
|
||||
stop() {
|
||||
this.updateStatus(SubscriberStatus.STOPPING);
|
||||
if (this.instance) {
|
||||
this.instance.disconnect();
|
||||
this.instance.removeAllListeners();
|
||||
this.messageListeners.clear();
|
||||
this.instance = null;
|
||||
}
|
||||
this.updateStatus(SubscriberStatus.ENDED);
|
||||
debug("stopped %s", this.nodeKey);
|
||||
}
|
||||
isStarted() {
|
||||
return [
|
||||
SubscriberStatus.CONNECTED,
|
||||
SubscriberStatus.STARTING,
|
||||
].includes(this.status);
|
||||
}
|
||||
get subscriberStatus() {
|
||||
return this.status;
|
||||
}
|
||||
isHealthy() {
|
||||
return ((this.status === SubscriberStatus.IDLE ||
|
||||
this.status === SubscriberStatus.CONNECTED ||
|
||||
this.status === SubscriberStatus.STARTING) &&
|
||||
this.instance !== null);
|
||||
}
|
||||
getInstance() {
|
||||
return this.instance;
|
||||
}
|
||||
getNodeKey() {
|
||||
return this.nodeKey;
|
||||
}
|
||||
isLazyConnect() {
|
||||
return this.lazyConnect;
|
||||
}
|
||||
updateStatus(nextStatus) {
|
||||
if (this.status === nextStatus) {
|
||||
return;
|
||||
}
|
||||
if (!ALLOWED_STATUS_UPDATES[this.status].includes(nextStatus)) {
|
||||
debug("Invalid status transition for %s: %s -> %s", this.nodeKey, this.status, nextStatus);
|
||||
return;
|
||||
}
|
||||
this.status = nextStatus;
|
||||
}
|
||||
}
|
||||
exports.default = ShardedSubscriber;
|
||||
166
node_modules/ioredis/built/cluster/index.d.ts
generated
vendored
Normal file
166
node_modules/ioredis/built/cluster/index.d.ts
generated
vendored
Normal file
@@ -0,0 +1,166 @@
|
||||
/// <reference types="node" />
|
||||
import { EventEmitter } from "events";
|
||||
import Command from "../Command";
|
||||
import Redis from "../Redis";
|
||||
import ScanStream from "../ScanStream";
|
||||
import { Transaction } from "../transaction";
|
||||
import { Callback, ReplyMappingFromClusterOptions, ReplyMappingMode, ScanStreamOptions, WriteableStream } from "../types";
|
||||
import Commander from "../utils/Commander";
|
||||
import { ClusterOptions, ClusterOptionsWithReplyMapping } from "./ClusterOptions";
|
||||
import { NodeKey, NodeRole } from "./util";
|
||||
export type ClusterNode = string | number | {
|
||||
host?: string | undefined;
|
||||
port?: number | undefined;
|
||||
};
|
||||
export type ClusterStatus = "end" | "close" | "wait" | "connecting" | "connect" | "ready" | "reconnecting" | "disconnecting";
|
||||
/**
|
||||
* Client for the official Redis Cluster
|
||||
*/
|
||||
declare class Cluster<ReplyMapping extends ReplyMappingMode = "legacy"> extends Commander<{
|
||||
type: "default";
|
||||
mapping: ReplyMapping extends "resp3" ? "resp3" : "resp2";
|
||||
}> {
|
||||
options: ClusterOptions;
|
||||
slots: NodeKey[][];
|
||||
status: ClusterStatus;
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
_groupsIds: {
|
||||
[key: string]: number;
|
||||
};
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
_groupsBySlot: number[];
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
isCluster: boolean;
|
||||
private startupNodes;
|
||||
private connectionPool;
|
||||
private manuallyClosing;
|
||||
private retryAttempts;
|
||||
private delayQueue;
|
||||
private offlineQueue;
|
||||
private subscriber;
|
||||
private shardedSubscribers;
|
||||
private slotsTimer;
|
||||
private reconnectTimeout;
|
||||
private isRefreshing;
|
||||
private _refreshSlotsCacheCallbacks;
|
||||
private _autoPipelines;
|
||||
private _runningAutoPipelines;
|
||||
private _readyDelayedCallbacks;
|
||||
private subscriberGroupEmitter;
|
||||
/**
|
||||
* Every time Cluster#connect() is called, this value will be
|
||||
* auto-incrementing. The purpose of this value is used for
|
||||
* discarding previous connect attampts when creating a new
|
||||
* connection.
|
||||
*/
|
||||
private connectionEpoch;
|
||||
/**
|
||||
* Creates an instance of Cluster.
|
||||
*/
|
||||
constructor(startupNodes: ClusterNode[], options?: ClusterOptionsWithReplyMapping<ReplyMapping>);
|
||||
/**
|
||||
* Connect to a cluster
|
||||
*/
|
||||
connect(): Promise<void>;
|
||||
/**
|
||||
* Disconnect from every node in the cluster.
|
||||
*/
|
||||
disconnect(reconnect?: boolean): void;
|
||||
/**
|
||||
* Quit the cluster gracefully.
|
||||
*/
|
||||
quit(callback?: Callback<"OK">): Promise<"OK">;
|
||||
/**
|
||||
* Create a new instance with the same startup nodes and options as the current one.
|
||||
*
|
||||
* @example
|
||||
* ```js
|
||||
* var cluster = new Redis.Cluster([{ host: "127.0.0.1", port: "30001" }]);
|
||||
* var anotherCluster = cluster.duplicate();
|
||||
* ```
|
||||
*/
|
||||
duplicate<OverrideOptions extends ClusterOptionsWithReplyMapping<ReplyMappingMode> | undefined = undefined>(overrideStartupNodes?: ClusterNode[], overrideOptions?: OverrideOptions): Cluster<ReplyMappingFromClusterOptions<ReplyMapping, OverrideOptions>>;
|
||||
/**
|
||||
* Get nodes with the specified role
|
||||
*/
|
||||
nodes(role?: NodeRole): Redis[];
|
||||
/**
|
||||
* This is needed in order not to install a listener for each auto pipeline
|
||||
*
|
||||
* @ignore
|
||||
*/
|
||||
delayUntilReady(callback: Callback): void;
|
||||
/**
|
||||
* Get the number of commands queued in automatic pipelines.
|
||||
*
|
||||
* This is not available (and returns 0) until the cluster is connected and slots information have been received.
|
||||
*/
|
||||
get autoPipelineQueueSize(): number;
|
||||
/**
|
||||
* Refresh the slot cache
|
||||
*
|
||||
* @ignore
|
||||
*/
|
||||
refreshSlotsCache(callback?: Callback<void>): void;
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
sendCommand(command: Command, stream?: WriteableStream, node?: any): unknown;
|
||||
sscanStream(key: string, options?: ScanStreamOptions): ScanStream;
|
||||
sscanBufferStream(key: string, options?: ScanStreamOptions): ScanStream;
|
||||
hscanStream(key: string, options?: ScanStreamOptions): ScanStream;
|
||||
hscanBufferStream(key: string, options?: ScanStreamOptions): ScanStream;
|
||||
zscanStream(key: string, options?: ScanStreamOptions): ScanStream;
|
||||
zscanBufferStream(key: string, options?: ScanStreamOptions): ScanStream;
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
handleError(error: Error, ttl: {
|
||||
value?: any;
|
||||
}, handlers: any): void;
|
||||
private resetOfflineQueue;
|
||||
private clearNodesRefreshInterval;
|
||||
private resetNodesRefreshInterval;
|
||||
/**
|
||||
* Change cluster instance's status
|
||||
*/
|
||||
private setStatus;
|
||||
/**
|
||||
* Called when closed to check whether a reconnection should be made
|
||||
*/
|
||||
private handleCloseEvent;
|
||||
/**
|
||||
* Flush offline queue with error.
|
||||
*/
|
||||
private flushQueue;
|
||||
private executeOfflineCommands;
|
||||
private natMapper;
|
||||
private getInfoFromNode;
|
||||
private invokeReadyDelayedCallbacks;
|
||||
/**
|
||||
* Check whether Cluster is able to process commands
|
||||
*/
|
||||
private readyCheck;
|
||||
private resolveSrv;
|
||||
private dnsLookup;
|
||||
/**
|
||||
* Normalize startup nodes, and resolving hostnames to IPs.
|
||||
*
|
||||
* This process happens every time when #connect() is called since
|
||||
* #startupNodes and DNS records may chanage.
|
||||
*/
|
||||
private resolveStartupNodeHostnames;
|
||||
private createScanStream;
|
||||
private createShardedSubscriberGroup;
|
||||
}
|
||||
interface Cluster<ReplyMapping extends "legacy" | "resp3" = "legacy"> extends EventEmitter {
|
||||
}
|
||||
interface Cluster<ReplyMapping extends "legacy" | "resp3" = "legacy"> extends Transaction<ReplyMapping extends "resp3" ? "resp3" : "resp2"> {
|
||||
}
|
||||
export default Cluster;
|
||||
1049
node_modules/ioredis/built/cluster/index.js
generated
vendored
Normal file
1049
node_modules/ioredis/built/cluster/index.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
27
node_modules/ioredis/built/cluster/util.d.ts
generated
vendored
Normal file
27
node_modules/ioredis/built/cluster/util.d.ts
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
/// <reference types="node" />
|
||||
import type Redis from "../Redis";
|
||||
import { SrvRecord } from "dns";
|
||||
export type NodeKey = string;
|
||||
export type NodeRole = "master" | "slave" | "all";
|
||||
export interface RedisOptions {
|
||||
port: number;
|
||||
host: string;
|
||||
username?: string | undefined;
|
||||
password?: string | undefined;
|
||||
[key: string]: any;
|
||||
}
|
||||
export interface SrvRecordsGroup {
|
||||
totalWeight: number;
|
||||
records: SrvRecord[];
|
||||
}
|
||||
export interface GroupedSrvRecords {
|
||||
[key: number]: SrvRecordsGroup;
|
||||
}
|
||||
export declare function waitForRedisReady(redis: Redis): Promise<void>;
|
||||
export declare function getNodeKey(node: RedisOptions): NodeKey;
|
||||
export declare function nodeKeyToRedisOptions(nodeKey: NodeKey): RedisOptions;
|
||||
export declare function normalizeNodeOptions(nodes: Array<string | number | object>): RedisOptions[];
|
||||
export declare function getUniqueHostnamesFromOptions(nodes: RedisOptions[]): string[];
|
||||
export declare function groupSrvRecords(records: SrvRecord[]): GroupedSrvRecords;
|
||||
export declare function weightSrvRecords(recordsGroup: SrvRecordsGroup): SrvRecord;
|
||||
export declare function getConnectionName(component: any, nodeConnectionName: any): string;
|
||||
124
node_modules/ioredis/built/cluster/util.js
generated
vendored
Normal file
124
node_modules/ioredis/built/cluster/util.js
generated
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getConnectionName = exports.weightSrvRecords = exports.groupSrvRecords = exports.getUniqueHostnamesFromOptions = exports.normalizeNodeOptions = exports.nodeKeyToRedisOptions = exports.getNodeKey = exports.waitForRedisReady = void 0;
|
||||
const utils_1 = require("../utils");
|
||||
const net_1 = require("net");
|
||||
function waitForRedisReady(redis) {
|
||||
if (redis.status === "ready") {
|
||||
return Promise.resolve();
|
||||
}
|
||||
if (redis.status === "wait") {
|
||||
return redis.connect();
|
||||
}
|
||||
if (redis.status === "end") {
|
||||
return Promise.reject(new Error(utils_1.CONNECTION_CLOSED_ERROR_MSG));
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
const onReady = () => {
|
||||
redis.removeListener("end", onEnd);
|
||||
resolve();
|
||||
};
|
||||
const onEnd = () => {
|
||||
redis.removeListener("ready", onReady);
|
||||
reject(new Error(utils_1.CONNECTION_CLOSED_ERROR_MSG));
|
||||
};
|
||||
redis.once("ready", onReady);
|
||||
redis.once("end", onEnd);
|
||||
});
|
||||
}
|
||||
exports.waitForRedisReady = waitForRedisReady;
|
||||
function getNodeKey(node) {
|
||||
node.port = node.port || 6379;
|
||||
node.host = node.host || "127.0.0.1";
|
||||
return node.host + ":" + node.port;
|
||||
}
|
||||
exports.getNodeKey = getNodeKey;
|
||||
function nodeKeyToRedisOptions(nodeKey) {
|
||||
const portIndex = nodeKey.lastIndexOf(":");
|
||||
if (portIndex === -1) {
|
||||
throw new Error(`Invalid node key ${nodeKey}`);
|
||||
}
|
||||
return {
|
||||
host: nodeKey.slice(0, portIndex),
|
||||
port: Number(nodeKey.slice(portIndex + 1)),
|
||||
};
|
||||
}
|
||||
exports.nodeKeyToRedisOptions = nodeKeyToRedisOptions;
|
||||
function normalizeNodeOptions(nodes) {
|
||||
return nodes.map((node) => {
|
||||
const options = {};
|
||||
if (typeof node === "object") {
|
||||
Object.assign(options, node);
|
||||
}
|
||||
else if (typeof node === "string") {
|
||||
Object.assign(options, (0, utils_1.parseURL)(node));
|
||||
}
|
||||
else if (typeof node === "number") {
|
||||
options.port = node;
|
||||
}
|
||||
else {
|
||||
throw new Error("Invalid argument " + node);
|
||||
}
|
||||
if (typeof options.port === "string") {
|
||||
options.port = parseInt(options.port, 10);
|
||||
}
|
||||
// Cluster mode only support db 0
|
||||
delete options.db;
|
||||
if (!options.port) {
|
||||
options.port = 6379;
|
||||
}
|
||||
if (!options.host) {
|
||||
options.host = "127.0.0.1";
|
||||
}
|
||||
return (0, utils_1.resolveTLSProfile)(options);
|
||||
});
|
||||
}
|
||||
exports.normalizeNodeOptions = normalizeNodeOptions;
|
||||
function getUniqueHostnamesFromOptions(nodes) {
|
||||
const uniqueHostsMap = {};
|
||||
nodes.forEach((node) => {
|
||||
uniqueHostsMap[node.host] = true;
|
||||
});
|
||||
return Object.keys(uniqueHostsMap).filter((host) => !(0, net_1.isIP)(host));
|
||||
}
|
||||
exports.getUniqueHostnamesFromOptions = getUniqueHostnamesFromOptions;
|
||||
function groupSrvRecords(records) {
|
||||
const recordsByPriority = {};
|
||||
for (const record of records) {
|
||||
if (!recordsByPriority.hasOwnProperty(record.priority)) {
|
||||
recordsByPriority[record.priority] = {
|
||||
totalWeight: record.weight,
|
||||
records: [record],
|
||||
};
|
||||
}
|
||||
else {
|
||||
recordsByPriority[record.priority].totalWeight += record.weight;
|
||||
recordsByPriority[record.priority].records.push(record);
|
||||
}
|
||||
}
|
||||
return recordsByPriority;
|
||||
}
|
||||
exports.groupSrvRecords = groupSrvRecords;
|
||||
function weightSrvRecords(recordsGroup) {
|
||||
if (recordsGroup.records.length === 1) {
|
||||
recordsGroup.totalWeight = 0;
|
||||
return recordsGroup.records.shift();
|
||||
}
|
||||
// + `recordsGroup.records.length` to support `weight` 0
|
||||
const random = Math.floor(Math.random() * (recordsGroup.totalWeight + recordsGroup.records.length));
|
||||
let total = 0;
|
||||
for (const [i, record] of recordsGroup.records.entries()) {
|
||||
total += 1 + record.weight;
|
||||
if (total > random) {
|
||||
recordsGroup.totalWeight -= record.weight;
|
||||
recordsGroup.records.splice(i, 1);
|
||||
return record;
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.weightSrvRecords = weightSrvRecords;
|
||||
function getConnectionName(component, nodeConnectionName) {
|
||||
const prefix = `ioredis-cluster(${component})`;
|
||||
return nodeConnectionName ? `${prefix}:${nodeConnectionName}` : prefix;
|
||||
}
|
||||
exports.getConnectionName = getConnectionName;
|
||||
12
node_modules/ioredis/built/connectors/AbstractConnector.d.ts
generated
vendored
Normal file
12
node_modules/ioredis/built/connectors/AbstractConnector.d.ts
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
import { NetStream } from "../types";
|
||||
export type ErrorEmitter = (type: string, err: Error) => void;
|
||||
export default abstract class AbstractConnector {
|
||||
firstError?: Error;
|
||||
protected connecting: boolean;
|
||||
protected stream: NetStream;
|
||||
private disconnectTimeout;
|
||||
constructor(disconnectTimeout: number);
|
||||
check(info: any): boolean;
|
||||
disconnect(): void;
|
||||
abstract connect(_: ErrorEmitter): Promise<NetStream>;
|
||||
}
|
||||
26
node_modules/ioredis/built/connectors/AbstractConnector.js
generated
vendored
Normal file
26
node_modules/ioredis/built/connectors/AbstractConnector.js
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const utils_1 = require("../utils");
|
||||
const debug = (0, utils_1.Debug)("AbstractConnector");
|
||||
class AbstractConnector {
|
||||
constructor(disconnectTimeout) {
|
||||
this.connecting = false;
|
||||
this.disconnectTimeout = disconnectTimeout;
|
||||
}
|
||||
check(info) {
|
||||
return true;
|
||||
}
|
||||
disconnect() {
|
||||
this.connecting = false;
|
||||
if (this.stream) {
|
||||
const stream = this.stream; // Make sure callbacks refer to the same instance
|
||||
const timeout = setTimeout(() => {
|
||||
debug("stream %s:%s still open, destroying it", stream.remoteAddress, stream.remotePort);
|
||||
stream.destroy();
|
||||
}, this.disconnectTimeout);
|
||||
stream.on("close", () => clearTimeout(timeout));
|
||||
stream.end();
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.default = AbstractConnector;
|
||||
5
node_modules/ioredis/built/connectors/ConnectorConstructor.d.ts
generated
vendored
Normal file
5
node_modules/ioredis/built/connectors/ConnectorConstructor.d.ts
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
import AbstractConnector from "./AbstractConnector";
|
||||
interface ConnectorConstructor {
|
||||
new (options: unknown): AbstractConnector;
|
||||
}
|
||||
export default ConnectorConstructor;
|
||||
2
node_modules/ioredis/built/connectors/ConnectorConstructor.js
generated
vendored
Normal file
2
node_modules/ioredis/built/connectors/ConnectorConstructor.js
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
11
node_modules/ioredis/built/connectors/SentinelConnector/FailoverDetector.d.ts
generated
vendored
Normal file
11
node_modules/ioredis/built/connectors/SentinelConnector/FailoverDetector.d.ts
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
import SentinelConnector from "./index";
|
||||
import { Sentinel } from "./types";
|
||||
export declare class FailoverDetector {
|
||||
private connector;
|
||||
private sentinels;
|
||||
private isDisconnected;
|
||||
constructor(connector: SentinelConnector, sentinels: Sentinel[]);
|
||||
cleanup(): void;
|
||||
subscribe(): Promise<void>;
|
||||
private disconnect;
|
||||
}
|
||||
45
node_modules/ioredis/built/connectors/SentinelConnector/FailoverDetector.js
generated
vendored
Normal file
45
node_modules/ioredis/built/connectors/SentinelConnector/FailoverDetector.js
generated
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.FailoverDetector = void 0;
|
||||
const utils_1 = require("../../utils");
|
||||
const debug = (0, utils_1.Debug)("FailoverDetector");
|
||||
const CHANNEL_NAME = "+switch-master";
|
||||
class FailoverDetector {
|
||||
// sentinels can't be used for regular commands after this
|
||||
constructor(connector, sentinels) {
|
||||
this.isDisconnected = false;
|
||||
this.connector = connector;
|
||||
this.sentinels = sentinels;
|
||||
}
|
||||
cleanup() {
|
||||
this.isDisconnected = true;
|
||||
for (const sentinel of this.sentinels) {
|
||||
sentinel.client.disconnect();
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
disconnect() {
|
||||
// Avoid disconnecting more than once per failover.
|
||||
// A new FailoverDetector will be created after reconnecting.
|
||||
this.isDisconnected = true;
|
||||
debug("Failover detected, disconnecting");
|
||||
// Will call this.cleanup()
|
||||
this.connector.disconnect();
|
||||
}
|
||||
}
|
||||
exports.FailoverDetector = FailoverDetector;
|
||||
13
node_modules/ioredis/built/connectors/SentinelConnector/SentinelIterator.d.ts
generated
vendored
Normal file
13
node_modules/ioredis/built/connectors/SentinelConnector/SentinelIterator.d.ts
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
import { SentinelAddress } from "./types";
|
||||
export default class SentinelIterator implements Iterator<Partial<SentinelAddress>> {
|
||||
private cursor;
|
||||
private sentinels;
|
||||
constructor(sentinels: Array<Partial<SentinelAddress>>);
|
||||
next(): {
|
||||
done: boolean;
|
||||
value: Partial<SentinelAddress>;
|
||||
};
|
||||
reset(moveCurrentEndpointToFirst: boolean): void;
|
||||
add(sentinel: SentinelAddress): boolean;
|
||||
toString(): string;
|
||||
}
|
||||
37
node_modules/ioredis/built/connectors/SentinelConnector/SentinelIterator.js
generated
vendored
Normal file
37
node_modules/ioredis/built/connectors/SentinelConnector/SentinelIterator.js
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
function isSentinelEql(a, b) {
|
||||
return ((a.host || "127.0.0.1") === (b.host || "127.0.0.1") &&
|
||||
(a.port || 26379) === (b.port || 26379));
|
||||
}
|
||||
class SentinelIterator {
|
||||
constructor(sentinels) {
|
||||
this.cursor = 0;
|
||||
this.sentinels = sentinels.slice(0);
|
||||
}
|
||||
next() {
|
||||
const done = this.cursor >= this.sentinels.length;
|
||||
return { done, value: done ? undefined : this.sentinels[this.cursor++] };
|
||||
}
|
||||
reset(moveCurrentEndpointToFirst) {
|
||||
if (moveCurrentEndpointToFirst &&
|
||||
this.sentinels.length > 1 &&
|
||||
this.cursor !== 1) {
|
||||
this.sentinels.unshift(...this.sentinels.splice(this.cursor - 1));
|
||||
}
|
||||
this.cursor = 0;
|
||||
}
|
||||
add(sentinel) {
|
||||
for (let i = 0; i < this.sentinels.length; i++) {
|
||||
if (isSentinelEql(sentinel, this.sentinels[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
this.sentinels.push(sentinel);
|
||||
return true;
|
||||
}
|
||||
toString() {
|
||||
return `${JSON.stringify(this.sentinels)} @${this.cursor}`;
|
||||
}
|
||||
}
|
||||
exports.default = SentinelIterator;
|
||||
77
node_modules/ioredis/built/connectors/SentinelConnector/index.d.ts
generated
vendored
Normal file
77
node_modules/ioredis/built/connectors/SentinelConnector/index.d.ts
generated
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
/// <reference types="node" />
|
||||
/// <reference types="node" />
|
||||
import { EventEmitter } from "events";
|
||||
import { NatMap } from "../../cluster/ClusterOptions";
|
||||
import { ConnectionOptions } from "tls";
|
||||
import SentinelIterator from "./SentinelIterator";
|
||||
import { SentinelAddress } from "./types";
|
||||
import AbstractConnector, { ErrorEmitter } from "../AbstractConnector";
|
||||
import { NetStream, ProtocolVersion, ReplyMappingMode } from "../../types";
|
||||
interface AddressFromResponse {
|
||||
port: string;
|
||||
ip: string;
|
||||
flags?: string | undefined;
|
||||
}
|
||||
type PreferredSlaves = ((slaves: AddressFromResponse[]) => AddressFromResponse | null) | Array<{
|
||||
port: string;
|
||||
ip: string;
|
||||
prio?: number | undefined;
|
||||
}> | {
|
||||
port: string;
|
||||
ip: string;
|
||||
prio?: number | undefined;
|
||||
};
|
||||
export { SentinelAddress, SentinelIterator };
|
||||
export interface SentinelConnectionOptions {
|
||||
/**
|
||||
* Master group name of the Sentinel
|
||||
*/
|
||||
name?: string | undefined;
|
||||
/**
|
||||
* @default "master"
|
||||
*/
|
||||
role?: "master" | "slave" | undefined;
|
||||
tls?: ConnectionOptions | undefined;
|
||||
sentinelUsername?: string | undefined;
|
||||
sentinelPassword?: string | undefined;
|
||||
sentinels?: Array<Partial<SentinelAddress>> | undefined;
|
||||
path?: string | undefined;
|
||||
family?: number | undefined;
|
||||
sentinelRetryStrategy?: ((retryAttempts: number) => number | void | null) | undefined;
|
||||
sentinelReconnectStrategy?: ((retryAttempts: number) => number | void | null) | undefined;
|
||||
preferredSlaves?: PreferredSlaves | undefined;
|
||||
connectTimeout?: number | undefined;
|
||||
disconnectTimeout?: number | undefined;
|
||||
sentinelCommandTimeout?: number | undefined;
|
||||
enableTLSForSentinelMode?: boolean | undefined;
|
||||
sentinelTLS?: ConnectionOptions | undefined;
|
||||
natMap?: NatMap | undefined;
|
||||
updateSentinels?: boolean | undefined;
|
||||
/**
|
||||
* @default 10
|
||||
*/
|
||||
sentinelMaxConnections?: number | undefined;
|
||||
failoverDetector?: boolean | undefined;
|
||||
protocol?: ProtocolVersion | undefined;
|
||||
replyMapping?: ReplyMappingMode | undefined;
|
||||
}
|
||||
export default class SentinelConnector extends AbstractConnector {
|
||||
protected options: SentinelConnectionOptions;
|
||||
emitter: EventEmitter | null;
|
||||
protected sentinelIterator: SentinelIterator;
|
||||
private retryAttempts;
|
||||
private failoverDetector;
|
||||
constructor(options: SentinelConnectionOptions);
|
||||
check(info: {
|
||||
role?: string;
|
||||
}): boolean;
|
||||
disconnect(): void;
|
||||
connect(eventEmitter: ErrorEmitter): Promise<NetStream>;
|
||||
private updateSentinels;
|
||||
private resolveMaster;
|
||||
private resolveSlave;
|
||||
private sentinelNatResolve;
|
||||
private connectToSentinel;
|
||||
private resolve;
|
||||
private initFailoverDetector;
|
||||
}
|
||||
301
node_modules/ioredis/built/connectors/SentinelConnector/index.js
generated
vendored
Normal file
301
node_modules/ioredis/built/connectors/SentinelConnector/index.js
generated
vendored
Normal file
@@ -0,0 +1,301 @@
|
||||
"use strict";
|
||||
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 SentinelIterator_1 = require("./SentinelIterator");
|
||||
exports.SentinelIterator = SentinelIterator_1.default;
|
||||
const AbstractConnector_1 = require("../AbstractConnector");
|
||||
const Redis_1 = require("../../Redis");
|
||||
const FailoverDetector_1 = require("./FailoverDetector");
|
||||
const debug = (0, utils_1.Debug)("SentinelConnector");
|
||||
class SentinelConnector extends AbstractConnector_1.default {
|
||||
constructor(options) {
|
||||
super(options.disconnectTimeout);
|
||||
this.options = options;
|
||||
this.emitter = null;
|
||||
this.failoverDetector = null;
|
||||
if (!this.options.sentinels.length) {
|
||||
throw new Error("Requires at least one sentinel to connect to.");
|
||||
}
|
||||
if (!this.options.name) {
|
||||
throw new Error("Requires the name of master.");
|
||||
}
|
||||
this.sentinelIterator = new SentinelIterator_1.default(this.options.sentinels);
|
||||
}
|
||||
check(info) {
|
||||
const roleMatches = !info.role || this.options.role === info.role;
|
||||
if (!roleMatches) {
|
||||
debug("role invalid, expected %s, but got %s", this.options.role, info.role);
|
||||
// Start from the next item.
|
||||
// Note that `reset` will move the cursor to the previous element,
|
||||
// so we advance two steps here.
|
||||
this.sentinelIterator.next();
|
||||
this.sentinelIterator.next();
|
||||
this.sentinelIterator.reset(true);
|
||||
}
|
||||
return roleMatches;
|
||||
}
|
||||
disconnect() {
|
||||
super.disconnect();
|
||||
if (this.failoverDetector) {
|
||||
this.failoverDetector.cleanup();
|
||||
}
|
||||
}
|
||||
connect(eventEmitter) {
|
||||
this.connecting = true;
|
||||
this.retryAttempts = 0;
|
||||
let lastError;
|
||||
const connectToNext = async () => {
|
||||
const endpoint = this.sentinelIterator.next();
|
||||
if (endpoint.done) {
|
||||
this.sentinelIterator.reset(false);
|
||||
const retryDelay = typeof this.options.sentinelRetryStrategy === "function"
|
||||
? this.options.sentinelRetryStrategy(++this.retryAttempts)
|
||||
: null;
|
||||
let errorMsg = typeof retryDelay !== "number"
|
||||
? "All sentinels are unreachable and retry is disabled."
|
||||
: `All sentinels are unreachable. Retrying from scratch after ${retryDelay}ms.`;
|
||||
if (lastError) {
|
||||
errorMsg += ` Last error: ${lastError.message}`;
|
||||
}
|
||||
debug(errorMsg);
|
||||
const error = new Error(errorMsg);
|
||||
if (typeof retryDelay === "number") {
|
||||
eventEmitter("error", error);
|
||||
await new Promise((resolve) => setTimeout(resolve, retryDelay));
|
||||
return connectToNext();
|
||||
}
|
||||
else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
let resolved = null;
|
||||
let err = null;
|
||||
try {
|
||||
resolved = await this.resolve(endpoint.value);
|
||||
}
|
||||
catch (error) {
|
||||
err = error;
|
||||
}
|
||||
if (!this.connecting) {
|
||||
throw new Error(utils_1.CONNECTION_CLOSED_ERROR_MSG);
|
||||
}
|
||||
const endpointAddress = endpoint.value.host + ":" + endpoint.value.port;
|
||||
if (resolved) {
|
||||
debug("resolved: %s:%s from sentinel %s", resolved.host, resolved.port, endpointAddress);
|
||||
if (this.options.enableTLSForSentinelMode && this.options.tls) {
|
||||
Object.assign(resolved, this.options.tls);
|
||||
this.stream = (0, tls_1.connect)(resolved);
|
||||
this.stream.once("secureConnect", this.initFailoverDetector.bind(this));
|
||||
}
|
||||
else {
|
||||
this.stream = (0, net_1.createConnection)(resolved);
|
||||
this.stream.once("connect", this.initFailoverDetector.bind(this));
|
||||
}
|
||||
this.stream.once("error", (err) => {
|
||||
this.firstError = err;
|
||||
});
|
||||
return this.stream;
|
||||
}
|
||||
else {
|
||||
const errorMsg = err
|
||||
? "failed to connect to sentinel " +
|
||||
endpointAddress +
|
||||
" because " +
|
||||
err.message
|
||||
: "connected to sentinel " +
|
||||
endpointAddress +
|
||||
" successfully, but got an invalid reply: " +
|
||||
resolved;
|
||||
debug(errorMsg);
|
||||
eventEmitter("sentinelError", new Error(errorMsg));
|
||||
if (err) {
|
||||
lastError = err;
|
||||
}
|
||||
return connectToNext();
|
||||
}
|
||||
};
|
||||
return connectToNext();
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
});
|
||||
debug("Updated internal sentinels: %s", this.sentinelIterator);
|
||||
}
|
||||
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);
|
||||
}
|
||||
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));
|
||||
}
|
||||
sentinelNatResolve(item) {
|
||||
if (!item || !this.options.natMap)
|
||||
return item;
|
||||
const key = `${item.host}:${item.port}`;
|
||||
let result = item;
|
||||
if (typeof this.options.natMap === "function") {
|
||||
result = this.options.natMap(key) || item;
|
||||
}
|
||||
else if (typeof this.options.natMap === "object") {
|
||||
result = this.options.natMap[key] || item;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
connectToSentinel(endpoint, 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 ||
|
||||
("path" in this.options && this.options.path
|
||||
? undefined
|
||||
: this.options.family),
|
||||
tls: this.options.sentinelTLS,
|
||||
retryStrategy: null,
|
||||
enableReadyCheck: false,
|
||||
protocol: this.options.protocol,
|
||||
replyMapping: "legacy",
|
||||
connectTimeout: this.options.connectTimeout,
|
||||
commandTimeout: this.options.sentinelCommandTimeout,
|
||||
...options,
|
||||
});
|
||||
// @ts-expect-error
|
||||
return redis;
|
||||
}
|
||||
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);
|
||||
}
|
||||
else {
|
||||
return await this.resolveMaster(client);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
client.disconnect();
|
||||
}
|
||||
}
|
||||
async initFailoverDetector() {
|
||||
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;
|
||||
}
|
||||
const client = this.connectToSentinel(value, {
|
||||
lazyConnect: true,
|
||||
retryStrategy: this.options.sentinelReconnectStrategy,
|
||||
protocol: this.options.protocol,
|
||||
replyMapping: "legacy",
|
||||
});
|
||||
client.on("reconnecting", () => {
|
||||
// Tests listen to this event
|
||||
this.emitter?.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
|
||||
this.emitter?.emit("failoverSubscribed");
|
||||
}
|
||||
}
|
||||
exports.default = SentinelConnector;
|
||||
function selectPreferredSentinel(availableSlaves, preferredSlaves) {
|
||||
if (availableSlaves.length === 0) {
|
||||
return null;
|
||||
}
|
||||
let selectedSlave;
|
||||
if (typeof preferredSlaves === "function") {
|
||||
selectedSlave = preferredSlaves(availableSlaves);
|
||||
}
|
||||
else if (preferredSlaves !== null && typeof preferredSlaves === "object") {
|
||||
const preferredSlavesArray = Array.isArray(preferredSlaves)
|
||||
? [...preferredSlaves]
|
||||
: [preferredSlaves];
|
||||
// sort by priority
|
||||
preferredSlavesArray.sort((a, b) => {
|
||||
// default the priority to 1
|
||||
const aPrio = a.prio ?? 1;
|
||||
const bPrio = b.prio ?? 1;
|
||||
// lowest priority first
|
||||
if (aPrio < bPrio) {
|
||||
return -1;
|
||||
}
|
||||
if (aPrio > bPrio) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
// loop over preferred slaves and return the first match
|
||||
for (let p = 0; p < preferredSlavesArray.length; p++) {
|
||||
for (let a = 0; a < availableSlaves.length; a++) {
|
||||
const slave = availableSlaves[a];
|
||||
if (slave.ip === preferredSlavesArray[p].ip) {
|
||||
if (slave.port === preferredSlavesArray[p].port) {
|
||||
selectedSlave = slave;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (selectedSlave) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// if none of the preferred slaves are available, a random available slave is returned
|
||||
if (!selectedSlave) {
|
||||
selectedSlave = (0, utils_1.sample)(availableSlaves);
|
||||
}
|
||||
return addressResponseToAddress(selectedSlave);
|
||||
}
|
||||
function addressResponseToAddress(input) {
|
||||
return { host: input.ip, port: Number(input.port) };
|
||||
}
|
||||
function noop() { }
|
||||
21
node_modules/ioredis/built/connectors/SentinelConnector/types.d.ts
generated
vendored
Normal file
21
node_modules/ioredis/built/connectors/SentinelConnector/types.d.ts
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
import { RedisOptions } from "../../redis/RedisOptions";
|
||||
export interface SentinelAddress {
|
||||
port: number;
|
||||
host: string;
|
||||
family?: number;
|
||||
}
|
||||
export interface RedisClient {
|
||||
options: RedisOptions;
|
||||
sentinel(subcommand: "sentinels", name: string): Promise<string[]>;
|
||||
sentinel(subcommand: "get-master-addr-by-name", name: string): Promise<string[]>;
|
||||
sentinel(subcommand: "slaves", name: string): Promise<string[]>;
|
||||
subscribe(...channelNames: string[]): Promise<number>;
|
||||
on(event: "message", callback: (channel: string, message: string) => void): void;
|
||||
on(event: "error", callback: (error: Error) => void): void;
|
||||
on(event: "reconnecting", callback: () => void): void;
|
||||
disconnect(): void;
|
||||
}
|
||||
export interface Sentinel {
|
||||
address: Partial<SentinelAddress>;
|
||||
client: RedisClient;
|
||||
}
|
||||
2
node_modules/ioredis/built/connectors/SentinelConnector/types.js
generated
vendored
Normal file
2
node_modules/ioredis/built/connectors/SentinelConnector/types.js
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
18
node_modules/ioredis/built/connectors/StandaloneConnector.d.ts
generated
vendored
Normal file
18
node_modules/ioredis/built/connectors/StandaloneConnector.d.ts
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
/// <reference types="node" />
|
||||
/// <reference types="node" />
|
||||
import { IpcNetConnectOpts, TcpNetConnectOpts } from "net";
|
||||
import { ConnectionOptions } from "tls";
|
||||
import { NetStream } from "../types";
|
||||
import AbstractConnector, { ErrorEmitter } from "./AbstractConnector";
|
||||
type TcpOptions = Pick<TcpNetConnectOpts, "port" | "host" | "family">;
|
||||
type IpcOptions = Pick<IpcNetConnectOpts, "path">;
|
||||
export type StandaloneConnectionOptions = Partial<TcpOptions & IpcOptions> & {
|
||||
disconnectTimeout?: number | undefined;
|
||||
tls?: ConnectionOptions | undefined;
|
||||
};
|
||||
export default class StandaloneConnector extends AbstractConnector {
|
||||
protected options: StandaloneConnectionOptions;
|
||||
constructor(options: StandaloneConnectionOptions);
|
||||
connect(_: ErrorEmitter): Promise<NetStream>;
|
||||
}
|
||||
export {};
|
||||
69
node_modules/ioredis/built/connectors/StandaloneConnector.js
generated
vendored
Normal file
69
node_modules/ioredis/built/connectors/StandaloneConnector.js
generated
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const net_1 = require("net");
|
||||
const tls_1 = require("tls");
|
||||
const utils_1 = require("../utils");
|
||||
const AbstractConnector_1 = require("./AbstractConnector");
|
||||
class StandaloneConnector extends AbstractConnector_1.default {
|
||||
constructor(options) {
|
||||
super(options.disconnectTimeout);
|
||||
this.options = options;
|
||||
}
|
||||
connect(_) {
|
||||
const { options } = this;
|
||||
this.connecting = true;
|
||||
let connectionOptions;
|
||||
if ("path" in options && options.path) {
|
||||
connectionOptions = {
|
||||
path: options.path,
|
||||
};
|
||||
}
|
||||
else {
|
||||
connectionOptions = {};
|
||||
if ("port" in options && options.port != null) {
|
||||
connectionOptions.port = options.port;
|
||||
}
|
||||
if ("host" in options && options.host != null) {
|
||||
connectionOptions.host = options.host;
|
||||
}
|
||||
if ("family" in options && options.family != null) {
|
||||
connectionOptions.family = options.family;
|
||||
}
|
||||
}
|
||||
if (options.tls) {
|
||||
Object.assign(connectionOptions, options.tls);
|
||||
}
|
||||
// TODO:
|
||||
// We use native Promise here since other Promise
|
||||
// implementation may use different schedulers that
|
||||
// cause issue when the stream is resolved in the
|
||||
// next tick.
|
||||
// Should use the provided promise in the next major
|
||||
// version and do not connect before resolved.
|
||||
return new Promise((resolve, reject) => {
|
||||
process.nextTick(() => {
|
||||
if (!this.connecting) {
|
||||
reject(new Error(utils_1.CONNECTION_CLOSED_ERROR_MSG));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (options.tls) {
|
||||
this.stream = (0, tls_1.connect)(connectionOptions);
|
||||
}
|
||||
else {
|
||||
this.stream = (0, net_1.createConnection)(connectionOptions);
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
this.stream.once("error", (err) => {
|
||||
this.firstError = err;
|
||||
});
|
||||
resolve(this.stream);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
exports.default = StandaloneConnector;
|
||||
3
node_modules/ioredis/built/connectors/index.d.ts
generated
vendored
Normal file
3
node_modules/ioredis/built/connectors/index.d.ts
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
import StandaloneConnector from "./StandaloneConnector";
|
||||
import SentinelConnector from "./SentinelConnector";
|
||||
export { StandaloneConnector, SentinelConnector };
|
||||
7
node_modules/ioredis/built/connectors/index.js
generated
vendored
Normal file
7
node_modules/ioredis/built/connectors/index.js
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.SentinelConnector = exports.StandaloneConnector = void 0;
|
||||
const StandaloneConnector_1 = require("./StandaloneConnector");
|
||||
exports.StandaloneConnector = StandaloneConnector_1.default;
|
||||
const SentinelConnector_1 = require("./SentinelConnector");
|
||||
exports.SentinelConnector = SentinelConnector_1.default;
|
||||
9
node_modules/ioredis/built/constants/TLSProfiles.d.ts
generated
vendored
Normal file
9
node_modules/ioredis/built/constants/TLSProfiles.d.ts
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
149
node_modules/ioredis/built/constants/TLSProfiles.js
generated
vendored
Normal file
149
node_modules/ioredis/built/constants/TLSProfiles.js
generated
vendored
Normal file
@@ -0,0 +1,149 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
/**
|
||||
* TLS settings for Redis Cloud. Updated on 2022-08-19.
|
||||
*/
|
||||
const RedisCloudCA = `-----BEGIN CERTIFICATE-----
|
||||
MIIDTzCCAjegAwIBAgIJAKSVpiDswLcwMA0GCSqGSIb3DQEBBQUAMD4xFjAUBgNV
|
||||
BAoMDUdhcmFudGlhIERhdGExJDAiBgNVBAMMG1NTTCBDZXJ0aWZpY2F0aW9uIEF1
|
||||
dGhvcml0eTAeFw0xMzEwMDExMjE0NTVaFw0yMzA5MjkxMjE0NTVaMD4xFjAUBgNV
|
||||
BAoMDUdhcmFudGlhIERhdGExJDAiBgNVBAMMG1NTTCBDZXJ0aWZpY2F0aW9uIEF1
|
||||
dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALZqkh/DczWP
|
||||
JnxnHLQ7QL0T4B4CDKWBKCcisriGbA6ZePWVNo4hfKQC6JrzfR+081NeD6VcWUiz
|
||||
rmd+jtPhIY4c+WVQYm5PKaN6DT1imYdxQw7aqO5j2KUCEh/cznpLxeSHoTxlR34E
|
||||
QwF28Wl3eg2vc5ct8LjU3eozWVk3gb7alx9mSA2SgmuX5lEQawl++rSjsBStemY2
|
||||
BDwOpAMXIrdEyP/cVn8mkvi/BDs5M5G+09j0gfhyCzRWMQ7Hn71u1eolRxwVxgi3
|
||||
TMn+/vTaFSqxKjgck6zuAYjBRPaHe7qLxHNr1So/Mc9nPy+3wHebFwbIcnUojwbp
|
||||
4nctkWbjb2cCAwEAAaNQME4wHQYDVR0OBBYEFP1whtcrydmW3ZJeuSoKZIKjze3w
|
||||
MB8GA1UdIwQYMBaAFP1whtcrydmW3ZJeuSoKZIKjze3wMAwGA1UdEwQFMAMBAf8w
|
||||
DQYJKoZIhvcNAQEFBQADggEBAG2erXhwRAa7+ZOBs0B6X57Hwyd1R4kfmXcs0rta
|
||||
lbPpvgULSiB+TCbf3EbhJnHGyvdCY1tvlffLjdA7HJ0PCOn+YYLBA0pTU/dyvrN6
|
||||
Su8NuS5yubnt9mb13nDGYo1rnt0YRfxN+8DM3fXIVr038A30UlPX2Ou1ExFJT0MZ
|
||||
uFKY6ZvLdI6/1cbgmguMlAhM+DhKyV6Sr5699LM3zqeI816pZmlREETYkGr91q7k
|
||||
BpXJu/dtHaGxg1ZGu6w/PCsYGUcECWENYD4VQPd8N32JjOfu6vEgoEAwfPP+3oGp
|
||||
Z4m3ewACcWOAenqflb+cQYC4PsF7qbXDmRaWrbKntOlZ3n0=
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIGMTCCBBmgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwajELMAkGA1UEBhMCVVMx
|
||||
CzAJBgNVBAgMAkNBMQswCQYDVQQHDAJDQTESMBAGA1UECgwJUmVkaXNMYWJzMS0w
|
||||
KwYDVQQDDCRSZWRpc0xhYnMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcN
|
||||
MTgwMjI1MTUzNzM3WhcNMjgwMjIzMTUzNzM3WjBfMQswCQYDVQQGEwJVUzELMAkG
|
||||
A1UECAwCQ0ExEjAQBgNVBAoMCVJlZGlzTGFiczEvMC0GA1UEAwwmUkNQIEludGVy
|
||||
bWVkaWF0ZSBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUA
|
||||
A4ICDwAwggIKAoICAQDf9dqbxc8Bq7Ctq9rWcxrGNKKHivqLAFpPq02yLPx6fsOv
|
||||
Tq7GsDChAYBBc4v7Y2Ap9RD5Vs3dIhEANcnolf27QwrG9RMnnvzk8pCvp1o6zSU4
|
||||
VuOE1W66/O1/7e2rVxyrnTcP7UgK43zNIXu7+tiAqWsO92uSnuMoGPGpeaUm1jym
|
||||
hjWKtkAwDFSqvHY+XL5qDVBEjeUe+WHkYUg40cAXjusAqgm2hZt29c2wnVrxW25W
|
||||
P0meNlzHGFdA2AC5z54iRiqj57dTfBTkHoBczQxcyw6hhzxZQ4e5I5zOKjXXEhZN
|
||||
r0tA3YC14CTabKRus/JmZieyZzRgEy2oti64tmLYTqSlAD78pRL40VNoaSYetXLw
|
||||
hhNsXCHgWaY6d5bLOc/aIQMAV5oLvZQKvuXAF1IDmhPA+bZbpWipp0zagf1P1H3s
|
||||
UzsMdn2KM0ejzgotbtNlj5TcrVwpmvE3ktvUAuA+hi3FkVx1US+2Gsp5x4YOzJ7u
|
||||
P1WPk6ShF0JgnJH2ILdj6kttTWwFzH17keSFICWDfH/+kM+k7Y1v3EXMQXE7y0T9
|
||||
MjvJskz6d/nv+sQhY04xt64xFMGTnZjlJMzfQNi7zWFLTZnDD0lPowq7l3YiPoTT
|
||||
t5Xky83lu0KZsZBo0WlWaDG00gLVdtRgVbcuSWxpi5BdLb1kRab66JptWjxwXQID
|
||||
AQABo4HrMIHoMDoGA1UdHwQzMDEwL6AtoCuGKWh0dHBzOi8vcmwtY2Etc2VydmVy
|
||||
LnJlZGlzbGFicy5jb20vdjEvY3JsMEYGCCsGAQUFBwEBBDowODA2BggrBgEFBQcw
|
||||
AYYqaHR0cHM6Ly9ybC1jYS1zZXJ2ZXIucmVkaXNsYWJzLmNvbS92MS9vY3NwMB0G
|
||||
A1UdDgQWBBQHar5OKvQUpP2qWt6mckzToeCOHDAfBgNVHSMEGDAWgBQi42wH6hM4
|
||||
L2sujEvLM0/u8lRXTzASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIB
|
||||
hjANBgkqhkiG9w0BAQsFAAOCAgEAirEn/iTsAKyhd+pu2W3Z5NjCko4NPU0EYUbr
|
||||
AP7+POK2rzjIrJO3nFYQ/LLuC7KCXG+2qwan2SAOGmqWst13Y+WHp44Kae0kaChW
|
||||
vcYLXXSoGQGC8QuFSNUdaeg3RbMDYFT04dOkqufeWVccoHVxyTSg9eD8LZuHn5jw
|
||||
7QDLiEECBmIJHk5Eeo2TAZrx4Yx6ufSUX5HeVjlAzqwtAqdt99uCJ/EL8bgpWbe+
|
||||
XoSpvUv0SEC1I1dCAhCKAvRlIOA6VBcmzg5Am12KzkqTul12/VEFIgzqu0Zy2Jbc
|
||||
AUPrYVu/+tOGXQaijy7YgwH8P8n3s7ZeUa1VABJHcxrxYduDDJBLZi+MjheUDaZ1
|
||||
jQRHYevI2tlqeSBqdPKG4zBY5lS0GiAlmuze5oENt0P3XboHoZPHiqcK3VECgTVh
|
||||
/BkJcuudETSJcZDmQ8YfoKfBzRQNg2sv/hwvUv73Ss51Sco8GEt2lD8uEdib1Q6z
|
||||
zDT5lXJowSzOD5ZA9OGDjnSRL+2riNtKWKEqvtEG3VBJoBzu9GoxbAc7wIZLxmli
|
||||
iF5a/Zf5X+UXD3s4TMmy6C4QZJpAA2egsSQCnraWO2ULhh7iXMysSkF/nzVfZn43
|
||||
iqpaB8++9a37hWq14ZmOv0TJIDz//b2+KC4VFXWQ5W5QC6whsjT+OlG4p5ZYG0jo
|
||||
616pxqo=
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFujCCA6KgAwIBAgIJAJ1aTT1lu2ScMA0GCSqGSIb3DQEBCwUAMGoxCzAJBgNV
|
||||
BAYTAlVTMQswCQYDVQQIDAJDQTELMAkGA1UEBwwCQ0ExEjAQBgNVBAoMCVJlZGlz
|
||||
TGFiczEtMCsGA1UEAwwkUmVkaXNMYWJzIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9y
|
||||
aXR5MB4XDTE4MDIyNTE1MjA0MloXDTM4MDIyMDE1MjA0MlowajELMAkGA1UEBhMC
|
||||
VVMxCzAJBgNVBAgMAkNBMQswCQYDVQQHDAJDQTESMBAGA1UECgwJUmVkaXNMYWJz
|
||||
MS0wKwYDVQQDDCRSZWRpc0xhYnMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkw
|
||||
ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDLEjXy7YrbN5Waau5cd6g1
|
||||
G5C2tMmeTpZ0duFAPxNU4oE3RHS5gGiok346fUXuUxbZ6QkuzeN2/2Z+RmRcJhQY
|
||||
Dm0ZgdG4x59An1TJfnzKKoWj8ISmoHS/TGNBdFzXV7FYNLBuqZouqePI6ReC6Qhl
|
||||
pp45huV32Q3a6IDrrvx7Wo5ZczEQeFNbCeCOQYNDdTmCyEkHqc2AGo8eoIlSTutT
|
||||
ULOC7R5gzJVTS0e1hesQ7jmqHjbO+VQS1NAL4/5K6cuTEqUl+XhVhPdLWBXJQ5ag
|
||||
54qhX4v+ojLzeU1R/Vc6NjMvVtptWY6JihpgplprN0Yh2556ewcXMeturcKgXfGJ
|
||||
xeYzsjzXerEjrVocX5V8BNrg64NlifzTMKNOOv4fVZszq1SIHR8F9ROrqiOdh8iC
|
||||
JpUbLpXH9hWCSEO6VRMB2xJoKu3cgl63kF30s77x7wLFMEHiwsQRKxooE1UhgS9K
|
||||
2sO4TlQ1eWUvFvHSTVDQDlGQ6zu4qjbOpb3Q8bQwoK+ai2alkXVR4Ltxe9QlgYK3
|
||||
StsnPhruzZGA0wbXdpw0bnM+YdlEm5ffSTpNIfgHeaa7Dtb801FtA71ZlH7A6TaI
|
||||
SIQuUST9EKmv7xrJyx0W1pGoPOLw5T029aTjnICSLdtV9bLwysrLhIYG5bnPq78B
|
||||
cS+jZHFGzD7PUVGQD01nOQIDAQABo2MwYTAdBgNVHQ4EFgQUIuNsB+oTOC9rLoxL
|
||||
yzNP7vJUV08wHwYDVR0jBBgwFoAUIuNsB+oTOC9rLoxLyzNP7vJUV08wDwYDVR0T
|
||||
AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADggIBAHfg
|
||||
z5pMNUAKdMzK1aS1EDdK9yKz4qicILz5czSLj1mC7HKDRy8cVADUxEICis++CsCu
|
||||
rYOvyCVergHQLREcxPq4rc5Nq1uj6J6649NEeh4WazOOjL4ZfQ1jVznMbGy+fJm3
|
||||
3Hoelv6jWRG9iqeJZja7/1s6YC6bWymI/OY1e4wUKeNHAo+Vger7MlHV+RuabaX+
|
||||
hSJ8bJAM59NCM7AgMTQpJCncrcdLeceYniGy5Q/qt2b5mJkQVkIdy4TPGGB+AXDJ
|
||||
D0q3I/JDRkDUFNFdeW0js7fHdsvCR7O3tJy5zIgEV/o/BCkmJVtuwPYOrw/yOlKj
|
||||
TY/U7ATAx9VFF6/vYEOMYSmrZlFX+98L6nJtwDqfLB5VTltqZ4H/KBxGE3IRSt9l
|
||||
FXy40U+LnXzhhW+7VBAvyYX8GEXhHkKU8Gqk1xitrqfBXY74xKgyUSTolFSfFVgj
|
||||
mcM/X4K45bka+qpkj7Kfv/8D4j6aZekwhN2ly6hhC1SmQ8qjMjpG/mrWOSSHZFmf
|
||||
ybu9iD2AYHeIOkshIl6xYIa++Q/00/vs46IzAbQyriOi0XxlSMMVtPx0Q3isp+ji
|
||||
n8Mq9eOuxYOEQ4of8twUkUDd528iwGtEdwf0Q01UyT84S62N8AySl1ZBKXJz6W4F
|
||||
UhWfa/HQYOAPDdEjNgnVwLI23b8t0TozyCWw7q8h
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEjzCCA3egAwIBAgIQe55B/ALCKJDZtdNT8kD6hTANBgkqhkiG9w0BAQsFADBM
|
||||
MSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEGA1UEChMKR2xv
|
||||
YmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjAeFw0yMjAxMjYxMjAwMDBaFw0y
|
||||
NTAxMjYwMDAwMDBaMFgxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWdu
|
||||
IG52LXNhMS4wLAYDVQQDEyVHbG9iYWxTaWduIEF0bGFzIFIzIE9WIFRMUyBDQSAy
|
||||
MDIyIFEyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmGmg1LW9b7Lf
|
||||
8zDD83yBDTEkt+FOxKJZqF4veWc5KZsQj9HfnUS2e5nj/E+JImlGPsQuoiosLuXD
|
||||
BVBNAMcUFa11buFMGMeEMwiTmCXoXRrXQmH0qjpOfKgYc5gHG3BsRGaRrf7VR4eg
|
||||
ofNMG9wUBw4/g/TT7+bQJdA4NfE7Y4d5gEryZiBGB/swaX6Jp/8MF4TgUmOWmalK
|
||||
dZCKyb4sPGQFRTtElk67F7vU+wdGcrcOx1tDcIB0ncjLPMnaFicagl+daWGsKqTh
|
||||
counQb6QJtYHa91KvCfKWocMxQ7OIbB5UARLPmC4CJ1/f8YFm35ebfzAeULYdGXu
|
||||
jE9CLor0OwIDAQABo4IBXzCCAVswDgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQG
|
||||
CCsGAQUFBwMBBggrBgEFBQcDAjASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQW
|
||||
BBSH5Zq7a7B/t95GfJWkDBpA8HHqdjAfBgNVHSMEGDAWgBSP8Et/qC5FJK5NUPpj
|
||||
move4t0bvDB7BggrBgEFBQcBAQRvMG0wLgYIKwYBBQUHMAGGImh0dHA6Ly9vY3Nw
|
||||
Mi5nbG9iYWxzaWduLmNvbS9yb290cjMwOwYIKwYBBQUHMAKGL2h0dHA6Ly9zZWN1
|
||||
cmUuZ2xvYmFsc2lnbi5jb20vY2FjZXJ0L3Jvb3QtcjMuY3J0MDYGA1UdHwQvMC0w
|
||||
K6ApoCeGJWh0dHA6Ly9jcmwuZ2xvYmFsc2lnbi5jb20vcm9vdC1yMy5jcmwwIQYD
|
||||
VR0gBBowGDAIBgZngQwBAgIwDAYKKwYBBAGgMgoBAjANBgkqhkiG9w0BAQsFAAOC
|
||||
AQEAKRic9/f+nmhQU/wz04APZLjgG5OgsuUOyUEZjKVhNGDwxGTvKhyXGGAMW2B/
|
||||
3bRi+aElpXwoxu3pL6fkElbX3B0BeS5LoDtxkyiVEBMZ8m+sXbocwlPyxrPbX6mY
|
||||
0rVIvnuUeBH8X0L5IwfpNVvKnBIilTbcebfHyXkPezGwz7E1yhUULjJFm2bt0SdX
|
||||
y+4X/WeiiYIv+fTVgZZgl+/2MKIsu/qdBJc3f3TvJ8nz+Eax1zgZmww+RSQWeOj3
|
||||
15Iw6Z5FX+NwzY/Ab+9PosR5UosSeq+9HhtaxZttXG1nVh+avYPGYddWmiMT90J5
|
||||
ZgKnO/Fx2hBgTxhOTMYaD312kg==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G
|
||||
A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp
|
||||
Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4
|
||||
MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEG
|
||||
A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI
|
||||
hvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWtiHL8
|
||||
RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsT
|
||||
gHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmm
|
||||
KPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd
|
||||
QQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZ
|
||||
XriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAw
|
||||
DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+o
|
||||
LkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZU
|
||||
RUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMp
|
||||
jjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK
|
||||
6fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQX
|
||||
mcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs
|
||||
Mx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH
|
||||
WD9f
|
||||
-----END CERTIFICATE-----`;
|
||||
const TLSProfiles = {
|
||||
RedisCloudFixed: { ca: RedisCloudCA },
|
||||
RedisCloudFlexible: { ca: RedisCloudCA },
|
||||
};
|
||||
exports.default = TLSProfiles;
|
||||
7
node_modules/ioredis/built/errors/ClusterAllFailedError.d.ts
generated
vendored
Normal file
7
node_modules/ioredis/built/errors/ClusterAllFailedError.d.ts
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
import { RedisError } from "redis-errors";
|
||||
export default class ClusterAllFailedError extends RedisError {
|
||||
lastNodeError: RedisError;
|
||||
static defaultMessage: string;
|
||||
constructor(message: any, lastNodeError: RedisError);
|
||||
get name(): string;
|
||||
}
|
||||
15
node_modules/ioredis/built/errors/ClusterAllFailedError.js
generated
vendored
Normal file
15
node_modules/ioredis/built/errors/ClusterAllFailedError.js
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const redis_errors_1 = require("redis-errors");
|
||||
class ClusterAllFailedError extends redis_errors_1.RedisError {
|
||||
constructor(message, lastNodeError) {
|
||||
super(message);
|
||||
this.lastNodeError = lastNodeError;
|
||||
Error.captureStackTrace(this, this.constructor);
|
||||
}
|
||||
get name() {
|
||||
return this.constructor.name;
|
||||
}
|
||||
}
|
||||
ClusterAllFailedError.defaultMessage = "Failed to refresh slots cache.";
|
||||
exports.default = ClusterAllFailedError;
|
||||
5
node_modules/ioredis/built/errors/MaxRetriesPerRequestError.d.ts
generated
vendored
Normal file
5
node_modules/ioredis/built/errors/MaxRetriesPerRequestError.d.ts
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
import { AbortError } from "redis-errors";
|
||||
export default class MaxRetriesPerRequestError extends AbortError {
|
||||
constructor(maxRetriesPerRequest: number);
|
||||
get name(): string;
|
||||
}
|
||||
14
node_modules/ioredis/built/errors/MaxRetriesPerRequestError.js
generated
vendored
Normal file
14
node_modules/ioredis/built/errors/MaxRetriesPerRequestError.js
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const redis_errors_1 = require("redis-errors");
|
||||
class MaxRetriesPerRequestError extends redis_errors_1.AbortError {
|
||||
constructor(maxRetriesPerRequest) {
|
||||
const message = `Reached the max retries per request limit (which is ${maxRetriesPerRequest}). Refer to "maxRetriesPerRequest" option for details.`;
|
||||
super(message);
|
||||
Error.captureStackTrace(this, this.constructor);
|
||||
}
|
||||
get name() {
|
||||
return this.constructor.name;
|
||||
}
|
||||
}
|
||||
exports.default = MaxRetriesPerRequestError;
|
||||
2
node_modules/ioredis/built/errors/index.d.ts
generated
vendored
Normal file
2
node_modules/ioredis/built/errors/index.d.ts
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
import MaxRetriesPerRequestError from "./MaxRetriesPerRequestError";
|
||||
export { MaxRetriesPerRequestError };
|
||||
5
node_modules/ioredis/built/errors/index.js
generated
vendored
Normal file
5
node_modules/ioredis/built/errors/index.js
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.MaxRetriesPerRequestError = void 0;
|
||||
const MaxRetriesPerRequestError_1 = require("./MaxRetriesPerRequestError");
|
||||
exports.MaxRetriesPerRequestError = MaxRetriesPerRequestError_1.default;
|
||||
76
node_modules/ioredis/built/himport/HimportCoordinator.d.ts
generated
vendored
Normal file
76
node_modules/ioredis/built/himport/HimportCoordinator.d.ts
generated
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
/// <reference types="node" />
|
||||
/// <reference types="node" />
|
||||
import Command from "../Command";
|
||||
import type { HimportFieldset } from "./types";
|
||||
type HimportConnectionRole = "standalone" | "cluster" | "master" | "replica";
|
||||
export interface HimportConnection {
|
||||
sendCommand(command: Command): unknown;
|
||||
}
|
||||
interface HimportPipelineOwner extends HimportConnection {
|
||||
slots?: readonly (readonly string[])[];
|
||||
connectionPool?: {
|
||||
getInstanceByKey(key: string): HimportConnection | undefined;
|
||||
getSampleInstance(role: "master"): HimportConnection | undefined;
|
||||
};
|
||||
}
|
||||
interface HimportPipelineInterception {
|
||||
owner: HimportPipelineOwner;
|
||||
commands: readonly Command[];
|
||||
slot?: number;
|
||||
preferredNodeKey?: string;
|
||||
setDestination(connection: HimportConnection): void;
|
||||
resume(): void;
|
||||
reject(error: Error): void;
|
||||
}
|
||||
interface HimportDefinition {
|
||||
readonly canonicalName: string;
|
||||
readonly name: string | Buffer;
|
||||
readonly fields: readonly (string | Buffer)[];
|
||||
}
|
||||
interface ManagedSetContext {
|
||||
definition: HimportDefinition;
|
||||
lastConnection?: HimportConnection;
|
||||
resumeSend?: () => void;
|
||||
recoveryAttempts: number;
|
||||
recoveryInstalled: boolean;
|
||||
sendWithoutPreparationOn?: HimportConnection;
|
||||
}
|
||||
export interface HimportBinding {
|
||||
coordinator: HimportCoordinator;
|
||||
role: HimportConnectionRole;
|
||||
}
|
||||
export declare const hasHimportCoordinator: unique symbol;
|
||||
export declare function cloneHimportFieldsets(fieldsets: readonly HimportFieldset[] | undefined): readonly HimportFieldset[] | undefined;
|
||||
export default class HimportCoordinator {
|
||||
private readonly definitions;
|
||||
private readonly definitionsByName;
|
||||
private readonly sessions;
|
||||
private readonly managedSets;
|
||||
constructor(fieldsets: readonly HimportFieldset[]);
|
||||
get size(): number;
|
||||
beginSession(connection: HimportConnection): void;
|
||||
detach(connection: HimportConnection): void;
|
||||
invalidate(connection: HimportConnection): void;
|
||||
getDefinitions(): readonly HimportDefinition[];
|
||||
classify(command: Command): ManagedSetContext | undefined;
|
||||
prepareCommand(connection: HimportConnection, command: Command): Promise<void> | undefined;
|
||||
hasManagedSet(commands: readonly Command[]): boolean;
|
||||
prepareCommands(connection: HimportConnection, commands: readonly Command[]): Promise<void> | undefined;
|
||||
interceptCommand(connection: HimportConnection, command: Command, ready: boolean, resumeSend: () => void): boolean;
|
||||
allowNextSend(connection: HimportConnection, command: Command): void;
|
||||
consumeAllowedSend(connection: HimportConnection, command: Command): boolean;
|
||||
installRecovery(connection: HimportConnection, command: Command, resumeSend: () => void): void;
|
||||
ensurePrepared(connection: HimportConnection, definition: HimportDefinition): Promise<void> | undefined;
|
||||
private getSession;
|
||||
private markUnprepared;
|
||||
}
|
||||
export declare function bindHimportCoordinator(owner: object, coordinator: HimportCoordinator, role: HimportConnectionRole): void;
|
||||
export declare function getHimportBinding(owner: object): HimportBinding | undefined;
|
||||
export declare function interceptHimportCommand(connection: HimportConnection, command: Command, ready: boolean, resumeSend: () => void): boolean;
|
||||
export declare function interceptHimportPipeline({ owner, commands, slot, preferredNodeKey, setDestination, resume, reject, }: HimportPipelineInterception): boolean;
|
||||
export declare function setHimportRole(owner: object, role: HimportConnectionRole): void;
|
||||
export declare function unbindHimportCoordinator(owner: object): void;
|
||||
export declare function isInternalHimportCommand(command: Command): boolean;
|
||||
export declare function isHimportControlCommand(command: Command): boolean;
|
||||
export declare function interceptHimportControlCommand(connections: readonly HimportConnection[], command: Command): boolean;
|
||||
export {};
|
||||
415
node_modules/ioredis/built/himport/HimportCoordinator.js
generated
vendored
Normal file
415
node_modules/ioredis/built/himport/HimportCoordinator.js
generated
vendored
Normal file
@@ -0,0 +1,415 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.interceptHimportControlCommand = exports.isHimportControlCommand = exports.isInternalHimportCommand = exports.unbindHimportCoordinator = exports.setHimportRole = exports.interceptHimportPipeline = exports.interceptHimportCommand = exports.getHimportBinding = exports.bindHimportCoordinator = exports.cloneHimportFieldsets = exports.hasHimportCoordinator = void 0;
|
||||
const Command_1 = require("../Command");
|
||||
const utils_1 = require("../utils");
|
||||
exports.hasHimportCoordinator = Symbol("hasHimportCoordinator");
|
||||
const bindings = new WeakMap();
|
||||
const internalCommands = new WeakSet();
|
||||
const debug = (0, utils_1.Debug)("himport");
|
||||
function copyValue(value) {
|
||||
return value instanceof Buffer ? Buffer.from(value) : value;
|
||||
}
|
||||
function canonicalize(value) {
|
||||
return Buffer.from(value).toString("base64");
|
||||
}
|
||||
function commandToken(value) {
|
||||
if (value === undefined) {
|
||||
return "";
|
||||
}
|
||||
return Buffer.isBuffer(value)
|
||||
? value.toString("utf8").toUpperCase()
|
||||
: String(value).toUpperCase();
|
||||
}
|
||||
function cloneHimportFieldsets(fieldsets) {
|
||||
if (fieldsets === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
if (!Array.isArray(fieldsets)) {
|
||||
throw new TypeError("himportFieldsets must be an array");
|
||||
}
|
||||
const names = new Set();
|
||||
const copied = fieldsets.map((fieldset) => {
|
||||
if (!fieldset || typeof fieldset !== "object") {
|
||||
throw new TypeError("Each HIMPORT fieldset must be an object");
|
||||
}
|
||||
if (typeof fieldset.name !== "string" && !Buffer.isBuffer(fieldset.name)) {
|
||||
throw new TypeError("Each HIMPORT fieldset name must be a string or Buffer");
|
||||
}
|
||||
if (!Array.isArray(fieldset.fields)) {
|
||||
throw new TypeError("Each HIMPORT fieldset fields value must be an array");
|
||||
}
|
||||
const name = copyValue(fieldset.name);
|
||||
const canonicalName = canonicalize(name);
|
||||
if (names.has(canonicalName)) {
|
||||
throw new TypeError("Duplicate HIMPORT fieldset name");
|
||||
}
|
||||
names.add(canonicalName);
|
||||
const fields = fieldset.fields.map((field) => {
|
||||
if (typeof field !== "string" && !Buffer.isBuffer(field)) {
|
||||
throw new TypeError("Each HIMPORT field must be a string or Buffer");
|
||||
}
|
||||
return copyValue(field);
|
||||
});
|
||||
return Object.freeze({
|
||||
name,
|
||||
fields: Object.freeze(fields),
|
||||
});
|
||||
});
|
||||
return Object.freeze(copied);
|
||||
}
|
||||
exports.cloneHimportFieldsets = cloneHimportFieldsets;
|
||||
class HimportCoordinator {
|
||||
constructor(fieldsets) {
|
||||
this.definitionsByName = new Map();
|
||||
this.sessions = new WeakMap();
|
||||
this.managedSets = new WeakMap();
|
||||
this.definitions = fieldsets.map((fieldset) => {
|
||||
const definition = {
|
||||
canonicalName: canonicalize(fieldset.name),
|
||||
name: fieldset.name,
|
||||
fields: fieldset.fields,
|
||||
};
|
||||
this.definitionsByName.set(definition.canonicalName, definition);
|
||||
return definition;
|
||||
});
|
||||
}
|
||||
get size() {
|
||||
return this.definitions.length;
|
||||
}
|
||||
beginSession(connection) {
|
||||
this.sessions.set(connection, {
|
||||
fieldsets: new Map(),
|
||||
});
|
||||
}
|
||||
detach(connection) {
|
||||
this.sessions.delete(connection);
|
||||
}
|
||||
invalidate(connection) {
|
||||
const session = this.sessions.get(connection);
|
||||
if (session) {
|
||||
session.fieldsets.clear();
|
||||
}
|
||||
}
|
||||
getDefinitions() {
|
||||
return this.definitions;
|
||||
}
|
||||
classify(command) {
|
||||
const existing = this.managedSets.get(command);
|
||||
if (existing) {
|
||||
return existing;
|
||||
}
|
||||
if (command.name.toLowerCase() !== "himport" ||
|
||||
commandToken(command.args[0]) !== "SET") {
|
||||
return undefined;
|
||||
}
|
||||
const fieldsetName = command.args[2];
|
||||
if (typeof fieldsetName !== "string" && !Buffer.isBuffer(fieldsetName)) {
|
||||
return undefined;
|
||||
}
|
||||
const definition = this.definitionsByName.get(canonicalize(fieldsetName));
|
||||
if (!definition) {
|
||||
return undefined;
|
||||
}
|
||||
const context = {
|
||||
definition,
|
||||
recoveryAttempts: 0,
|
||||
recoveryInstalled: false,
|
||||
};
|
||||
this.managedSets.set(command, context);
|
||||
return context;
|
||||
}
|
||||
prepareCommand(connection, command) {
|
||||
const context = this.classify(command);
|
||||
if (!context) {
|
||||
return undefined;
|
||||
}
|
||||
return this.ensurePrepared(connection, context.definition);
|
||||
}
|
||||
hasManagedSet(commands) {
|
||||
return commands.some((command) => this.classify(command) !== undefined);
|
||||
}
|
||||
prepareCommands(connection, commands) {
|
||||
const preparations = new Set();
|
||||
for (const command of commands) {
|
||||
const preparation = this.prepareCommand(connection, command);
|
||||
if (preparation) {
|
||||
preparations.add(preparation);
|
||||
}
|
||||
}
|
||||
if (preparations.size === 0) {
|
||||
return undefined;
|
||||
}
|
||||
return Promise.all(preparations).then(() => undefined);
|
||||
}
|
||||
interceptCommand(connection, command, ready, resumeSend) {
|
||||
if (command.isSettled) {
|
||||
return true;
|
||||
}
|
||||
if (command.name.toLowerCase() === "reset") {
|
||||
this.invalidate(connection);
|
||||
}
|
||||
const managedSet = this.classify(command);
|
||||
if (!managedSet) {
|
||||
return false;
|
||||
}
|
||||
this.installRecovery(connection, command, resumeSend);
|
||||
const maySend = this.consumeAllowedSend(connection, command);
|
||||
if (!ready || maySend) {
|
||||
return false;
|
||||
}
|
||||
const preparation = this.prepareCommand(connection, command);
|
||||
if (!preparation) {
|
||||
return false;
|
||||
}
|
||||
preparation.then(() => {
|
||||
if (command.isSettled) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
this.allowNextSend(connection, command);
|
||||
resumeSend();
|
||||
}
|
||||
catch (error) {
|
||||
command.reject(error);
|
||||
}
|
||||
}, (error) => {
|
||||
if (!command.isSettled) {
|
||||
command.reject(error);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
allowNextSend(connection, command) {
|
||||
const context = this.classify(command);
|
||||
if (context) {
|
||||
context.sendWithoutPreparationOn = connection;
|
||||
}
|
||||
}
|
||||
consumeAllowedSend(connection, command) {
|
||||
const context = this.managedSets.get(command);
|
||||
if (context?.sendWithoutPreparationOn !== connection) {
|
||||
return false;
|
||||
}
|
||||
context.sendWithoutPreparationOn = undefined;
|
||||
return true;
|
||||
}
|
||||
installRecovery(connection, command, resumeSend) {
|
||||
const context = this.classify(command);
|
||||
if (!context) {
|
||||
return;
|
||||
}
|
||||
context.lastConnection = connection;
|
||||
context.resumeSend = resumeSend;
|
||||
if (context.recoveryInstalled) {
|
||||
return;
|
||||
}
|
||||
context.recoveryInstalled = true;
|
||||
const reject = command.reject;
|
||||
command.reject = (error) => {
|
||||
if (command.isSettled) {
|
||||
return;
|
||||
}
|
||||
const recoveryConnection = context.lastConnection;
|
||||
const recoverySend = context.resumeSend;
|
||||
if (context.recoveryAttempts > 0 ||
|
||||
!recoveryConnection ||
|
||||
!recoverySend ||
|
||||
!isMissingFieldsetError(error)) {
|
||||
reject.call(command, error);
|
||||
return;
|
||||
}
|
||||
context.recoveryAttempts += 1;
|
||||
this.markUnprepared(recoveryConnection, context.definition);
|
||||
const preparation = this.ensurePrepared(recoveryConnection, context.definition);
|
||||
Promise.resolve(preparation).then(() => {
|
||||
if (command.isSettled) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
this.allowNextSend(recoveryConnection, command);
|
||||
recoverySend();
|
||||
}
|
||||
catch (sendError) {
|
||||
reject.call(command, sendError);
|
||||
}
|
||||
}, (preparationError) => {
|
||||
if (!command.isSettled) {
|
||||
reject.call(command, preparationError);
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
ensurePrepared(connection, definition) {
|
||||
const session = this.getSession(connection);
|
||||
const current = session.fieldsets.get(definition.canonicalName);
|
||||
if (current?.status === "prepared") {
|
||||
return undefined;
|
||||
}
|
||||
if (current?.status === "preparing") {
|
||||
return current.promise;
|
||||
}
|
||||
const command = new Command_1.default("himport", [
|
||||
"PREPARE",
|
||||
definition.name,
|
||||
...definition.fields,
|
||||
]);
|
||||
internalCommands.add(command);
|
||||
const promise = Promise.resolve(connection.sendCommand(command)).then(() => {
|
||||
if (this.sessions.get(connection) !== session) {
|
||||
return (this.ensurePrepared(connection, definition) ?? Promise.resolve());
|
||||
}
|
||||
const latest = session.fieldsets.get(definition.canonicalName);
|
||||
if (latest?.status === "preparing" && latest.promise === promise) {
|
||||
session.fieldsets.set(definition.canonicalName, {
|
||||
status: "prepared",
|
||||
});
|
||||
}
|
||||
}, (error) => {
|
||||
if (this.sessions.get(connection) !== session) {
|
||||
return (this.ensurePrepared(connection, definition) ?? Promise.resolve());
|
||||
}
|
||||
const latest = session.fieldsets.get(definition.canonicalName);
|
||||
if (latest?.status === "preparing" && latest.promise === promise) {
|
||||
session.fieldsets.delete(definition.canonicalName);
|
||||
}
|
||||
throw error;
|
||||
});
|
||||
session.fieldsets.set(definition.canonicalName, {
|
||||
status: "preparing",
|
||||
promise,
|
||||
});
|
||||
return promise;
|
||||
}
|
||||
getSession(connection) {
|
||||
let session = this.sessions.get(connection);
|
||||
if (!session) {
|
||||
session = {
|
||||
fieldsets: new Map(),
|
||||
};
|
||||
this.sessions.set(connection, session);
|
||||
}
|
||||
return session;
|
||||
}
|
||||
markUnprepared(connection, definition) {
|
||||
const fieldsets = this.getSession(connection).fieldsets;
|
||||
if (fieldsets.get(definition.canonicalName)?.status === "prepared") {
|
||||
fieldsets.delete(definition.canonicalName);
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.default = HimportCoordinator;
|
||||
function bindHimportCoordinator(owner, coordinator, role) {
|
||||
bindings.set(owner, { coordinator, role });
|
||||
owner[exports.hasHimportCoordinator] = true;
|
||||
}
|
||||
exports.bindHimportCoordinator = bindHimportCoordinator;
|
||||
function getHimportBinding(owner) {
|
||||
return bindings.get(owner);
|
||||
}
|
||||
exports.getHimportBinding = getHimportBinding;
|
||||
function interceptHimportCommand(connection, command, ready, resumeSend) {
|
||||
const binding = bindings.get(connection);
|
||||
if (!binding ||
|
||||
binding.role === "replica" ||
|
||||
isInternalHimportCommand(command)) {
|
||||
return false;
|
||||
}
|
||||
return binding.coordinator.interceptCommand(connection, command, ready, resumeSend);
|
||||
}
|
||||
exports.interceptHimportCommand = interceptHimportCommand;
|
||||
function interceptHimportPipeline({ owner, commands, slot, preferredNodeKey, setDestination, resume, reject, }) {
|
||||
const binding = bindings.get(owner);
|
||||
if (!binding || !binding.coordinator.hasManagedSet(commands)) {
|
||||
return false;
|
||||
}
|
||||
let connection = owner;
|
||||
if (binding.role === "cluster") {
|
||||
const nodeKey = preferredNodeKey ?? owner.slots?.[slot]?.[0];
|
||||
const connectionPool = owner.connectionPool;
|
||||
const clusterConnection = (nodeKey && connectionPool?.getInstanceByKey(nodeKey)) ||
|
||||
connectionPool?.getSampleInstance("master");
|
||||
if (!clusterConnection) {
|
||||
reject(new Error("No master node is available for the pipeline"));
|
||||
return true;
|
||||
}
|
||||
connection = clusterConnection;
|
||||
setDestination(connection);
|
||||
}
|
||||
const preparation = binding.coordinator.prepareCommands(connection, commands);
|
||||
if (!preparation) {
|
||||
return false;
|
||||
}
|
||||
preparation.then(() => {
|
||||
try {
|
||||
resume();
|
||||
}
|
||||
catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
}, (error) => {
|
||||
reject(error);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
exports.interceptHimportPipeline = interceptHimportPipeline;
|
||||
function setHimportRole(owner, role) {
|
||||
const binding = bindings.get(owner);
|
||||
if (binding) {
|
||||
binding.role = role;
|
||||
}
|
||||
}
|
||||
exports.setHimportRole = setHimportRole;
|
||||
function unbindHimportCoordinator(owner) {
|
||||
const binding = bindings.get(owner);
|
||||
if (binding) {
|
||||
binding.coordinator.detach(owner);
|
||||
bindings.delete(owner);
|
||||
}
|
||||
owner[exports.hasHimportCoordinator] = false;
|
||||
}
|
||||
exports.unbindHimportCoordinator = unbindHimportCoordinator;
|
||||
function isInternalHimportCommand(command) {
|
||||
return internalCommands.has(command);
|
||||
}
|
||||
exports.isInternalHimportCommand = isInternalHimportCommand;
|
||||
function isHimportControlCommand(command) {
|
||||
if (command.name.toLowerCase() !== "himport") {
|
||||
return false;
|
||||
}
|
||||
return ["PREPARE", "DISCARD", "DISCARDALL"].includes(commandToken(command.args[0]));
|
||||
}
|
||||
exports.isHimportControlCommand = isHimportControlCommand;
|
||||
function interceptHimportControlCommand(connections, command) {
|
||||
if (!isHimportControlCommand(command) || connections.length === 0) {
|
||||
return false;
|
||||
}
|
||||
const replies = connections.map((connection) => {
|
||||
const clone = new Command_1.default(command.name, command.args);
|
||||
connection.sendCommand(clone);
|
||||
return clone.promise;
|
||||
});
|
||||
Promise.allSettled(replies).then((results) => {
|
||||
let firstReply;
|
||||
let hasFirstReply = false;
|
||||
for (const result of results) {
|
||||
if (result.status === "rejected") {
|
||||
command.reject(result.reason);
|
||||
return;
|
||||
}
|
||||
if (!hasFirstReply) {
|
||||
firstReply = result.value;
|
||||
hasFirstReply = true;
|
||||
}
|
||||
else if (String(result.value) !== String(firstReply)) {
|
||||
debug("divergent HIMPORT reply across masters (%s != %s)", result.value, firstReply);
|
||||
}
|
||||
}
|
||||
command.resolve(firstReply);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
exports.interceptHimportControlCommand = interceptHimportControlCommand;
|
||||
function isMissingFieldsetError(error) {
|
||||
return error.message.toLowerCase().includes("no such fieldset");
|
||||
}
|
||||
21
node_modules/ioredis/built/himport/types.d.ts
generated
vendored
Normal file
21
node_modules/ioredis/built/himport/types.d.ts
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
/// <reference types="node" />
|
||||
/// <reference types="node" />
|
||||
/**
|
||||
* A long-lived HIMPORT fieldset managed by ioredis for the lifetime of a
|
||||
* logical Redis or Cluster client.
|
||||
*
|
||||
* Requires Redis 8.10 or newer.
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
export interface HimportFieldset {
|
||||
/**
|
||||
* The name referenced by later `HIMPORT SET` commands.
|
||||
*/
|
||||
readonly name: string | Buffer;
|
||||
/**
|
||||
* Ordered hash field names. Values passed to `HIMPORT SET` are paired with
|
||||
* fields positionally.
|
||||
*/
|
||||
readonly fields: readonly (string | Buffer)[];
|
||||
}
|
||||
2
node_modules/ioredis/built/himport/types.js
generated
vendored
Normal file
2
node_modules/ioredis/built/himport/types.js
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
48
node_modules/ioredis/built/index.d.ts
generated
vendored
Normal file
48
node_modules/ioredis/built/index.d.ts
generated
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
export { default } from "./Redis";
|
||||
export { default as Redis } from "./Redis";
|
||||
export { default as Cluster } from "./cluster";
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
export { default as Command } from "./Command";
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
export { default as RedisCommander, Result, ClientContext, } from "./utils/RedisCommander";
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
export { default as ScanStream } from "./ScanStream";
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
export { default as Pipeline } from "./Pipeline";
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
export { default as AbstractConnector } from "./connectors/AbstractConnector";
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
export { default as SentinelConnector, SentinelIterator, } from "./connectors/SentinelConnector";
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
export { Callback } from "./types";
|
||||
export { SentinelAddress, SentinelConnectionOptions, } from "./connectors/SentinelConnector";
|
||||
export { StandaloneConnectionOptions } from "./connectors/StandaloneConnector";
|
||||
export { RedisOptions, CommonRedisOptions } from "./redis/RedisOptions";
|
||||
export type { HimportFieldset } from "./himport/types";
|
||||
export { ClusterNode } from "./cluster";
|
||||
export { ClusterOptions, DNSLookupFunction, DNSResolveSrvFunction, NatMap, } from "./cluster/ClusterOptions";
|
||||
export { NodeRole } from "./cluster/util";
|
||||
export type { RedisKey, RedisValue, ChainableCommander, } from "./utils/RedisCommander";
|
||||
export type { ScanStreamOptions } from "./types";
|
||||
export type { RedisStatus } from "./Redis";
|
||||
export type { ClusterStatus } from "./cluster";
|
||||
export type { CommandTraceContext, BatchOperationContext, ConnectTraceContext, } from "./tracing";
|
||||
export declare const ReplyError: any;
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
export declare function print(err: Error | null, reply?: any): void;
|
||||
62
node_modules/ioredis/built/index.js
generated
vendored
Normal file
62
node_modules/ioredis/built/index.js
generated
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.print = exports.ReplyError = exports.SentinelIterator = exports.SentinelConnector = exports.AbstractConnector = exports.Pipeline = exports.ScanStream = exports.Command = exports.Cluster = exports.Redis = 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 Redis_2 = require("./Redis");
|
||||
Object.defineProperty(exports, "Redis", { enumerable: true, get: function () { return Redis_2.default; } });
|
||||
var cluster_1 = require("./cluster");
|
||||
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");
|
||||
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");
|
||||
Object.defineProperty(exports, "AbstractConnector", { enumerable: true, get: function () { return AbstractConnector_1.default; } });
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
var SentinelConnector_1 = require("./connectors/SentinelConnector");
|
||||
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;
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
Object.defineProperty(exports, "Promise", {
|
||||
get() {
|
||||
console.warn("ioredis v5 does not support plugging third-party Promise library anymore. Native Promise will be used.");
|
||||
return Promise;
|
||||
},
|
||||
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) {
|
||||
if (err) {
|
||||
console.log("Error: " + err);
|
||||
}
|
||||
else {
|
||||
console.log("Reply: " + reply);
|
||||
}
|
||||
}
|
||||
exports.print = print;
|
||||
248
node_modules/ioredis/built/redis/RedisOptions.d.ts
generated
vendored
Normal file
248
node_modules/ioredis/built/redis/RedisOptions.d.ts
generated
vendored
Normal file
@@ -0,0 +1,248 @@
|
||||
import { CommanderOptions } from "../utils/Commander";
|
||||
import ConnectorConstructor from "../connectors/ConnectorConstructor";
|
||||
import { SentinelConnectionOptions } from "../connectors/SentinelConnector";
|
||||
import { StandaloneConnectionOptions } from "../connectors/StandaloneConnector";
|
||||
import { ProtocolVersion, ReplyMappingMode } from "../types";
|
||||
import type { HimportFieldset } from "../himport/types";
|
||||
export type ReconnectOnError = (err: Error) => boolean | 1 | 2;
|
||||
export type RetryStrategy = ((times: number) => number | void | null) | null | undefined;
|
||||
export interface CommonRedisOptions extends CommanderOptions {
|
||||
Connector?: ConnectorConstructor | undefined;
|
||||
/**
|
||||
* Determines the delay in milliseconds before reconnecting after a connection loss.
|
||||
*
|
||||
* @default Exponential backoff capped at 5000ms, plus 0-199ms of random jitter.
|
||||
*/
|
||||
retryStrategy?: RetryStrategy;
|
||||
/**
|
||||
* If a command does not return a reply within a set number of milliseconds,
|
||||
* a "Command timed out" error will be thrown.
|
||||
*/
|
||||
commandTimeout?: number | undefined;
|
||||
/**
|
||||
* Enables client-side timeout protection for blocking commands when set
|
||||
* to a positive number. If `blockingTimeout` is undefined, `0`, or
|
||||
* negative (e.g. `-1`), the protection is disabled and no client-side
|
||||
* timers are installed for blocking commands.
|
||||
*/
|
||||
blockingTimeout?: number | undefined;
|
||||
/**
|
||||
* Grace period (ms) added to blocking command timeouts. Only used when
|
||||
* `blockingTimeout` is a positive number. Defaults to 100ms.
|
||||
*/
|
||||
blockingTimeoutGrace?: number | undefined;
|
||||
/**
|
||||
* If the socket does not receive data within a set number of milliseconds:
|
||||
* 1. the socket is considered "dead" and will be destroyed
|
||||
* 2. the client will reject any running commands (altought they might have been processed by the server)
|
||||
* 3. the reconnect strategy will kick in (depending on the configuration)
|
||||
*/
|
||||
socketTimeout?: number | undefined;
|
||||
/**
|
||||
* Initial delay in milliseconds before the first TCP keep-alive probe.
|
||||
* @link https://nodejs.org/api/net.html#socketsetkeepaliveenable-initialdelay
|
||||
* @default 30000
|
||||
*/
|
||||
keepAlive?: number | undefined;
|
||||
/**
|
||||
* Enable/disable the use of Nagle's algorithm.
|
||||
* @link https://nodejs.org/api/net.html#socketsetnodelaynodelay
|
||||
* @default true
|
||||
*/
|
||||
noDelay?: boolean | undefined;
|
||||
/**
|
||||
* Set the name of the connection to make it easier to identity the connection
|
||||
* in client list.
|
||||
* @link https://redis.io/commands/client-setname
|
||||
*/
|
||||
connectionName?: string | undefined;
|
||||
/**
|
||||
* If true, skips setting library info via CLIENT SETINFO.
|
||||
* @link https://redis.io/docs/latest/commands/client-setinfo/
|
||||
* @default false
|
||||
*/
|
||||
disableClientInfo?: boolean | undefined;
|
||||
/**
|
||||
* Tag to append to the library name in CLIENT SETINFO (ioredis(tag)).
|
||||
* @link https://redis.io/docs/latest/commands/client-setinfo/
|
||||
* @default undefined
|
||||
*/
|
||||
clientInfoTag?: string | undefined;
|
||||
/**
|
||||
* If set, client will send AUTH command with the value of this option as the first argument when connected.
|
||||
* This is supported since Redis 6.
|
||||
*/
|
||||
username?: string | undefined;
|
||||
/**
|
||||
* If set, client will send AUTH command with the value of this option when connected.
|
||||
*/
|
||||
password?: string | undefined;
|
||||
/**
|
||||
* Database index to use.
|
||||
*
|
||||
* @default 0
|
||||
*/
|
||||
db?: number | undefined;
|
||||
/**
|
||||
* When the client reconnects, channels subscribed in the previous connection will be
|
||||
* resubscribed automatically if `autoResubscribe` is `true`.
|
||||
* @default true
|
||||
*/
|
||||
autoResubscribe?: boolean | undefined;
|
||||
/**
|
||||
* Whether or not to resend unfulfilled commands on reconnect.
|
||||
* Unfulfilled commands are most likely to be blocking commands such as `brpop` or `blpop`.
|
||||
* @default true
|
||||
*/
|
||||
autoResendUnfulfilledCommands?: boolean | undefined;
|
||||
/**
|
||||
* Whether or not to reconnect on certain Redis errors.
|
||||
* This options by default is `null`, which means it should never reconnect on Redis errors.
|
||||
* You can pass a function that accepts an Redis error, and returns:
|
||||
* - `true` or `1` to trigger a reconnection.
|
||||
* - `false` or `0` to not reconnect.
|
||||
* - `2` to reconnect and resend the failed command (who triggered the error) after reconnection.
|
||||
* @example
|
||||
* ```js
|
||||
* const redis = new Redis({
|
||||
* reconnectOnError(err) {
|
||||
* const targetError = "READONLY";
|
||||
* if (err.message.includes(targetError)) {
|
||||
* // Only reconnect when the error contains "READONLY"
|
||||
* return true; // or `return 1;`
|
||||
* }
|
||||
* },
|
||||
* });
|
||||
* ```
|
||||
* @default null
|
||||
*/
|
||||
reconnectOnError?: ReconnectOnError | null | undefined;
|
||||
/**
|
||||
* @default false
|
||||
*/
|
||||
readOnly?: boolean | undefined;
|
||||
/**
|
||||
* When enabled, numbers returned by Redis will be converted to JavaScript strings instead of numbers.
|
||||
* This is necessary if you want to handle big numbers (above `Number.MAX_SAFE_INTEGER` === 2^53).
|
||||
* @default false
|
||||
*/
|
||||
stringNumbers?: boolean | undefined;
|
||||
/**
|
||||
* The RESP protocol version to use.
|
||||
* @default 3
|
||||
*/
|
||||
protocol?: ProtocolVersion | undefined;
|
||||
/**
|
||||
* How RESP3-only reply types are represented in JavaScript.
|
||||
* Only supported when `protocol` is 3.
|
||||
*
|
||||
* - `"legacy"` (default): RESP2-compatible shapes. Map replies arrive as
|
||||
* flat `[key, value, ...]` arrays and doubles as strings, so replies are
|
||||
* identical across both protocols.
|
||||
* - `"resp3"`: map replies arrive as plain objects (with string keys) and
|
||||
* doubles as numbers.
|
||||
*
|
||||
* @default "legacy"
|
||||
*/
|
||||
replyMapping?: ReplyMappingMode | undefined;
|
||||
/**
|
||||
* How long the client will wait before killing a socket due to inactivity during initial connection.
|
||||
* @default 10000
|
||||
*/
|
||||
connectTimeout?: number | undefined;
|
||||
/**
|
||||
* This option is used internally when you call `redis.monitor()` to tell Redis
|
||||
* to enter the monitor mode when the connection is established.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
monitor?: boolean | undefined;
|
||||
/**
|
||||
* The commands that don't get a reply due to the connection to the server is lost are
|
||||
* put into a queue and will be resent on reconnect (if allowed by the `retryStrategy` option).
|
||||
* This option is used to configure how many reconnection attempts should be allowed before
|
||||
* the queue is flushed with a `MaxRetriesPerRequestError` error.
|
||||
* Set this options to `null` instead of a number to let commands wait forever
|
||||
* until the connection is alive again.
|
||||
*
|
||||
* @default 20
|
||||
*/
|
||||
maxRetriesPerRequest?: number | null | undefined;
|
||||
/**
|
||||
* @default 10000
|
||||
*/
|
||||
maxLoadingRetryTime?: number | undefined;
|
||||
/**
|
||||
* @default false
|
||||
*/
|
||||
enableAutoPipelining?: boolean | undefined;
|
||||
/**
|
||||
* @default []
|
||||
*/
|
||||
autoPipeliningIgnoredCommands?: string[] | undefined;
|
||||
offlineQueue?: boolean | undefined;
|
||||
commandQueue?: boolean | undefined;
|
||||
/**
|
||||
*
|
||||
* By default, if the connection to Redis server has not been established, commands are added to a queue
|
||||
* and are executed once the connection is "ready" (when `enableReadyCheck` is true, "ready" means
|
||||
* the Redis server has loaded the database from disk, otherwise means the connection to the Redis
|
||||
* server has been established). If this option is false, when execute the command when the connection
|
||||
* isn't ready, an error will be returned.
|
||||
*
|
||||
* @default true
|
||||
*/
|
||||
enableOfflineQueue?: boolean | undefined;
|
||||
/**
|
||||
* The client will sent an INFO command to check whether the server is still loading data from the disk (
|
||||
* which happens when the server is just launched) when the connection is established, and only wait until
|
||||
* the loading process is finished before emitting the `ready` event.
|
||||
*
|
||||
* @default true
|
||||
*/
|
||||
enableReadyCheck?: boolean | undefined;
|
||||
/**
|
||||
* When a Redis instance is initialized, a connection to the server is immediately established. Set this to
|
||||
* true will delay the connection to the server until the first command is sent or `redis.connect()` is called
|
||||
* explicitly. When `redis.connect()` is called explicitly, a Promise is returned, which will be resolved
|
||||
* when the connection is ready or rejected when it fails. The rejection should be handled by the user.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
lazyConnect?: boolean | undefined;
|
||||
/**
|
||||
* @default undefined
|
||||
*/
|
||||
scripts?: Record<string, {
|
||||
lua: string;
|
||||
numberOfKeys?: number | undefined;
|
||||
readOnly?: boolean | undefined;
|
||||
}> | undefined;
|
||||
/**
|
||||
* Managed-fieldset support is experimental and requires Redis 8.10 or newer.
|
||||
*
|
||||
* Long-lived HIMPORT fieldsets managed for the lifetime of this client.
|
||||
* Definitions are copied during construction and prepared again whenever
|
||||
* the physical Redis connection changes.
|
||||
*
|
||||
* When a managed `HIMPORT SET` needs fieldset preparation or recovery,
|
||||
* later commands issued on this client may be sent before that SET resumes.
|
||||
* Await the SET before issuing commands that depend on its write.
|
||||
*
|
||||
* Explicit pipelines containing a managed `HIMPORT SET` wait for required
|
||||
* fieldset preparation before the batch is sent.
|
||||
*
|
||||
* Background preparation failures do not prevent the connection from
|
||||
* becoming ready and are reported through the `error` event. A dependent
|
||||
* managed `HIMPORT SET` retries preparation and rejects if recovery fails.
|
||||
*
|
||||
* Use explicit `HIMPORT PREPARE` and `DISCARD` commands on a separate
|
||||
* client for bounded, manually managed batches.
|
||||
*
|
||||
* @default undefined
|
||||
* @experimental
|
||||
*/
|
||||
himportFieldsets?: readonly HimportFieldset[] | undefined;
|
||||
}
|
||||
export type RedisOptions = CommonRedisOptions & SentinelConnectionOptions & StandaloneConnectionOptions;
|
||||
export declare const DEFAULT_REDIS_OPTIONS: RedisOptions;
|
||||
63
node_modules/ioredis/built/redis/RedisOptions.js
generated
vendored
Normal file
63
node_modules/ioredis/built/redis/RedisOptions.js
generated
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.DEFAULT_REDIS_OPTIONS = void 0;
|
||||
exports.DEFAULT_REDIS_OPTIONS = {
|
||||
// Connection
|
||||
port: 6379,
|
||||
host: "localhost",
|
||||
family: 0,
|
||||
connectTimeout: 10000,
|
||||
disconnectTimeout: 2000,
|
||||
retryStrategy: function (times) {
|
||||
const jitter = Math.floor(Math.random() * 200);
|
||||
// `times` is one-based, so the first retry uses an exponent of zero.
|
||||
const delay = Math.min(Math.pow(2, times - 1) * 50, 5000);
|
||||
return delay + jitter;
|
||||
},
|
||||
keepAlive: 30000,
|
||||
noDelay: true,
|
||||
connectionName: null,
|
||||
disableClientInfo: false,
|
||||
clientInfoTag: undefined,
|
||||
// Sentinel
|
||||
sentinels: null,
|
||||
name: null,
|
||||
role: "master",
|
||||
sentinelRetryStrategy: function (times) {
|
||||
return Math.min(times * 10, 1000);
|
||||
},
|
||||
sentinelReconnectStrategy: function () {
|
||||
// This strategy only applies when sentinels are used for detecting
|
||||
// a failover, not during initial master resolution.
|
||||
// The deployment can still function when some of the sentinels are down
|
||||
// for a long period of time, so we may not want to attempt reconnection
|
||||
// very often. Therefore the default interval is fairly long (1 minute).
|
||||
return 60000;
|
||||
},
|
||||
natMap: null,
|
||||
enableTLSForSentinelMode: false,
|
||||
updateSentinels: true,
|
||||
failoverDetector: false,
|
||||
// Status
|
||||
username: null,
|
||||
password: null,
|
||||
db: 0,
|
||||
// Others
|
||||
enableOfflineQueue: true,
|
||||
enableReadyCheck: true,
|
||||
autoResubscribe: true,
|
||||
autoResendUnfulfilledCommands: true,
|
||||
lazyConnect: false,
|
||||
keyPrefix: "",
|
||||
reconnectOnError: null,
|
||||
readOnly: false,
|
||||
stringNumbers: false,
|
||||
protocol: 3,
|
||||
replyMapping: "legacy",
|
||||
maxRetriesPerRequest: 20,
|
||||
maxLoadingRetryTime: 10000,
|
||||
enableAutoPipelining: false,
|
||||
autoPipeliningIgnoredCommands: [],
|
||||
sentinelMaxConnections: 10,
|
||||
blockingTimeoutGrace: 100,
|
||||
};
|
||||
4
node_modules/ioredis/built/redis/event_handler.d.ts
generated
vendored
Normal file
4
node_modules/ioredis/built/redis/event_handler.d.ts
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
export declare function connectHandler(self: any): () => Promise<any>;
|
||||
export declare function closeHandler(self: any): () => void;
|
||||
export declare function errorHandler(self: any): (error: any) => void;
|
||||
export declare function readyHandler(self: any): () => void;
|
||||
450
node_modules/ioredis/built/redis/event_handler.js
generated
vendored
Normal file
450
node_modules/ioredis/built/redis/event_handler.js
generated
vendored
Normal file
@@ -0,0 +1,450 @@
|
||||
"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 errors_1 = require("../errors");
|
||||
const utils_1 = require("../utils");
|
||||
const version_1 = require("../utils/version");
|
||||
const DataHandler_1 = require("../DataHandler");
|
||||
const HimportCoordinator_1 = require("../himport/HimportCoordinator");
|
||||
const debug = (0, utils_1.Debug)("connection");
|
||||
function getHandshakeCommands(self) {
|
||||
const commands = [];
|
||||
if (self.condition.protocol === 3) {
|
||||
const helloCommandArgs = [self.condition.protocol];
|
||||
if (self.condition.auth) {
|
||||
helloCommandArgs.push("AUTH");
|
||||
if (Array.isArray(self.condition.auth)) {
|
||||
helloCommandArgs.push(self.condition.auth[0], self.condition.auth[1]);
|
||||
}
|
||||
else {
|
||||
helloCommandArgs.push("default", self.condition.auth);
|
||||
}
|
||||
}
|
||||
commands.push({
|
||||
kind: "hello",
|
||||
send: () => self.hello(helloCommandArgs),
|
||||
errorHandler: handleAuthError,
|
||||
});
|
||||
}
|
||||
else if (self.condition.auth) {
|
||||
commands.push({
|
||||
kind: "auth",
|
||||
send: () => self.auth(self.condition.auth),
|
||||
errorHandler: handleAuthError,
|
||||
});
|
||||
}
|
||||
if (self.condition.select) {
|
||||
commands.push({
|
||||
kind: "select",
|
||||
send: () => self.select(self.condition.select),
|
||||
errorHandler: (err) => self.silentEmit("error", err),
|
||||
});
|
||||
}
|
||||
if (self.options.connectionName) {
|
||||
debug("set the connection name [%s]", self.options.connectionName);
|
||||
commands.push({
|
||||
kind: "client",
|
||||
send: () => self.client("setname", self.options.connectionName),
|
||||
errorHandler: utils_1.noop,
|
||||
});
|
||||
}
|
||||
if (self.options.readOnly) {
|
||||
debug("set the connection to readonly mode");
|
||||
commands.push({
|
||||
kind: "readonly",
|
||||
send: () => self.readonly(),
|
||||
errorHandler: utils_1.noop,
|
||||
});
|
||||
}
|
||||
if (!self.options.disableClientInfo) {
|
||||
debug("set the client info");
|
||||
commands.push({
|
||||
kind: "client",
|
||||
send: () => self.client("SETINFO", "LIB-VER", version_1.PACKAGE_VERSION),
|
||||
errorHandler: utils_1.noop,
|
||||
});
|
||||
commands.push({
|
||||
kind: "client",
|
||||
send: () => self.client("SETINFO", "LIB-NAME", self.options?.clientInfoTag
|
||||
? `ioredis(${self.options.clientInfoTag})`
|
||||
: "ioredis"),
|
||||
errorHandler: utils_1.noop,
|
||||
});
|
||||
}
|
||||
const himportBinding = (0, HimportCoordinator_1.getHimportBinding)(self);
|
||||
if (himportBinding && himportBinding.role !== "replica") {
|
||||
for (const definition of himportBinding.coordinator.getDefinitions()) {
|
||||
commands.push({
|
||||
kind: "himport",
|
||||
send: () => himportBinding.coordinator.ensurePrepared(self, definition) ??
|
||||
Promise.resolve(),
|
||||
errorHandler: (err) => self.silentEmit("error", err),
|
||||
});
|
||||
}
|
||||
}
|
||||
return commands;
|
||||
}
|
||||
async function sendHandshake(commands, protocol) {
|
||||
if (protocol !== 3) {
|
||||
await Promise.all(commands.map(({ send, errorHandler }) => errorHandler ? send().catch(errorHandler) : send()));
|
||||
return;
|
||||
}
|
||||
// HELLO may carry AUTH. Send everything optimistically, then inspect
|
||||
// HELLO before surfacing setup errors.
|
||||
const results = await Promise.allSettled(commands.map(({ send }) => send()));
|
||||
const helloIndex = commands.findIndex(({ kind }) => kind === "hello");
|
||||
const helloResult = helloIndex === -1 ? undefined : results[helloIndex];
|
||||
if (helloResult?.status === "rejected" &&
|
||||
isProtocolNegotiationError(helloResult.reason)) {
|
||||
throw helloResult.reason;
|
||||
}
|
||||
for (let i = 0; i < results.length; i++) {
|
||||
const result = results[i];
|
||||
if (result.status === "rejected") {
|
||||
const { errorHandler } = commands[i];
|
||||
if (errorHandler) {
|
||||
errorHandler(result.reason);
|
||||
continue;
|
||||
}
|
||||
throw result.reason;
|
||||
}
|
||||
}
|
||||
}
|
||||
function connectHandler(self) {
|
||||
return async function () {
|
||||
try {
|
||||
self.resetCommandQueue();
|
||||
self.condition.handshake = true;
|
||||
self.setStatus("connect");
|
||||
/*
|
||||
No need to keep the reference of DataHandler here
|
||||
because we don't need to do the cleanup.
|
||||
`Stream#end()` will remove all listeners for us.
|
||||
*/
|
||||
new DataHandler_1.default(self, {
|
||||
stringNumbers: self.options.stringNumbers,
|
||||
replyMapping: self.condition.replyMapping,
|
||||
});
|
||||
const { connectionEpoch } = self;
|
||||
const himportBinding = (0, HimportCoordinator_1.getHimportBinding)(self);
|
||||
himportBinding?.coordinator.beginSession(self);
|
||||
const isActiveConnect = () => connectionEpoch === self.connectionEpoch && self.status === "connect";
|
||||
const finishHandshake = () => {
|
||||
if (!isActiveConnect()) {
|
||||
return false;
|
||||
}
|
||||
self.condition.handshake = false;
|
||||
return true;
|
||||
};
|
||||
try {
|
||||
await sendHandshake(getHandshakeCommands(self), self.condition.protocol);
|
||||
}
|
||||
catch (err) {
|
||||
// The connection may have been closed (and possibly already
|
||||
// reconnected) while the client setup commands above were still
|
||||
// in flight. In that case this callback is stale and the
|
||||
// connection is no longer the one we are setting up, so calling
|
||||
// readyHandler() would mark a dead connection as "ready" and
|
||||
// permanently stop reconnection. The enableReadyCheck branch
|
||||
// below is already guarded by the same connectionEpoch check.
|
||||
// See https://github.com/redis/ioredis/issues/2099
|
||||
if (!isActiveConnect()) {
|
||||
return;
|
||||
}
|
||||
// Only an unsupported-RESP3 error is recoverable; everything else is fatal.
|
||||
if (!isProtocolNegotiationError(err)) {
|
||||
return self.recoverFromFatalError(err, err);
|
||||
}
|
||||
// resp3 shapes are unavailable on RESP2, but the data is unaffected
|
||||
// (RESP2 never sends the remapped frames), so just warn — don't touch
|
||||
// the option.
|
||||
if (self.options.replyMapping === "resp3") {
|
||||
console.warn('[WARN] replyMapping "resp3" was requested, but the server does not support RESP3. ' +
|
||||
"Replies will use RESP2-compatible shapes until connected to a server that supports RESP3.");
|
||||
}
|
||||
debug("server rejected RESP3, downgrading connection to RESP2");
|
||||
self.condition.protocol = 2;
|
||||
self.condition.replyMapping = "legacy";
|
||||
try {
|
||||
await sendHandshake(getHandshakeCommands(self), self.condition.protocol);
|
||||
}
|
||||
catch (downgradeErr) {
|
||||
if (!isActiveConnect()) {
|
||||
return;
|
||||
}
|
||||
return self.recoverFromFatalError(downgradeErr, downgradeErr);
|
||||
}
|
||||
}
|
||||
if (!isActiveConnect()) {
|
||||
return;
|
||||
}
|
||||
// Keep the public "connect" state/event while the internal handshake gate
|
||||
// stays closed. INFO is a handshake command so it's still writable here,
|
||||
// while a user SUBSCRIBE is not — it stays queued and can't race the
|
||||
// connection into subscriber mode before we're ready (which would get
|
||||
// INFO rejected).
|
||||
if (!self.options.enableReadyCheck) {
|
||||
if (!finishHandshake()) {
|
||||
return;
|
||||
}
|
||||
return exports.readyHandler(self)();
|
||||
}
|
||||
self._readyCheck(function (err, info) {
|
||||
if (!isActiveConnect()) {
|
||||
return;
|
||||
}
|
||||
if (err) {
|
||||
self.recoverFromFatalError(err, err);
|
||||
}
|
||||
else if (self.connector.check(info)) {
|
||||
if (!finishHandshake()) {
|
||||
return;
|
||||
}
|
||||
exports.readyHandler(self)();
|
||||
}
|
||||
else {
|
||||
self.disconnect(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (err) {
|
||||
self.recoverFromFatalError(err, err);
|
||||
}
|
||||
};
|
||||
}
|
||||
exports.connectHandler = connectHandler;
|
||||
function handleAuthError(err) {
|
||||
const msg = err.message || "";
|
||||
if (msg.indexOf("no password is set") !== -1) {
|
||||
console.warn("[WARN] Redis server does not require a password, but a password was supplied.");
|
||||
return;
|
||||
}
|
||||
if (msg.indexOf("without any password configured for the default user") !== -1) {
|
||||
console.warn("[WARN] This Redis server's `default` user does not require a password, but a password was supplied");
|
||||
return;
|
||||
}
|
||||
if (msg.indexOf("wrong number of arguments for 'auth' command") !== -1) {
|
||||
console.warn(`[ERROR] The server returned "wrong number of arguments for 'auth' command". You are probably passing both username and password to Redis version 5 or below. You should only pass the 'password' option for Redis version 5 and under.`);
|
||||
return;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
// True when HELLO 3 failed because the server can't do RESP3: no HELLO at all
|
||||
// (Redis < 6) or HELLO present but the version refused (NOPROTO).
|
||||
function isProtocolNegotiationError(err) {
|
||||
const msg = (err.message || "").toUpperCase();
|
||||
return (msg.includes("NOPROTO") ||
|
||||
(msg.includes("UNKNOWN COMMAND") && msg.includes("HELLO")));
|
||||
}
|
||||
function abortError(command) {
|
||||
const err = new redis_errors_1.AbortError("Command aborted due to connection close");
|
||||
err.command = {
|
||||
name: command.name,
|
||||
args: command.args,
|
||||
};
|
||||
return err;
|
||||
}
|
||||
// If a contiguous set of pipeline commands starts from index zero then they
|
||||
// can be safely reattempted. If however we have a chain of pipelined commands
|
||||
// starting at index 1 or more it means we received a partial response before
|
||||
// the connection close and those pipelined commands must be aborted. For
|
||||
// example, if the queue looks like this: [2, 3, 4, 0, 1, 2] then after
|
||||
// aborting and purging we'll have a queue that looks like this: [0, 1, 2]
|
||||
function abortIncompletePipelines(commandQueue) {
|
||||
let expectedIndex = 0;
|
||||
for (let i = 0; i < commandQueue.length;) {
|
||||
const command = commandQueue.peekAt(i)?.command;
|
||||
const pipelineIndex = command.pipelineIndex;
|
||||
if (pipelineIndex === undefined || pipelineIndex === 0) {
|
||||
expectedIndex = 0;
|
||||
}
|
||||
if (pipelineIndex !== undefined && pipelineIndex !== expectedIndex++) {
|
||||
commandQueue.remove(i, 1);
|
||||
command.reject(abortError(command));
|
||||
continue;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
// If only a partial transaction result was received before connection close,
|
||||
// we have to abort any transaction fragments that may have ended up in the
|
||||
// offline queue
|
||||
function abortTransactionFragments(commandQueue) {
|
||||
for (let i = 0; i < commandQueue.length;) {
|
||||
const command = commandQueue.peekAt(i)?.command;
|
||||
if (command.name === "multi") {
|
||||
break;
|
||||
}
|
||||
if (command.name === "exec") {
|
||||
commandQueue.remove(i, 1);
|
||||
command.reject(abortError(command));
|
||||
break;
|
||||
}
|
||||
if (command.inTransaction) {
|
||||
commandQueue.remove(i, 1);
|
||||
command.reject(abortError(command));
|
||||
}
|
||||
else {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
function closeHandler(self) {
|
||||
return function () {
|
||||
const prevStatus = self.status;
|
||||
self.setStatus("close");
|
||||
// The timeout belongs to the stream that just closed and must not survive reconnect.
|
||||
if (self.socketTimeoutTimer !== undefined) {
|
||||
clearTimeout(self.socketTimeoutTimer);
|
||||
self.socketTimeoutTimer = undefined;
|
||||
}
|
||||
if (self.commandQueue.length) {
|
||||
abortIncompletePipelines(self.commandQueue);
|
||||
}
|
||||
if (self.offlineQueue.length) {
|
||||
abortTransactionFragments(self.offlineQueue);
|
||||
}
|
||||
if (prevStatus === "ready") {
|
||||
if (!self.prevCondition) {
|
||||
self.prevCondition = self.condition;
|
||||
}
|
||||
if (self.commandQueue.length) {
|
||||
self.prevCommandQueue = self.commandQueue;
|
||||
}
|
||||
}
|
||||
if (self.manuallyClosing) {
|
||||
self.manuallyClosing = false;
|
||||
debug("skip reconnecting since the connection is manually closed.");
|
||||
return close();
|
||||
}
|
||||
if (typeof self.options.retryStrategy !== "function") {
|
||||
debug("skip reconnecting because `retryStrategy` is not a function");
|
||||
return close();
|
||||
}
|
||||
const retryDelay = self.options.retryStrategy(++self.retryAttempts);
|
||||
if (typeof retryDelay !== "number") {
|
||||
debug("skip reconnecting because `retryStrategy` doesn't return a number");
|
||||
return close();
|
||||
}
|
||||
debug("reconnect in %sms", retryDelay);
|
||||
self.setStatus("reconnecting", retryDelay);
|
||||
self.reconnectTimeout = setTimeout(function () {
|
||||
self.reconnectTimeout = null;
|
||||
self.connect().catch(utils_1.noop);
|
||||
}, retryDelay);
|
||||
const { maxRetriesPerRequest } = self.options;
|
||||
if (typeof maxRetriesPerRequest === "number") {
|
||||
if (maxRetriesPerRequest < 0) {
|
||||
debug("maxRetriesPerRequest is negative, ignoring...");
|
||||
}
|
||||
else {
|
||||
const remainder = self.retryAttempts % (maxRetriesPerRequest + 1);
|
||||
if (remainder === 0) {
|
||||
debug("reach maxRetriesPerRequest limitation, flushing command queue...");
|
||||
self.flushQueue(new errors_1.MaxRetriesPerRequestError(maxRetriesPerRequest));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
function close() {
|
||||
self.setStatus("end");
|
||||
self.flushQueue(new Error(utils_1.CONNECTION_CLOSED_ERROR_MSG));
|
||||
}
|
||||
}
|
||||
exports.closeHandler = closeHandler;
|
||||
function errorHandler(self) {
|
||||
return function (error) {
|
||||
debug("error: %s", error);
|
||||
self.silentEmit("error", error);
|
||||
};
|
||||
}
|
||||
exports.errorHandler = errorHandler;
|
||||
function readyHandler(self) {
|
||||
return function () {
|
||||
self.setStatus("ready");
|
||||
self.retryAttempts = 0;
|
||||
if (self.options.monitor) {
|
||||
self.call("monitor").then(() => self.setStatus("monitoring"), (error) => self.emit("error", error));
|
||||
const { sendCommand } = self;
|
||||
self.sendCommand = function (command) {
|
||||
if (Command_1.default.checkFlag("VALID_IN_MONITOR_MODE", command.name)) {
|
||||
return sendCommand.call(self, command);
|
||||
}
|
||||
command.reject(new Error("Connection is in monitoring mode, can't process commands."));
|
||||
return command.promise;
|
||||
};
|
||||
self.once("close", function () {
|
||||
delete self.sendCommand;
|
||||
});
|
||||
return;
|
||||
}
|
||||
const finalSelect = self.prevCondition
|
||||
? self.prevCondition.select
|
||||
: self.condition.select;
|
||||
if (self.prevCondition) {
|
||||
const condition = self.prevCondition;
|
||||
self.prevCondition = null;
|
||||
if (condition.subscriber && self.options.autoResubscribe) {
|
||||
// We re-select the previous db first since
|
||||
// `SELECT` command is not valid in sub mode.
|
||||
if (self.condition.select !== finalSelect) {
|
||||
debug("connect to db [%d]", finalSelect);
|
||||
self.select(finalSelect);
|
||||
}
|
||||
const subscribeChannels = condition.subscriber.channels("subscribe");
|
||||
if (subscribeChannels.length) {
|
||||
debug("subscribe %d channels", subscribeChannels.length);
|
||||
self.subscribe(subscribeChannels);
|
||||
}
|
||||
const psubscribeChannels = condition.subscriber.channels("psubscribe");
|
||||
if (psubscribeChannels.length) {
|
||||
debug("psubscribe %d channels", psubscribeChannels.length);
|
||||
self.psubscribe(psubscribeChannels);
|
||||
}
|
||||
const ssubscribeChannels = condition.subscriber.channels("ssubscribe");
|
||||
if (ssubscribeChannels.length) {
|
||||
debug("ssubscribe %s", ssubscribeChannels.length);
|
||||
for (const channel of ssubscribeChannels) {
|
||||
self.ssubscribe(channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (self.prevCommandQueue) {
|
||||
if (self.options.autoResendUnfulfilledCommands) {
|
||||
debug("resend %d unfulfilled commands", self.prevCommandQueue.length);
|
||||
while (self.prevCommandQueue.length > 0) {
|
||||
const item = self.prevCommandQueue.shift();
|
||||
if (item.select !== self.condition.select &&
|
||||
item.command.name !== "select") {
|
||||
self.select(item.select);
|
||||
}
|
||||
self.sendCommand(item.command, item.stream);
|
||||
}
|
||||
}
|
||||
else {
|
||||
self.prevCommandQueue = null;
|
||||
}
|
||||
}
|
||||
if (self.offlineQueue.length) {
|
||||
debug("send %d commands in offline queue", self.offlineQueue.length);
|
||||
const offlineQueue = self.offlineQueue;
|
||||
self.resetOfflineQueue();
|
||||
while (offlineQueue.length > 0) {
|
||||
const item = offlineQueue.shift();
|
||||
if (item.select !== self.condition.select &&
|
||||
item.command.name !== "select") {
|
||||
self.select(item.select);
|
||||
}
|
||||
self.sendCommand(item.command, item.stream);
|
||||
}
|
||||
}
|
||||
if (self.condition.select !== finalSelect) {
|
||||
debug("connect to db [%d]", finalSelect);
|
||||
self.select(finalSelect);
|
||||
}
|
||||
};
|
||||
}
|
||||
exports.readyHandler = readyHandler;
|
||||
18
node_modules/ioredis/built/replyTransformers.d.ts
generated
vendored
Normal file
18
node_modules/ioredis/built/replyTransformers.d.ts
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
import { ProtocolVersion, ReplyMappingMode } from "./types";
|
||||
export interface ReplyTransformerContext {
|
||||
commandName: string;
|
||||
protocol: ProtocolVersion;
|
||||
replyMapping: ReplyMappingMode;
|
||||
}
|
||||
export interface ReplyContext {
|
||||
protocol?: ProtocolVersion;
|
||||
replyMapping?: ReplyMappingMode;
|
||||
}
|
||||
export type ReplyTransformer = (reply: any, context: ReplyTransformerContext) => any;
|
||||
export declare const passthroughReplyTransformer: ReplyTransformer;
|
||||
export declare const flattenNestedArrayItems: ReplyTransformer;
|
||||
export declare const wrapStreamMapPairs: ReplyTransformer;
|
||||
export declare const transformVsimReply: ReplyTransformer;
|
||||
export declare const transformPairReply: ReplyTransformer;
|
||||
export declare const transformStreamReadReply: ReplyTransformer;
|
||||
export declare const sortedSetWithScorePairCommands: readonly ["zdiff", "zinter", "zpopmax", "zpopmin", "zunion", "zrandmember", "zrange", "zrangebyscore", "zrevrange", "zrevrangebyscore"];
|
||||
92
node_modules/ioredis/built/replyTransformers.js
generated
vendored
Normal file
92
node_modules/ioredis/built/replyTransformers.js
generated
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.sortedSetWithScorePairCommands = exports.transformStreamReadReply = exports.transformPairReply = exports.transformVsimReply = exports.wrapStreamMapPairs = exports.flattenNestedArrayItems = exports.passthroughReplyTransformer = void 0;
|
||||
const passthroughReplyTransformer = function (result) {
|
||||
return result;
|
||||
};
|
||||
exports.passthroughReplyTransformer = passthroughReplyTransformer;
|
||||
const flattenNestedArrayItems = function (result) {
|
||||
if (!Array.isArray(result)) {
|
||||
return result;
|
||||
}
|
||||
let flat = null;
|
||||
for (let i = 0; i < result.length; i++) {
|
||||
const item = result[i];
|
||||
if (Array.isArray(item)) {
|
||||
if (!flat) {
|
||||
flat = result.slice(0, i);
|
||||
}
|
||||
flat.push(...item);
|
||||
}
|
||||
else if (flat) {
|
||||
flat.push(item);
|
||||
}
|
||||
}
|
||||
return flat ?? result;
|
||||
};
|
||||
exports.flattenNestedArrayItems = flattenNestedArrayItems;
|
||||
const flattenPairTransformers = {
|
||||
2: {
|
||||
legacy: exports.passthroughReplyTransformer,
|
||||
resp3: exports.passthroughReplyTransformer,
|
||||
},
|
||||
3: {
|
||||
legacy: exports.flattenNestedArrayItems,
|
||||
resp3: exports.passthroughReplyTransformer,
|
||||
},
|
||||
};
|
||||
const vsimTransformers = {
|
||||
2: {
|
||||
legacy: exports.flattenNestedArrayItems,
|
||||
resp3: exports.passthroughReplyTransformer,
|
||||
},
|
||||
3: {
|
||||
legacy: exports.flattenNestedArrayItems,
|
||||
resp3: exports.passthroughReplyTransformer,
|
||||
},
|
||||
};
|
||||
const wrapStreamMapPairs = function (result) {
|
||||
if (!Array.isArray(result)) {
|
||||
return result;
|
||||
}
|
||||
const wrapped = [];
|
||||
for (let i = 0; i < result.length; i += 2) {
|
||||
wrapped.push([result[i], result[i + 1]]);
|
||||
}
|
||||
return wrapped;
|
||||
};
|
||||
exports.wrapStreamMapPairs = wrapStreamMapPairs;
|
||||
const streamReadTransformers = {
|
||||
2: {
|
||||
legacy: exports.passthroughReplyTransformer,
|
||||
resp3: exports.passthroughReplyTransformer,
|
||||
},
|
||||
3: {
|
||||
legacy: exports.wrapStreamMapPairs,
|
||||
resp3: exports.passthroughReplyTransformer,
|
||||
},
|
||||
};
|
||||
const transformVsimReply = function (result, context) {
|
||||
return vsimTransformers[context.protocol][context.replyMapping](result, context);
|
||||
};
|
||||
exports.transformVsimReply = transformVsimReply;
|
||||
const transformPairReply = function (result, context) {
|
||||
return flattenPairTransformers[context.protocol][context.replyMapping](result, context);
|
||||
};
|
||||
exports.transformPairReply = transformPairReply;
|
||||
const transformStreamReadReply = function (result, context) {
|
||||
return streamReadTransformers[context.protocol][context.replyMapping](result, context);
|
||||
};
|
||||
exports.transformStreamReadReply = transformStreamReadReply;
|
||||
exports.sortedSetWithScorePairCommands = [
|
||||
"zdiff",
|
||||
"zinter",
|
||||
"zpopmax",
|
||||
"zpopmin",
|
||||
"zunion",
|
||||
"zrandmember",
|
||||
"zrange",
|
||||
"zrangebyscore",
|
||||
"zrevrange",
|
||||
"zrevrangebyscore",
|
||||
];
|
||||
40
node_modules/ioredis/built/resp/decoder.d.ts
generated
vendored
Normal file
40
node_modules/ioredis/built/resp/decoder.d.ts
generated
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
/// <reference types="node" />
|
||||
/// <reference types="node" />
|
||||
import { ErrorReply } from "./errors";
|
||||
import { TypeMapping } from "./types";
|
||||
export declare const RESP_TYPES: {
|
||||
readonly NULL: 95;
|
||||
readonly BOOLEAN: 35;
|
||||
readonly NUMBER: 58;
|
||||
readonly BIG_NUMBER: 40;
|
||||
readonly DOUBLE: 44;
|
||||
readonly SIMPLE_STRING: 43;
|
||||
readonly BLOB_STRING: 36;
|
||||
readonly VERBATIM_STRING: 61;
|
||||
readonly SIMPLE_ERROR: 45;
|
||||
readonly BLOB_ERROR: 33;
|
||||
readonly ARRAY: 42;
|
||||
readonly SET: 126;
|
||||
readonly MAP: 37;
|
||||
readonly PUSH: 62;
|
||||
};
|
||||
export declare const PUSH_TYPE_MAPPING: {
|
||||
36: BufferConstructor;
|
||||
};
|
||||
interface DecoderOptions {
|
||||
onReply(reply: any): unknown;
|
||||
onErrorReply(err: ErrorReply): unknown;
|
||||
onPush(push: Array<any>): unknown;
|
||||
getTypeMapping(): TypeMapping;
|
||||
}
|
||||
export declare class Decoder {
|
||||
#private;
|
||||
onReply: any;
|
||||
onErrorReply: any;
|
||||
onPush: any;
|
||||
getTypeMapping: any;
|
||||
constructor(config: DecoderOptions);
|
||||
reset(): void;
|
||||
write(chunk: any): void;
|
||||
}
|
||||
export {};
|
||||
738
node_modules/ioredis/built/resp/decoder.js
generated
vendored
Normal file
738
node_modules/ioredis/built/resp/decoder.js
generated
vendored
Normal file
@@ -0,0 +1,738 @@
|
||||
"use strict";
|
||||
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
||||
if (kind === "m") throw new TypeError("Private method is not writable");
|
||||
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
||||
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
||||
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
||||
};
|
||||
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
||||
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
||||
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
||||
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
||||
};
|
||||
var _Decoder_instances, _a, _Decoder_cursor, _Decoder_next, _Decoder_pushTypeMapping, _Decoder_continueDecodeTypeValue, _Decoder_decodeTypeValue, _Decoder_handleDecodedValue, _Decoder_continueDecodeValue, _Decoder_decodeNull, _Decoder_decodeBoolean, _Decoder_decodeNumber, _Decoder_maybeDecodeNumberValue, _Decoder_decodeNumberValue, _Decoder_decodeUnsingedNumber, _Decoder_decodeBigNumber, _Decoder_maybeDecodeBigNumberValue, _Decoder_decodeBigNumberValue, _Decoder_decodeUnsingedBigNumber, _Decoder_decodeDouble, _Decoder_maybeDecodeDoubleInteger, _Decoder_decodeDoubleInteger, _Decoder_continueDecodeDoubleInteger, _Decoder_DOUBLE_DECIMAL_MULTIPLIERS, _Decoder_decodeDoubleDecimal, _Decoder_decodeDoubleExponent, _Decoder_continueDecodeDoubleExponent, _Decoder_findCRLF, _Decoder_decodeSimpleString, _Decoder_continueDecodeSimpleString, _Decoder_decodeBlobString, _Decoder_continueDecodeBlobStringLength, _Decoder_decodeStringWithLength, _Decoder_continueDecodeStringWithLength, _Decoder_decodeBlobStringWithLength, _Decoder_decodeVerbatimString, _Decoder_continueDecodeVerbatimStringLength, _Decoder_decodeVerbatimStringWithLength, _Decoder_decodeVerbatimStringFormat, _Decoder_continueDecodeVerbatimStringFormat, _Decoder_decodeVerbatimStringWithFormat, _Decoder_continueDecodeVerbatimStringWithFormat, _Decoder_decodeSimpleError, _Decoder_continueDecodeSimpleError, _Decoder_decodeBlobError, _Decoder_continueDecodeBlobError, _Decoder_decodeNestedType, _Decoder_decodeNestedTypeValue, _Decoder_decodeArray, _Decoder_decodeArrayWithLength, _Decoder_continueDecodeArrayLength, _Decoder_decodeArrayItems, _Decoder_continueDecodeArrayItems, _Decoder_decodeSet, _Decoder_continueDecodeSetLength, _Decoder_decodeSetItems, _Decoder_decodeSetAsSet, _Decoder_continueDecodeSetAsSet, _Decoder_decodeMap, _Decoder_continueDecodeMapLength, _Decoder_decodeMapItems, _Decoder_decodeMapAsMap, _Decoder_decodeMapKey, _Decoder_decodeMapKeyValue, _Decoder_continueDecodeMapKey, _Decoder_continueDecodeMapValue, _Decoder_decodeMapAsObject, _Decoder_continueDecodeMapAsObjectKey, _Decoder_continueDecodeMapAsObjectValue;
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.Decoder = exports.PUSH_TYPE_MAPPING = exports.RESP_TYPES = void 0;
|
||||
/*
|
||||
* Portions adapted from node-redis:
|
||||
* https://github.com/redis/node-redis
|
||||
*
|
||||
* Copyright (c) 2022-2023, Redis, Inc.
|
||||
* Licensed under the MIT License.
|
||||
*/
|
||||
// @ts-nocheck
|
||||
const verbatim_string_1 = require("./verbatim-string");
|
||||
const errors_1 = require("./errors");
|
||||
// https://github.com/redis/redis-specifications/blob/master/protocol/RESP3.md
|
||||
exports.RESP_TYPES = {
|
||||
NULL: 95, // _
|
||||
BOOLEAN: 35, // #
|
||||
NUMBER: 58, // :
|
||||
BIG_NUMBER: 40, // (
|
||||
DOUBLE: 44, // ,
|
||||
SIMPLE_STRING: 43, // +
|
||||
BLOB_STRING: 36, // $
|
||||
VERBATIM_STRING: 61, // =
|
||||
SIMPLE_ERROR: 45, // -
|
||||
BLOB_ERROR: 33, // !
|
||||
ARRAY: 42, // *
|
||||
SET: 126, // ~
|
||||
MAP: 37, // %
|
||||
PUSH: 62, // >
|
||||
};
|
||||
const ASCII = {
|
||||
"\r": 13,
|
||||
t: 116,
|
||||
"+": 43,
|
||||
"-": 45,
|
||||
"0": 48,
|
||||
".": 46,
|
||||
i: 105,
|
||||
n: 110,
|
||||
E: 69,
|
||||
e: 101,
|
||||
};
|
||||
exports.PUSH_TYPE_MAPPING = {
|
||||
[exports.RESP_TYPES.BLOB_STRING]: Buffer,
|
||||
};
|
||||
class Decoder {
|
||||
constructor(config) {
|
||||
_Decoder_instances.add(this);
|
||||
_Decoder_cursor.set(this, 0);
|
||||
_Decoder_next.set(this, void 0);
|
||||
_Decoder_pushTypeMapping.set(this, void 0);
|
||||
this.onReply = config.onReply;
|
||||
this.onErrorReply = config.onErrorReply;
|
||||
this.onPush = config.onPush;
|
||||
this.getTypeMapping = config.getTypeMapping;
|
||||
__classPrivateFieldSet(this, _Decoder_pushTypeMapping, {
|
||||
...config.getTypeMapping(),
|
||||
...exports.PUSH_TYPE_MAPPING,
|
||||
}, "f");
|
||||
}
|
||||
reset() {
|
||||
__classPrivateFieldSet(this, _Decoder_cursor, 0, "f");
|
||||
__classPrivateFieldSet(this, _Decoder_next, undefined, "f");
|
||||
}
|
||||
write(chunk) {
|
||||
var _b;
|
||||
if (__classPrivateFieldGet(this, _Decoder_cursor, "f") >= chunk.length) {
|
||||
__classPrivateFieldSet(this, _Decoder_cursor, __classPrivateFieldGet(this, _Decoder_cursor, "f") - chunk.length, "f");
|
||||
return;
|
||||
}
|
||||
if (__classPrivateFieldGet(this, _Decoder_next, "f")) {
|
||||
if (__classPrivateFieldGet(this, _Decoder_next, "f").call(this, chunk) || __classPrivateFieldGet(this, _Decoder_cursor, "f") >= chunk.length) {
|
||||
__classPrivateFieldSet(this, _Decoder_cursor, __classPrivateFieldGet(this, _Decoder_cursor, "f") - chunk.length, "f");
|
||||
return;
|
||||
}
|
||||
}
|
||||
do {
|
||||
const type = chunk[__classPrivateFieldGet(this, _Decoder_cursor, "f")];
|
||||
if (__classPrivateFieldSet(this, _Decoder_cursor, (_b = __classPrivateFieldGet(this, _Decoder_cursor, "f"), ++_b), "f") === chunk.length) {
|
||||
__classPrivateFieldSet(this, _Decoder_next, __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_continueDecodeTypeValue).bind(this, type), "f");
|
||||
break;
|
||||
}
|
||||
if (__classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeTypeValue).call(this, type, chunk)) {
|
||||
break;
|
||||
}
|
||||
} while (__classPrivateFieldGet(this, _Decoder_cursor, "f") < chunk.length);
|
||||
__classPrivateFieldSet(this, _Decoder_cursor, __classPrivateFieldGet(this, _Decoder_cursor, "f") - chunk.length, "f");
|
||||
}
|
||||
}
|
||||
exports.Decoder = Decoder;
|
||||
_a = Decoder, _Decoder_cursor = new WeakMap(), _Decoder_next = new WeakMap(), _Decoder_pushTypeMapping = new WeakMap(), _Decoder_instances = new WeakSet(), _Decoder_continueDecodeTypeValue = function _Decoder_continueDecodeTypeValue(type, chunk) {
|
||||
__classPrivateFieldSet(this, _Decoder_next, undefined, "f");
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeTypeValue).call(this, type, chunk);
|
||||
}, _Decoder_decodeTypeValue = function _Decoder_decodeTypeValue(type, chunk) {
|
||||
switch (type) {
|
||||
case exports.RESP_TYPES.NULL:
|
||||
this.onReply(__classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeNull).call(this));
|
||||
return false;
|
||||
case exports.RESP_TYPES.BOOLEAN:
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_handleDecodedValue).call(this, this.onReply, __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeBoolean).call(this, this.getTypeMapping()[exports.RESP_TYPES.BOOLEAN], chunk));
|
||||
case exports.RESP_TYPES.NUMBER:
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_handleDecodedValue).call(this, this.onReply, __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeNumber).call(this, this.getTypeMapping()[exports.RESP_TYPES.NUMBER], chunk));
|
||||
case exports.RESP_TYPES.BIG_NUMBER:
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_handleDecodedValue).call(this, this.onReply, __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeBigNumber).call(this, this.getTypeMapping()[exports.RESP_TYPES.BIG_NUMBER], chunk));
|
||||
case exports.RESP_TYPES.DOUBLE:
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_handleDecodedValue).call(this, this.onReply, __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeDouble).call(this, this.getTypeMapping()[exports.RESP_TYPES.DOUBLE], chunk));
|
||||
case exports.RESP_TYPES.SIMPLE_STRING:
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_handleDecodedValue).call(this, this.onReply, __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeSimpleString).call(this, this.getTypeMapping()[exports.RESP_TYPES.SIMPLE_STRING], chunk));
|
||||
case exports.RESP_TYPES.BLOB_STRING:
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_handleDecodedValue).call(this, this.onReply, __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeBlobString).call(this, this.getTypeMapping()[exports.RESP_TYPES.BLOB_STRING], chunk));
|
||||
case exports.RESP_TYPES.VERBATIM_STRING:
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_handleDecodedValue).call(this, this.onReply, __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeVerbatimString).call(this, this.getTypeMapping()[exports.RESP_TYPES.VERBATIM_STRING], chunk));
|
||||
case exports.RESP_TYPES.SIMPLE_ERROR:
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_handleDecodedValue).call(this, this.onErrorReply, __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeSimpleError).call(this, chunk));
|
||||
case exports.RESP_TYPES.BLOB_ERROR:
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_handleDecodedValue).call(this, this.onErrorReply, __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeBlobError).call(this, chunk));
|
||||
case exports.RESP_TYPES.ARRAY:
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_handleDecodedValue).call(this, this.onReply, __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeArray).call(this, this.getTypeMapping(), chunk));
|
||||
case exports.RESP_TYPES.SET:
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_handleDecodedValue).call(this, this.onReply, __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeSet).call(this, this.getTypeMapping(), chunk));
|
||||
case exports.RESP_TYPES.MAP:
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_handleDecodedValue).call(this, this.onReply, __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeMap).call(this, this.getTypeMapping(), chunk));
|
||||
case exports.RESP_TYPES.PUSH:
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_handleDecodedValue).call(this, this.onPush, __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeArray).call(this, __classPrivateFieldGet(this, _Decoder_pushTypeMapping, "f"), chunk));
|
||||
default:
|
||||
throw new Error(`Unknown RESP type ${type} "${String.fromCharCode(type)}"`);
|
||||
}
|
||||
}, _Decoder_handleDecodedValue = function _Decoder_handleDecodedValue(cb, value) {
|
||||
if (typeof value === "function") {
|
||||
__classPrivateFieldSet(this, _Decoder_next, __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_continueDecodeValue).bind(this, cb, value), "f");
|
||||
return true;
|
||||
}
|
||||
cb(value);
|
||||
return false;
|
||||
}, _Decoder_continueDecodeValue = function _Decoder_continueDecodeValue(cb, next, chunk) {
|
||||
__classPrivateFieldSet(this, _Decoder_next, undefined, "f");
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_handleDecodedValue).call(this, cb, next(chunk));
|
||||
}, _Decoder_decodeNull = function _Decoder_decodeNull() {
|
||||
__classPrivateFieldSet(this, _Decoder_cursor, __classPrivateFieldGet(this, _Decoder_cursor, "f") + 2, "f"); // skip \r\n
|
||||
return null;
|
||||
}, _Decoder_decodeBoolean = function _Decoder_decodeBoolean(type, chunk) {
|
||||
const boolean = chunk[__classPrivateFieldGet(this, _Decoder_cursor, "f")] === ASCII.t;
|
||||
__classPrivateFieldSet(this, _Decoder_cursor, __classPrivateFieldGet(this, _Decoder_cursor, "f") + 3, "f"); // skip {t | f}\r\n
|
||||
// RESP2 had no boolean type: commands now answering `#t`/`#f` returned
|
||||
// integer `1`/`0`. The legacy mapping requests Number here to stay
|
||||
// wire-compatible; resp3 mapping leaves the value as a JS boolean.
|
||||
return type === Number ? (boolean ? 1 : 0) : boolean;
|
||||
}, _Decoder_decodeNumber = function _Decoder_decodeNumber(type, chunk) {
|
||||
if (type === String) {
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeSimpleString).call(this, String, chunk);
|
||||
}
|
||||
switch (chunk[__classPrivateFieldGet(this, _Decoder_cursor, "f")]) {
|
||||
case ASCII["+"]:
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_maybeDecodeNumberValue).call(this, false, chunk);
|
||||
case ASCII["-"]:
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_maybeDecodeNumberValue).call(this, true, chunk);
|
||||
default:
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeNumberValue).call(this, false, __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeUnsingedNumber).bind(this, 0), chunk);
|
||||
}
|
||||
}, _Decoder_maybeDecodeNumberValue = function _Decoder_maybeDecodeNumberValue(isNegative, chunk) {
|
||||
var _b;
|
||||
const cb = __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeUnsingedNumber).bind(this, 0);
|
||||
return __classPrivateFieldSet(this, _Decoder_cursor, (_b = __classPrivateFieldGet(this, _Decoder_cursor, "f"), ++_b), "f") === chunk.length
|
||||
? __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeNumberValue).bind(this, isNegative, cb)
|
||||
: __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeNumberValue).call(this, isNegative, cb, chunk);
|
||||
}, _Decoder_decodeNumberValue = function _Decoder_decodeNumberValue(isNegative, numberCb, chunk) {
|
||||
const number = numberCb(chunk);
|
||||
return typeof number === "function"
|
||||
? __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeNumberValue).bind(this, isNegative, number)
|
||||
: isNegative
|
||||
? -number
|
||||
: number;
|
||||
}, _Decoder_decodeUnsingedNumber = function _Decoder_decodeUnsingedNumber(number, chunk) {
|
||||
let cursor = __classPrivateFieldGet(this, _Decoder_cursor, "f");
|
||||
do {
|
||||
const byte = chunk[cursor];
|
||||
if (byte === ASCII["\r"]) {
|
||||
__classPrivateFieldSet(this, _Decoder_cursor, cursor + 2, "f"); // skip \r\n
|
||||
return number;
|
||||
}
|
||||
number = number * 10 + byte - ASCII["0"];
|
||||
} while (++cursor < chunk.length);
|
||||
__classPrivateFieldSet(this, _Decoder_cursor, cursor, "f");
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeUnsingedNumber).bind(this, number);
|
||||
}, _Decoder_decodeBigNumber = function _Decoder_decodeBigNumber(type, chunk) {
|
||||
if (type === String) {
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeSimpleString).call(this, String, chunk);
|
||||
}
|
||||
switch (chunk[__classPrivateFieldGet(this, _Decoder_cursor, "f")]) {
|
||||
case ASCII["+"]:
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_maybeDecodeBigNumberValue).call(this, false, chunk);
|
||||
case ASCII["-"]:
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_maybeDecodeBigNumberValue).call(this, true, chunk);
|
||||
default:
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeBigNumberValue).call(this, false, __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeUnsingedBigNumber).bind(this, 0n), chunk);
|
||||
}
|
||||
}, _Decoder_maybeDecodeBigNumberValue = function _Decoder_maybeDecodeBigNumberValue(isNegative, chunk) {
|
||||
var _b;
|
||||
const cb = __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeUnsingedBigNumber).bind(this, 0n);
|
||||
return __classPrivateFieldSet(this, _Decoder_cursor, (_b = __classPrivateFieldGet(this, _Decoder_cursor, "f"), ++_b), "f") === chunk.length
|
||||
? __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeBigNumberValue).bind(this, isNegative, cb)
|
||||
: __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeBigNumberValue).call(this, isNegative, cb, chunk);
|
||||
}, _Decoder_decodeBigNumberValue = function _Decoder_decodeBigNumberValue(isNegative, bigNumberCb, chunk) {
|
||||
const bigNumber = bigNumberCb(chunk);
|
||||
return typeof bigNumber === "function"
|
||||
? __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeBigNumberValue).bind(this, isNegative, bigNumber)
|
||||
: isNegative
|
||||
? -bigNumber
|
||||
: bigNumber;
|
||||
}, _Decoder_decodeUnsingedBigNumber = function _Decoder_decodeUnsingedBigNumber(bigNumber, chunk) {
|
||||
let cursor = __classPrivateFieldGet(this, _Decoder_cursor, "f");
|
||||
do {
|
||||
const byte = chunk[cursor];
|
||||
if (byte === ASCII["\r"]) {
|
||||
__classPrivateFieldSet(this, _Decoder_cursor, cursor + 2, "f"); // skip \r\n
|
||||
return bigNumber;
|
||||
}
|
||||
bigNumber = bigNumber * 10n + BigInt(byte - ASCII["0"]);
|
||||
} while (++cursor < chunk.length);
|
||||
__classPrivateFieldSet(this, _Decoder_cursor, cursor, "f");
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeUnsingedBigNumber).bind(this, bigNumber);
|
||||
}, _Decoder_decodeDouble = function _Decoder_decodeDouble(type, chunk) {
|
||||
// RESP2 sent doubles as bulk strings, so the legacy mapping decodes the
|
||||
// raw textual form (Buffer or String) and lets `replyEncoding` decide the
|
||||
// final shape per command. resp3 mapping leaves `type` unset -> JS number.
|
||||
if (type === String || type === Buffer) {
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeSimpleString).call(this, type, chunk);
|
||||
}
|
||||
switch (chunk[__classPrivateFieldGet(this, _Decoder_cursor, "f")]) {
|
||||
case ASCII.n:
|
||||
__classPrivateFieldSet(this, _Decoder_cursor, __classPrivateFieldGet(this, _Decoder_cursor, "f") + 5, "f"); // skip nan\r\n
|
||||
return NaN;
|
||||
case ASCII["+"]:
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_maybeDecodeDoubleInteger).call(this, false, chunk);
|
||||
case ASCII["-"]:
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_maybeDecodeDoubleInteger).call(this, true, chunk);
|
||||
default:
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeDoubleInteger).call(this, false, 0, chunk);
|
||||
}
|
||||
}, _Decoder_maybeDecodeDoubleInteger = function _Decoder_maybeDecodeDoubleInteger(isNegative, chunk) {
|
||||
var _b;
|
||||
return __classPrivateFieldSet(this, _Decoder_cursor, (_b = __classPrivateFieldGet(this, _Decoder_cursor, "f"), ++_b), "f") === chunk.length
|
||||
? __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeDoubleInteger).bind(this, isNegative, 0)
|
||||
: __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeDoubleInteger).call(this, isNegative, 0, chunk);
|
||||
}, _Decoder_decodeDoubleInteger = function _Decoder_decodeDoubleInteger(isNegative, integer, chunk) {
|
||||
if (chunk[__classPrivateFieldGet(this, _Decoder_cursor, "f")] === ASCII.i) {
|
||||
__classPrivateFieldSet(this, _Decoder_cursor, __classPrivateFieldGet(this, _Decoder_cursor, "f") + 5, "f"); // skip inf\r\n
|
||||
return isNegative ? -Infinity : Infinity;
|
||||
}
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_continueDecodeDoubleInteger).call(this, isNegative, integer, chunk);
|
||||
}, _Decoder_continueDecodeDoubleInteger = function _Decoder_continueDecodeDoubleInteger(isNegative, integer, chunk) {
|
||||
let cursor = __classPrivateFieldGet(this, _Decoder_cursor, "f");
|
||||
do {
|
||||
const byte = chunk[cursor];
|
||||
switch (byte) {
|
||||
case ASCII["."]:
|
||||
__classPrivateFieldSet(this, _Decoder_cursor, cursor + 1, "f"); // skip .
|
||||
return __classPrivateFieldGet(this, _Decoder_cursor, "f") < chunk.length
|
||||
? __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeDoubleDecimal).call(this, isNegative, 0, integer, chunk)
|
||||
: __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeDoubleDecimal).bind(this, isNegative, 0, integer);
|
||||
case ASCII.E:
|
||||
case ASCII.e: {
|
||||
__classPrivateFieldSet(this, _Decoder_cursor, cursor + 1, "f"); // skip E/e
|
||||
const i = isNegative ? -integer : integer;
|
||||
return __classPrivateFieldGet(this, _Decoder_cursor, "f") < chunk.length
|
||||
? __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeDoubleExponent).call(this, i, chunk)
|
||||
: __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeDoubleExponent).bind(this, i);
|
||||
}
|
||||
case ASCII["\r"]:
|
||||
__classPrivateFieldSet(this, _Decoder_cursor, cursor + 2, "f"); // skip \r\n
|
||||
return isNegative ? -integer : integer;
|
||||
default:
|
||||
integer = integer * 10 + byte - ASCII["0"];
|
||||
}
|
||||
} while (++cursor < chunk.length);
|
||||
__classPrivateFieldSet(this, _Decoder_cursor, cursor, "f");
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_continueDecodeDoubleInteger).bind(this, isNegative, integer);
|
||||
}, _Decoder_decodeDoubleDecimal = function _Decoder_decodeDoubleDecimal(isNegative, decimalIndex, double, chunk) {
|
||||
let cursor = __classPrivateFieldGet(this, _Decoder_cursor, "f");
|
||||
do {
|
||||
const byte = chunk[cursor];
|
||||
switch (byte) {
|
||||
case ASCII.E:
|
||||
case ASCII.e: {
|
||||
__classPrivateFieldSet(this, _Decoder_cursor, cursor + 1, "f"); // skip E/e
|
||||
const d = isNegative ? -double : double;
|
||||
return __classPrivateFieldGet(this, _Decoder_cursor, "f") === chunk.length
|
||||
? __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeDoubleExponent).bind(this, d)
|
||||
: __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeDoubleExponent).call(this, d, chunk);
|
||||
}
|
||||
case ASCII["\r"]:
|
||||
__classPrivateFieldSet(this, _Decoder_cursor, cursor + 2, "f"); // skip \r\n
|
||||
return isNegative ? -double : double;
|
||||
}
|
||||
if (decimalIndex < __classPrivateFieldGet(_a, _a, "f", _Decoder_DOUBLE_DECIMAL_MULTIPLIERS).length) {
|
||||
double +=
|
||||
(byte - ASCII["0"]) *
|
||||
__classPrivateFieldGet(_a, _a, "f", _Decoder_DOUBLE_DECIMAL_MULTIPLIERS)[decimalIndex++];
|
||||
}
|
||||
} while (++cursor < chunk.length);
|
||||
__classPrivateFieldSet(this, _Decoder_cursor, cursor, "f");
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeDoubleDecimal).bind(this, isNegative, decimalIndex, double);
|
||||
}, _Decoder_decodeDoubleExponent = function _Decoder_decodeDoubleExponent(double, chunk) {
|
||||
var _b, _c;
|
||||
switch (chunk[__classPrivateFieldGet(this, _Decoder_cursor, "f")]) {
|
||||
case ASCII["+"]:
|
||||
return __classPrivateFieldSet(this, _Decoder_cursor, (_b = __classPrivateFieldGet(this, _Decoder_cursor, "f"), ++_b), "f") === chunk.length
|
||||
? __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_continueDecodeDoubleExponent).bind(this, false, double, 0)
|
||||
: __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_continueDecodeDoubleExponent).call(this, false, double, 0, chunk);
|
||||
case ASCII["-"]:
|
||||
return __classPrivateFieldSet(this, _Decoder_cursor, (_c = __classPrivateFieldGet(this, _Decoder_cursor, "f"), ++_c), "f") === chunk.length
|
||||
? __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_continueDecodeDoubleExponent).bind(this, true, double, 0)
|
||||
: __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_continueDecodeDoubleExponent).call(this, true, double, 0, chunk);
|
||||
}
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_continueDecodeDoubleExponent).call(this, false, double, 0, chunk);
|
||||
}, _Decoder_continueDecodeDoubleExponent = function _Decoder_continueDecodeDoubleExponent(isNegative, double, exponent, chunk) {
|
||||
let cursor = __classPrivateFieldGet(this, _Decoder_cursor, "f");
|
||||
do {
|
||||
const byte = chunk[cursor];
|
||||
if (byte === ASCII["\r"]) {
|
||||
__classPrivateFieldSet(this, _Decoder_cursor, cursor + 2, "f"); // skip \r\n
|
||||
return double * 10 ** (isNegative ? -exponent : exponent);
|
||||
}
|
||||
exponent = exponent * 10 + byte - ASCII["0"];
|
||||
} while (++cursor < chunk.length);
|
||||
__classPrivateFieldSet(this, _Decoder_cursor, cursor, "f");
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_continueDecodeDoubleExponent).bind(this, isNegative, double, exponent);
|
||||
}, _Decoder_findCRLF = function _Decoder_findCRLF(chunk, cursor) {
|
||||
while (chunk[cursor] !== ASCII["\r"]) {
|
||||
if (++cursor === chunk.length) {
|
||||
__classPrivateFieldSet(this, _Decoder_cursor, chunk.length, "f");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
__classPrivateFieldSet(this, _Decoder_cursor, cursor + 2, "f"); // skip \r\n
|
||||
return cursor;
|
||||
}, _Decoder_decodeSimpleString = function _Decoder_decodeSimpleString(type, chunk) {
|
||||
const start = __classPrivateFieldGet(this, _Decoder_cursor, "f"), crlfIndex = __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_findCRLF).call(this, chunk, start);
|
||||
if (crlfIndex === -1) {
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_continueDecodeSimpleString).bind(this, [chunk.subarray(start)], type);
|
||||
}
|
||||
const slice = chunk.subarray(start, crlfIndex);
|
||||
return type === Buffer ? slice : slice.toString();
|
||||
}, _Decoder_continueDecodeSimpleString = function _Decoder_continueDecodeSimpleString(chunks, type, chunk) {
|
||||
const start = __classPrivateFieldGet(this, _Decoder_cursor, "f"), crlfIndex = __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_findCRLF).call(this, chunk, start);
|
||||
if (crlfIndex === -1) {
|
||||
chunks.push(chunk.subarray(start));
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_continueDecodeSimpleString).bind(this, chunks, type);
|
||||
}
|
||||
chunks.push(chunk.subarray(start, crlfIndex));
|
||||
const buffer = Buffer.concat(chunks);
|
||||
return type === Buffer ? buffer : buffer.toString();
|
||||
}, _Decoder_decodeBlobString = function _Decoder_decodeBlobString(type, chunk) {
|
||||
// RESP 2 bulk string null
|
||||
// https://github.com/redis/redis-specifications/blob/master/protocol/RESP2.md#resp-bulk-strings
|
||||
if (chunk[__classPrivateFieldGet(this, _Decoder_cursor, "f")] === ASCII["-"]) {
|
||||
__classPrivateFieldSet(this, _Decoder_cursor, __classPrivateFieldGet(this, _Decoder_cursor, "f") + 4, "f"); // skip -1\r\n
|
||||
return null;
|
||||
}
|
||||
const length = __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeUnsingedNumber).call(this, 0, chunk);
|
||||
if (typeof length === "function") {
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_continueDecodeBlobStringLength).bind(this, length, type);
|
||||
}
|
||||
else if (__classPrivateFieldGet(this, _Decoder_cursor, "f") >= chunk.length) {
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeBlobStringWithLength).bind(this, length, type);
|
||||
}
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeBlobStringWithLength).call(this, length, type, chunk);
|
||||
}, _Decoder_continueDecodeBlobStringLength = function _Decoder_continueDecodeBlobStringLength(lengthCb, type, chunk) {
|
||||
const length = lengthCb(chunk);
|
||||
if (typeof length === "function") {
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_continueDecodeBlobStringLength).bind(this, length, type);
|
||||
}
|
||||
else if (__classPrivateFieldGet(this, _Decoder_cursor, "f") >= chunk.length) {
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeBlobStringWithLength).bind(this, length, type);
|
||||
}
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeBlobStringWithLength).call(this, length, type, chunk);
|
||||
}, _Decoder_decodeStringWithLength = function _Decoder_decodeStringWithLength(length, skip, type, chunk) {
|
||||
const end = __classPrivateFieldGet(this, _Decoder_cursor, "f") + length;
|
||||
if (end >= chunk.length) {
|
||||
const slice = chunk.subarray(__classPrivateFieldGet(this, _Decoder_cursor, "f"));
|
||||
__classPrivateFieldSet(this, _Decoder_cursor, chunk.length, "f");
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_continueDecodeStringWithLength).bind(this, length - slice.length, [slice], skip, type);
|
||||
}
|
||||
const slice = chunk.subarray(__classPrivateFieldGet(this, _Decoder_cursor, "f"), end);
|
||||
__classPrivateFieldSet(this, _Decoder_cursor, end + skip, "f");
|
||||
return type === Buffer ? slice : slice.toString();
|
||||
}, _Decoder_continueDecodeStringWithLength = function _Decoder_continueDecodeStringWithLength(length, chunks, skip, type, chunk) {
|
||||
const end = __classPrivateFieldGet(this, _Decoder_cursor, "f") + length;
|
||||
if (end >= chunk.length) {
|
||||
const slice = chunk.subarray(__classPrivateFieldGet(this, _Decoder_cursor, "f"));
|
||||
chunks.push(slice);
|
||||
__classPrivateFieldSet(this, _Decoder_cursor, chunk.length, "f");
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_continueDecodeStringWithLength).bind(this, length - slice.length, chunks, skip, type);
|
||||
}
|
||||
chunks.push(chunk.subarray(__classPrivateFieldGet(this, _Decoder_cursor, "f"), end));
|
||||
__classPrivateFieldSet(this, _Decoder_cursor, end + skip, "f");
|
||||
const buffer = Buffer.concat(chunks);
|
||||
return type === Buffer ? buffer : buffer.toString();
|
||||
}, _Decoder_decodeBlobStringWithLength = function _Decoder_decodeBlobStringWithLength(length, type, chunk) {
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeStringWithLength).call(this, length, 2, type, chunk);
|
||||
}, _Decoder_decodeVerbatimString = function _Decoder_decodeVerbatimString(type, chunk) {
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_continueDecodeVerbatimStringLength).call(this, __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeUnsingedNumber).bind(this, 0), type, chunk);
|
||||
}, _Decoder_continueDecodeVerbatimStringLength = function _Decoder_continueDecodeVerbatimStringLength(lengthCb, type, chunk) {
|
||||
const length = lengthCb(chunk);
|
||||
return typeof length === "function"
|
||||
? __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_continueDecodeVerbatimStringLength).bind(this, length, type)
|
||||
: __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeVerbatimStringWithLength).call(this, length, type, chunk);
|
||||
}, _Decoder_decodeVerbatimStringWithLength = function _Decoder_decodeVerbatimStringWithLength(length, type, chunk) {
|
||||
const stringLength = length - 4; // skip <format>:
|
||||
if (type === verbatim_string_1.VerbatimString) {
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeVerbatimStringFormat).call(this, stringLength, chunk);
|
||||
}
|
||||
__classPrivateFieldSet(this, _Decoder_cursor, __classPrivateFieldGet(this, _Decoder_cursor, "f") + 4, "f"); // skip <format>:
|
||||
return __classPrivateFieldGet(this, _Decoder_cursor, "f") >= chunk.length
|
||||
? __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeBlobStringWithLength).bind(this, stringLength, type)
|
||||
: __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeBlobStringWithLength).call(this, stringLength, type, chunk);
|
||||
}, _Decoder_decodeVerbatimStringFormat = function _Decoder_decodeVerbatimStringFormat(stringLength, chunk) {
|
||||
const formatCb = __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeStringWithLength).bind(this, 3, 1, String);
|
||||
return __classPrivateFieldGet(this, _Decoder_cursor, "f") >= chunk.length
|
||||
? __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_continueDecodeVerbatimStringFormat).bind(this, stringLength, formatCb)
|
||||
: __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_continueDecodeVerbatimStringFormat).call(this, stringLength, formatCb, chunk);
|
||||
}, _Decoder_continueDecodeVerbatimStringFormat = function _Decoder_continueDecodeVerbatimStringFormat(stringLength, formatCb, chunk) {
|
||||
const format = formatCb(chunk);
|
||||
return typeof format === "function"
|
||||
? __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_continueDecodeVerbatimStringFormat).bind(this, stringLength, format)
|
||||
: __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeVerbatimStringWithFormat).call(this, stringLength, format, chunk);
|
||||
}, _Decoder_decodeVerbatimStringWithFormat = function _Decoder_decodeVerbatimStringWithFormat(stringLength, format, chunk) {
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_continueDecodeVerbatimStringWithFormat).call(this, format, __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeBlobStringWithLength).bind(this, stringLength, String), chunk);
|
||||
}, _Decoder_continueDecodeVerbatimStringWithFormat = function _Decoder_continueDecodeVerbatimStringWithFormat(format, stringCb, chunk) {
|
||||
const string = stringCb(chunk);
|
||||
return typeof string === "function"
|
||||
? __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_continueDecodeVerbatimStringWithFormat).bind(this, format, string)
|
||||
: new verbatim_string_1.VerbatimString(format, string);
|
||||
}, _Decoder_decodeSimpleError = function _Decoder_decodeSimpleError(chunk) {
|
||||
const string = __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeSimpleString).call(this, String, chunk);
|
||||
return typeof string === "function"
|
||||
? __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_continueDecodeSimpleError).bind(this, string)
|
||||
: new errors_1.SimpleError(string);
|
||||
}, _Decoder_continueDecodeSimpleError = function _Decoder_continueDecodeSimpleError(stringCb, chunk) {
|
||||
const string = stringCb(chunk);
|
||||
return typeof string === "function"
|
||||
? __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_continueDecodeSimpleError).bind(this, string)
|
||||
: new errors_1.SimpleError(string);
|
||||
}, _Decoder_decodeBlobError = function _Decoder_decodeBlobError(chunk) {
|
||||
const string = __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeBlobString).call(this, String, chunk);
|
||||
return typeof string === "function"
|
||||
? __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_continueDecodeBlobError).bind(this, string)
|
||||
: new errors_1.BlobError(string);
|
||||
}, _Decoder_continueDecodeBlobError = function _Decoder_continueDecodeBlobError(stringCb, chunk) {
|
||||
const string = stringCb(chunk);
|
||||
return typeof string === "function"
|
||||
? __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_continueDecodeBlobError).bind(this, string)
|
||||
: new errors_1.BlobError(string);
|
||||
}, _Decoder_decodeNestedType = function _Decoder_decodeNestedType(typeMapping, chunk) {
|
||||
var _b;
|
||||
const type = chunk[__classPrivateFieldGet(this, _Decoder_cursor, "f")];
|
||||
return __classPrivateFieldSet(this, _Decoder_cursor, (_b = __classPrivateFieldGet(this, _Decoder_cursor, "f"), ++_b), "f") === chunk.length
|
||||
? __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeNestedTypeValue).bind(this, type, typeMapping)
|
||||
: __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeNestedTypeValue).call(this, type, typeMapping, chunk);
|
||||
}, _Decoder_decodeNestedTypeValue = function _Decoder_decodeNestedTypeValue(type, typeMapping, chunk) {
|
||||
switch (type) {
|
||||
case exports.RESP_TYPES.NULL:
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeNull).call(this);
|
||||
case exports.RESP_TYPES.BOOLEAN:
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeBoolean).call(this, typeMapping[exports.RESP_TYPES.BOOLEAN], chunk);
|
||||
case exports.RESP_TYPES.NUMBER:
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeNumber).call(this, typeMapping[exports.RESP_TYPES.NUMBER], chunk);
|
||||
case exports.RESP_TYPES.BIG_NUMBER:
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeBigNumber).call(this, typeMapping[exports.RESP_TYPES.BIG_NUMBER], chunk);
|
||||
case exports.RESP_TYPES.DOUBLE:
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeDouble).call(this, typeMapping[exports.RESP_TYPES.DOUBLE], chunk);
|
||||
case exports.RESP_TYPES.SIMPLE_STRING:
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeSimpleString).call(this, typeMapping[exports.RESP_TYPES.SIMPLE_STRING], chunk);
|
||||
case exports.RESP_TYPES.BLOB_STRING:
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeBlobString).call(this, typeMapping[exports.RESP_TYPES.BLOB_STRING], chunk);
|
||||
case exports.RESP_TYPES.VERBATIM_STRING:
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeVerbatimString).call(this, typeMapping[exports.RESP_TYPES.VERBATIM_STRING], chunk);
|
||||
case exports.RESP_TYPES.SIMPLE_ERROR:
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeSimpleError).call(this, chunk);
|
||||
case exports.RESP_TYPES.BLOB_ERROR:
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeBlobError).call(this, chunk);
|
||||
case exports.RESP_TYPES.ARRAY:
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeArray).call(this, typeMapping, chunk);
|
||||
case exports.RESP_TYPES.SET:
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeSet).call(this, typeMapping, chunk);
|
||||
case exports.RESP_TYPES.MAP:
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeMap).call(this, typeMapping, chunk);
|
||||
default:
|
||||
throw new Error(`Unknown RESP type ${type} "${String.fromCharCode(type)}"`);
|
||||
}
|
||||
}, _Decoder_decodeArray = function _Decoder_decodeArray(typeMapping, chunk) {
|
||||
// RESP 2 null
|
||||
// https://github.com/redis/redis-specifications/blob/master/protocol/RESP2.md#resp-arrays
|
||||
if (chunk[__classPrivateFieldGet(this, _Decoder_cursor, "f")] === ASCII["-"]) {
|
||||
__classPrivateFieldSet(this, _Decoder_cursor, __classPrivateFieldGet(this, _Decoder_cursor, "f") + 4, "f"); // skip -1\r\n
|
||||
return null;
|
||||
}
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeArrayWithLength).call(this, __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeUnsingedNumber).call(this, 0, chunk), typeMapping, chunk);
|
||||
}, _Decoder_decodeArrayWithLength = function _Decoder_decodeArrayWithLength(length, typeMapping, chunk) {
|
||||
return typeof length === "function"
|
||||
? __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_continueDecodeArrayLength).bind(this, length, typeMapping)
|
||||
: __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeArrayItems).call(this, new Array(length), 0, typeMapping, chunk);
|
||||
}, _Decoder_continueDecodeArrayLength = function _Decoder_continueDecodeArrayLength(lengthCb, typeMapping, chunk) {
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeArrayWithLength).call(this, lengthCb(chunk), typeMapping, chunk);
|
||||
}, _Decoder_decodeArrayItems = function _Decoder_decodeArrayItems(array, filled, typeMapping, chunk) {
|
||||
for (let i = filled; i < array.length; i++) {
|
||||
if (__classPrivateFieldGet(this, _Decoder_cursor, "f") >= chunk.length) {
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeArrayItems).bind(this, array, i, typeMapping);
|
||||
}
|
||||
const item = __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeNestedType).call(this, typeMapping, chunk);
|
||||
if (typeof item === "function") {
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_continueDecodeArrayItems).bind(this, array, i, item, typeMapping);
|
||||
}
|
||||
array[i] = item;
|
||||
}
|
||||
return array;
|
||||
}, _Decoder_continueDecodeArrayItems = function _Decoder_continueDecodeArrayItems(array, filled, itemCb, typeMapping, chunk) {
|
||||
const item = itemCb(chunk);
|
||||
if (typeof item === "function") {
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_continueDecodeArrayItems).bind(this, array, filled, item, typeMapping);
|
||||
}
|
||||
array[filled++] = item;
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeArrayItems).call(this, array, filled, typeMapping, chunk);
|
||||
}, _Decoder_decodeSet = function _Decoder_decodeSet(typeMapping, chunk) {
|
||||
const length = __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeUnsingedNumber).call(this, 0, chunk);
|
||||
if (typeof length === "function") {
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_continueDecodeSetLength).bind(this, length, typeMapping);
|
||||
}
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeSetItems).call(this, length, typeMapping, chunk);
|
||||
}, _Decoder_continueDecodeSetLength = function _Decoder_continueDecodeSetLength(lengthCb, typeMapping, chunk) {
|
||||
const length = lengthCb(chunk);
|
||||
return typeof length === "function"
|
||||
? __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_continueDecodeSetLength).bind(this, length, typeMapping)
|
||||
: __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeSetItems).call(this, length, typeMapping, chunk);
|
||||
}, _Decoder_decodeSetItems = function _Decoder_decodeSetItems(length, typeMapping, chunk) {
|
||||
return typeMapping[exports.RESP_TYPES.SET] === Set
|
||||
? __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeSetAsSet).call(this, new Set(), length, typeMapping, chunk)
|
||||
: __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeArrayItems).call(this, new Array(length), 0, typeMapping, chunk);
|
||||
}, _Decoder_decodeSetAsSet = function _Decoder_decodeSetAsSet(set, remaining, typeMapping, chunk) {
|
||||
// using `remaining` instead of `length` & `set.size` to make it work even if the set contains duplicates
|
||||
while (remaining > 0) {
|
||||
if (__classPrivateFieldGet(this, _Decoder_cursor, "f") >= chunk.length) {
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeSetAsSet).bind(this, set, remaining, typeMapping);
|
||||
}
|
||||
const item = __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeNestedType).call(this, typeMapping, chunk);
|
||||
if (typeof item === "function") {
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_continueDecodeSetAsSet).bind(this, set, remaining, item, typeMapping);
|
||||
}
|
||||
set.add(item);
|
||||
--remaining;
|
||||
}
|
||||
return set;
|
||||
}, _Decoder_continueDecodeSetAsSet = function _Decoder_continueDecodeSetAsSet(set, remaining, itemCb, typeMapping, chunk) {
|
||||
const item = itemCb(chunk);
|
||||
if (typeof item === "function") {
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_continueDecodeSetAsSet).bind(this, set, remaining, item, typeMapping);
|
||||
}
|
||||
set.add(item);
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeSetAsSet).call(this, set, remaining - 1, typeMapping, chunk);
|
||||
}, _Decoder_decodeMap = function _Decoder_decodeMap(typeMapping, chunk) {
|
||||
const length = __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeUnsingedNumber).call(this, 0, chunk);
|
||||
if (typeof length === "function") {
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_continueDecodeMapLength).bind(this, length, typeMapping);
|
||||
}
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeMapItems).call(this, length, typeMapping, chunk);
|
||||
}, _Decoder_continueDecodeMapLength = function _Decoder_continueDecodeMapLength(lengthCb, typeMapping, chunk) {
|
||||
const length = lengthCb(chunk);
|
||||
return typeof length === "function"
|
||||
? __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_continueDecodeMapLength).bind(this, length, typeMapping)
|
||||
: __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeMapItems).call(this, length, typeMapping, chunk);
|
||||
}, _Decoder_decodeMapItems = function _Decoder_decodeMapItems(length, typeMapping, chunk) {
|
||||
switch (typeMapping[exports.RESP_TYPES.MAP]) {
|
||||
case Map:
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeMapAsMap).call(this, new Map(), length, typeMapping, chunk);
|
||||
case Array:
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeArrayItems).call(this, new Array(length * 2), 0, typeMapping, chunk);
|
||||
default:
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeMapAsObject).call(this, {}, length, typeMapping, chunk);
|
||||
}
|
||||
}, _Decoder_decodeMapAsMap = function _Decoder_decodeMapAsMap(map, remaining, typeMapping, chunk) {
|
||||
// using `remaining` instead of `length` & `map.size` to make it work even if the map contains duplicate keys
|
||||
while (remaining > 0) {
|
||||
if (__classPrivateFieldGet(this, _Decoder_cursor, "f") >= chunk.length) {
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeMapAsMap).bind(this, map, remaining, typeMapping);
|
||||
}
|
||||
const key = __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeMapKey).call(this, typeMapping, chunk);
|
||||
if (typeof key === "function") {
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_continueDecodeMapKey).bind(this, map, remaining, key, typeMapping);
|
||||
}
|
||||
if (__classPrivateFieldGet(this, _Decoder_cursor, "f") >= chunk.length) {
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_continueDecodeMapValue).bind(this, map, remaining, key, __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeNestedType).bind(this, typeMapping), typeMapping);
|
||||
}
|
||||
const value = __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeNestedType).call(this, typeMapping, chunk);
|
||||
if (typeof value === "function") {
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_continueDecodeMapValue).bind(this, map, remaining, key, value, typeMapping);
|
||||
}
|
||||
map.set(key, value);
|
||||
--remaining;
|
||||
}
|
||||
return map;
|
||||
}, _Decoder_decodeMapKey = function _Decoder_decodeMapKey(typeMapping, chunk) {
|
||||
var _b;
|
||||
const type = chunk[__classPrivateFieldGet(this, _Decoder_cursor, "f")];
|
||||
return __classPrivateFieldSet(this, _Decoder_cursor, (_b = __classPrivateFieldGet(this, _Decoder_cursor, "f"), ++_b), "f") === chunk.length
|
||||
? __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeMapKeyValue).bind(this, type, typeMapping)
|
||||
: __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeMapKeyValue).call(this, type, typeMapping, chunk);
|
||||
}, _Decoder_decodeMapKeyValue = function _Decoder_decodeMapKeyValue(type, typeMapping, chunk) {
|
||||
switch (type) {
|
||||
// decode simple string map key as string (and not as buffer)
|
||||
case exports.RESP_TYPES.SIMPLE_STRING:
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeSimpleString).call(this, String, chunk);
|
||||
// decode blob string map key as string (and not as buffer)
|
||||
case exports.RESP_TYPES.BLOB_STRING:
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeBlobString).call(this, String, chunk);
|
||||
default:
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeNestedTypeValue).call(this, type, typeMapping, chunk);
|
||||
}
|
||||
}, _Decoder_continueDecodeMapKey = function _Decoder_continueDecodeMapKey(map, remaining, keyCb, typeMapping, chunk) {
|
||||
const key = keyCb(chunk);
|
||||
if (typeof key === "function") {
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_continueDecodeMapKey).bind(this, map, remaining, key, typeMapping);
|
||||
}
|
||||
if (__classPrivateFieldGet(this, _Decoder_cursor, "f") >= chunk.length) {
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_continueDecodeMapValue).bind(this, map, remaining, key, __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeNestedType).bind(this, typeMapping), typeMapping);
|
||||
}
|
||||
const value = __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeNestedType).call(this, typeMapping, chunk);
|
||||
if (typeof value === "function") {
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_continueDecodeMapValue).bind(this, map, remaining, key, value, typeMapping);
|
||||
}
|
||||
map.set(key, value);
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeMapAsMap).call(this, map, remaining - 1, typeMapping, chunk);
|
||||
}, _Decoder_continueDecodeMapValue = function _Decoder_continueDecodeMapValue(map, remaining, key, valueCb, typeMapping, chunk) {
|
||||
const value = valueCb(chunk);
|
||||
if (typeof value === "function") {
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_continueDecodeMapValue).bind(this, map, remaining, key, value, typeMapping);
|
||||
}
|
||||
map.set(key, value);
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeMapAsMap).call(this, map, remaining - 1, typeMapping, chunk);
|
||||
}, _Decoder_decodeMapAsObject = function _Decoder_decodeMapAsObject(object, remaining, typeMapping, chunk) {
|
||||
while (remaining > 0) {
|
||||
if (__classPrivateFieldGet(this, _Decoder_cursor, "f") >= chunk.length) {
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeMapAsObject).bind(this, object, remaining, typeMapping);
|
||||
}
|
||||
const key = __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeMapKey).call(this, typeMapping, chunk);
|
||||
if (typeof key === "function") {
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_continueDecodeMapAsObjectKey).bind(this, object, remaining, key, typeMapping);
|
||||
}
|
||||
if (__classPrivateFieldGet(this, _Decoder_cursor, "f") >= chunk.length) {
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_continueDecodeMapAsObjectValue).bind(this, object, remaining, key, __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeNestedType).bind(this, typeMapping), typeMapping);
|
||||
}
|
||||
const value = __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeNestedType).call(this, typeMapping, chunk);
|
||||
if (typeof value === "function") {
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_continueDecodeMapAsObjectValue).bind(this, object, remaining, key, value, typeMapping);
|
||||
}
|
||||
// Guard "__proto__"/"constructor" via defineProperty, where a bare
|
||||
// assignment would tamper with the object. Issue #1267. Normal keys take
|
||||
// the fast plain-assignment path.
|
||||
if (key === "__proto__" || key === "constructor") {
|
||||
Object.defineProperty(object, key, {
|
||||
value,
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
});
|
||||
}
|
||||
else {
|
||||
object[key] = value;
|
||||
}
|
||||
--remaining;
|
||||
}
|
||||
return object;
|
||||
}, _Decoder_continueDecodeMapAsObjectKey = function _Decoder_continueDecodeMapAsObjectKey(object, remaining, keyCb, typeMapping, chunk) {
|
||||
const key = keyCb(chunk);
|
||||
if (typeof key === "function") {
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_continueDecodeMapAsObjectKey).bind(this, object, remaining, key, typeMapping);
|
||||
}
|
||||
if (__classPrivateFieldGet(this, _Decoder_cursor, "f") >= chunk.length) {
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_continueDecodeMapAsObjectValue).bind(this, object, remaining, key, __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeNestedType).bind(this, typeMapping), typeMapping);
|
||||
}
|
||||
const value = __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeNestedType).call(this, typeMapping, chunk);
|
||||
if (typeof value === "function") {
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_continueDecodeMapAsObjectValue).bind(this, object, remaining, key, value, typeMapping);
|
||||
}
|
||||
// Guard "__proto__"/"constructor" via defineProperty, where a bare
|
||||
// assignment would tamper with the object. Issue #1267. Normal keys take
|
||||
// the fast plain-assignment path.
|
||||
if (key === "__proto__" || key === "constructor") {
|
||||
Object.defineProperty(object, key, {
|
||||
value,
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
});
|
||||
}
|
||||
else {
|
||||
object[key] = value;
|
||||
}
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeMapAsObject).call(this, object, remaining - 1, typeMapping, chunk);
|
||||
}, _Decoder_continueDecodeMapAsObjectValue = function _Decoder_continueDecodeMapAsObjectValue(object, remaining, key, valueCb, typeMapping, chunk) {
|
||||
const value = valueCb(chunk);
|
||||
if (typeof value === "function") {
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_continueDecodeMapAsObjectValue).bind(this, object, remaining, key, value, typeMapping);
|
||||
}
|
||||
// Guard "__proto__"/"constructor" via defineProperty, where a bare
|
||||
// assignment would tamper with the object. Issue #1267. Normal keys take
|
||||
// the fast plain-assignment path.
|
||||
if (key === "__proto__" || key === "constructor") {
|
||||
Object.defineProperty(object, key, {
|
||||
value,
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
});
|
||||
}
|
||||
else {
|
||||
object[key] = value;
|
||||
}
|
||||
return __classPrivateFieldGet(this, _Decoder_instances, "m", _Decoder_decodeMapAsObject).call(this, object, remaining - 1, typeMapping, chunk);
|
||||
};
|
||||
// Precalculated multipliers for decimal points to improve performance
|
||||
// "... about 15 to 17 decimal places ..."
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number#:~:text=about%2015%20to%2017%20decimal%20places
|
||||
_Decoder_DOUBLE_DECIMAL_MULTIPLIERS = { value: [
|
||||
1e-1, 1e-2, 1e-3, 1e-4, 1e-5, 1e-6, 1e-7, 1e-8, 1e-9, 1e-10, 1e-11, 1e-12,
|
||||
1e-13, 1e-14, 1e-15, 1e-16, 1e-17,
|
||||
] };
|
||||
8
node_modules/ioredis/built/resp/errors.d.ts
generated
vendored
Normal file
8
node_modules/ioredis/built/resp/errors.d.ts
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
import { ReplyError } from "redis-errors";
|
||||
export type ErrorReply = ReplyError;
|
||||
export declare class SimpleError extends ReplyError {
|
||||
get name(): string;
|
||||
}
|
||||
export declare class BlobError extends ReplyError {
|
||||
get name(): string;
|
||||
}
|
||||
16
node_modules/ioredis/built/resp/errors.js
generated
vendored
Normal file
16
node_modules/ioredis/built/resp/errors.js
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.BlobError = exports.SimpleError = void 0;
|
||||
const redis_errors_1 = require("redis-errors");
|
||||
class SimpleError extends redis_errors_1.ReplyError {
|
||||
get name() {
|
||||
return "ReplyError";
|
||||
}
|
||||
}
|
||||
exports.SimpleError = SimpleError;
|
||||
class BlobError extends redis_errors_1.ReplyError {
|
||||
get name() {
|
||||
return "ReplyError";
|
||||
}
|
||||
}
|
||||
exports.BlobError = BlobError;
|
||||
7
node_modules/ioredis/built/resp/types.d.ts
generated
vendored
Normal file
7
node_modules/ioredis/built/resp/types.d.ts
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
import { RESP_TYPES } from "./decoder";
|
||||
export type RESP_TYPES = typeof RESP_TYPES;
|
||||
export type RespTypes = RESP_TYPES[keyof RESP_TYPES];
|
||||
export type MappedType<T = unknown> = ((...args: any[]) => T) | (new (...args: any[]) => T);
|
||||
export type TypeMapping = {
|
||||
[P in RespTypes]?: MappedType;
|
||||
};
|
||||
2
node_modules/ioredis/built/resp/types.js
generated
vendored
Normal file
2
node_modules/ioredis/built/resp/types.js
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
4
node_modules/ioredis/built/resp/verbatim-string.d.ts
generated
vendored
Normal file
4
node_modules/ioredis/built/resp/verbatim-string.d.ts
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
export declare class VerbatimString extends String {
|
||||
format: string;
|
||||
constructor(format: string, value: string);
|
||||
}
|
||||
17
node_modules/ioredis/built/resp/verbatim-string.js
generated
vendored
Normal file
17
node_modules/ioredis/built/resp/verbatim-string.js
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.VerbatimString = void 0;
|
||||
/*
|
||||
* Portions adapted from node-redis:
|
||||
* https://github.com/redis/node-redis
|
||||
*
|
||||
* Copyright (c) 2022-2023, Redis, Inc.
|
||||
* Licensed under the MIT License.
|
||||
*/
|
||||
class VerbatimString extends String {
|
||||
constructor(format, value) {
|
||||
super(value);
|
||||
this.format = format;
|
||||
}
|
||||
}
|
||||
exports.VerbatimString = VerbatimString;
|
||||
26
node_modules/ioredis/built/tracing.d.ts
generated
vendored
Normal file
26
node_modules/ioredis/built/tracing.d.ts
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
import type { CommandParameter } from "./types";
|
||||
export declare function sanitizeArgs(commandName: string, args: CommandParameter[]): string[];
|
||||
export interface CommandTraceContext {
|
||||
command: string;
|
||||
args: string[];
|
||||
database: number;
|
||||
serverAddress: string;
|
||||
serverPort: number | undefined;
|
||||
}
|
||||
export interface BatchOperationContext {
|
||||
batchMode: "MULTI";
|
||||
batchSize: number;
|
||||
database: number;
|
||||
serverAddress: string;
|
||||
serverPort: number | undefined;
|
||||
}
|
||||
export interface ConnectTraceContext {
|
||||
serverAddress: string;
|
||||
serverPort: number | undefined;
|
||||
connectionEpoch: number;
|
||||
}
|
||||
type CommandContext = CommandTraceContext;
|
||||
export declare function traceCommand<T>(fn: () => Promise<T>, contextFactory: () => CommandContext): Promise<T>;
|
||||
export declare function traceBatch<T>(fn: () => Promise<T>, contextFactory: () => BatchOperationContext): Promise<T>;
|
||||
export declare function traceConnect<T>(fn: () => Promise<T>, contextFactory: () => ConnectTraceContext): Promise<T>;
|
||||
export {};
|
||||
96
node_modules/ioredis/built/tracing.js
generated
vendored
Normal file
96
node_modules/ioredis/built/tracing.js
generated
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.traceConnect = exports.traceBatch = exports.traceCommand = exports.sanitizeArgs = void 0;
|
||||
// Argument sanitization rules adapted from @opentelemetry/redis-common (Apache 2.0).
|
||||
// https://github.com/open-telemetry/opentelemetry-js-contrib/blob/main/packages/redis-common/src/index.ts
|
||||
//
|
||||
// Each entry specifies how many positional args (after the command name) are safe
|
||||
// to emit. -1 means all args are safe (read-only/structural commands).
|
||||
// Unlisted commands default to 0 (all args redacted) for safe-by-default behavior,
|
||||
// which covers AUTH, HELLO, and unknown/custom commands.
|
||||
const SERIALIZATION_SUBSETS = [
|
||||
{ regex: /^ECHO/i, args: 0 },
|
||||
{
|
||||
regex: /^(GETSET|LPUSH|MSET|PFA|PSETEX|PUBLISH|RPUSH|SADD|SET|SPUBLISH|XADD|ZADD)/i,
|
||||
args: 1,
|
||||
},
|
||||
{ regex: /^(HSET|HMSET|LSET|LINSERT)/i, args: 2 },
|
||||
{
|
||||
regex: /^(ACL|BIT|B[LRZ]|CLIENT|CLUSTER|CONFIG|COMMAND|DECR|DEL|EVAL|EX|FUNCTION|GEO|GET|HINCR|HMGET|HSCAN|INCR|L[TRLM]|MEMORY|P[EFISTU]|RPOP|S[CDIMORSU]|XACK|X[CDGILPRT]|Z[CDILMPRS])/i,
|
||||
args: -1,
|
||||
},
|
||||
];
|
||||
function sanitizeArgs(commandName, args) {
|
||||
let allowedArgCount = 0;
|
||||
for (const subset of SERIALIZATION_SUBSETS) {
|
||||
if (subset.regex.test(commandName)) {
|
||||
allowedArgCount = subset.args;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (allowedArgCount === -1) {
|
||||
return args.map((a) => String(a));
|
||||
}
|
||||
const result = [];
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
if (i < allowedArgCount) {
|
||||
result.push(String(args[i]));
|
||||
}
|
||||
else {
|
||||
result.push("?");
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
exports.sanitizeArgs = sanitizeArgs;
|
||||
// Load diagnostics_channel with Node 18 compatibility
|
||||
const dc = (() => {
|
||||
try {
|
||||
return "getBuiltinModule" in process
|
||||
? process.getBuiltinModule("node:diagnostics_channel")
|
||||
: require("node:diagnostics_channel");
|
||||
}
|
||||
catch {
|
||||
return undefined;
|
||||
}
|
||||
})();
|
||||
const hasTracingChannel = dc && typeof dc.tracingChannel === "function";
|
||||
const commandChannel = hasTracingChannel
|
||||
? dc.tracingChannel("ioredis:command")
|
||||
: undefined;
|
||||
const batchChannel = hasTracingChannel
|
||||
? dc.tracingChannel("ioredis:batch")
|
||||
: undefined;
|
||||
const connectChannel = hasTracingChannel
|
||||
? dc.tracingChannel("ioredis:connect")
|
||||
: undefined;
|
||||
function shouldTrace(channel) {
|
||||
return !!channel && channel.hasSubscribers !== false;
|
||||
}
|
||||
const noop = () => { };
|
||||
function traceCommand(fn, contextFactory) {
|
||||
if (!shouldTrace(commandChannel))
|
||||
return fn();
|
||||
// tracePromise returns a wrapper promise that re-rejects on error.
|
||||
// Silence the wrapper to prevent unhandled rejections when callers
|
||||
// (e.g. Pipeline) discard the return value. Callers that await this
|
||||
// promise still see the rejection through their own .then() chain.
|
||||
const traced = commandChannel.tracePromise(fn, contextFactory());
|
||||
traced.catch(noop);
|
||||
return traced;
|
||||
}
|
||||
exports.traceCommand = traceCommand;
|
||||
function traceBatch(fn, contextFactory) {
|
||||
if (!shouldTrace(batchChannel))
|
||||
return fn();
|
||||
const traced = batchChannel.tracePromise(fn, contextFactory());
|
||||
traced.catch(noop);
|
||||
return traced;
|
||||
}
|
||||
exports.traceBatch = traceBatch;
|
||||
function traceConnect(fn, contextFactory) {
|
||||
if (!shouldTrace(connectChannel))
|
||||
return fn();
|
||||
return connectChannel.tracePromise(fn, contextFactory());
|
||||
}
|
||||
exports.traceConnect = traceConnect;
|
||||
13
node_modules/ioredis/built/transaction.d.ts
generated
vendored
Normal file
13
node_modules/ioredis/built/transaction.d.ts
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
import { ChainableCommander } from "./utils/RedisCommander";
|
||||
export interface Transaction<Mapping extends "resp2" | "resp3" = "resp2"> {
|
||||
pipeline(commands?: unknown[][]): ChainableCommander<Mapping>;
|
||||
multi(options: {
|
||||
pipeline: false;
|
||||
}): Promise<"OK">;
|
||||
multi(): ChainableCommander<Mapping>;
|
||||
multi(options: {
|
||||
pipeline: true;
|
||||
}): ChainableCommander<Mapping>;
|
||||
multi(commands?: unknown[][]): ChainableCommander<Mapping>;
|
||||
}
|
||||
export declare function addTransactionSupport(redis: any): void;
|
||||
100
node_modules/ioredis/built/transaction.js
generated
vendored
Normal file
100
node_modules/ioredis/built/transaction.js
generated
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
"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 tracing_1 = require("./tracing");
|
||||
function addTransactionSupport(redis) {
|
||||
redis.pipeline = function (commands) {
|
||||
const pipeline = new Pipeline_1.default(this);
|
||||
if (Array.isArray(commands)) {
|
||||
pipeline.addBatch(commands);
|
||||
}
|
||||
return pipeline;
|
||||
};
|
||||
const { multi } = redis;
|
||||
redis.multi = function (commands, options) {
|
||||
if (typeof options === "undefined" && !Array.isArray(commands)) {
|
||||
options = commands;
|
||||
commands = null;
|
||||
}
|
||||
if (options && options.pipeline === false) {
|
||||
return multi.call(this);
|
||||
}
|
||||
const pipeline = new Pipeline_1.default(this);
|
||||
// @ts-expect-error
|
||||
pipeline.multi();
|
||||
if (Array.isArray(commands)) {
|
||||
pipeline.addBatch(commands);
|
||||
}
|
||||
const exec = pipeline.exec;
|
||||
pipeline.exec = function (callback) {
|
||||
// Wait for the cluster to be connected, since we need nodes information before continuing
|
||||
if (this.isCluster && !this.redis.slots.length) {
|
||||
if (this.redis.status === "wait")
|
||||
this.redis.connect().catch(utils_1.noop);
|
||||
return (0, standard_as_callback_1.default)(new Promise((resolve, reject) => {
|
||||
this.redis.delayUntilReady((err) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
this.exec(pipeline).then(resolve, reject);
|
||||
});
|
||||
}), callback);
|
||||
}
|
||||
if (this._transactions > 0) {
|
||||
exec.call(pipeline);
|
||||
}
|
||||
// Returns directly when the pipeline
|
||||
// has been called multiple times (retries).
|
||||
if (this.nodeifiedPromise) {
|
||||
return exec.call(pipeline);
|
||||
}
|
||||
// Count only user commands (exclude MULTI/EXEC wrappers).
|
||||
const batchSize = Math.max(pipeline.length - 2, 0);
|
||||
const execAndUnwrap = () => exec.call(pipeline).then(function (result) {
|
||||
const execResult = result[result.length - 1];
|
||||
if (typeof execResult === "undefined") {
|
||||
throw new Error("Pipeline cannot be used to send any commands when the `exec()` has been called on it.");
|
||||
}
|
||||
if (execResult[0]) {
|
||||
execResult[0].previousErrors = [];
|
||||
for (let i = 0; i < result.length - 1; ++i) {
|
||||
if (result[i][0]) {
|
||||
execResult[0].previousErrors.push(result[i][0]);
|
||||
}
|
||||
}
|
||||
throw execResult[0];
|
||||
}
|
||||
return (0, utils_1.wrapMultiResult)(execResult[1]);
|
||||
});
|
||||
// Trace MULTI as a single batch operation on ioredis:batch.
|
||||
const promise = "_buildBatchContext" in this.redis
|
||||
? (0, tracing_1.traceBatch)(execAndUnwrap, () => this.redis._buildBatchContext(batchSize))
|
||||
: execAndUnwrap();
|
||||
return (0, standard_as_callback_1.default)(promise, callback);
|
||||
};
|
||||
// @ts-expect-error
|
||||
const { execBuffer } = pipeline;
|
||||
// @ts-expect-error
|
||||
pipeline.execBuffer = function (callback) {
|
||||
if (this._transactions > 0) {
|
||||
execBuffer.call(pipeline);
|
||||
}
|
||||
return pipeline.exec(callback);
|
||||
};
|
||||
return pipeline;
|
||||
};
|
||||
const { exec } = redis;
|
||||
redis.exec = function (callback) {
|
||||
return (0, standard_as_callback_1.default)(exec.call(this).then(function (results) {
|
||||
if (Array.isArray(results)) {
|
||||
results = (0, utils_1.wrapMultiResult)(results);
|
||||
}
|
||||
return results;
|
||||
}), callback);
|
||||
};
|
||||
}
|
||||
exports.addTransactionSupport = addTransactionSupport;
|
||||
46
node_modules/ioredis/built/types.d.ts
generated
vendored
Normal file
46
node_modules/ioredis/built/types.d.ts
generated
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
/// <reference types="node" />
|
||||
/// <reference types="node" />
|
||||
/// <reference types="node" />
|
||||
/// <reference types="node" />
|
||||
import { Socket } from "net";
|
||||
import { TLSSocket } from "tls";
|
||||
export type Callback<T = any> = (err?: Error | null, result?: T) => void;
|
||||
export type NetStream = Socket | TLSSocket;
|
||||
export type ProtocolVersion = 2 | 3;
|
||||
export type ReplyMappingMode = "legacy" | "resp3";
|
||||
export type ReplyMappingFromOptions<Current extends ReplyMappingMode, Options> = Options extends {
|
||||
replyMapping: infer Mapping;
|
||||
} ? Extract<Mapping, ReplyMappingMode> extends never ? Current : Extract<Mapping, ReplyMappingMode> : Current;
|
||||
export type ReplyMappingFromClusterOptions<Current extends ReplyMappingMode, Options> = Options extends {
|
||||
redisOptions: {
|
||||
replyMapping: infer Mapping;
|
||||
};
|
||||
} ? Extract<Mapping, ReplyMappingMode> extends never ? Current : Extract<Mapping, ReplyMappingMode> : Current;
|
||||
export type CommandParameter = string | Buffer | number | any[];
|
||||
export interface Respondable {
|
||||
name: string;
|
||||
args: CommandParameter[];
|
||||
resolve(result: any): void;
|
||||
reject(error: Error): void;
|
||||
}
|
||||
export interface PipelineWriteableStream {
|
||||
isPipeline: true;
|
||||
write(data: string | Buffer): unknown;
|
||||
destination: {
|
||||
redis: {
|
||||
stream: NetStream;
|
||||
};
|
||||
};
|
||||
}
|
||||
export type WriteableStream = NetStream | PipelineWriteableStream;
|
||||
export interface CommandItem {
|
||||
command: Respondable;
|
||||
stream: WriteableStream;
|
||||
select: number;
|
||||
}
|
||||
export interface ScanStreamOptions {
|
||||
match?: string;
|
||||
type?: string;
|
||||
count?: number;
|
||||
noValues?: boolean;
|
||||
}
|
||||
2
node_modules/ioredis/built/types.js
generated
vendored
Normal file
2
node_modules/ioredis/built/types.js
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
50
node_modules/ioredis/built/utils/Commander.d.ts
generated
vendored
Normal file
50
node_modules/ioredis/built/utils/Commander.d.ts
generated
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
import Command from "../Command";
|
||||
import { WriteableStream } from "../types";
|
||||
import RedisCommander, { ClientContext } from "./RedisCommander";
|
||||
export interface CommanderOptions {
|
||||
keyPrefix?: string | undefined;
|
||||
showFriendlyErrorStack?: boolean | undefined;
|
||||
}
|
||||
declare class Commander<Context extends ClientContext = {
|
||||
type: "default";
|
||||
}> {
|
||||
options: CommanderOptions;
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
scriptsSet: {};
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
addedBuiltinSet: Set<string>;
|
||||
/**
|
||||
* Return supported builtin commands
|
||||
*/
|
||||
getBuiltinCommands(): string[];
|
||||
/**
|
||||
* Create a builtin command
|
||||
*/
|
||||
createBuiltinCommand(commandName: string): {
|
||||
string: any;
|
||||
buffer: any;
|
||||
};
|
||||
/**
|
||||
* Create add builtin command
|
||||
*/
|
||||
addBuiltinCommand(commandName: string): void;
|
||||
/**
|
||||
* Define a custom command using lua script
|
||||
*/
|
||||
defineCommand(name: string, definition: {
|
||||
lua: string;
|
||||
numberOfKeys?: number;
|
||||
readOnly?: boolean;
|
||||
}): void;
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
sendCommand(command: Command, stream?: WriteableStream, node?: unknown): unknown;
|
||||
}
|
||||
interface Commander<Context> extends RedisCommander<Context> {
|
||||
}
|
||||
export default Commander;
|
||||
117
node_modules/ioredis/built/utils/Commander.js
generated
vendored
Normal file
117
node_modules/ioredis/built/utils/Commander.js
generated
vendored
Normal file
@@ -0,0 +1,117 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const commands_1 = require("@ioredis/commands");
|
||||
const autoPipelining_1 = require("../autoPipelining");
|
||||
const Command_1 = require("../Command");
|
||||
const Script_1 = require("../Script");
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
class Commander {
|
||||
constructor() {
|
||||
this.options = {};
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
this.scriptsSet = {};
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
this.addedBuiltinSet = new Set();
|
||||
}
|
||||
/**
|
||||
* Return supported builtin commands
|
||||
*/
|
||||
getBuiltinCommands() {
|
||||
return commands.slice(0);
|
||||
}
|
||||
/**
|
||||
* Create a builtin command
|
||||
*/
|
||||
createBuiltinCommand(commandName) {
|
||||
return {
|
||||
string: generateFunction(null, commandName, "utf8"),
|
||||
buffer: generateFunction(null, commandName, null),
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Create add builtin command
|
||||
*/
|
||||
addBuiltinCommand(commandName) {
|
||||
this.addedBuiltinSet.add(commandName);
|
||||
this[commandName] = generateFunction(commandName, commandName, "utf8");
|
||||
this[commandName + "Buffer"] = generateFunction(commandName + "Buffer", commandName, null);
|
||||
}
|
||||
/**
|
||||
* Define a custom command using lua script
|
||||
*/
|
||||
defineCommand(name, definition) {
|
||||
const script = new Script_1.default(definition.lua, definition.numberOfKeys, this.options.keyPrefix, definition.readOnly);
|
||||
this.scriptsSet[name] = script;
|
||||
this[name] = generateScriptingFunction(name, name, script, "utf8");
|
||||
this[name + "Buffer"] = generateScriptingFunction(name + "Buffer", name, script, null);
|
||||
}
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
sendCommand(command, stream, node) {
|
||||
throw new Error('"sendCommand" is not implemented');
|
||||
}
|
||||
}
|
||||
const commands = commands_1.list.filter((command) => command !== "monitor");
|
||||
commands.push("sentinel");
|
||||
commands.forEach(function (commandName) {
|
||||
Commander.prototype[commandName] = generateFunction(commandName, commandName, "utf8");
|
||||
Commander.prototype[commandName + "Buffer"] = generateFunction(commandName + "Buffer", commandName, null);
|
||||
});
|
||||
Commander.prototype.call = generateFunction("call", "utf8");
|
||||
Commander.prototype.callBuffer = generateFunction("callBuffer", null);
|
||||
// @ts-expect-error
|
||||
Commander.prototype.send_command = Commander.prototype.call;
|
||||
function generateFunction(functionName, _commandName, _encoding) {
|
||||
if (typeof _encoding === "undefined") {
|
||||
_encoding = _commandName;
|
||||
_commandName = null;
|
||||
}
|
||||
return function (...args) {
|
||||
const commandName = (_commandName || args.shift());
|
||||
let callback = args[args.length - 1];
|
||||
if (typeof callback === "function") {
|
||||
args.pop();
|
||||
}
|
||||
else {
|
||||
callback = undefined;
|
||||
}
|
||||
const options = {
|
||||
errorStack: this.options.showFriendlyErrorStack ? new Error() : undefined,
|
||||
keyPrefix: this.options.keyPrefix,
|
||||
replyEncoding: _encoding,
|
||||
};
|
||||
// No auto pipeline, use regular command sending
|
||||
if (!(0, autoPipelining_1.shouldUseAutoPipelining)(this, functionName, commandName)) {
|
||||
return this.sendCommand(
|
||||
// @ts-expect-error
|
||||
new Command_1.default(commandName, args, options, callback));
|
||||
}
|
||||
// Create a new pipeline and make sure it's scheduled
|
||||
return (0, autoPipelining_1.executeWithAutoPipelining)(this, functionName, commandName,
|
||||
// @ts-expect-error
|
||||
args, callback);
|
||||
};
|
||||
}
|
||||
function generateScriptingFunction(functionName, commandName, script, encoding) {
|
||||
return function (...args) {
|
||||
const callback = typeof args[args.length - 1] === "function" ? args.pop() : undefined;
|
||||
const options = {
|
||||
replyEncoding: encoding,
|
||||
};
|
||||
if (this.options.showFriendlyErrorStack) {
|
||||
options.errorStack = new Error();
|
||||
}
|
||||
// No auto pipeline, use regular command sending
|
||||
if (!(0, autoPipelining_1.shouldUseAutoPipelining)(this, functionName, commandName)) {
|
||||
return script.execute(this, args, options, callback);
|
||||
}
|
||||
// Create a new pipeline and make sure it's scheduled
|
||||
return (0, autoPipelining_1.executeWithAutoPipelining)(this, functionName, commandName, args, callback);
|
||||
};
|
||||
}
|
||||
exports.default = Commander;
|
||||
5545
node_modules/ioredis/built/utils/RedisCommander.d.ts
generated
vendored
Normal file
5545
node_modules/ioredis/built/utils/RedisCommander.d.ts
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
7
node_modules/ioredis/built/utils/RedisCommander.js
generated
vendored
Normal file
7
node_modules/ioredis/built/utils/RedisCommander.js
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
"use strict";
|
||||
/**
|
||||
* This file is generated by @ioredis/interface-generator.
|
||||
* Don't edit it manually. Instead, run `npm run generate` to update
|
||||
* this file.
|
||||
*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
3
node_modules/ioredis/built/utils/applyMixin.d.ts
generated
vendored
Normal file
3
node_modules/ioredis/built/utils/applyMixin.d.ts
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
type Constructor = new (...args: any[]) => void;
|
||||
declare function applyMixin(derivedConstructor: Constructor, mixinConstructor: Constructor): void;
|
||||
export default applyMixin;
|
||||
8
node_modules/ioredis/built/utils/applyMixin.js
generated
vendored
Normal file
8
node_modules/ioredis/built/utils/applyMixin.js
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
function applyMixin(derivedConstructor, mixinConstructor) {
|
||||
Object.getOwnPropertyNames(mixinConstructor.prototype).forEach((name) => {
|
||||
Object.defineProperty(derivedConstructor.prototype, name, Object.getOwnPropertyDescriptor(mixinConstructor.prototype, name));
|
||||
});
|
||||
}
|
||||
exports.default = applyMixin;
|
||||
14
node_modules/ioredis/built/utils/argumentParsers.d.ts
generated
vendored
Normal file
14
node_modules/ioredis/built/utils/argumentParsers.d.ts
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
import { CommandParameter } from "../types";
|
||||
/**
|
||||
* Parses a command parameter as seconds and converts to milliseconds.
|
||||
* @param arg - The command parameter representing seconds
|
||||
* @returns The value in milliseconds, 0 if value is <= 0, or undefined if parsing fails
|
||||
*/
|
||||
export declare const parseSecondsArgument: (arg: CommandParameter | undefined) => number | undefined;
|
||||
/**
|
||||
* Parses the BLOCK option from Redis command arguments (e.g., XREAD, XREADGROUP).
|
||||
* @param args - Array of command parameters to search for the BLOCK option
|
||||
* @returns The block duration in milliseconds, 0 if duration is <= 0,
|
||||
* null if BLOCK option is not found, or undefined if BLOCK is found but duration is invalid
|
||||
*/
|
||||
export declare const parseBlockOption: (args: CommandParameter[]) => number | null | undefined;
|
||||
74
node_modules/ioredis/built/utils/argumentParsers.js
generated
vendored
Normal file
74
node_modules/ioredis/built/utils/argumentParsers.js
generated
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.parseBlockOption = exports.parseSecondsArgument = void 0;
|
||||
/**
|
||||
* Parses a command parameter to a number.
|
||||
* @param arg - The command parameter to parse (number, string, or Buffer)
|
||||
* @returns The parsed number, or undefined if parsing fails or arg is undefined
|
||||
*/
|
||||
const parseNumberArgument = (arg) => {
|
||||
if (typeof arg === "number") {
|
||||
return arg;
|
||||
}
|
||||
if (Buffer.isBuffer(arg)) {
|
||||
return parseNumberArgument(arg.toString());
|
||||
}
|
||||
if (typeof arg === "string") {
|
||||
const value = Number(arg);
|
||||
return Number.isFinite(value) ? value : undefined;
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
/**
|
||||
* Parses a command parameter to a string.
|
||||
* @param arg - The command parameter to parse (string or Buffer)
|
||||
* @returns The parsed string, or undefined if arg is not a string/Buffer or is undefined
|
||||
*/
|
||||
const parseStringArgument = (arg) => {
|
||||
if (typeof arg === "string") {
|
||||
return arg;
|
||||
}
|
||||
if (Buffer.isBuffer(arg)) {
|
||||
return arg.toString();
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
/**
|
||||
* Parses a command parameter as seconds and converts to milliseconds.
|
||||
* @param arg - The command parameter representing seconds
|
||||
* @returns The value in milliseconds, 0 if value is <= 0, or undefined if parsing fails
|
||||
*/
|
||||
const parseSecondsArgument = (arg) => {
|
||||
const value = parseNumberArgument(arg);
|
||||
if (value === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
if (value <= 0) {
|
||||
return 0;
|
||||
}
|
||||
return value * 1000;
|
||||
};
|
||||
exports.parseSecondsArgument = parseSecondsArgument;
|
||||
/**
|
||||
* Parses the BLOCK option from Redis command arguments (e.g., XREAD, XREADGROUP).
|
||||
* @param args - Array of command parameters to search for the BLOCK option
|
||||
* @returns The block duration in milliseconds, 0 if duration is <= 0,
|
||||
* null if BLOCK option is not found, or undefined if BLOCK is found but duration is invalid
|
||||
*/
|
||||
const parseBlockOption = (args) => {
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
const token = parseStringArgument(args[i]);
|
||||
if (token && token.toLowerCase() === "block") {
|
||||
const duration = parseNumberArgument(args[i + 1]);
|
||||
if (duration === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
if (duration <= 0) {
|
||||
return 0;
|
||||
}
|
||||
return duration;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
exports.parseBlockOption = parseBlockOption;
|
||||
16
node_modules/ioredis/built/utils/debug.d.ts
generated
vendored
Normal file
16
node_modules/ioredis/built/utils/debug.d.ts
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
declare const MAX_ARGUMENT_LENGTH = 200;
|
||||
/**
|
||||
* helper function that tried to get a string value for
|
||||
* arbitrary "debug" arg
|
||||
*/
|
||||
declare function getStringValue(v: any): string | void;
|
||||
/**
|
||||
* helper function that redacts a string representation of a "debug" arg
|
||||
*/
|
||||
declare function genRedactedString(str: string, maxLen: number): string;
|
||||
/**
|
||||
* a wrapper for the `debug` module, used to generate
|
||||
* "debug functions" that trim the values in their output
|
||||
*/
|
||||
export default function genDebugFunction(namespace: string): (...args: any[]) => void;
|
||||
export { MAX_ARGUMENT_LENGTH, getStringValue, genRedactedString };
|
||||
95
node_modules/ioredis/built/utils/debug.js
generated
vendored
Normal file
95
node_modules/ioredis/built/utils/debug.js
generated
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.genRedactedString = exports.getStringValue = exports.MAX_ARGUMENT_LENGTH = void 0;
|
||||
const debug_1 = require("debug");
|
||||
const MAX_ARGUMENT_LENGTH = 200;
|
||||
exports.MAX_ARGUMENT_LENGTH = MAX_ARGUMENT_LENGTH;
|
||||
const NAMESPACE_PREFIX = "ioredis";
|
||||
/**
|
||||
* helper function that tried to get a string value for
|
||||
* arbitrary "debug" arg
|
||||
*/
|
||||
function getStringValue(v) {
|
||||
if (v === null) {
|
||||
return;
|
||||
}
|
||||
switch (typeof v) {
|
||||
case "boolean":
|
||||
return;
|
||||
case "number":
|
||||
return;
|
||||
case "object":
|
||||
if (Buffer.isBuffer(v)) {
|
||||
return v.toString("hex");
|
||||
}
|
||||
if (Array.isArray(v)) {
|
||||
return v.join(",");
|
||||
}
|
||||
try {
|
||||
return JSON.stringify(v);
|
||||
}
|
||||
catch (e) {
|
||||
return;
|
||||
}
|
||||
case "string":
|
||||
return v;
|
||||
}
|
||||
}
|
||||
exports.getStringValue = getStringValue;
|
||||
/**
|
||||
* helper function that redacts a string representation of a "debug" arg
|
||||
*/
|
||||
function genRedactedString(str, maxLen) {
|
||||
const { length } = str;
|
||||
return length <= maxLen
|
||||
? str
|
||||
: str.slice(0, maxLen) + ' ... <REDACTED full-length="' + length + '">';
|
||||
}
|
||||
exports.genRedactedString = genRedactedString;
|
||||
/**
|
||||
* a wrapper for the `debug` module, used to generate
|
||||
* "debug functions" that trim the values in their output
|
||||
*/
|
||||
function genDebugFunction(namespace) {
|
||||
const fn = (0, debug_1.default)(`${NAMESPACE_PREFIX}:${namespace}`);
|
||||
function wrappedDebug(...args) {
|
||||
if (!fn.enabled) {
|
||||
return; // no-op
|
||||
}
|
||||
// we skip the first arg because that is the message
|
||||
for (let i = 1; i < args.length; i++) {
|
||||
const str = getStringValue(args[i]);
|
||||
if (typeof str === "string" && str.length > MAX_ARGUMENT_LENGTH) {
|
||||
args[i] = genRedactedString(str, MAX_ARGUMENT_LENGTH);
|
||||
}
|
||||
}
|
||||
return fn.apply(null, args);
|
||||
}
|
||||
Object.defineProperties(wrappedDebug, {
|
||||
namespace: {
|
||||
get() {
|
||||
return fn.namespace;
|
||||
},
|
||||
},
|
||||
enabled: {
|
||||
get() {
|
||||
return fn.enabled;
|
||||
},
|
||||
},
|
||||
destroy: {
|
||||
get() {
|
||||
return fn.destroy;
|
||||
},
|
||||
},
|
||||
log: {
|
||||
get() {
|
||||
return fn.log;
|
||||
},
|
||||
set(l) {
|
||||
fn.log = l;
|
||||
},
|
||||
},
|
||||
});
|
||||
return wrappedDebug;
|
||||
}
|
||||
exports.default = genDebugFunction;
|
||||
6
node_modules/ioredis/built/utils/defaults.d.ts
generated
vendored
Normal file
6
node_modules/ioredis/built/utils/defaults.d.ts
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
export declare function defaults<T, S>(object: T, source: S): NonNullable<S & T>;
|
||||
export declare function defaults<T, S1, S2>(object: T, source1: S1, source2: S2): NonNullable<S2 & S1 & T>;
|
||||
export declare function defaults<T, S1, S2, S3>(object: T, source1: S1, source2: S2, source3: S3): NonNullable<S3 & S2 & S1 & T>;
|
||||
export declare function defaults<T, S1, S2, S3, S4>(object: T, source1: S1, source2: S2, source3: S3, source4: S4): NonNullable<S4 & S3 & S2 & S1 & T>;
|
||||
export declare function defaults<T>(object: T): NonNullable<T>;
|
||||
export declare function defaults(object: any, ...sources: any[]): any;
|
||||
102
node_modules/ioredis/built/utils/defaults.js
generated
vendored
Normal file
102
node_modules/ioredis/built/utils/defaults.js
generated
vendored
Normal file
@@ -0,0 +1,102 @@
|
||||
"use strict";
|
||||
/*
|
||||
* Derived from `src/compat/object/defaults.ts` in es-toolkit
|
||||
* (https://github.com/toss/es-toolkit).
|
||||
*
|
||||
* Copyright (c) 2024 Viva Republica, Inc
|
||||
* Copyright OpenJS Foundation and other contributors
|
||||
*
|
||||
* Parts of the compatibility layer in `es-toolkit/compat` are derived from
|
||||
* Lodash (https://github.com/lodash/lodash) by the OpenJS Foundation
|
||||
* (https://openjsf.org/) and Underscore.js by Jeremy Ashkenas, DocumentCloud
|
||||
* and Investigative Reporters & Editors (http://underscorejs.org/).
|
||||
*
|
||||
* This file has been modified from the original source to create a standalone
|
||||
* adaptation.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.defaults = void 0;
|
||||
const IS_UNSIGNED_INTEGER = /^(?:0|[1-9]\d*)$/;
|
||||
function isNil(value) {
|
||||
return value == null;
|
||||
}
|
||||
function eq(value, other) {
|
||||
return value === other || (Number.isNaN(value) && Number.isNaN(other));
|
||||
}
|
||||
function isLength(value) {
|
||||
return Number.isSafeInteger(value) && value >= 0;
|
||||
}
|
||||
function isArrayLike(value) {
|
||||
return (value != null &&
|
||||
typeof value !== "function" &&
|
||||
isLength(value.length));
|
||||
}
|
||||
function isObject(value) {
|
||||
return (value !== null && (typeof value === "object" || typeof value === "function"));
|
||||
}
|
||||
function isIndex(value, length = Number.MAX_SAFE_INTEGER) {
|
||||
switch (typeof value) {
|
||||
case "number":
|
||||
return Number.isInteger(value) && value >= 0 && value < length;
|
||||
case "symbol":
|
||||
return false;
|
||||
case "string":
|
||||
return IS_UNSIGNED_INTEGER.test(value);
|
||||
}
|
||||
}
|
||||
function isIterateeCall(value, index, object) {
|
||||
if (!isObject(object)) {
|
||||
return false;
|
||||
}
|
||||
if ((typeof index === "number" &&
|
||||
isArrayLike(object) &&
|
||||
isIndex(index) &&
|
||||
index < object.length) ||
|
||||
(typeof index === "string" && index in object)) {
|
||||
return eq(object[index], value);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function defaults(object, ...sources) {
|
||||
object = Object(object);
|
||||
const objectProto = Object.prototype;
|
||||
let length = sources.length;
|
||||
const guard = length > 2 ? sources[2] : undefined;
|
||||
if (guard && isIterateeCall(sources[0], sources[1], guard)) {
|
||||
length = 1;
|
||||
}
|
||||
for (let i = 0; i < length; i++) {
|
||||
if (isNil(sources[i])) {
|
||||
continue;
|
||||
}
|
||||
const source = sources[i];
|
||||
for (const key in source) {
|
||||
const value = object[key];
|
||||
if (value === undefined ||
|
||||
(!objectProto.hasOwnProperty.call(object, key) &&
|
||||
eq(value, objectProto[key]))) {
|
||||
object[key] = source[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
return object;
|
||||
}
|
||||
exports.defaults = defaults;
|
||||
125
node_modules/ioredis/built/utils/index.d.ts
generated
vendored
Normal file
125
node_modules/ioredis/built/utils/index.d.ts
generated
vendored
Normal file
@@ -0,0 +1,125 @@
|
||||
/// <reference types="node" />
|
||||
import { defaults, isArguments, noop } from "./lodash";
|
||||
import { Callback, ProtocolVersion } from "../types";
|
||||
import Debug from "./debug";
|
||||
/**
|
||||
* Convert a buffer to string, supports buffer array and plain objects
|
||||
* (RESP3 map replies decoded with `replyMapping: "resp3"`)
|
||||
*
|
||||
* @example
|
||||
* ```js
|
||||
* const input = [Buffer.from('foo'), [Buffer.from('bar')]]
|
||||
* const res = convertBufferToString(input, 'utf8')
|
||||
* expect(res).to.eql(['foo', ['bar']])
|
||||
* ```
|
||||
*/
|
||||
export declare function convertBufferToString(value: any, encoding?: BufferEncoding): any;
|
||||
/**
|
||||
* Convert a list of results to node-style
|
||||
*
|
||||
* @example
|
||||
* ```js
|
||||
* 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'])
|
||||
* ```
|
||||
*/
|
||||
export declare function wrapMultiResult(arr: unknown[] | null): unknown[][] | null;
|
||||
/**
|
||||
* Detect if the argument is a int
|
||||
* @example
|
||||
* ```js
|
||||
* > isInt('123')
|
||||
* true
|
||||
* > isInt('123.3')
|
||||
* false
|
||||
* > isInt('1x')
|
||||
* false
|
||||
* > isInt(123)
|
||||
* true
|
||||
* > isInt(true)
|
||||
* false
|
||||
* ```
|
||||
*/
|
||||
export declare function isInt(value: any): value is string;
|
||||
/**
|
||||
* Pack an array to an Object
|
||||
*
|
||||
* @example
|
||||
* ```js
|
||||
* > packObject(['a', 'b', 'c', 'd'])
|
||||
* { a: 'b', c: 'd' }
|
||||
* ```
|
||||
*/
|
||||
export declare function packObject(array: any[]): Record<string, any>;
|
||||
/**
|
||||
* Return a callback with timeout
|
||||
*/
|
||||
export declare function timeout<T>(callback: Callback<T>, timeout: number): Callback<T>;
|
||||
/**
|
||||
* Convert an object to an array
|
||||
* @example
|
||||
* ```js
|
||||
* > convertObjectToArray({ a: '1' })
|
||||
* ['a', '1']
|
||||
* ```
|
||||
*/
|
||||
export declare function convertObjectToArray<T>(obj: Record<string, T>): (string | T)[];
|
||||
/**
|
||||
* Convert a map to an array
|
||||
* @example
|
||||
* ```js
|
||||
* > convertMapToArray(new Map([[1, '2']]))
|
||||
* [1, '2']
|
||||
* ```
|
||||
*/
|
||||
export declare function convertMapToArray<K, V>(map: Map<K, V>): (K | V)[];
|
||||
/**
|
||||
* Convert a non-string arg to a string
|
||||
*/
|
||||
export declare function toArg(arg: any): string;
|
||||
/**
|
||||
* Optimize error stack
|
||||
*
|
||||
* @param error actually error
|
||||
* @param friendlyStack the stack that more meaningful
|
||||
* @param filterPath only show stacks with the specified path
|
||||
*/
|
||||
export declare function optimizeErrorStack(error: Error, friendlyStack: string, filterPath: string): Error;
|
||||
/**
|
||||
* Parse the redis protocol url
|
||||
*/
|
||||
export declare function parseURL(url: string): Record<string, unknown>;
|
||||
interface TLSOptions {
|
||||
port: number;
|
||||
host: string;
|
||||
[key: string]: any;
|
||||
}
|
||||
/**
|
||||
* Resolve TLS profile shortcut in connection options
|
||||
*/
|
||||
export declare function resolveTLSProfile(options: TLSOptions): TLSOptions;
|
||||
/**
|
||||
* Get a random element from `array`
|
||||
*/
|
||||
export declare function sample<T>(array: T[], from?: number): T;
|
||||
/**
|
||||
* Shuffle the array using the Fisher-Yates Shuffle.
|
||||
* This method will mutate the original array.
|
||||
*/
|
||||
export declare function shuffle<T>(array: T[]): T[];
|
||||
/**
|
||||
* Error message for connection being disconnected
|
||||
*/
|
||||
export declare const CONNECTION_CLOSED_ERROR_MSG = "Connection is closed.";
|
||||
/**
|
||||
* Whether a connection is restricted to subscriber commands.
|
||||
* Only RESP2 has a restricted subscriber mode; with RESP3 (HELLO 3),
|
||||
* regular commands remain allowed while subscribed.
|
||||
*/
|
||||
export declare function isResp2SubscriberMode(condition: {
|
||||
subscriber: false | object;
|
||||
protocol: ProtocolVersion;
|
||||
} | null | undefined): boolean;
|
||||
export declare function zipMap<K, V>(keys: K[], values: V[]): Map<K, V>;
|
||||
export { Debug, defaults, isArguments, noop };
|
||||
392
node_modules/ioredis/built/utils/index.js
generated
vendored
Normal file
392
node_modules/ioredis/built/utils/index.js
generated
vendored
Normal file
@@ -0,0 +1,392 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.noop = exports.isArguments = exports.defaults = exports.Debug = exports.zipMap = exports.isResp2SubscriberMode = 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 lodash_1 = require("./lodash");
|
||||
Object.defineProperty(exports, "defaults", { enumerable: true, get: function () { return lodash_1.defaults; } });
|
||||
Object.defineProperty(exports, "isArguments", { enumerable: true, get: function () { return lodash_1.isArguments; } });
|
||||
Object.defineProperty(exports, "noop", { enumerable: true, get: function () { return lodash_1.noop; } });
|
||||
const debug_1 = require("./debug");
|
||||
exports.Debug = debug_1.default;
|
||||
const TLSProfiles_1 = require("../constants/TLSProfiles");
|
||||
/**
|
||||
* Convert a buffer to string, supports buffer array and plain objects
|
||||
* (RESP3 map replies decoded with `replyMapping: "resp3"`)
|
||||
*
|
||||
* @example
|
||||
* ```js
|
||||
* const input = [Buffer.from('foo'), [Buffer.from('bar')]]
|
||||
* const res = convertBufferToString(input, 'utf8')
|
||||
* expect(res).to.eql(['foo', ['bar']])
|
||||
* ```
|
||||
*/
|
||||
function convertBufferToString(value, encoding) {
|
||||
if (value instanceof Buffer) {
|
||||
return value.toString(encoding);
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
const length = value.length;
|
||||
const res = Array(length);
|
||||
for (let i = 0; i < length; ++i) {
|
||||
res[i] =
|
||||
value[i] instanceof Buffer && encoding === "utf8"
|
||||
? value[i].toString()
|
||||
: convertBufferToString(value[i], encoding);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
if (isPlainObject(value)) {
|
||||
const keys = Object.keys(value);
|
||||
const res = {};
|
||||
for (const element of keys) {
|
||||
const item = value[element];
|
||||
const converted = item instanceof Buffer && encoding === "utf8"
|
||||
? item.toString()
|
||||
: convertBufferToString(item, encoding);
|
||||
// Guard "__proto__"/"constructor" via defineProperty, where a bare
|
||||
// assignment would tamper with the object. Issue #1267. Normal keys take
|
||||
// the fast plain-assignment path.
|
||||
if (element === "__proto__" || element === "constructor") {
|
||||
Object.defineProperty(res, element, {
|
||||
value: converted,
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
});
|
||||
}
|
||||
else {
|
||||
res[element] = converted;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
exports.convertBufferToString = convertBufferToString;
|
||||
/**
|
||||
* Matches only the plain objects produced by RESP3 map replies, so other
|
||||
* object types (class instances etc.) pass through reply conversion untouched.
|
||||
*/
|
||||
function isPlainObject(value) {
|
||||
if (value === null || typeof value !== "object") {
|
||||
return false;
|
||||
}
|
||||
const proto = Object.getPrototypeOf(value);
|
||||
return proto === Object.prototype || proto === null;
|
||||
}
|
||||
/**
|
||||
* Convert a list of results to node-style
|
||||
*
|
||||
* @example
|
||||
* ```js
|
||||
* 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'])
|
||||
* ```
|
||||
*/
|
||||
function wrapMultiResult(arr) {
|
||||
// When using WATCH/EXEC transactions, the EXEC will return
|
||||
// a null instead of an array
|
||||
if (!arr) {
|
||||
return null;
|
||||
}
|
||||
const result = [];
|
||||
const length = arr.length;
|
||||
for (let i = 0; i < length; ++i) {
|
||||
const item = arr[i];
|
||||
if (item instanceof Error) {
|
||||
result.push([item]);
|
||||
}
|
||||
else {
|
||||
result.push([null, item]);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
exports.wrapMultiResult = wrapMultiResult;
|
||||
/**
|
||||
* Detect if the argument is a int
|
||||
* @example
|
||||
* ```js
|
||||
* > isInt('123')
|
||||
* true
|
||||
* > isInt('123.3')
|
||||
* false
|
||||
* > isInt('1x')
|
||||
* false
|
||||
* > isInt(123)
|
||||
* true
|
||||
* > isInt(true)
|
||||
* false
|
||||
* ```
|
||||
*/
|
||||
function isInt(value) {
|
||||
const x = parseFloat(value);
|
||||
return !isNaN(value) && (x | 0) === x;
|
||||
}
|
||||
exports.isInt = isInt;
|
||||
/**
|
||||
* Pack an array to an Object
|
||||
*
|
||||
* @example
|
||||
* ```js
|
||||
* > packObject(['a', 'b', 'c', 'd'])
|
||||
* { a: 'b', c: 'd' }
|
||||
* ```
|
||||
*/
|
||||
function packObject(array) {
|
||||
const result = {};
|
||||
const length = array.length;
|
||||
for (let i = 1; i < length; i += 2) {
|
||||
result[array[i - 1]] = array[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
exports.packObject = packObject;
|
||||
/**
|
||||
* Return a callback with timeout
|
||||
*/
|
||||
function timeout(callback, timeout) {
|
||||
let timer = null;
|
||||
const run = function () {
|
||||
if (timer) {
|
||||
clearTimeout(timer);
|
||||
timer = null;
|
||||
callback.apply(this, arguments);
|
||||
}
|
||||
};
|
||||
timer = setTimeout(run, timeout, new Error("timeout"));
|
||||
return run;
|
||||
}
|
||||
exports.timeout = timeout;
|
||||
/**
|
||||
* Convert an object to an array
|
||||
* @example
|
||||
* ```js
|
||||
* > convertObjectToArray({ a: '1' })
|
||||
* ['a', '1']
|
||||
* ```
|
||||
*/
|
||||
function convertObjectToArray(obj) {
|
||||
const result = [];
|
||||
const keys = Object.keys(obj); // Object.entries requires node 7+
|
||||
for (let i = 0, l = keys.length; i < l; i++) {
|
||||
result.push(keys[i], obj[keys[i]]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
exports.convertObjectToArray = convertObjectToArray;
|
||||
/**
|
||||
* Convert a map to an array
|
||||
* @example
|
||||
* ```js
|
||||
* > convertMapToArray(new Map([[1, '2']]))
|
||||
* [1, '2']
|
||||
* ```
|
||||
*/
|
||||
function convertMapToArray(map) {
|
||||
const result = [];
|
||||
let pos = 0;
|
||||
map.forEach(function (value, key) {
|
||||
result[pos] = key;
|
||||
result[pos + 1] = value;
|
||||
pos += 2;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
exports.convertMapToArray = convertMapToArray;
|
||||
/**
|
||||
* Convert a non-string arg to a string
|
||||
*/
|
||||
function toArg(arg) {
|
||||
if (arg === null || typeof arg === "undefined") {
|
||||
return "";
|
||||
}
|
||||
if (typeof arg === "number" &&
|
||||
Number.isInteger(arg) &&
|
||||
!Number.isSafeInteger(arg)) {
|
||||
return BigInt(arg).toString();
|
||||
}
|
||||
return String(arg);
|
||||
}
|
||||
exports.toArg = toArg;
|
||||
/**
|
||||
* Optimize error stack
|
||||
*
|
||||
* @param error actually error
|
||||
* @param friendlyStack the stack that more meaningful
|
||||
* @param filterPath only show stacks with the specified path
|
||||
*/
|
||||
function optimizeErrorStack(error, friendlyStack, filterPath) {
|
||||
const stacks = friendlyStack.split("\n");
|
||||
let lines = "";
|
||||
let i;
|
||||
for (i = 1; i < stacks.length; ++i) {
|
||||
if (stacks[i].indexOf(filterPath) === -1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (let j = i; j < stacks.length; ++j) {
|
||||
lines += "\n" + stacks[j];
|
||||
}
|
||||
if (error.stack) {
|
||||
const pos = error.stack.indexOf("\n");
|
||||
error.stack = error.stack.slice(0, pos) + lines;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
exports.optimizeErrorStack = optimizeErrorStack;
|
||||
/**
|
||||
* Parse the redis protocol url
|
||||
*/
|
||||
function parseURL(url) {
|
||||
if (isInt(url)) {
|
||||
return { port: url };
|
||||
}
|
||||
// Re-assert string type: isInt's type guard (value is string) incorrectly
|
||||
// narrows url to 'never' in the false branch since url is already string.
|
||||
const rawUrl = url;
|
||||
// Determine if the URL has a known protocol (redis:// or rediss://)
|
||||
const hasProtocol = /^rediss?:\/\//i.test(rawUrl);
|
||||
const isProtocolRelative = rawUrl.startsWith("//");
|
||||
// TODO(next major): Consider rejecting protocol-relative Redis URLs early and
|
||||
// requiring explicit redis:// or rediss:// schemes instead. Keep them working
|
||||
// in 5.x for backward compatibility.
|
||||
// Unix socket paths (starting with "/") are not valid URLs — handle separately.
|
||||
// Preserve query params (e.g., /tmp/redis.sock?db=2)
|
||||
if (rawUrl[0] === "/" && !isProtocolRelative) {
|
||||
const qIdx = rawUrl.indexOf("?");
|
||||
const result = {
|
||||
path: qIdx === -1 ? rawUrl : rawUrl.slice(0, qIdx),
|
||||
};
|
||||
if (qIdx !== -1) {
|
||||
const options = {};
|
||||
const params = new URLSearchParams(rawUrl.slice(qIdx + 1));
|
||||
params.forEach((value, key) => {
|
||||
options[key] = parseURLQueryItem(key, value);
|
||||
});
|
||||
(0, lodash_1.defaults)(result, options);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
let parsed;
|
||||
if (hasProtocol) {
|
||||
parsed = new URL(rawUrl);
|
||||
}
|
||||
else if (isProtocolRelative) {
|
||||
parsed = new URL("redis:" + rawUrl);
|
||||
}
|
||||
else {
|
||||
// Prepend a dummy protocol so new URL() can parse "host:port?query" correctly
|
||||
parsed = new URL("redis://" + rawUrl);
|
||||
}
|
||||
// Convert searchParams to a plain object (equivalent to url.parse(url, true).query)
|
||||
const options = {};
|
||||
parsed.searchParams.forEach((value, key) => {
|
||||
options[key] = parseURLQueryItem(key, value);
|
||||
});
|
||||
const result = {};
|
||||
// Extract auth — WHATWG URL percent-encodes special chars, so decode them.
|
||||
// Check both username AND password: redis://:password@host has empty username but valid password.
|
||||
if (parsed.username || parsed.password) {
|
||||
result.username = decodeURIComponent(parsed.username);
|
||||
result.password = decodeURIComponent(parsed.password);
|
||||
}
|
||||
if (parsed.pathname && parsed.pathname !== "/") {
|
||||
if (hasProtocol || isProtocolRelative) {
|
||||
// For redis://, rediss://, and protocol-relative URLs, the path after /
|
||||
// is the db number
|
||||
if (parsed.pathname.length > 1) {
|
||||
result.db = parsed.pathname.slice(1);
|
||||
}
|
||||
}
|
||||
else {
|
||||
result.path = parsed.pathname;
|
||||
}
|
||||
}
|
||||
if (parsed.hostname) {
|
||||
// WHATWG URL keeps brackets around IPv6 addresses (e.g., "[::1]").
|
||||
// Strip them to match the old url.parse() behavior.
|
||||
result.host = parsed.hostname.replace(/^\[|\]$/g, "");
|
||||
}
|
||||
if (parsed.port) {
|
||||
result.port = parsed.port;
|
||||
}
|
||||
(0, lodash_1.defaults)(result, options);
|
||||
return result;
|
||||
}
|
||||
exports.parseURL = parseURL;
|
||||
function parseURLQueryItem(key, value) {
|
||||
if (key === "family") {
|
||||
const intFamily = Number.parseInt(value, 10);
|
||||
if (!Number.isNaN(intFamily)) {
|
||||
return intFamily;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
/**
|
||||
* Resolve TLS profile shortcut in connection options
|
||||
*/
|
||||
function resolveTLSProfile(options) {
|
||||
let tls = options?.tls;
|
||||
if (typeof tls === "string")
|
||||
tls = { profile: tls };
|
||||
const profile = TLSProfiles_1.default[tls?.profile];
|
||||
if (profile) {
|
||||
tls = Object.assign({}, profile, tls);
|
||||
delete tls.profile;
|
||||
options = Object.assign({}, options, { tls });
|
||||
}
|
||||
return options;
|
||||
}
|
||||
exports.resolveTLSProfile = resolveTLSProfile;
|
||||
/**
|
||||
* Get a random element from `array`
|
||||
*/
|
||||
function sample(array, from = 0) {
|
||||
const length = array.length;
|
||||
if (from >= length) {
|
||||
return null;
|
||||
}
|
||||
return array[from + Math.floor(Math.random() * (length - from))];
|
||||
}
|
||||
exports.sample = sample;
|
||||
/**
|
||||
* Shuffle the array using the Fisher-Yates Shuffle.
|
||||
* This method will mutate the original array.
|
||||
*/
|
||||
function shuffle(array) {
|
||||
let counter = array.length;
|
||||
// While there are elements in the array
|
||||
while (counter > 0) {
|
||||
// Pick a random index
|
||||
const index = Math.floor(Math.random() * counter);
|
||||
// Decrease counter by 1
|
||||
counter--;
|
||||
// And swap the last element with it
|
||||
[array[counter], array[index]] = [array[index], array[counter]];
|
||||
}
|
||||
return array;
|
||||
}
|
||||
exports.shuffle = shuffle;
|
||||
/**
|
||||
* Error message for connection being disconnected
|
||||
*/
|
||||
exports.CONNECTION_CLOSED_ERROR_MSG = "Connection is closed.";
|
||||
/**
|
||||
* Whether a connection is restricted to subscriber commands.
|
||||
* Only RESP2 has a restricted subscriber mode; with RESP3 (HELLO 3),
|
||||
* regular commands remain allowed while subscribed.
|
||||
*/
|
||||
function isResp2SubscriberMode(condition) {
|
||||
return Boolean(condition?.subscriber) && condition?.protocol !== 3;
|
||||
}
|
||||
exports.isResp2SubscriberMode = isResp2SubscriberMode;
|
||||
function zipMap(keys, values) {
|
||||
const map = new Map();
|
||||
keys.forEach((key, index) => {
|
||||
map.set(key, values[index]);
|
||||
});
|
||||
return map;
|
||||
}
|
||||
exports.zipMap = zipMap;
|
||||
1
node_modules/ioredis/built/utils/isArguments.d.ts
generated
vendored
Normal file
1
node_modules/ioredis/built/utils/isArguments.d.ts
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
export declare function isArguments(value?: unknown): value is IArguments;
|
||||
48
node_modules/ioredis/built/utils/isArguments.js
generated
vendored
Normal file
48
node_modules/ioredis/built/utils/isArguments.js
generated
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
"use strict";
|
||||
/*
|
||||
* Derived from `src/compat/predicate/isArguments.ts` in es-toolkit
|
||||
* (https://github.com/toss/es-toolkit).
|
||||
*
|
||||
* Copyright (c) 2024 Viva Republica, Inc
|
||||
* Copyright OpenJS Foundation and other contributors
|
||||
*
|
||||
* Parts of the compatibility layer in `es-toolkit/compat` are derived from
|
||||
* Lodash (https://github.com/lodash/lodash) by the OpenJS Foundation
|
||||
* (https://openjsf.org/) and Underscore.js by Jeremy Ashkenas, DocumentCloud
|
||||
* and Investigative Reporters & Editors (http://underscorejs.org/).
|
||||
*
|
||||
* This file has been modified from the original source to create a standalone
|
||||
* adaptation.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.isArguments = void 0;
|
||||
function getTag(value) {
|
||||
if (value == null) {
|
||||
return value === undefined ? "[object Undefined]" : "[object Null]";
|
||||
}
|
||||
return Object.prototype.toString.call(value);
|
||||
}
|
||||
function isArguments(value) {
|
||||
return (value !== null &&
|
||||
typeof value === "object" &&
|
||||
getTag(value) === "[object Arguments]");
|
||||
}
|
||||
exports.isArguments = isArguments;
|
||||
3
node_modules/ioredis/built/utils/lodash.d.ts
generated
vendored
Normal file
3
node_modules/ioredis/built/utils/lodash.d.ts
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
export declare function noop(): void;
|
||||
export * from "./defaults";
|
||||
export * from "./isArguments";
|
||||
21
node_modules/ioredis/built/utils/lodash.js
generated
vendored
Normal file
21
node_modules/ioredis/built/utils/lodash.js
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
||||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.noop = void 0;
|
||||
function noop() { }
|
||||
exports.noop = noop;
|
||||
__exportStar(require("./defaults"), exports);
|
||||
__exportStar(require("./isArguments"), exports);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user