Review funkt

This commit is contained in:
2026-08-09 21:56:00 +00:00
parent f522ae9953
commit fe9dfa4519
11366 changed files with 2335077 additions and 97 deletions

179
node_modules/ioredis/built/Command.d.ts generated vendored Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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,
};

View 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
View 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;

View 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;
}

View 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
View 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
View 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
View 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
View 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;

View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

27
node_modules/ioredis/built/cluster/util.d.ts generated vendored Normal file
View 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
View 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;

View 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>;
}

View 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;

View File

@@ -0,0 +1,5 @@
import AbstractConnector from "./AbstractConnector";
interface ConnectorConstructor {
new (options: unknown): AbstractConnector;
}
export default ConnectorConstructor;

View File

@@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

View 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;
}

View 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;

View 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;
}

View 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;

View 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;
}

View 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() { }

View 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;
}

View File

@@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

View 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 {};

View 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
View 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
View 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;

File diff suppressed because one or more lines are too long

149
node_modules/ioredis/built/constants/TLSProfiles.js generated vendored Normal file
View 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;

View 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;
}

View 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;

View File

@@ -0,0 +1,5 @@
import { AbortError } from "redis-errors";
export default class MaxRetriesPerRequestError extends AbortError {
constructor(maxRetriesPerRequest: number);
get name(): string;
}

View 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
View File

@@ -0,0 +1,2 @@
import MaxRetriesPerRequestError from "./MaxRetriesPerRequestError";
export { MaxRetriesPerRequestError };

5
node_modules/ioredis/built/errors/index.js generated vendored Normal file
View 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;

View 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 {};

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

7
node_modules/ioredis/built/utils/RedisCommander.js generated vendored Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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