-first commit

This commit is contained in:
2025-11-11 12:36:06 +07:00
commit b99c214434
5683 changed files with 713336 additions and 0 deletions

3
node_modules/@redis/client/README.md generated vendored Normal file
View File

@@ -0,0 +1,3 @@
# @redis/client
The source code and documentation for this package are in the main [node-redis](https://github.com/redis/node-redis) repo.

22
node_modules/@redis/client/dist/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,22 @@
export { RedisArgument, RedisFunctions, RedisModules, RedisScripts, RespVersions, TypeMapping, } from './lib/RESP/types';
export { RESP_TYPES } from './lib/RESP/decoder';
export { VerbatimString } from './lib/RESP/verbatim-string';
export { defineScript } from './lib/lua-script';
export * from './lib/errors';
import RedisClient, { RedisClientOptions, RedisClientType } from './lib/client';
export { RedisClientOptions, RedisClientType };
export declare const createClient: typeof RedisClient.create;
export { CommandParser } from './lib/client/parser';
import { RedisClientPool, RedisPoolOptions, RedisClientPoolType } from './lib/client/pool';
export { RedisClientPoolType, RedisPoolOptions };
export declare const createClientPool: typeof RedisClientPool.create;
import RedisCluster, { RedisClusterOptions, RedisClusterType } from './lib/cluster';
export { RedisClusterType, RedisClusterOptions };
export declare const createCluster: typeof RedisCluster.create;
import RedisSentinel from './lib/sentinel';
export { RedisSentinelOptions, RedisSentinelType } from './lib/sentinel/types';
export declare const createSentinel: typeof RedisSentinel.create;
export { GEO_REPLY_WITH, GeoReplyWith } from './lib/commands/GEOSEARCH_WITH';
export { SetOptions, CLIENT_KILL_FILTERS, FAILOVER_MODES, CLUSTER_SLOT_STATES, COMMAND_LIST_FILTER_BY, REDIS_FLUSH_MODES } from './lib/commands';
export { BasicClientSideCache, BasicPooledClientSideCache } from './lib/client/cache';
//# sourceMappingURL=index.d.ts.map

1
node_modules/@redis/client/dist/index.d.ts.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,aAAa,EACb,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,WAAW,GACZ,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,cAAc,cAAc,CAAC;AAE7B,OAAO,WAAW,EAAE,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAChF,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,CAAC;AAC/C,eAAO,MAAM,YAAY,2BAAqB,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAC3F,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,CAAC;AACjD,eAAO,MAAM,gBAAgB,+BAAyB,CAAC;AAEvD,OAAO,YAAY,EAAE,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACpF,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,CAAC;AACjD,eAAO,MAAM,aAAa,4BAAsB,CAAC;AAEjD,OAAO,aAAa,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAC/E,eAAO,MAAM,cAAc,6BAAuB,CAAC;AAEnD,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAG7E,OAAO,EAAE,UAAU,EAAE,mBAAmB,EAAE,cAAc,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AAEhJ,OAAO,EAAE,oBAAoB,EAAE,0BAA0B,EAAE,MAAM,oBAAoB,CAAC"}

47
node_modules/@redis/client/dist/index.js generated vendored Normal file
View File

@@ -0,0 +1,47 @@
"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);
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.BasicPooledClientSideCache = exports.BasicClientSideCache = exports.REDIS_FLUSH_MODES = exports.COMMAND_LIST_FILTER_BY = exports.CLUSTER_SLOT_STATES = exports.FAILOVER_MODES = exports.CLIENT_KILL_FILTERS = exports.GEO_REPLY_WITH = exports.createSentinel = exports.createCluster = exports.createClientPool = exports.createClient = exports.defineScript = exports.VerbatimString = exports.RESP_TYPES = void 0;
var decoder_1 = require("./lib/RESP/decoder");
Object.defineProperty(exports, "RESP_TYPES", { enumerable: true, get: function () { return decoder_1.RESP_TYPES; } });
var verbatim_string_1 = require("./lib/RESP/verbatim-string");
Object.defineProperty(exports, "VerbatimString", { enumerable: true, get: function () { return verbatim_string_1.VerbatimString; } });
var lua_script_1 = require("./lib/lua-script");
Object.defineProperty(exports, "defineScript", { enumerable: true, get: function () { return lua_script_1.defineScript; } });
__exportStar(require("./lib/errors"), exports);
const client_1 = __importDefault(require("./lib/client"));
exports.createClient = client_1.default.create;
const pool_1 = require("./lib/client/pool");
exports.createClientPool = pool_1.RedisClientPool.create;
const cluster_1 = __importDefault(require("./lib/cluster"));
exports.createCluster = cluster_1.default.create;
const sentinel_1 = __importDefault(require("./lib/sentinel"));
exports.createSentinel = sentinel_1.default.create;
var GEOSEARCH_WITH_1 = require("./lib/commands/GEOSEARCH_WITH");
Object.defineProperty(exports, "GEO_REPLY_WITH", { enumerable: true, get: function () { return GEOSEARCH_WITH_1.GEO_REPLY_WITH; } });
var commands_1 = require("./lib/commands");
Object.defineProperty(exports, "CLIENT_KILL_FILTERS", { enumerable: true, get: function () { return commands_1.CLIENT_KILL_FILTERS; } });
Object.defineProperty(exports, "FAILOVER_MODES", { enumerable: true, get: function () { return commands_1.FAILOVER_MODES; } });
Object.defineProperty(exports, "CLUSTER_SLOT_STATES", { enumerable: true, get: function () { return commands_1.CLUSTER_SLOT_STATES; } });
Object.defineProperty(exports, "COMMAND_LIST_FILTER_BY", { enumerable: true, get: function () { return commands_1.COMMAND_LIST_FILTER_BY; } });
Object.defineProperty(exports, "REDIS_FLUSH_MODES", { enumerable: true, get: function () { return commands_1.REDIS_FLUSH_MODES; } });
var cache_1 = require("./lib/client/cache");
Object.defineProperty(exports, "BasicClientSideCache", { enumerable: true, get: function () { return cache_1.BasicClientSideCache; } });
Object.defineProperty(exports, "BasicPooledClientSideCache", { enumerable: true, get: function () { return cache_1.BasicPooledClientSideCache; } });
//# sourceMappingURL=index.js.map

1
node_modules/@redis/client/dist/index.js.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;AASA,8CAAgD;AAAvC,qGAAA,UAAU,OAAA;AACnB,8DAA4D;AAAnD,iHAAA,cAAc,OAAA;AACvB,+CAAgD;AAAvC,0GAAA,YAAY,OAAA;AACrB,+CAA6B;AAE7B,0DAAgF;AAEnE,QAAA,YAAY,GAAG,gBAAW,CAAC,MAAM,CAAC;AAG/C,4CAA2F;AAE9E,QAAA,gBAAgB,GAAG,sBAAe,CAAC,MAAM,CAAC;AAEvD,4DAAoF;AAEvE,QAAA,aAAa,GAAG,iBAAY,CAAC,MAAM,CAAC;AAEjD,8DAA2C;AAE9B,QAAA,cAAc,GAAG,kBAAa,CAAC,MAAM,CAAC;AAEnD,gEAA6E;AAApE,gHAAA,cAAc,OAAA;AAGvB,2CAAgJ;AAA3H,+GAAA,mBAAmB,OAAA;AAAE,0GAAA,cAAc,OAAA;AAAE,+GAAA,mBAAmB,OAAA;AAAE,kHAAA,sBAAsB,OAAA;AAAE,6GAAA,iBAAiB,OAAA;AAExH,4CAAsF;AAA7E,6GAAA,oBAAoB,OAAA;AAAE,mHAAA,0BAA0B,OAAA"}

40
node_modules/@redis/client/dist/lib/RESP/decoder.d.ts generated vendored Normal file
View File

@@ -0,0 +1,40 @@
/// <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: (reply: any) => unknown;
onErrorReply: (err: ErrorReply) => unknown;
onPush: (push: any[]) => unknown;
getTypeMapping: () => TypeMapping;
constructor(config: DecoderOptions);
reset(): void;
write(chunk: any): void;
}
export {};
//# sourceMappingURL=decoder.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"decoder.d.ts","sourceRoot":"","sources":["../../../lib/RESP/decoder.ts"],"names":[],"mappings":";AAEA,OAAO,EAA0B,UAAU,EAAE,MAAM,WAAW,CAAC;AAC/D,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAGtC,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;CAeb,CAAC;AAeX,eAAO,MAAM,iBAAiB;;CAE7B,CAAC;AAIF,UAAU,cAAc;IACtB,OAAO,CAAC,KAAK,EAAE,GAAG,GAAG,OAAO,CAAC;IAC7B,YAAY,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC;IACvC,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC;IAClC,cAAc,IAAI,WAAW,CAAC;CAC/B;AAED,qBAAa,OAAO;;IAClB,OAAO,0BAAC;IACR,YAAY,+BAAC;IACb,MAAM,2BAAC;IACP,cAAc,oBAAC;gBAIH,MAAM,EAAE,cAAc;IAOlC,KAAK;IAKL,KAAK,CAAC,KAAK,KAAA;CAklCZ"}

727
node_modules/@redis/client/dist/lib/RESP/decoder.js generated vendored Normal file
View File

@@ -0,0 +1,727 @@
"use strict";
var _a;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Decoder = exports.PUSH_TYPE_MAPPING = exports.RESP_TYPES = void 0;
// @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 {
onReply;
onErrorReply;
onPush;
getTypeMapping;
#cursor = 0;
#next;
constructor(config) {
this.onReply = config.onReply;
this.onErrorReply = config.onErrorReply;
this.onPush = config.onPush;
this.getTypeMapping = config.getTypeMapping;
}
reset() {
this.#cursor = 0;
this.#next = undefined;
}
write(chunk) {
if (this.#cursor >= chunk.length) {
this.#cursor -= chunk.length;
return;
}
if (this.#next) {
if (this.#next(chunk) || this.#cursor >= chunk.length) {
this.#cursor -= chunk.length;
return;
}
}
do {
const type = chunk[this.#cursor];
if (++this.#cursor === chunk.length) {
this.#next = this.#continueDecodeTypeValue.bind(this, type);
break;
}
if (this.#decodeTypeValue(type, chunk)) {
break;
}
} while (this.#cursor < chunk.length);
this.#cursor -= chunk.length;
}
#continueDecodeTypeValue(type, chunk) {
this.#next = undefined;
return this.#decodeTypeValue(type, chunk);
}
#decodeTypeValue(type, chunk) {
switch (type) {
case exports.RESP_TYPES.NULL:
this.onReply(this.#decodeNull());
return false;
case exports.RESP_TYPES.BOOLEAN:
return this.#handleDecodedValue(this.onReply, this.#decodeBoolean(chunk));
case exports.RESP_TYPES.NUMBER:
return this.#handleDecodedValue(this.onReply, this.#decodeNumber(this.getTypeMapping()[exports.RESP_TYPES.NUMBER], chunk));
case exports.RESP_TYPES.BIG_NUMBER:
return this.#handleDecodedValue(this.onReply, this.#decodeBigNumber(this.getTypeMapping()[exports.RESP_TYPES.BIG_NUMBER], chunk));
case exports.RESP_TYPES.DOUBLE:
return this.#handleDecodedValue(this.onReply, this.#decodeDouble(this.getTypeMapping()[exports.RESP_TYPES.DOUBLE], chunk));
case exports.RESP_TYPES.SIMPLE_STRING:
return this.#handleDecodedValue(this.onReply, this.#decodeSimpleString(this.getTypeMapping()[exports.RESP_TYPES.SIMPLE_STRING], chunk));
case exports.RESP_TYPES.BLOB_STRING:
return this.#handleDecodedValue(this.onReply, this.#decodeBlobString(this.getTypeMapping()[exports.RESP_TYPES.BLOB_STRING], chunk));
case exports.RESP_TYPES.VERBATIM_STRING:
return this.#handleDecodedValue(this.onReply, this.#decodeVerbatimString(this.getTypeMapping()[exports.RESP_TYPES.VERBATIM_STRING], chunk));
case exports.RESP_TYPES.SIMPLE_ERROR:
return this.#handleDecodedValue(this.onErrorReply, this.#decodeSimpleError(chunk));
case exports.RESP_TYPES.BLOB_ERROR:
return this.#handleDecodedValue(this.onErrorReply, this.#decodeBlobError(chunk));
case exports.RESP_TYPES.ARRAY:
return this.#handleDecodedValue(this.onReply, this.#decodeArray(this.getTypeMapping(), chunk));
case exports.RESP_TYPES.SET:
return this.#handleDecodedValue(this.onReply, this.#decodeSet(this.getTypeMapping(), chunk));
case exports.RESP_TYPES.MAP:
return this.#handleDecodedValue(this.onReply, this.#decodeMap(this.getTypeMapping(), chunk));
case exports.RESP_TYPES.PUSH:
return this.#handleDecodedValue(this.onPush, this.#decodeArray(exports.PUSH_TYPE_MAPPING, chunk));
default:
throw new Error(`Unknown RESP type ${type} "${String.fromCharCode(type)}"`);
}
}
#handleDecodedValue(cb, value) {
if (typeof value === 'function') {
this.#next = this.#continueDecodeValue.bind(this, cb, value);
return true;
}
cb(value);
return false;
}
#continueDecodeValue(cb, next, chunk) {
this.#next = undefined;
return this.#handleDecodedValue(cb, next(chunk));
}
#decodeNull() {
this.#cursor += 2; // skip \r\n
return null;
}
#decodeBoolean(chunk) {
const boolean = chunk[this.#cursor] === ASCII.t;
this.#cursor += 3; // skip {t | f}\r\n
return boolean;
}
#decodeNumber(type, chunk) {
if (type === String) {
return this.#decodeSimpleString(String, chunk);
}
switch (chunk[this.#cursor]) {
case ASCII['+']:
return this.#maybeDecodeNumberValue(false, chunk);
case ASCII['-']:
return this.#maybeDecodeNumberValue(true, chunk);
default:
return this.#decodeNumberValue(false, this.#decodeUnsingedNumber.bind(this, 0), chunk);
}
}
#maybeDecodeNumberValue(isNegative, chunk) {
const cb = this.#decodeUnsingedNumber.bind(this, 0);
return ++this.#cursor === chunk.length ?
this.#decodeNumberValue.bind(this, isNegative, cb) :
this.#decodeNumberValue(isNegative, cb, chunk);
}
#decodeNumberValue(isNegative, numberCb, chunk) {
const number = numberCb(chunk);
return typeof number === 'function' ?
this.#decodeNumberValue.bind(this, isNegative, number) :
isNegative ? -number : number;
}
#decodeUnsingedNumber(number, chunk) {
let cursor = this.#cursor;
do {
const byte = chunk[cursor];
if (byte === ASCII['\r']) {
this.#cursor = cursor + 2; // skip \r\n
return number;
}
number = number * 10 + byte - ASCII['0'];
} while (++cursor < chunk.length);
this.#cursor = cursor;
return this.#decodeUnsingedNumber.bind(this, number);
}
#decodeBigNumber(type, chunk) {
if (type === String) {
return this.#decodeSimpleString(String, chunk);
}
switch (chunk[this.#cursor]) {
case ASCII['+']:
return this.#maybeDecodeBigNumberValue(false, chunk);
case ASCII['-']:
return this.#maybeDecodeBigNumberValue(true, chunk);
default:
return this.#decodeBigNumberValue(false, this.#decodeUnsingedBigNumber.bind(this, 0n), chunk);
}
}
#maybeDecodeBigNumberValue(isNegative, chunk) {
const cb = this.#decodeUnsingedBigNumber.bind(this, 0n);
return ++this.#cursor === chunk.length ?
this.#decodeBigNumberValue.bind(this, isNegative, cb) :
this.#decodeBigNumberValue(isNegative, cb, chunk);
}
#decodeBigNumberValue(isNegative, bigNumberCb, chunk) {
const bigNumber = bigNumberCb(chunk);
return typeof bigNumber === 'function' ?
this.#decodeBigNumberValue.bind(this, isNegative, bigNumber) :
isNegative ? -bigNumber : bigNumber;
}
#decodeUnsingedBigNumber(bigNumber, chunk) {
let cursor = this.#cursor;
do {
const byte = chunk[cursor];
if (byte === ASCII['\r']) {
this.#cursor = cursor + 2; // skip \r\n
return bigNumber;
}
bigNumber = bigNumber * 10n + BigInt(byte - ASCII['0']);
} while (++cursor < chunk.length);
this.#cursor = cursor;
return this.#decodeUnsingedBigNumber.bind(this, bigNumber);
}
#decodeDouble(type, chunk) {
if (type === String) {
return this.#decodeSimpleString(String, chunk);
}
switch (chunk[this.#cursor]) {
case ASCII.n:
this.#cursor += 5; // skip nan\r\n
return NaN;
case ASCII['+']:
return this.#maybeDecodeDoubleInteger(false, chunk);
case ASCII['-']:
return this.#maybeDecodeDoubleInteger(true, chunk);
default:
return this.#decodeDoubleInteger(false, 0, chunk);
}
}
#maybeDecodeDoubleInteger(isNegative, chunk) {
return ++this.#cursor === chunk.length ?
this.#decodeDoubleInteger.bind(this, isNegative, 0) :
this.#decodeDoubleInteger(isNegative, 0, chunk);
}
#decodeDoubleInteger(isNegative, integer, chunk) {
if (chunk[this.#cursor] === ASCII.i) {
this.#cursor += 5; // skip inf\r\n
return isNegative ? -Infinity : Infinity;
}
return this.#continueDecodeDoubleInteger(isNegative, integer, chunk);
}
#continueDecodeDoubleInteger(isNegative, integer, chunk) {
let cursor = this.#cursor;
do {
const byte = chunk[cursor];
switch (byte) {
case ASCII['.']:
this.#cursor = cursor + 1; // skip .
return this.#cursor < chunk.length ?
this.#decodeDoubleDecimal(isNegative, 0, integer, chunk) :
this.#decodeDoubleDecimal.bind(this, isNegative, 0, integer);
case ASCII.E:
case ASCII.e:
this.#cursor = cursor + 1; // skip E/e
const i = isNegative ? -integer : integer;
return this.#cursor < chunk.length ?
this.#decodeDoubleExponent(i, chunk) :
this.#decodeDoubleExponent.bind(this, i);
case ASCII['\r']:
this.#cursor = cursor + 2; // skip \r\n
return isNegative ? -integer : integer;
default:
integer = integer * 10 + byte - ASCII['0'];
}
} while (++cursor < chunk.length);
this.#cursor = cursor;
return this.#continueDecodeDoubleInteger.bind(this, isNegative, integer);
}
// 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
static #DOUBLE_DECIMAL_MULTIPLIERS = [
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
];
#decodeDoubleDecimal(isNegative, decimalIndex, double, chunk) {
let cursor = this.#cursor;
do {
const byte = chunk[cursor];
switch (byte) {
case ASCII.E:
case ASCII.e:
this.#cursor = cursor + 1; // skip E/e
const d = isNegative ? -double : double;
return this.#cursor === chunk.length ?
this.#decodeDoubleExponent.bind(this, d) :
this.#decodeDoubleExponent(d, chunk);
case ASCII['\r']:
this.#cursor = cursor + 2; // skip \r\n
return isNegative ? -double : double;
}
if (decimalIndex < _a.#DOUBLE_DECIMAL_MULTIPLIERS.length) {
double += (byte - ASCII['0']) * _a.#DOUBLE_DECIMAL_MULTIPLIERS[decimalIndex++];
}
} while (++cursor < chunk.length);
this.#cursor = cursor;
return this.#decodeDoubleDecimal.bind(this, isNegative, decimalIndex, double);
}
#decodeDoubleExponent(double, chunk) {
switch (chunk[this.#cursor]) {
case ASCII['+']:
return ++this.#cursor === chunk.length ?
this.#continueDecodeDoubleExponent.bind(this, false, double, 0) :
this.#continueDecodeDoubleExponent(false, double, 0, chunk);
case ASCII['-']:
return ++this.#cursor === chunk.length ?
this.#continueDecodeDoubleExponent.bind(this, true, double, 0) :
this.#continueDecodeDoubleExponent(true, double, 0, chunk);
}
return this.#continueDecodeDoubleExponent(false, double, 0, chunk);
}
#continueDecodeDoubleExponent(isNegative, double, exponent, chunk) {
let cursor = this.#cursor;
do {
const byte = chunk[cursor];
if (byte === ASCII['\r']) {
this.#cursor = cursor + 2; // skip \r\n
return double * 10 ** (isNegative ? -exponent : exponent);
}
exponent = exponent * 10 + byte - ASCII['0'];
} while (++cursor < chunk.length);
this.#cursor = cursor;
return this.#continueDecodeDoubleExponent.bind(this, isNegative, double, exponent);
}
#findCRLF(chunk, cursor) {
while (chunk[cursor] !== ASCII['\r']) {
if (++cursor === chunk.length) {
this.#cursor = chunk.length;
return -1;
}
}
this.#cursor = cursor + 2; // skip \r\n
return cursor;
}
#decodeSimpleString(type, chunk) {
const start = this.#cursor, crlfIndex = this.#findCRLF(chunk, start);
if (crlfIndex === -1) {
return this.#continueDecodeSimpleString.bind(this, [chunk.subarray(start)], type);
}
const slice = chunk.subarray(start, crlfIndex);
return type === Buffer ?
slice :
slice.toString();
}
#continueDecodeSimpleString(chunks, type, chunk) {
const start = this.#cursor, crlfIndex = this.#findCRLF(chunk, start);
if (crlfIndex === -1) {
chunks.push(chunk.subarray(start));
return this.#continueDecodeSimpleString.bind(this, chunks, type);
}
chunks.push(chunk.subarray(start, crlfIndex));
const buffer = Buffer.concat(chunks);
return type === Buffer ? buffer : buffer.toString();
}
#decodeBlobString(type, chunk) {
// RESP 2 bulk string null
// https://github.com/redis/redis-specifications/blob/master/protocol/RESP2.md#resp-bulk-strings
if (chunk[this.#cursor] === ASCII['-']) {
this.#cursor += 4; // skip -1\r\n
return null;
}
const length = this.#decodeUnsingedNumber(0, chunk);
if (typeof length === 'function') {
return this.#continueDecodeBlobStringLength.bind(this, length, type);
}
else if (this.#cursor >= chunk.length) {
return this.#decodeBlobStringWithLength.bind(this, length, type);
}
return this.#decodeBlobStringWithLength(length, type, chunk);
}
#continueDecodeBlobStringLength(lengthCb, type, chunk) {
const length = lengthCb(chunk);
if (typeof length === 'function') {
return this.#continueDecodeBlobStringLength.bind(this, length, type);
}
else if (this.#cursor >= chunk.length) {
return this.#decodeBlobStringWithLength.bind(this, length, type);
}
return this.#decodeBlobStringWithLength(length, type, chunk);
}
#decodeStringWithLength(length, skip, type, chunk) {
const end = this.#cursor + length;
if (end >= chunk.length) {
const slice = chunk.subarray(this.#cursor);
this.#cursor = chunk.length;
return this.#continueDecodeStringWithLength.bind(this, length - slice.length, [slice], skip, type);
}
const slice = chunk.subarray(this.#cursor, end);
this.#cursor = end + skip;
return type === Buffer ?
slice :
slice.toString();
}
#continueDecodeStringWithLength(length, chunks, skip, type, chunk) {
const end = this.#cursor + length;
if (end >= chunk.length) {
const slice = chunk.subarray(this.#cursor);
chunks.push(slice);
this.#cursor = chunk.length;
return this.#continueDecodeStringWithLength.bind(this, length - slice.length, chunks, skip, type);
}
chunks.push(chunk.subarray(this.#cursor, end));
this.#cursor = end + skip;
const buffer = Buffer.concat(chunks);
return type === Buffer ? buffer : buffer.toString();
}
#decodeBlobStringWithLength(length, type, chunk) {
return this.#decodeStringWithLength(length, 2, type, chunk);
}
#decodeVerbatimString(type, chunk) {
return this.#continueDecodeVerbatimStringLength(this.#decodeUnsingedNumber.bind(this, 0), type, chunk);
}
#continueDecodeVerbatimStringLength(lengthCb, type, chunk) {
const length = lengthCb(chunk);
return typeof length === 'function' ?
this.#continueDecodeVerbatimStringLength.bind(this, length, type) :
this.#decodeVerbatimStringWithLength(length, type, chunk);
}
#decodeVerbatimStringWithLength(length, type, chunk) {
const stringLength = length - 4; // skip <format>:
if (type === verbatim_string_1.VerbatimString) {
return this.#decodeVerbatimStringFormat(stringLength, chunk);
}
this.#cursor += 4; // skip <format>:
return this.#cursor >= chunk.length ?
this.#decodeBlobStringWithLength.bind(this, stringLength, type) :
this.#decodeBlobStringWithLength(stringLength, type, chunk);
}
#decodeVerbatimStringFormat(stringLength, chunk) {
const formatCb = this.#decodeStringWithLength.bind(this, 3, 1, String);
return this.#cursor >= chunk.length ?
this.#continueDecodeVerbatimStringFormat.bind(this, stringLength, formatCb) :
this.#continueDecodeVerbatimStringFormat(stringLength, formatCb, chunk);
}
#continueDecodeVerbatimStringFormat(stringLength, formatCb, chunk) {
const format = formatCb(chunk);
return typeof format === 'function' ?
this.#continueDecodeVerbatimStringFormat.bind(this, stringLength, format) :
this.#decodeVerbatimStringWithFormat(stringLength, format, chunk);
}
#decodeVerbatimStringWithFormat(stringLength, format, chunk) {
return this.#continueDecodeVerbatimStringWithFormat(format, this.#decodeBlobStringWithLength.bind(this, stringLength, String), chunk);
}
#continueDecodeVerbatimStringWithFormat(format, stringCb, chunk) {
const string = stringCb(chunk);
return typeof string === 'function' ?
this.#continueDecodeVerbatimStringWithFormat.bind(this, format, string) :
new verbatim_string_1.VerbatimString(format, string);
}
#decodeSimpleError(chunk) {
const string = this.#decodeSimpleString(String, chunk);
return typeof string === 'function' ?
this.#continueDecodeSimpleError.bind(this, string) :
new errors_1.SimpleError(string);
}
#continueDecodeSimpleError(stringCb, chunk) {
const string = stringCb(chunk);
return typeof string === 'function' ?
this.#continueDecodeSimpleError.bind(this, string) :
new errors_1.SimpleError(string);
}
#decodeBlobError(chunk) {
const string = this.#decodeBlobString(String, chunk);
return typeof string === 'function' ?
this.#continueDecodeBlobError.bind(this, string) :
new errors_1.BlobError(string);
}
#continueDecodeBlobError(stringCb, chunk) {
const string = stringCb(chunk);
return typeof string === 'function' ?
this.#continueDecodeBlobError.bind(this, string) :
new errors_1.BlobError(string);
}
#decodeNestedType(typeMapping, chunk) {
const type = chunk[this.#cursor];
return ++this.#cursor === chunk.length ?
this.#decodeNestedTypeValue.bind(this, type, typeMapping) :
this.#decodeNestedTypeValue(type, typeMapping, chunk);
}
#decodeNestedTypeValue(type, typeMapping, chunk) {
switch (type) {
case exports.RESP_TYPES.NULL:
return this.#decodeNull();
case exports.RESP_TYPES.BOOLEAN:
return this.#decodeBoolean(chunk);
case exports.RESP_TYPES.NUMBER:
return this.#decodeNumber(typeMapping[exports.RESP_TYPES.NUMBER], chunk);
case exports.RESP_TYPES.BIG_NUMBER:
return this.#decodeBigNumber(typeMapping[exports.RESP_TYPES.BIG_NUMBER], chunk);
case exports.RESP_TYPES.DOUBLE:
return this.#decodeDouble(typeMapping[exports.RESP_TYPES.DOUBLE], chunk);
case exports.RESP_TYPES.SIMPLE_STRING:
return this.#decodeSimpleString(typeMapping[exports.RESP_TYPES.SIMPLE_STRING], chunk);
case exports.RESP_TYPES.BLOB_STRING:
return this.#decodeBlobString(typeMapping[exports.RESP_TYPES.BLOB_STRING], chunk);
case exports.RESP_TYPES.VERBATIM_STRING:
return this.#decodeVerbatimString(typeMapping[exports.RESP_TYPES.VERBATIM_STRING], chunk);
case exports.RESP_TYPES.SIMPLE_ERROR:
return this.#decodeSimpleError(chunk);
case exports.RESP_TYPES.BLOB_ERROR:
return this.#decodeBlobError(chunk);
case exports.RESP_TYPES.ARRAY:
return this.#decodeArray(typeMapping, chunk);
case exports.RESP_TYPES.SET:
return this.#decodeSet(typeMapping, chunk);
case exports.RESP_TYPES.MAP:
return this.#decodeMap(typeMapping, chunk);
default:
throw new Error(`Unknown RESP type ${type} "${String.fromCharCode(type)}"`);
}
}
#decodeArray(typeMapping, chunk) {
// RESP 2 null
// https://github.com/redis/redis-specifications/blob/master/protocol/RESP2.md#resp-arrays
if (chunk[this.#cursor] === ASCII['-']) {
this.#cursor += 4; // skip -1\r\n
return null;
}
return this.#decodeArrayWithLength(this.#decodeUnsingedNumber(0, chunk), typeMapping, chunk);
}
#decodeArrayWithLength(length, typeMapping, chunk) {
return typeof length === 'function' ?
this.#continueDecodeArrayLength.bind(this, length, typeMapping) :
this.#decodeArrayItems(new Array(length), 0, typeMapping, chunk);
}
#continueDecodeArrayLength(lengthCb, typeMapping, chunk) {
return this.#decodeArrayWithLength(lengthCb(chunk), typeMapping, chunk);
}
#decodeArrayItems(array, filled, typeMapping, chunk) {
for (let i = filled; i < array.length; i++) {
if (this.#cursor >= chunk.length) {
return this.#decodeArrayItems.bind(this, array, i, typeMapping);
}
const item = this.#decodeNestedType(typeMapping, chunk);
if (typeof item === 'function') {
return this.#continueDecodeArrayItems.bind(this, array, i, item, typeMapping);
}
array[i] = item;
}
return array;
}
#continueDecodeArrayItems(array, filled, itemCb, typeMapping, chunk) {
const item = itemCb(chunk);
if (typeof item === 'function') {
return this.#continueDecodeArrayItems.bind(this, array, filled, item, typeMapping);
}
array[filled++] = item;
return this.#decodeArrayItems(array, filled, typeMapping, chunk);
}
#decodeSet(typeMapping, chunk) {
const length = this.#decodeUnsingedNumber(0, chunk);
if (typeof length === 'function') {
return this.#continueDecodeSetLength.bind(this, length, typeMapping);
}
return this.#decodeSetItems(length, typeMapping, chunk);
}
#continueDecodeSetLength(lengthCb, typeMapping, chunk) {
const length = lengthCb(chunk);
return typeof length === 'function' ?
this.#continueDecodeSetLength.bind(this, length, typeMapping) :
this.#decodeSetItems(length, typeMapping, chunk);
}
#decodeSetItems(length, typeMapping, chunk) {
return typeMapping[exports.RESP_TYPES.SET] === Set ?
this.#decodeSetAsSet(new Set(), length, typeMapping, chunk) :
this.#decodeArrayItems(new Array(length), 0, typeMapping, chunk);
}
#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 (this.#cursor >= chunk.length) {
return this.#decodeSetAsSet.bind(this, set, remaining, typeMapping);
}
const item = this.#decodeNestedType(typeMapping, chunk);
if (typeof item === 'function') {
return this.#continueDecodeSetAsSet.bind(this, set, remaining, item, typeMapping);
}
set.add(item);
--remaining;
}
return set;
}
#continueDecodeSetAsSet(set, remaining, itemCb, typeMapping, chunk) {
const item = itemCb(chunk);
if (typeof item === 'function') {
return this.#continueDecodeSetAsSet.bind(this, set, remaining, item, typeMapping);
}
set.add(item);
return this.#decodeSetAsSet(set, remaining - 1, typeMapping, chunk);
}
#decodeMap(typeMapping, chunk) {
const length = this.#decodeUnsingedNumber(0, chunk);
if (typeof length === 'function') {
return this.#continueDecodeMapLength.bind(this, length, typeMapping);
}
return this.#decodeMapItems(length, typeMapping, chunk);
}
#continueDecodeMapLength(lengthCb, typeMapping, chunk) {
const length = lengthCb(chunk);
return typeof length === 'function' ?
this.#continueDecodeMapLength.bind(this, length, typeMapping) :
this.#decodeMapItems(length, typeMapping, chunk);
}
#decodeMapItems(length, typeMapping, chunk) {
switch (typeMapping[exports.RESP_TYPES.MAP]) {
case Map:
return this.#decodeMapAsMap(new Map(), length, typeMapping, chunk);
case Array:
return this.#decodeArrayItems(new Array(length * 2), 0, typeMapping, chunk);
default:
return this.#decodeMapAsObject(Object.create(null), length, typeMapping, chunk);
}
}
#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 (this.#cursor >= chunk.length) {
return this.#decodeMapAsMap.bind(this, map, remaining, typeMapping);
}
const key = this.#decodeMapKey(typeMapping, chunk);
if (typeof key === 'function') {
return this.#continueDecodeMapKey.bind(this, map, remaining, key, typeMapping);
}
if (this.#cursor >= chunk.length) {
return this.#continueDecodeMapValue.bind(this, map, remaining, key, this.#decodeNestedType.bind(this, typeMapping), typeMapping);
}
const value = this.#decodeNestedType(typeMapping, chunk);
if (typeof value === 'function') {
return this.#continueDecodeMapValue.bind(this, map, remaining, key, value, typeMapping);
}
map.set(key, value);
--remaining;
}
return map;
}
#decodeMapKey(typeMapping, chunk) {
const type = chunk[this.#cursor];
return ++this.#cursor === chunk.length ?
this.#decodeMapKeyValue.bind(this, type, typeMapping) :
this.#decodeMapKeyValue(type, typeMapping, chunk);
}
#decodeMapKeyValue(type, typeMapping, chunk) {
switch (type) {
// decode simple string map key as string (and not as buffer)
case exports.RESP_TYPES.SIMPLE_STRING:
return this.#decodeSimpleString(String, chunk);
// decode blob string map key as string (and not as buffer)
case exports.RESP_TYPES.BLOB_STRING:
return this.#decodeBlobString(String, chunk);
default:
return this.#decodeNestedTypeValue(type, typeMapping, chunk);
}
}
#continueDecodeMapKey(map, remaining, keyCb, typeMapping, chunk) {
const key = keyCb(chunk);
if (typeof key === 'function') {
return this.#continueDecodeMapKey.bind(this, map, remaining, key, typeMapping);
}
if (this.#cursor >= chunk.length) {
return this.#continueDecodeMapValue.bind(this, map, remaining, key, this.#decodeNestedType.bind(this, typeMapping), typeMapping);
}
const value = this.#decodeNestedType(typeMapping, chunk);
if (typeof value === 'function') {
return this.#continueDecodeMapValue.bind(this, map, remaining, key, value, typeMapping);
}
map.set(key, value);
return this.#decodeMapAsMap(map, remaining - 1, typeMapping, chunk);
}
#continueDecodeMapValue(map, remaining, key, valueCb, typeMapping, chunk) {
const value = valueCb(chunk);
if (typeof value === 'function') {
return this.#continueDecodeMapValue.bind(this, map, remaining, key, value, typeMapping);
}
map.set(key, value);
return this.#decodeMapAsMap(map, remaining - 1, typeMapping, chunk);
}
#decodeMapAsObject(object, remaining, typeMapping, chunk) {
while (remaining > 0) {
if (this.#cursor >= chunk.length) {
return this.#decodeMapAsObject.bind(this, object, remaining, typeMapping);
}
const key = this.#decodeMapKey(typeMapping, chunk);
if (typeof key === 'function') {
return this.#continueDecodeMapAsObjectKey.bind(this, object, remaining, key, typeMapping);
}
if (this.#cursor >= chunk.length) {
return this.#continueDecodeMapAsObjectValue.bind(this, object, remaining, key, this.#decodeNestedType.bind(this, typeMapping), typeMapping);
}
const value = this.#decodeNestedType(typeMapping, chunk);
if (typeof value === 'function') {
return this.#continueDecodeMapAsObjectValue.bind(this, object, remaining, key, value, typeMapping);
}
object[key] = value;
--remaining;
}
return object;
}
#continueDecodeMapAsObjectKey(object, remaining, keyCb, typeMapping, chunk) {
const key = keyCb(chunk);
if (typeof key === 'function') {
return this.#continueDecodeMapAsObjectKey.bind(this, object, remaining, key, typeMapping);
}
if (this.#cursor >= chunk.length) {
return this.#continueDecodeMapAsObjectValue.bind(this, object, remaining, key, this.#decodeNestedType.bind(this, typeMapping), typeMapping);
}
const value = this.#decodeNestedType(typeMapping, chunk);
if (typeof value === 'function') {
return this.#continueDecodeMapAsObjectValue.bind(this, object, remaining, key, value, typeMapping);
}
object[key] = value;
return this.#decodeMapAsObject(object, remaining - 1, typeMapping, chunk);
}
#continueDecodeMapAsObjectValue(object, remaining, key, valueCb, typeMapping, chunk) {
const value = valueCb(chunk);
if (typeof value === 'function') {
return this.#continueDecodeMapAsObjectValue.bind(this, object, remaining, key, value, typeMapping);
}
object[key] = value;
return this.#decodeMapAsObject(object, remaining - 1, typeMapping, chunk);
}
}
exports.Decoder = Decoder;
_a = Decoder;
//# sourceMappingURL=decoder.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,3 @@
import { RedisArgument } from './types';
export default function encodeCommand(args: ReadonlyArray<RedisArgument>): ReadonlyArray<RedisArgument>;
//# sourceMappingURL=encoder.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"encoder.d.ts","sourceRoot":"","sources":["../../../lib/RESP/encoder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAIxC,MAAM,CAAC,OAAO,UAAU,aAAa,CAAC,IAAI,EAAE,aAAa,CAAC,aAAa,CAAC,GAAG,aAAa,CAAC,aAAa,CAAC,CAuBtG"}

24
node_modules/@redis/client/dist/lib/RESP/encoder.js generated vendored Normal file
View File

@@ -0,0 +1,24 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const CRLF = '\r\n';
function encodeCommand(args) {
const toWrite = [];
let strings = '*' + args.length + CRLF;
for (let i = 0; i < args.length; i++) {
const arg = args[i];
if (typeof arg === 'string') {
strings += '$' + Buffer.byteLength(arg) + CRLF + arg + CRLF;
}
else if (arg instanceof Buffer) {
toWrite.push(strings + '$' + arg.length.toString() + CRLF, arg);
strings = CRLF;
}
else {
throw new TypeError(`"arguments[${i}]" must be of type "string | Buffer", got ${typeof arg} instead.`);
}
}
toWrite.push(strings);
return toWrite;
}
exports.default = encodeCommand;
//# sourceMappingURL=encoder.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"encoder.js","sourceRoot":"","sources":["../../../lib/RESP/encoder.ts"],"names":[],"mappings":";;AAEA,MAAM,IAAI,GAAG,MAAM,CAAC;AAEpB,SAAwB,aAAa,CAAC,IAAkC;IACtE,MAAM,OAAO,GAAyB,EAAE,CAAC;IAEzC,IAAI,OAAO,GAAG,GAAG,GAAG,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;IAEvC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QACpB,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;YAC5B,OAAO,IAAI,GAAG,GAAG,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,GAAG,GAAG,IAAI,CAAC;QAC9D,CAAC;aAAM,IAAI,GAAG,YAAY,MAAM,EAAE,CAAC;YACjC,OAAO,CAAC,IAAI,CACV,OAAO,GAAG,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,EAAE,GAAG,IAAI,EAC5C,GAAG,CACJ,CAAC;YACF,OAAO,GAAG,IAAI,CAAC;QACjB,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,SAAS,CAAC,cAAc,CAAC,6CAA6C,OAAO,GAAG,WAAW,CAAC,CAAC;QACzG,CAAC;IACH,CAAC;IAED,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAEtB,OAAO,OAAO,CAAC;AACjB,CAAC;AAvBD,gCAuBC"}

127
node_modules/@redis/client/dist/lib/RESP/types.d.ts generated vendored Normal file
View File

@@ -0,0 +1,127 @@
/// <reference types="node" />
import { CommandParser } from '../client/parser';
import { Tail } from '../commands/generic-transformers';
import { BlobError, SimpleError } from '../errors';
import { RedisScriptConfig, SHA1 } from '../lua-script';
import { RESP_TYPES } from './decoder';
import { VerbatimString } from './verbatim-string';
export type RESP_TYPES = typeof RESP_TYPES;
export type RespTypes = RESP_TYPES[keyof RESP_TYPES];
export interface RespType<RESP_TYPE extends RespTypes, DEFAULT, TYPES = never, TYPE_MAPPING = DEFAULT | TYPES> {
RESP_TYPE: RESP_TYPE;
DEFAULT: DEFAULT;
TYPES: TYPES;
TYPE_MAPPING: MappedType<TYPE_MAPPING>;
}
export interface NullReply extends RespType<RESP_TYPES['NULL'], null> {
}
export interface BooleanReply<T extends boolean = boolean> extends RespType<RESP_TYPES['BOOLEAN'], T> {
}
export interface NumberReply<T extends number = number> extends RespType<RESP_TYPES['NUMBER'], T, `${T}`, number | string> {
}
export interface BigNumberReply<T extends bigint = bigint> extends RespType<RESP_TYPES['BIG_NUMBER'], T, number | `${T}`, bigint | number | string> {
}
export interface DoubleReply<T extends number = number> extends RespType<RESP_TYPES['DOUBLE'], T, `${T}`, number | string> {
}
export interface SimpleStringReply<T extends string = string> extends RespType<RESP_TYPES['SIMPLE_STRING'], T, Buffer, string | Buffer> {
}
export interface BlobStringReply<T extends string = string> extends RespType<RESP_TYPES['BLOB_STRING'], T, Buffer, string | Buffer> {
toString(): string;
}
export interface VerbatimStringReply<T extends string = string> extends RespType<RESP_TYPES['VERBATIM_STRING'], T, Buffer | VerbatimString, string | Buffer | VerbatimString> {
}
export interface SimpleErrorReply extends RespType<RESP_TYPES['SIMPLE_ERROR'], SimpleError, Buffer> {
}
export interface BlobErrorReply extends RespType<RESP_TYPES['BLOB_ERROR'], BlobError, Buffer> {
}
export interface ArrayReply<T> extends RespType<RESP_TYPES['ARRAY'], Array<T>, never, Array<any>> {
}
export interface TuplesReply<T extends [...Array<unknown>]> extends RespType<RESP_TYPES['ARRAY'], T, never, Array<any>> {
}
export interface SetReply<T> extends RespType<RESP_TYPES['SET'], Array<T>, Set<T>, Array<any> | Set<any>> {
}
export interface MapReply<K, V> extends RespType<RESP_TYPES['MAP'], {
[key: string]: V;
}, Map<K, V> | Array<K | V>, Map<any, any> | Array<any>> {
}
type MapKeyValue = [key: BlobStringReply | SimpleStringReply, value: unknown];
type MapTuples = Array<MapKeyValue>;
type ExtractMapKey<T> = (T extends BlobStringReply<infer S> ? S : T extends SimpleStringReply<infer S> ? S : never);
export interface TuplesToMapReply<T extends MapTuples> extends RespType<RESP_TYPES['MAP'], {
[P in T[number] as ExtractMapKey<P[0]>]: P[1];
}, Map<ExtractMapKey<T[number][0]>, T[number][1]> | FlattenTuples<T>> {
}
type FlattenTuples<T> = (T extends [] ? [] : T extends [MapKeyValue] ? T[0] : T extends [MapKeyValue, ...infer R] ? [
...T[0],
...FlattenTuples<R>
] : never);
export type ReplyUnion = (NullReply | BooleanReply | NumberReply | BigNumberReply | DoubleReply | SimpleStringReply | BlobStringReply | VerbatimStringReply | SimpleErrorReply | BlobErrorReply | ArrayReply<ReplyUnion> | SetReply<ReplyUnion> | MapReply<ReplyUnion, ReplyUnion>);
export type MappedType<T> = ((...args: any) => T) | (new (...args: any) => T);
type InferTypeMapping<T> = T extends RespType<RespTypes, unknown, unknown, infer FLAG_TYPES> ? FLAG_TYPES : never;
export type TypeMapping = {
[P in RespTypes]?: MappedType<InferTypeMapping<Extract<ReplyUnion, RespType<P, any, any, any>>>>;
};
type MapKey<T, TYPE_MAPPING extends TypeMapping> = ReplyWithTypeMapping<T, TYPE_MAPPING & {
[RESP_TYPES.SIMPLE_STRING]: StringConstructor;
[RESP_TYPES.BLOB_STRING]: StringConstructor;
}>;
export type UnwrapReply<REPLY extends RespType<any, any, any, any>> = REPLY['DEFAULT' | 'TYPES'];
export type ReplyWithTypeMapping<REPLY, TYPE_MAPPING extends TypeMapping> = (REPLY extends RespType<infer RESP_TYPE, infer DEFAULT, infer TYPES, unknown> ? TYPE_MAPPING[RESP_TYPE] extends MappedType<infer T> ? ReplyWithTypeMapping<Extract<DEFAULT | TYPES, T>, TYPE_MAPPING> : ReplyWithTypeMapping<DEFAULT, TYPE_MAPPING> : (REPLY extends Array<infer T> ? Array<ReplyWithTypeMapping<T, TYPE_MAPPING>> : REPLY extends Set<infer T> ? Set<ReplyWithTypeMapping<T, TYPE_MAPPING>> : REPLY extends Map<infer K, infer V> ? Map<MapKey<K, TYPE_MAPPING>, ReplyWithTypeMapping<V, TYPE_MAPPING>> : REPLY extends Date | Buffer | Error ? REPLY : REPLY extends Record<PropertyKey, any> ? {
[P in keyof REPLY]: ReplyWithTypeMapping<REPLY[P], TYPE_MAPPING>;
} : REPLY));
export type TransformReply = (this: void, reply: any, preserve?: any, typeMapping?: TypeMapping) => any;
export type RedisArgument = string | Buffer;
export type CommandArguments = Array<RedisArgument> & {
preserve?: unknown;
};
export type Command = {
CACHEABLE?: boolean;
IS_READ_ONLY?: boolean;
/**
* @internal
* TODO: remove once `POLICIES` is implemented
*/
IS_FORWARD_COMMAND?: boolean;
NOT_KEYED_COMMAND?: true;
parseCommand(this: void, parser: CommandParser, ...args: Array<any>): void;
TRANSFORM_LEGACY_REPLY?: boolean;
transformReply: TransformReply | Record<RespVersions, TransformReply>;
unstableResp3?: boolean;
};
export type RedisCommands = Record<string, Command>;
export type RedisModules = Record<string, RedisCommands>;
export interface RedisFunction extends Command {
NUMBER_OF_KEYS?: number;
}
export type RedisFunctions = Record<string, Record<string, RedisFunction>>;
export type RedisScript = RedisScriptConfig & SHA1;
export type RedisScripts = Record<string, RedisScript>;
export interface CommanderConfig<M extends RedisModules, F extends RedisFunctions, S extends RedisScripts, RESP extends RespVersions> {
modules?: M;
functions?: F;
scripts?: S;
/**
* Specifies the Redis Serialization Protocol version to use.
* RESP2 is the default (value 2), while RESP3 (value 3) provides
* additional data types and features introduced in Redis 6.0.
*/
RESP?: RESP;
/**
* When set to true, enables commands that have unstable RESP3 implementations.
* When using RESP3 protocol, commands marked as having unstable RESP3 support
* will throw an error unless this flag is explicitly set to true.
* This primarily affects modules like Redis Search where response formats
* in RESP3 mode may change in future versions.
*/
unstableResp3?: boolean;
}
type Resp2Array<T> = (T extends [] ? [] : T extends [infer ITEM] ? [Resp2Reply<ITEM>] : T extends [infer ITEM, ...infer REST] ? [
Resp2Reply<ITEM>,
...Resp2Array<REST>
] : T extends Array<infer ITEM> ? Array<Resp2Reply<ITEM>> : never);
export type Resp2Reply<RESP3REPLY> = (RESP3REPLY extends RespType<infer RESP_TYPE, infer DEFAULT, infer TYPES, unknown> ? RESP_TYPE extends RESP_TYPES['DOUBLE'] ? BlobStringReply : RESP_TYPE extends RESP_TYPES['ARRAY'] | RESP_TYPES['SET'] ? RespType<RESP_TYPE, Resp2Array<DEFAULT>> : RESP_TYPE extends RESP_TYPES['MAP'] ? RespType<RESP_TYPES['ARRAY'], Resp2Array<Extract<TYPES, Array<any>>>> : RESP3REPLY : RESP3REPLY);
export type RespVersions = 2 | 3;
export type CommandReply<COMMAND extends Command, RESP extends RespVersions> = (COMMAND['transformReply'] extends (...args: any) => infer T ? T : COMMAND['transformReply'] extends Record<RESP, (...args: any) => infer T> ? T : ReplyUnion);
export type CommandSignature<COMMAND extends Command, RESP extends RespVersions, TYPE_MAPPING extends TypeMapping> = (...args: Tail<Parameters<COMMAND['parseCommand']>>) => Promise<ReplyWithTypeMapping<CommandReply<COMMAND, RESP>, TYPE_MAPPING>>;
export {};
//# sourceMappingURL=types.d.ts.map

File diff suppressed because one or more lines are too long

31
node_modules/@redis/client/dist/lib/RESP/types.js generated vendored Normal file
View File

@@ -0,0 +1,31 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const decoder_1 = require("./decoder");
// export type CommandWithPoliciesSignature<
// COMMAND extends Command,
// RESP extends RespVersions,
// TYPE_MAPPING extends TypeMapping,
// POLICIES extends CommandPolicies
// > = (...args: Parameters<COMMAND['transformArguments']>) => Promise<
// ReplyWithPolicy<
// ReplyWithTypeMapping<CommandReply<COMMAND, RESP>, TYPE_MAPPING>,
// MergePolicies<COMMAND, POLICIES>
// >
// >;
// export type MergePolicies<
// COMMAND extends Command,
// POLICIES extends CommandPolicies
// > = Omit<COMMAND['POLICIES'], keyof POLICIES> & POLICIES;
// type ReplyWithPolicy<
// REPLY,
// POLICIES extends CommandPolicies,
// > = (
// POLICIES['request'] extends REQUEST_POLICIES['SPECIAL'] ? never :
// POLICIES['request'] extends null | undefined ? REPLY :
// unknown extends POLICIES['request'] ? REPLY :
// POLICIES['response'] extends RESPONSE_POLICIES['SPECIAL'] ? never :
// POLICIES['response'] extends RESPONSE_POLICIES['ALL_SUCCEEDED' | 'ONE_SUCCEEDED' | 'LOGICAL_AND'] ? REPLY :
// // otherwise, return array of replies
// Array<REPLY>
// );
//# sourceMappingURL=types.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../lib/RESP/types.ts"],"names":[],"mappings":";;AAIA,uCAAuC;AAsXvC,4CAA4C;AAC5C,6BAA6B;AAC7B,+BAA+B;AAC/B,sCAAsC;AACtC,qCAAqC;AACrC,uEAAuE;AACvE,qBAAqB;AACrB,uEAAuE;AACvE,uCAAuC;AACvC,MAAM;AACN,KAAK;AAEL,6BAA6B;AAC7B,6BAA6B;AAC7B,qCAAqC;AACrC,4DAA4D;AAE5D,wBAAwB;AACxB,WAAW;AACX,sCAAsC;AACtC,QAAQ;AACR,sEAAsE;AACtE,2DAA2D;AAC3D,kDAAkD;AAClD,wEAAwE;AACxE,gHAAgH;AAChH,0CAA0C;AAC1C,iBAAiB;AACjB,KAAK"}

View File

@@ -0,0 +1,5 @@
export declare class VerbatimString extends String {
format: string;
constructor(format: string, value: string);
}
//# sourceMappingURL=verbatim-string.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"verbatim-string.d.ts","sourceRoot":"","sources":["../../../lib/RESP/verbatim-string.ts"],"names":[],"mappings":"AAAA,qBAAa,cAAe,SAAQ,MAAM;IAE/B,MAAM,EAAE,MAAM;gBAAd,MAAM,EAAE,MAAM,EACrB,KAAK,EAAE,MAAM;CAIhB"}

View File

@@ -0,0 +1,12 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.VerbatimString = void 0;
class VerbatimString extends String {
format;
constructor(format, value) {
super(value);
this.format = format;
}
}
exports.VerbatimString = VerbatimString;
//# sourceMappingURL=verbatim-string.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"verbatim-string.js","sourceRoot":"","sources":["../../../lib/RESP/verbatim-string.ts"],"names":[],"mappings":";;;AAAA,MAAa,cAAe,SAAQ,MAAM;IAE/B;IADT,YACS,MAAc,EACrB,KAAa;QAEb,KAAK,CAAC,KAAK,CAAC,CAAC;QAHN,WAAM,GAAN,MAAM,CAAQ;IAIvB,CAAC;CACF;AAPD,wCAOC"}

View File

@@ -0,0 +1,88 @@
import { Disposable } from './disposable';
/**
* Provides credentials asynchronously.
*/
export interface AsyncCredentialsProvider {
readonly type: 'async-credentials-provider';
credentials: () => Promise<BasicAuth>;
}
/**
* Provides credentials asynchronously with support for continuous updates via a subscription model.
* This is useful for environments where credentials are frequently rotated or updated or can be revoked.
*/
export interface StreamingCredentialsProvider {
readonly type: 'streaming-credentials-provider';
/**
* Provides initial credentials and subscribes to subsequent updates. This is used internally by the node-redis client
* to handle credential rotation and re-authentication.
*
* Note: The node-redis client manages the subscription lifecycle automatically. Users only need to implement
* onReAuthenticationError if they want to be notified about authentication failures.
*
* Error handling:
* - Errors received via onError indicate a fatal issue with the credentials stream
* - The stream is automatically closed(disposed) when onError occurs
* - onError typically mean the provider failed to fetch new credentials after retrying
*
* @example
* ```ts
* const provider = getStreamingProvider();
* const [initialCredentials, disposable] = await provider.subscribe({
* onNext: (newCredentials) => {
* // Handle credential update
* },
* onError: (error) => {
* // Handle fatal stream error
* }
* });
*
* @param listener - Callbacks to handle credential updates and errors
* @returns A Promise resolving to [initial credentials, cleanup function]
*/
subscribe: (listener: StreamingCredentialsListener<BasicAuth>) => Promise<[BasicAuth, Disposable]>;
/**
* Called when authentication fails or credentials cannot be renewed in time.
* Implement this to handle authentication errors in your application.
*
* @param error - Either a CredentialsError (invalid/expired credentials) or
* UnableToObtainNewCredentialsError (failed to fetch new credentials on time)
*/
onReAuthenticationError: (error: ReAuthenticationError) => void;
}
/**
* Type representing basic authentication credentials.
*/
export type BasicAuth = {
username?: string;
password?: string;
};
/**
* Callback to handle credential updates and errors.
*/
export type StreamingCredentialsListener<T> = {
onNext: (credentials: T) => void;
onError: (e: Error) => void;
};
/**
* Providers that can supply authentication credentials
*/
export type CredentialsProvider = AsyncCredentialsProvider | StreamingCredentialsProvider;
/**
* Errors that can occur during re-authentication.
*/
export type ReAuthenticationError = CredentialsError | UnableToObtainNewCredentialsError;
/**
* Thrown when re-authentication fails with provided credentials .
* e.g. when the credentials are invalid, expired or revoked.
*
*/
export declare class CredentialsError extends Error {
constructor(message: string);
}
/**
* Thrown when new credentials cannot be obtained before current ones expire
*/
export declare class UnableToObtainNewCredentialsError extends Error {
constructor(message: string);
}
//# sourceMappingURL=credentials-provider.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"credentials-provider.d.ts","sourceRoot":"","sources":["../../../lib/authx/credentials-provider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,IAAI,EAAE,4BAA4B,CAAC;IAC5C,WAAW,EAAE,MAAM,OAAO,CAAC,SAAS,CAAC,CAAA;CACtC;AAED;;;GAGG;AACH,MAAM,WAAW,4BAA4B;IAC3C,QAAQ,CAAC,IAAI,EAAE,gCAAgC,CAAC;IAEhD;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACH,SAAS,EAAE,CAAC,QAAQ,EAAE,4BAA4B,CAAC,SAAS,CAAC,KAAK,OAAO,CAAC,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC,CAAA;IAElG;;;;;;OAMG;IACH,uBAAuB,EAAE,CAAC,KAAK,EAAE,qBAAqB,KAAK,IAAI,CAAC;CAEjE;AAED;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAEhE;;GAEG;AACH,MAAM,MAAM,4BAA4B,CAAC,CAAC,IAAI;IAC5C,MAAM,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,IAAI,CAAC;IACjC,OAAO,EAAE,CAAC,CAAC,EAAE,KAAK,KAAK,IAAI,CAAC;CAC7B,CAAA;AAGD;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAAG,wBAAwB,GAAG,4BAA4B,CAAA;AAEzF;;GAEG;AACH,MAAM,MAAM,qBAAqB,GAAG,gBAAgB,GAAG,iCAAiC,CAAA;AAExF;;;;GAIG;AACH,qBAAa,gBAAiB,SAAQ,KAAK;gBAC7B,OAAO,EAAE,MAAM;CAK5B;AAED;;GAEG;AACH,qBAAa,iCAAkC,SAAQ,KAAK;gBAC9C,OAAO,EAAE,MAAM;CAI5B"}

View File

@@ -0,0 +1,26 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.UnableToObtainNewCredentialsError = exports.CredentialsError = void 0;
/**
* Thrown when re-authentication fails with provided credentials .
* e.g. when the credentials are invalid, expired or revoked.
*
*/
class CredentialsError extends Error {
constructor(message) {
super(`Re-authentication with latest credentials failed: ${message}`);
this.name = 'CredentialsError';
}
}
exports.CredentialsError = CredentialsError;
/**
* Thrown when new credentials cannot be obtained before current ones expire
*/
class UnableToObtainNewCredentialsError extends Error {
constructor(message) {
super(`Unable to obtain new credentials : ${message}`);
this.name = 'UnableToObtainNewCredentialsError';
}
}
exports.UnableToObtainNewCredentialsError = UnableToObtainNewCredentialsError;
//# sourceMappingURL=credentials-provider.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"credentials-provider.js","sourceRoot":"","sources":["../../../lib/authx/credentials-provider.ts"],"names":[],"mappings":";;;AAgFA;;;;GAIG;AACH,MAAa,gBAAiB,SAAQ,KAAK;IACzC,YAAY,OAAe;QACzB,KAAK,CAAC,qDAAqD,OAAO,EAAE,CAAC,CAAC;QACtE,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;IACjC,CAAC;CAEF;AAND,4CAMC;AAED;;GAEG;AACH,MAAa,iCAAkC,SAAQ,KAAK;IAC1D,YAAY,OAAe;QACzB,KAAK,CAAC,sCAAsC,OAAO,EAAE,CAAC,CAAC;QACvD,IAAI,CAAC,IAAI,GAAG,mCAAmC,CAAC;IAClD,CAAC;CACF;AALD,8EAKC"}

View File

@@ -0,0 +1,7 @@
/**
* Represents a resource that can be disposed.
*/
export interface Disposable {
dispose(): void;
}
//# sourceMappingURL=disposable.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"disposable.d.ts","sourceRoot":"","sources":["../../../lib/authx/disposable.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,OAAO,IAAI,IAAI,CAAC;CACjB"}

View File

@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=disposable.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"disposable.js","sourceRoot":"","sources":["../../../lib/authx/disposable.ts"],"names":[],"mappings":""}

View File

@@ -0,0 +1,24 @@
/**
* An identity provider is responsible for providing a token that can be used to authenticate with a service.
*/
/**
* The response from an identity provider when requesting a token.
*
* note: "native" refers to the type of the token that the actual identity provider library is using.
*
* @type T The type of the native idp token.
* @property token The token.
* @property ttlMs The time-to-live of the token in epoch milliseconds extracted from the native token in local time.
*/
export type TokenResponse<T> = {
token: T;
ttlMs: number;
};
export interface IdentityProvider<T> {
/**
* Request a token from the identity provider.
* @returns A promise that resolves to an object containing the token and the time-to-live in epoch milliseconds.
*/
requestToken(): Promise<TokenResponse<T>>;
}
//# sourceMappingURL=identity-provider.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"identity-provider.d.ts","sourceRoot":"","sources":["../../../lib/authx/identity-provider.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;;;;;;GAQG;AACH,MAAM,MAAM,aAAa,CAAC,CAAC,IAAI;IAAE,KAAK,EAAE,CAAC,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3D,MAAM,WAAW,gBAAgB,CAAC,CAAC;IACjC;;;OAGG;IACH,YAAY,IAAI,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;CAC3C"}

View File

@@ -0,0 +1,6 @@
"use strict";
/**
* An identity provider is responsible for providing a token that can be used to authenticate with a service.
*/
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=identity-provider.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"identity-provider.js","sourceRoot":"","sources":["../../../lib/authx/identity-provider.ts"],"names":[],"mappings":";AAAA;;GAEG"}

6
node_modules/@redis/client/dist/lib/authx/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,6 @@
export { TokenManager, TokenManagerConfig, TokenStreamListener, RetryPolicy, IDPError } from './token-manager';
export { CredentialsProvider, StreamingCredentialsProvider, UnableToObtainNewCredentialsError, CredentialsError, StreamingCredentialsListener, AsyncCredentialsProvider, ReAuthenticationError, BasicAuth } from './credentials-provider';
export { Token } from './token';
export { IdentityProvider, TokenResponse } from './identity-provider';
export { Disposable } from './disposable';
//# sourceMappingURL=index.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../lib/authx/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC/G,OAAO,EACL,mBAAmB,EACnB,4BAA4B,EAC5B,iCAAiC,EACjC,gBAAgB,EAChB,4BAA4B,EAC5B,wBAAwB,EACxB,qBAAqB,EACrB,SAAS,EACV,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEtE,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA"}

12
node_modules/@redis/client/dist/lib/authx/index.js generated vendored Normal file
View File

@@ -0,0 +1,12 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Token = exports.CredentialsError = exports.UnableToObtainNewCredentialsError = exports.IDPError = exports.TokenManager = void 0;
var token_manager_1 = require("./token-manager");
Object.defineProperty(exports, "TokenManager", { enumerable: true, get: function () { return token_manager_1.TokenManager; } });
Object.defineProperty(exports, "IDPError", { enumerable: true, get: function () { return token_manager_1.IDPError; } });
var credentials_provider_1 = require("./credentials-provider");
Object.defineProperty(exports, "UnableToObtainNewCredentialsError", { enumerable: true, get: function () { return credentials_provider_1.UnableToObtainNewCredentialsError; } });
Object.defineProperty(exports, "CredentialsError", { enumerable: true, get: function () { return credentials_provider_1.CredentialsError; } });
var token_1 = require("./token");
Object.defineProperty(exports, "Token", { enumerable: true, get: function () { return token_1.Token; } });
//# sourceMappingURL=index.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../lib/authx/index.ts"],"names":[],"mappings":";;;AAAA,iDAA+G;AAAtG,6GAAA,YAAY,OAAA;AAAwD,yGAAA,QAAQ,OAAA;AACrF,+DASgC;AAN9B,yIAAA,iCAAiC,OAAA;AACjC,wHAAA,gBAAgB,OAAA;AAMlB,iCAAgC;AAAvB,8FAAA,KAAK,OAAA"}

View File

@@ -0,0 +1,164 @@
import { IdentityProvider } from './identity-provider';
import { Token } from './token';
import { Disposable } from './disposable';
/**
* The configuration for retrying token refreshes.
*/
export interface RetryPolicy {
/**
* The maximum number of attempts to retry token refreshes.
*/
maxAttempts: number;
/**
* The initial delay in milliseconds before the first retry.
*/
initialDelayMs: number;
/**
* The maximum delay in milliseconds between retries.
* The calculated delay will be capped at this value.
*/
maxDelayMs: number;
/**
* The multiplier for exponential backoff between retries.
* @example
* A value of 2 will double the delay each time:
* - 1st retry: initialDelayMs
* - 2nd retry: initialDelayMs * 2
* - 3rd retry: initialDelayMs * 4
*/
backoffMultiplier: number;
/**
* The percentage of jitter to apply to the delay.
* @example
* A value of 0.1 will add or subtract up to 10% of the delay.
*/
jitterPercentage?: number;
/**
* Function to classify errors from the identity provider as retryable or non-retryable.
* Used to determine if a token refresh failure should be retried based on the type of error.
*
* The default behavior is to retry all types of errors if no function is provided.
*
* Common use cases:
* - Network errors that may be transient (should retry)
* - Invalid credentials (should not retry)
* - Rate limiting responses (should retry)
*
* @param error - The error from the identity provider3
* @param attempt - Current retry attempt (0-based)
* @returns `true` if the error is considered transient and the operation should be retried
*
* @example
* ```typescript
* const retryPolicy: RetryPolicy = {
* maxAttempts: 3,
* initialDelayMs: 1000,
* maxDelayMs: 5000,
* backoffMultiplier: 2,
* isRetryable: (error) => {
* // Retry on network errors or rate limiting
* return error instanceof NetworkError ||
* error instanceof RateLimitError;
* }
* };
* ```
*/
isRetryable?: (error: unknown, attempt: number) => boolean;
}
/**
* the configuration for the TokenManager.
*/
export interface TokenManagerConfig {
/**
* Represents the ratio of a token's lifetime at which a refresh should be triggered.
* For example, a value of 0.75 means the token should be refreshed when 75% of its lifetime has elapsed (or when
* 25% of its lifetime remains).
*/
expirationRefreshRatio: number;
retry?: RetryPolicy;
}
/**
* IDPError indicates a failure from the identity provider.
*
* The `isRetryable` flag is determined by the RetryPolicy's error classification function - if an error is
* classified as retryable, it will be marked as transient and the token manager will attempt to recover.
*/
export declare class IDPError extends Error {
readonly message: string;
readonly isRetryable: boolean;
constructor(message: string, isRetryable: boolean);
}
/**
* TokenStreamListener is an interface for objects that listen to token changes.
*/
export type TokenStreamListener<T> = {
/**
* Called each time a new token is received.
* @param token
*/
onNext: (token: Token<T>) => void;
/**
* Called when an error occurs while calling the underlying IdentityProvider. The error can be
* transient and the token manager will attempt to obtain a token again if retry policy is configured.
*
* Only fatal errors will terminate the stream and stop the token manager.
*
* @param error
*/
onError: (error: IDPError) => void;
};
/**
* TokenManager is responsible for obtaining/refreshing tokens and notifying listeners about token changes.
* It uses an IdentityProvider to request tokens. The token refresh is scheduled based on the token's TTL and
* the expirationRefreshRatio configuration.
*
* The TokenManager should be disposed when it is no longer needed by calling the dispose method on the Disposable
* returned by start.
*/
export declare class TokenManager<T> {
private readonly identityProvider;
private readonly config;
private currentToken;
private refreshTimeout;
private listener;
private retryAttempt;
constructor(identityProvider: IdentityProvider<T>, config: TokenManagerConfig);
/**
* Starts the token manager and returns a Disposable that can be used to stop the token manager.
*
* @param listener The listener that will receive token updates.
* @param initialDelayMs The initial delay in milliseconds before the first token refresh.
*/
start(listener: TokenStreamListener<T>, initialDelayMs?: number): Disposable;
calculateRetryDelay(): number;
private shouldRetry;
isRunning(): boolean;
private refresh;
private handleNewToken;
/**
* Creates a Token object from a native token and sets it as the current token.
*
* @param nativeToken - The raw token received from the identity provider
* @param ttlMs - Time-to-live in milliseconds for the token
*
* @returns A new Token instance containing the wrapped native token and expiration details
*
*/
wrapAndSetCurrentToken(nativeToken: T, ttlMs: number): Token<T>;
private scheduleNextRefresh;
/**
* Calculates the time in milliseconds when the token should be refreshed
* based on the token's TTL and the expirationRefreshRatio configuration.
*
* @param token The token to calculate the refresh time for.
* @param now The current time in milliseconds. Defaults to Date.now().
*/
calculateRefreshTime(token: Token<T>, now?: number): number;
private stop;
/**
* Returns the current token or null if no token is available.
*/
getCurrentToken(): Token<T> | null;
private notifyError;
}
//# sourceMappingURL=token-manager.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"token-manager.d.ts","sourceRoot":"","sources":["../../../lib/authx/token-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAiB,MAAM,qBAAqB,CAAC;AACtE,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAC,UAAU,EAAC,MAAM,cAAc,CAAC;AAExC;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B;;OAEG;IACH,WAAW,EAAE,MAAM,CAAC;IAEpB;;OAEG;IACH,cAAc,EAAE,MAAM,CAAC;IAEvB;;;OAGG;IACH,UAAU,EAAE,MAAM,CAAC;IAEnB;;;;;;;OAOG;IACH,iBAAiB,EAAE,MAAM,CAAC;IAE1B;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA6BG;IACH,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC;CAC5D;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IAEjC;;;;OAIG;IACH,sBAAsB,EAAE,MAAM,CAAC;IAG/B,KAAK,CAAC,EAAE,WAAW,CAAC;CACrB;AAED;;;;;GAKG;AACH,qBAAa,QAAS,SAAQ,KAAK;aACL,OAAO,EAAE,MAAM;aAAkB,WAAW,EAAE,OAAO;gBAArD,OAAO,EAAE,MAAM,EAAkB,WAAW,EAAE,OAAO;CAIlF;AAED;;GAEG;AACH,MAAM,MAAM,mBAAmB,CAAC,CAAC,IAAI;IACnC;;;OAGG;IACH,MAAM,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;IAElC;;;;;;;OAOG;IACH,OAAO,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,CAAC;CAEpC,CAAA;AAED;;;;;;;GAOG;AACH,qBAAa,YAAY,CAAC,CAAC;IAOvB,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IACjC,OAAO,CAAC,QAAQ,CAAC,MAAM;IAPzB,OAAO,CAAC,YAAY,CAAyB;IAC7C,OAAO,CAAC,cAAc,CAA+B;IACrD,OAAO,CAAC,QAAQ,CAAuC;IACvD,OAAO,CAAC,YAAY,CAAa;gBAGd,gBAAgB,EAAE,gBAAgB,CAAC,CAAC,CAAC,EACrC,MAAM,EAAE,kBAAkB;IAU7C;;;;;OAKG;IACI,KAAK,CAAC,QAAQ,EAAE,mBAAmB,CAAC,CAAC,CAAC,EAAE,cAAc,GAAE,MAAU,GAAG,UAAU;IAe/E,mBAAmB,IAAI,MAAM;IAoBpC,OAAO,CAAC,WAAW;IAgBZ,SAAS,IAAI,OAAO;YAIb,OAAO;IAsBrB,OAAO,CAAC,cAAc,CAQrB;IAED;;;;;;;;OAQG;IACI,sBAAsB,CAAC,WAAW,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC;IAWtE,OAAO,CAAC,mBAAmB;IAa3B;;;;;;OAMG;IACI,oBAAoB,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,GAAE,MAAmB,GAAG,MAAM;IAK9E,OAAO,CAAC,IAAI;IAYZ;;OAEG;IACI,eAAe,IAAI,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI;IAIzC,OAAO,CAAC,WAAW;CASpB"}

View File

@@ -0,0 +1,184 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TokenManager = exports.IDPError = void 0;
const token_1 = require("./token");
/**
* IDPError indicates a failure from the identity provider.
*
* The `isRetryable` flag is determined by the RetryPolicy's error classification function - if an error is
* classified as retryable, it will be marked as transient and the token manager will attempt to recover.
*/
class IDPError extends Error {
message;
isRetryable;
constructor(message, isRetryable) {
super(message);
this.message = message;
this.isRetryable = isRetryable;
this.name = 'IDPError';
}
}
exports.IDPError = IDPError;
/**
* TokenManager is responsible for obtaining/refreshing tokens and notifying listeners about token changes.
* It uses an IdentityProvider to request tokens. The token refresh is scheduled based on the token's TTL and
* the expirationRefreshRatio configuration.
*
* The TokenManager should be disposed when it is no longer needed by calling the dispose method on the Disposable
* returned by start.
*/
class TokenManager {
identityProvider;
config;
currentToken = null;
refreshTimeout = null;
listener = null;
retryAttempt = 0;
constructor(identityProvider, config) {
this.identityProvider = identityProvider;
this.config = config;
if (this.config.expirationRefreshRatio > 1) {
throw new Error('expirationRefreshRatio must be less than or equal to 1');
}
if (this.config.expirationRefreshRatio < 0) {
throw new Error('expirationRefreshRatio must be greater or equal to 0');
}
}
/**
* Starts the token manager and returns a Disposable that can be used to stop the token manager.
*
* @param listener The listener that will receive token updates.
* @param initialDelayMs The initial delay in milliseconds before the first token refresh.
*/
start(listener, initialDelayMs = 0) {
if (this.listener) {
this.stop();
}
this.listener = listener;
this.retryAttempt = 0;
this.scheduleNextRefresh(initialDelayMs);
return {
dispose: () => this.stop()
};
}
calculateRetryDelay() {
if (!this.config.retry)
return 0;
const { initialDelayMs, maxDelayMs, backoffMultiplier, jitterPercentage } = this.config.retry;
let delay = initialDelayMs * Math.pow(backoffMultiplier, this.retryAttempt - 1);
delay = Math.min(delay, maxDelayMs);
if (jitterPercentage) {
const jitterRange = delay * (jitterPercentage / 100);
const jitterAmount = Math.random() * jitterRange - (jitterRange / 2);
delay += jitterAmount;
}
let result = Math.max(0, Math.floor(delay));
return result;
}
shouldRetry(error) {
if (!this.config.retry)
return false;
const { maxAttempts, isRetryable } = this.config.retry;
if (this.retryAttempt >= maxAttempts) {
return false;
}
if (isRetryable) {
return isRetryable(error, this.retryAttempt);
}
return false;
}
isRunning() {
return this.listener !== null;
}
async refresh() {
if (!this.listener) {
throw new Error('TokenManager is not running, but refresh was called');
}
try {
await this.identityProvider.requestToken().then(this.handleNewToken);
this.retryAttempt = 0;
}
catch (error) {
if (this.shouldRetry(error)) {
this.retryAttempt++;
const retryDelay = this.calculateRetryDelay();
this.notifyError(`Token refresh failed (attempt ${this.retryAttempt}), retrying in ${retryDelay}ms: ${error}`, true);
this.scheduleNextRefresh(retryDelay);
}
else {
this.notifyError(error, false);
this.stop();
}
}
}
handleNewToken = async ({ token: nativeToken, ttlMs }) => {
if (!this.listener) {
throw new Error('TokenManager is not running, but a new token was received');
}
const token = this.wrapAndSetCurrentToken(nativeToken, ttlMs);
this.listener.onNext(token);
this.scheduleNextRefresh(this.calculateRefreshTime(token));
};
/**
* Creates a Token object from a native token and sets it as the current token.
*
* @param nativeToken - The raw token received from the identity provider
* @param ttlMs - Time-to-live in milliseconds for the token
*
* @returns A new Token instance containing the wrapped native token and expiration details
*
*/
wrapAndSetCurrentToken(nativeToken, ttlMs) {
const now = Date.now();
const token = new token_1.Token(nativeToken, now + ttlMs, now);
this.currentToken = token;
return token;
}
scheduleNextRefresh(delayMs) {
if (this.refreshTimeout) {
clearTimeout(this.refreshTimeout);
this.refreshTimeout = null;
}
if (delayMs === 0) {
this.refresh();
}
else {
this.refreshTimeout = setTimeout(() => this.refresh(), delayMs);
}
}
/**
* Calculates the time in milliseconds when the token should be refreshed
* based on the token's TTL and the expirationRefreshRatio configuration.
*
* @param token The token to calculate the refresh time for.
* @param now The current time in milliseconds. Defaults to Date.now().
*/
calculateRefreshTime(token, now = Date.now()) {
const ttlMs = token.getTtlMs(now);
return Math.floor(ttlMs * this.config.expirationRefreshRatio);
}
stop() {
if (this.refreshTimeout) {
clearTimeout(this.refreshTimeout);
this.refreshTimeout = null;
}
this.listener = null;
this.currentToken = null;
this.retryAttempt = 0;
}
/**
* Returns the current token or null if no token is available.
*/
getCurrentToken() {
return this.currentToken;
}
notifyError(error, isRetryable) {
const errorMessage = error instanceof Error ? error.message : String(error);
if (!this.listener) {
throw new Error(`TokenManager is not running but received an error: ${errorMessage}`);
}
this.listener.onError(new IDPError(errorMessage, isRetryable));
}
}
exports.TokenManager = TokenManager;
//# sourceMappingURL=token-manager.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"token-manager.js","sourceRoot":"","sources":["../../../lib/authx/token-manager.ts"],"names":[],"mappings":"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"}

15
node_modules/@redis/client/dist/lib/authx/token.d.ts generated vendored Normal file
View File

@@ -0,0 +1,15 @@
/**
* A token that can be used to authenticate with a service.
*/
export declare class Token<T> {
readonly value: T;
readonly expiresAtMs: number;
readonly receivedAtMs: number;
constructor(value: T, expiresAtMs: number, receivedAtMs: number);
/**
* Returns the time-to-live of the token in milliseconds.
* @param now The current time in milliseconds since the Unix epoch.
*/
getTtlMs(now: number): number;
}
//# sourceMappingURL=token.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"token.d.ts","sourceRoot":"","sources":["../../../lib/authx/token.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,qBAAa,KAAK,CAAC,CAAC;aAEA,KAAK,EAAE,CAAC;aAER,WAAW,EAAE,MAAM;aAEnB,YAAY,EAAE,MAAM;gBAJpB,KAAK,EAAE,CAAC,EAER,WAAW,EAAE,MAAM,EAEnB,YAAY,EAAE,MAAM;IAGtC;;;OAGG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;CAM9B"}

32
node_modules/@redis/client/dist/lib/authx/token.js generated vendored Normal file
View File

@@ -0,0 +1,32 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Token = void 0;
/**
* A token that can be used to authenticate with a service.
*/
class Token {
value;
expiresAtMs;
receivedAtMs;
constructor(value,
//represents the token deadline - the time in milliseconds since the Unix epoch at which the token expires
expiresAtMs,
//represents the time in milliseconds since the Unix epoch at which the token was received
receivedAtMs) {
this.value = value;
this.expiresAtMs = expiresAtMs;
this.receivedAtMs = receivedAtMs;
}
/**
* Returns the time-to-live of the token in milliseconds.
* @param now The current time in milliseconds since the Unix epoch.
*/
getTtlMs(now) {
if (this.expiresAtMs < now) {
return 0;
}
return this.expiresAtMs - now;
}
}
exports.Token = Token;
//# sourceMappingURL=token.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"token.js","sourceRoot":"","sources":["../../../lib/authx/token.ts"],"names":[],"mappings":";;;AAAA;;GAEG;AACH,MAAa,KAAK;IAEE;IAEA;IAEA;IALlB,YACkB,KAAQ;IACxB,0GAA0G;IAC1F,WAAmB;IACnC,0FAA0F;IAC1E,YAAoB;QAJpB,UAAK,GAAL,KAAK,CAAG;QAER,gBAAW,GAAX,WAAW,CAAQ;QAEnB,iBAAY,GAAZ,YAAY,CAAQ;IACnC,CAAC;IAEJ;;;OAGG;IACH,QAAQ,CAAC,GAAW;QAClB,IAAI,IAAI,CAAC,WAAW,GAAG,GAAG,EAAE,CAAC;YAC3B,OAAO,CAAC,CAAC;QACX,CAAC;QACD,OAAO,IAAI,CAAC,WAAW,GAAG,GAAG,CAAC;IAChC,CAAC;CACF;AAnBD,sBAmBC"}

297
node_modules/@redis/client/dist/lib/client/cache.d.ts generated vendored Normal file
View File

@@ -0,0 +1,297 @@
/// <reference types="node" />
/// <reference types="node" />
import { EventEmitter } from 'stream';
import RedisClient from '.';
import { RedisArgument, ReplyUnion, TransformReply, TypeMapping } from '../RESP/types';
import { BasicCommandParser } from './parser';
/**
* A snapshot of cache statistics.
*
* This class provides an immutable view of the cache's operational statistics at a particular
* point in time. It is heavily inspired by the statistics reporting capabilities found in
* Ben Manes's Caffeine cache (https://github.com/ben-manes/caffeine).
*
* Instances of `CacheStats` are typically obtained from a {@link StatsCounter} and can be used
* for performance monitoring, debugging, or logging. It includes metrics such as hit rate,
* miss rate, load success/failure rates, average load penalty, and eviction counts.
*
* All statistics are non-negative. Rates and averages are typically in the range `[0.0, 1.0]`,
* or `0` if the an operation has not occurred (e.g. hit rate is 0 if there are no requests).
*
* Cache statistics are incremented according to specific rules:
* - When a cache lookup encounters an existing entry, hitCount is incremented.
* - When a cache lookup encounters a missing entry, missCount is incremented.
* - When a new entry is successfully loaded, loadSuccessCount is incremented and the
* loading time is added to totalLoadTime.
* - When an entry fails to load, loadFailureCount is incremented and the
* loading time is added to totalLoadTime.
* - When an entry is evicted due to size constraints or expiration,
* evictionCount is incremented.
*/
export declare class CacheStats {
readonly hitCount: number;
readonly missCount: number;
readonly loadSuccessCount: number;
readonly loadFailureCount: number;
readonly totalLoadTime: number;
readonly evictionCount: number;
/**
* Creates a new CacheStats instance with the specified statistics.
*/
private constructor();
/**
* Creates a new CacheStats instance with the specified statistics.
*
* @param hitCount - Number of cache hits
* @param missCount - Number of cache misses
* @param loadSuccessCount - Number of successful cache loads
* @param loadFailureCount - Number of failed cache loads
* @param totalLoadTime - Total load time in milliseconds
* @param evictionCount - Number of cache evictions
*/
static of(hitCount?: number, missCount?: number, loadSuccessCount?: number, loadFailureCount?: number, totalLoadTime?: number, evictionCount?: number): CacheStats;
/**
* Returns a statistics instance where no cache events have been recorded.
*
* @returns An empty statistics instance
*/
static empty(): CacheStats;
/**
* An empty stats instance with all counters set to zero.
*/
private static readonly EMPTY_STATS;
/**
* Returns the total number of times cache lookup methods have returned
* either a cached or uncached value.
*
* @returns Total number of requests (hits + misses)
*/
requestCount(): number;
/**
* Returns the hit rate of the cache.
* This is defined as hitCount / requestCount, or 1.0 when requestCount is 0.
*
* @returns The ratio of cache requests that were hits (between 0.0 and 1.0)
*/
hitRate(): number;
/**
* Returns the miss rate of the cache.
* This is defined as missCount / requestCount, or 0.0 when requestCount is 0.
*
* @returns The ratio of cache requests that were misses (between 0.0 and 1.0)
*/
missRate(): number;
/**
* Returns the total number of load operations (successful + failed).
*
* @returns Total number of load operations
*/
loadCount(): number;
/**
* Returns the ratio of cache loading attempts that failed.
* This is defined as loadFailureCount / loadCount, or 0.0 when loadCount is 0.
*
* @returns Ratio of load operations that failed (between 0.0 and 1.0)
*/
loadFailureRate(): number;
/**
* Returns the average time spent loading new values, in milliseconds.
* This is defined as totalLoadTime / loadCount, or 0.0 when loadCount is 0.
*
* @returns Average load time in milliseconds
*/
averageLoadPenalty(): number;
/**
* Returns a new CacheStats representing the difference between this CacheStats
* and another. Negative values are rounded up to zero.
*
* @param other - The statistics to subtract from this instance
* @returns The difference between this instance and other
*/
minus(other: CacheStats): CacheStats;
/**
* Returns a new CacheStats representing the sum of this CacheStats and another.
*
* @param other - The statistics to add to this instance
* @returns The sum of this instance and other
*/
plus(other: CacheStats): CacheStats;
}
/**
* An accumulator for cache statistics.
*
* This interface defines the contract for objects that record cache-related events
* such as hits, misses, loads (successes and failures), and evictions. The design
* is inspired by the statistics collection mechanisms in Ben Manes's Caffeine cache
* (https://github.com/ben-manes/caffeine).
*
* Implementations of this interface are responsible for aggregating these events.
* A snapshot of the current statistics can be obtained by calling the `snapshot()`
* method, which returns an immutable {@link CacheStats} object.
*
* Common implementations include `DefaultStatsCounter` for active statistics collection
* and `DisabledStatsCounter` for a no-op version when stats are not needed.
*/
export interface StatsCounter {
/**
* Records cache hits. This should be called when a cache request returns a cached value.
*
* @param count - The number of hits to record
*/
recordHits(count: number): void;
/**
* Records cache misses. This should be called when a cache request returns a value that was not
* found in the cache.
*
* @param count - The number of misses to record
*/
recordMisses(count: number): void;
/**
* Records the successful load of a new entry. This method should be called when a cache request
* causes an entry to be loaded and the loading completes successfully.
*
* @param loadTime - The number of milliseconds the cache spent computing or retrieving the new value
*/
recordLoadSuccess(loadTime: number): void;
/**
* Records the failed load of a new entry. This method should be called when a cache request
* causes an entry to be loaded, but an exception is thrown while loading the entry.
*
* @param loadTime - The number of milliseconds the cache spent computing or retrieving the new value
* prior to the failure
*/
recordLoadFailure(loadTime: number): void;
/**
* Records the eviction of an entry from the cache. This should only be called when an entry is
* evicted due to the cache's eviction strategy, and not as a result of manual invalidations.
*
* @param count - The number of evictions to record
*/
recordEvictions(count: number): void;
/**
* Returns a snapshot of this counter's values. Note that this may be an inconsistent view, as it
* may be interleaved with update operations.
*
* @return A snapshot of this counter's values
*/
snapshot(): CacheStats;
}
type CachingClient = RedisClient<any, any, any, any, any>;
type CmdFunc = () => Promise<ReplyUnion>;
type EvictionPolicy = "LRU" | "FIFO";
/**
* Configuration options for Client Side Cache
*/
export interface ClientSideCacheConfig {
/**
* Time-to-live in milliseconds for cached entries.
* Use 0 for no expiration.
* @default 0
*/
ttl?: number;
/**
* Maximum number of entries to store in the cache.
* Use 0 for unlimited entries.
* @default 0
*/
maxEntries?: number;
/**
* Eviction policy to use when the cache reaches its capacity.
* - "LRU" (Least Recently Used): Evicts least recently accessed entries first
* - "FIFO" (First In First Out): Evicts oldest entries first
* @default "LRU"
*/
evictPolicy?: EvictionPolicy;
/**
* Whether to collect statistics about cache operations.
* @default true
*/
recordStats?: boolean;
}
interface ClientSideCacheEntry {
invalidate(): void;
validate(): boolean;
}
declare abstract class ClientSideCacheEntryBase implements ClientSideCacheEntry {
#private;
constructor(ttl: number);
invalidate(): void;
validate(): boolean;
}
declare class ClientSideCacheEntryValue extends ClientSideCacheEntryBase {
#private;
get value(): any;
constructor(ttl: number, value: any);
}
declare class ClientSideCacheEntryPromise extends ClientSideCacheEntryBase {
#private;
get promise(): Promise<ReplyUnion>;
constructor(ttl: number, sendCommandPromise: Promise<ReplyUnion>);
}
export declare abstract class ClientSideCacheProvider extends EventEmitter {
abstract handleCache(client: CachingClient, parser: BasicCommandParser, fn: CmdFunc, transformReply: TransformReply | undefined, typeMapping: TypeMapping | undefined): Promise<any>;
abstract trackingOn(): Array<RedisArgument>;
abstract invalidate(key: RedisArgument | null): void;
abstract clear(): void;
abstract stats(): CacheStats;
abstract onError(): void;
abstract onClose(): void;
}
export declare class BasicClientSideCache extends ClientSideCacheProvider {
#private;
readonly ttl: number;
readonly maxEntries: number;
readonly lru: boolean;
recordEvictions(count: number): void;
recordHits(count: number): void;
recordMisses(count: number): void;
constructor(config?: ClientSideCacheConfig);
handleCache(client: CachingClient, parser: BasicCommandParser, fn: CmdFunc, transformReply?: TransformReply, typeMapping?: TypeMapping): Promise<any>;
trackingOn(): string[];
invalidate(key: RedisArgument | null): void;
clear(resetStats?: boolean): void;
get(cacheKey: string): ClientSideCacheEntry | undefined;
delete(cacheKey: string): void;
has(cacheKey: string): boolean;
set(cacheKey: string, cacheEntry: ClientSideCacheEntry, keys: Array<RedisArgument>): void;
size(): number;
createValueEntry(client: CachingClient, value: any): ClientSideCacheEntryValue;
createPromiseEntry(client: CachingClient, sendCommandPromise: Promise<ReplyUnion>): ClientSideCacheEntryPromise;
stats(): CacheStats;
onError(): void;
onClose(): void;
/**
* @internal
*/
deleteOldest(): void;
/**
* Get cache entries for debugging
* @internal
*/
entryEntries(): IterableIterator<[string, ClientSideCacheEntry]>;
/**
* Get key set entries for debugging
* @internal
*/
keySetEntries(): IterableIterator<[string, Set<string>]>;
}
export declare abstract class PooledClientSideCacheProvider extends BasicClientSideCache {
#private;
disable(): void;
enable(): void;
get(cacheKey: string): ClientSideCacheEntry | undefined;
has(cacheKey: string): boolean;
onPoolClose(): void;
}
export declare class BasicPooledClientSideCache extends PooledClientSideCacheProvider {
onError(): void;
onClose(): void;
}
export declare class PooledNoRedirectClientSideCache extends BasicPooledClientSideCache {
createValueEntry(client: CachingClient, value: any): ClientSideCacheEntryValue;
createPromiseEntry(client: CachingClient, sendCommandPromise: Promise<ReplyUnion>): ClientSideCacheEntryPromise;
onError(): void;
onClose(): void;
}
export {};
//# sourceMappingURL=cache.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../../lib/client/cache.ts"],"names":[],"mappings":";;AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,WAAW,MAAM,GAAG,CAAC;AAC5B,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACvF,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAE9C;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,qBAAa,UAAU;aAKH,QAAQ,EAAE,MAAM;aAChB,SAAS,EAAE,MAAM;aACjB,gBAAgB,EAAE,MAAM;aACxB,gBAAgB,EAAE,MAAM;aACxB,aAAa,EAAE,MAAM;aACrB,aAAa,EAAE,MAAM;IATvC;;OAEG;IACH,OAAO;IAoBP;;;;;;;;;OASG;IACH,MAAM,CAAC,EAAE,CACP,QAAQ,SAAI,EACZ,SAAS,SAAI,EACb,gBAAgB,SAAI,EACpB,gBAAgB,SAAI,EACpB,aAAa,SAAI,EACjB,aAAa,SAAI,GAChB,UAAU;IAWb;;;;OAIG;IACH,MAAM,CAAC,KAAK,IAAI,UAAU;IAI1B;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAoC;IAEvE;;;;;MAKE;IACF,YAAY,IAAI,MAAM;IAItB;;;;;OAKG;IACH,OAAO,IAAI,MAAM;IAKjB;;;;;OAKG;IACH,QAAQ,IAAI,MAAM;IAKlB;;;;MAIE;IACF,SAAS,IAAI,MAAM;IAInB;;;;;OAKG;IACH,eAAe,IAAI,MAAM;IAKzB;;;;;OAKG;IACH,kBAAkB,IAAI,MAAM;IAK5B;;;;;;MAME;IACF,KAAK,CAAC,KAAK,EAAE,UAAU,GAAG,UAAU;IAWpC;;;;;OAKG;IACH,IAAI,CAAC,KAAK,EAAE,UAAU,GAAG,UAAU;CAUpC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,YAAY;IAC3B;;;;OAIG;IACH,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAEhC;;;;;OAKG;IACH,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAElC;;;;;OAKG;IACH,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IAE1C;;;;;;OAMG;IACH,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IAE1C;;;;;OAKG;IACH,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAErC;;;;;OAKG;IACH,QAAQ,IAAI,UAAU,CAAC;CACxB;AA+GD,KAAK,aAAa,GAAG,WAAW,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;AAC1D,KAAK,OAAO,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC,CAAC;AAEzC,KAAK,cAAc,GAAG,KAAK,GAAG,MAAM,CAAA;AAEpC;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC;;;;OAIG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,cAAc,CAAC;IAE7B;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAOD,UAAU,oBAAoB;IAC5B,UAAU,IAAI,IAAI,CAAC;IACnB,QAAQ,IAAI,OAAO,CAAC;CACrB;AAmBD,uBAAe,wBAAyB,YAAW,oBAAoB;;gBAIzD,GAAG,EAAE,MAAM;IAQvB,UAAU,IAAI,IAAI;IAIlB,QAAQ,IAAI,OAAO;CAGpB;AAED,cAAM,yBAA0B,SAAQ,wBAAwB;;IAG9D,IAAI,KAAK,QAER;gBAEW,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG;CAIpC;AAED,cAAM,2BAA4B,SAAQ,wBAAwB;;IAGhE,IAAI,OAAO,wBAEV;gBAEW,GAAG,EAAE,MAAM,EAAE,kBAAkB,EAAE,OAAO,CAAC,UAAU,CAAC;CAIjE;AAED,8BAAsB,uBAAwB,SAAQ,YAAY;IAChE,QAAQ,CAAC,WAAW,CAAC,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,kBAAkB,EAAE,EAAE,EAAE,OAAO,EAAE,cAAc,EAAE,cAAc,GAAG,SAAS,EAAE,WAAW,EAAE,WAAW,GAAG,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC;IACpL,QAAQ,CAAC,UAAU,IAAI,KAAK,CAAC,aAAa,CAAC;IAC3C,QAAQ,CAAC,UAAU,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,GAAG,IAAI;IACpD,QAAQ,CAAC,KAAK,IAAI,IAAI;IACtB,QAAQ,CAAC,KAAK,IAAI,UAAU;IAC5B,QAAQ,CAAC,OAAO,IAAI,IAAI;IACxB,QAAQ,CAAC,OAAO,IAAI,IAAI;CACzB;AAED,qBAAa,oBAAqB,SAAQ,uBAAuB;;IAG/D,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IAItB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAIpC,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAI/B,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;gBAIrB,MAAM,CAAC,EAAE,qBAAqB;IA6B3B,WAAW,CACxB,MAAM,EAAE,aAAa,EACrB,MAAM,EAAE,kBAAkB,EAC1B,EAAE,EAAE,OAAO,EACX,cAAc,CAAC,EAAE,cAAc,EAC/B,WAAW,CAAC,EAAE,WAAW;IAmElB,UAAU;IAIV,UAAU,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI;IAuBpC,KAAK,CAAC,UAAU,UAAO;IAiBhC,GAAG,CAAC,QAAQ,EAAE,MAAM;IAmBpB,MAAM,CAAC,QAAQ,EAAE,MAAM;IAQvB,GAAG,CAAC,QAAQ,EAAE,MAAM;IAIpB,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,oBAAoB,EAAE,IAAI,EAAE,KAAK,CAAC,aAAa,CAAC;IA0BlF,IAAI;IAIJ,gBAAgB,CAAC,MAAM,EAAE,aAAa,EAAE,KAAK,EAAE,GAAG,GAAG,yBAAyB;IAI9E,kBAAkB,CAAC,MAAM,EAAE,aAAa,EAAE,kBAAkB,EAAE,OAAO,CAAC,UAAU,CAAC,GAAG,2BAA2B;IAItG,KAAK,IAAI,UAAU;IAInB,OAAO,IAAI,IAAI;IAIf,OAAO;IAIhB;;OAEG;IACH,YAAY;IAaZ;;;OAGG;IACH,YAAY,IAAI,gBAAgB,CAAC,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;IAIhE;;;OAGG;IACH,aAAa,IAAI,gBAAgB,CAAC,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;CAGzD;AAED,8BAAsB,6BAA8B,SAAQ,oBAAoB;;IAG9E,OAAO,IAAI,IAAI;IAIf,MAAM,IAAI,IAAI;IAIL,GAAG,CAAC,QAAQ,EAAE,MAAM,GAAG,oBAAoB,GAAG,SAAS;IAQvD,GAAG,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAQvC,WAAW,IAAI,IAAI;CAGpB;AAED,qBAAa,0BAA2B,SAAQ,6BAA6B;IAClE,OAAO;IAIP,OAAO;CAGjB;AAqCD,qBAAa,+BAAgC,SAAQ,0BAA0B;IACpE,gBAAgB,CAAC,MAAM,EAAE,aAAa,EAAE,KAAK,EAAE,GAAG,GAAG,yBAAyB;IAS9E,kBAAkB,CAAC,MAAM,EAAE,aAAa,EAAE,kBAAkB,EAAE,OAAO,CAAC,UAAU,CAAC,GAAG,2BAA2B;IAS/G,OAAO;IAEP,OAAO;CACjB"}

614
node_modules/@redis/client/dist/lib/client/cache.js generated vendored Normal file
View File

@@ -0,0 +1,614 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.PooledNoRedirectClientSideCache = exports.BasicPooledClientSideCache = exports.PooledClientSideCacheProvider = exports.BasicClientSideCache = exports.ClientSideCacheProvider = exports.CacheStats = void 0;
const stream_1 = require("stream");
/**
* A snapshot of cache statistics.
*
* This class provides an immutable view of the cache's operational statistics at a particular
* point in time. It is heavily inspired by the statistics reporting capabilities found in
* Ben Manes's Caffeine cache (https://github.com/ben-manes/caffeine).
*
* Instances of `CacheStats` are typically obtained from a {@link StatsCounter} and can be used
* for performance monitoring, debugging, or logging. It includes metrics such as hit rate,
* miss rate, load success/failure rates, average load penalty, and eviction counts.
*
* All statistics are non-negative. Rates and averages are typically in the range `[0.0, 1.0]`,
* or `0` if the an operation has not occurred (e.g. hit rate is 0 if there are no requests).
*
* Cache statistics are incremented according to specific rules:
* - When a cache lookup encounters an existing entry, hitCount is incremented.
* - When a cache lookup encounters a missing entry, missCount is incremented.
* - When a new entry is successfully loaded, loadSuccessCount is incremented and the
* loading time is added to totalLoadTime.
* - When an entry fails to load, loadFailureCount is incremented and the
* loading time is added to totalLoadTime.
* - When an entry is evicted due to size constraints or expiration,
* evictionCount is incremented.
*/
class CacheStats {
hitCount;
missCount;
loadSuccessCount;
loadFailureCount;
totalLoadTime;
evictionCount;
/**
* Creates a new CacheStats instance with the specified statistics.
*/
constructor(hitCount, missCount, loadSuccessCount, loadFailureCount, totalLoadTime, evictionCount) {
this.hitCount = hitCount;
this.missCount = missCount;
this.loadSuccessCount = loadSuccessCount;
this.loadFailureCount = loadFailureCount;
this.totalLoadTime = totalLoadTime;
this.evictionCount = evictionCount;
if (hitCount < 0 ||
missCount < 0 ||
loadSuccessCount < 0 ||
loadFailureCount < 0 ||
totalLoadTime < 0 ||
evictionCount < 0) {
throw new Error('All statistics values must be non-negative');
}
}
/**
* Creates a new CacheStats instance with the specified statistics.
*
* @param hitCount - Number of cache hits
* @param missCount - Number of cache misses
* @param loadSuccessCount - Number of successful cache loads
* @param loadFailureCount - Number of failed cache loads
* @param totalLoadTime - Total load time in milliseconds
* @param evictionCount - Number of cache evictions
*/
static of(hitCount = 0, missCount = 0, loadSuccessCount = 0, loadFailureCount = 0, totalLoadTime = 0, evictionCount = 0) {
return new CacheStats(hitCount, missCount, loadSuccessCount, loadFailureCount, totalLoadTime, evictionCount);
}
/**
* Returns a statistics instance where no cache events have been recorded.
*
* @returns An empty statistics instance
*/
static empty() {
return CacheStats.EMPTY_STATS;
}
/**
* An empty stats instance with all counters set to zero.
*/
static EMPTY_STATS = new CacheStats(0, 0, 0, 0, 0, 0);
/**
* Returns the total number of times cache lookup methods have returned
* either a cached or uncached value.
*
* @returns Total number of requests (hits + misses)
*/
requestCount() {
return this.hitCount + this.missCount;
}
/**
* Returns the hit rate of the cache.
* This is defined as hitCount / requestCount, or 1.0 when requestCount is 0.
*
* @returns The ratio of cache requests that were hits (between 0.0 and 1.0)
*/
hitRate() {
const requestCount = this.requestCount();
return requestCount === 0 ? 1.0 : this.hitCount / requestCount;
}
/**
* Returns the miss rate of the cache.
* This is defined as missCount / requestCount, or 0.0 when requestCount is 0.
*
* @returns The ratio of cache requests that were misses (between 0.0 and 1.0)
*/
missRate() {
const requestCount = this.requestCount();
return requestCount === 0 ? 0.0 : this.missCount / requestCount;
}
/**
* Returns the total number of load operations (successful + failed).
*
* @returns Total number of load operations
*/
loadCount() {
return this.loadSuccessCount + this.loadFailureCount;
}
/**
* Returns the ratio of cache loading attempts that failed.
* This is defined as loadFailureCount / loadCount, or 0.0 when loadCount is 0.
*
* @returns Ratio of load operations that failed (between 0.0 and 1.0)
*/
loadFailureRate() {
const loadCount = this.loadCount();
return loadCount === 0 ? 0.0 : this.loadFailureCount / loadCount;
}
/**
* Returns the average time spent loading new values, in milliseconds.
* This is defined as totalLoadTime / loadCount, or 0.0 when loadCount is 0.
*
* @returns Average load time in milliseconds
*/
averageLoadPenalty() {
const loadCount = this.loadCount();
return loadCount === 0 ? 0.0 : this.totalLoadTime / loadCount;
}
/**
* Returns a new CacheStats representing the difference between this CacheStats
* and another. Negative values are rounded up to zero.
*
* @param other - The statistics to subtract from this instance
* @returns The difference between this instance and other
*/
minus(other) {
return CacheStats.of(Math.max(0, this.hitCount - other.hitCount), Math.max(0, this.missCount - other.missCount), Math.max(0, this.loadSuccessCount - other.loadSuccessCount), Math.max(0, this.loadFailureCount - other.loadFailureCount), Math.max(0, this.totalLoadTime - other.totalLoadTime), Math.max(0, this.evictionCount - other.evictionCount));
}
/**
* Returns a new CacheStats representing the sum of this CacheStats and another.
*
* @param other - The statistics to add to this instance
* @returns The sum of this instance and other
*/
plus(other) {
return CacheStats.of(this.hitCount + other.hitCount, this.missCount + other.missCount, this.loadSuccessCount + other.loadSuccessCount, this.loadFailureCount + other.loadFailureCount, this.totalLoadTime + other.totalLoadTime, this.evictionCount + other.evictionCount);
}
}
exports.CacheStats = CacheStats;
/**
* A StatsCounter implementation that does nothing and always returns empty stats.
*/
class DisabledStatsCounter {
static INSTANCE = new DisabledStatsCounter();
constructor() { }
recordHits(count) { }
recordMisses(count) { }
recordLoadSuccess(loadTime) { }
recordLoadFailure(loadTime) { }
recordEvictions(count) { }
snapshot() { return CacheStats.empty(); }
}
/**
* Returns a StatsCounter that does not record any cache events.
*
* @return A StatsCounter that does not record metrics
*/
function disabledStatsCounter() {
return DisabledStatsCounter.INSTANCE;
}
/**
* A StatsCounter implementation that maintains cache statistics.
*/
class DefaultStatsCounter {
#hitCount = 0;
#missCount = 0;
#loadSuccessCount = 0;
#loadFailureCount = 0;
#totalLoadTime = 0;
#evictionCount = 0;
/**
* Records cache hits.
*
* @param count - The number of hits to record
*/
recordHits(count) {
this.#hitCount += count;
}
/**
* Records cache misses.
*
* @param count - The number of misses to record
*/
recordMisses(count) {
this.#missCount += count;
}
/**
* Records the successful load of a new entry.
*
* @param loadTime - The number of milliseconds spent loading the entry
*/
recordLoadSuccess(loadTime) {
this.#loadSuccessCount++;
this.#totalLoadTime += loadTime;
}
/**
* Records the failed load of a new entry.
*
* @param loadTime - The number of milliseconds spent attempting to load the entry
*/
recordLoadFailure(loadTime) {
this.#loadFailureCount++;
this.#totalLoadTime += loadTime;
}
/**
* Records cache evictions.
*
* @param count - The number of evictions to record
*/
recordEvictions(count) {
this.#evictionCount += count;
}
/**
* Returns a snapshot of the current statistics.
*
* @returns A snapshot of the current statistics
*/
snapshot() {
return CacheStats.of(this.#hitCount, this.#missCount, this.#loadSuccessCount, this.#loadFailureCount, this.#totalLoadTime, this.#evictionCount);
}
/**
* Creates a new DefaultStatsCounter.
*
* @returns A new DefaultStatsCounter instance
*/
static create() {
return new DefaultStatsCounter();
}
}
/**
* Generates a unique cache key from Redis command arguments
*
* @param redisArgs - Array of Redis command arguments
* @returns A unique string key for caching
*/
function generateCacheKey(redisArgs) {
const tmp = new Array(redisArgs.length * 2);
for (let i = 0; i < redisArgs.length; i++) {
tmp[i] = redisArgs[i].length;
tmp[i + redisArgs.length] = redisArgs[i];
}
return tmp.join('_');
}
class ClientSideCacheEntryBase {
#invalidated = false;
#expireTime;
constructor(ttl) {
if (ttl == 0) {
this.#expireTime = 0;
}
else {
this.#expireTime = Date.now() + ttl;
}
}
invalidate() {
this.#invalidated = true;
}
validate() {
return !this.#invalidated && (this.#expireTime == 0 || (Date.now() < this.#expireTime));
}
}
class ClientSideCacheEntryValue extends ClientSideCacheEntryBase {
#value;
get value() {
return this.#value;
}
constructor(ttl, value) {
super(ttl);
this.#value = value;
}
}
class ClientSideCacheEntryPromise extends ClientSideCacheEntryBase {
#sendCommandPromise;
get promise() {
return this.#sendCommandPromise;
}
constructor(ttl, sendCommandPromise) {
super(ttl);
this.#sendCommandPromise = sendCommandPromise;
}
}
class ClientSideCacheProvider extends stream_1.EventEmitter {
}
exports.ClientSideCacheProvider = ClientSideCacheProvider;
class BasicClientSideCache extends ClientSideCacheProvider {
#cacheKeyToEntryMap;
#keyToCacheKeySetMap;
ttl;
maxEntries;
lru;
#statsCounter;
recordEvictions(count) {
this.#statsCounter.recordEvictions(count);
}
recordHits(count) {
this.#statsCounter.recordHits(count);
}
recordMisses(count) {
this.#statsCounter.recordMisses(count);
}
constructor(config) {
super();
this.#cacheKeyToEntryMap = new Map();
this.#keyToCacheKeySetMap = new Map();
this.ttl = config?.ttl ?? 0;
this.maxEntries = config?.maxEntries ?? 0;
this.lru = config?.evictPolicy !== "FIFO";
const recordStats = config?.recordStats !== false;
this.#statsCounter = recordStats ? DefaultStatsCounter.create() : disabledStatsCounter();
}
/* logic of how caching works:
1. commands use a CommandParser
it enables us to define/retrieve
cacheKey - a unique key that corresponds to this command and its arguments
redisKeys - an array of redis keys as strings that if the key is modified, will cause redis to invalidate this result when cached
2. check if cacheKey is in our cache
2b1. if its a value cacheEntry - return it
2b2. if it's a promise cache entry - wait on promise and then go to 3c.
3. if cacheEntry is not in cache
3a. send the command save the promise into a a cacheEntry and then wait on result
3b. transform reply (if required) based on transformReply
3b. check the cacheEntry is still valid - in cache and hasn't been deleted)
3c. if valid - overwrite with value entry
4. return previously non cached result
*/
async handleCache(client, parser, fn, transformReply, typeMapping) {
let reply;
const cacheKey = generateCacheKey(parser.redisArgs);
// "2"
let cacheEntry = this.get(cacheKey);
if (cacheEntry) {
// If instanceof is "too slow", can add a "type" and then use an "as" cast to call proper getters.
if (cacheEntry instanceof ClientSideCacheEntryValue) { // "2b1"
this.#statsCounter.recordHits(1);
return structuredClone(cacheEntry.value);
}
else if (cacheEntry instanceof ClientSideCacheEntryPromise) { // 2b2
// This counts as a miss since the value hasn't been fully loaded yet.
this.#statsCounter.recordMisses(1);
reply = await cacheEntry.promise;
}
else {
throw new Error("unknown cache entry type");
}
}
else { // 3/3a
this.#statsCounter.recordMisses(1);
const startTime = performance.now();
const promise = fn();
cacheEntry = this.createPromiseEntry(client, promise);
this.set(cacheKey, cacheEntry, parser.keys);
try {
reply = await promise;
const loadTime = performance.now() - startTime;
this.#statsCounter.recordLoadSuccess(loadTime);
}
catch (err) {
const loadTime = performance.now() - startTime;
this.#statsCounter.recordLoadFailure(loadTime);
if (cacheEntry.validate()) {
this.delete(cacheKey);
}
throw err;
}
}
// 3b
let val;
if (transformReply) {
val = transformReply(reply, parser.preserve, typeMapping);
}
else {
val = reply;
}
// 3c
if (cacheEntry.validate()) { // revalidating promise entry (dont save value, if promise entry has been invalidated)
// 3d
cacheEntry = this.createValueEntry(client, val);
this.set(cacheKey, cacheEntry, parser.keys);
this.emit("cached-key", cacheKey);
}
else {
// cache entry for key got invalidated between execution and saving, so not saving
}
return structuredClone(val);
}
trackingOn() {
return ['CLIENT', 'TRACKING', 'ON'];
}
invalidate(key) {
if (key === null) {
this.clear(false);
this.emit("invalidate", key);
return;
}
const keySet = this.#keyToCacheKeySetMap.get(key.toString());
if (keySet) {
for (const cacheKey of keySet) {
const entry = this.#cacheKeyToEntryMap.get(cacheKey);
if (entry) {
entry.invalidate();
}
this.#cacheKeyToEntryMap.delete(cacheKey);
}
this.#keyToCacheKeySetMap.delete(key.toString());
}
this.emit('invalidate', key);
}
clear(resetStats = true) {
const oldSize = this.#cacheKeyToEntryMap.size;
this.#cacheKeyToEntryMap.clear();
this.#keyToCacheKeySetMap.clear();
if (resetStats) {
if (!(this.#statsCounter instanceof DisabledStatsCounter)) {
this.#statsCounter = DefaultStatsCounter.create();
}
}
else {
// If old entries were evicted due to clear, record them as evictions
if (oldSize > 0) {
this.#statsCounter.recordEvictions(oldSize);
}
}
}
get(cacheKey) {
const val = this.#cacheKeyToEntryMap.get(cacheKey);
if (val && !val.validate()) {
this.delete(cacheKey);
this.#statsCounter.recordEvictions(1);
this.emit("cache-evict", cacheKey);
return undefined;
}
if (val !== undefined && this.lru) {
this.#cacheKeyToEntryMap.delete(cacheKey);
this.#cacheKeyToEntryMap.set(cacheKey, val);
}
return val;
}
delete(cacheKey) {
const entry = this.#cacheKeyToEntryMap.get(cacheKey);
if (entry) {
entry.invalidate();
this.#cacheKeyToEntryMap.delete(cacheKey);
}
}
has(cacheKey) {
return this.#cacheKeyToEntryMap.has(cacheKey);
}
set(cacheKey, cacheEntry, keys) {
let count = this.#cacheKeyToEntryMap.size;
const oldEntry = this.#cacheKeyToEntryMap.get(cacheKey);
if (oldEntry) {
count--; // overwriting, so not incrementig
oldEntry.invalidate();
}
if (this.maxEntries > 0 && count >= this.maxEntries) {
this.deleteOldest();
this.#statsCounter.recordEvictions(1);
}
this.#cacheKeyToEntryMap.set(cacheKey, cacheEntry);
for (const key of keys) {
if (!this.#keyToCacheKeySetMap.has(key.toString())) {
this.#keyToCacheKeySetMap.set(key.toString(), new Set());
}
const cacheKeySet = this.#keyToCacheKeySetMap.get(key.toString());
cacheKeySet.add(cacheKey);
}
}
size() {
return this.#cacheKeyToEntryMap.size;
}
createValueEntry(client, value) {
return new ClientSideCacheEntryValue(this.ttl, value);
}
createPromiseEntry(client, sendCommandPromise) {
return new ClientSideCacheEntryPromise(this.ttl, sendCommandPromise);
}
stats() {
return this.#statsCounter.snapshot();
}
onError() {
this.clear();
}
onClose() {
this.clear();
}
/**
* @internal
*/
deleteOldest() {
const it = this.#cacheKeyToEntryMap[Symbol.iterator]();
const n = it.next();
if (!n.done) {
const key = n.value[0];
const entry = this.#cacheKeyToEntryMap.get(key);
if (entry) {
entry.invalidate();
}
this.#cacheKeyToEntryMap.delete(key);
}
}
/**
* Get cache entries for debugging
* @internal
*/
entryEntries() {
return this.#cacheKeyToEntryMap.entries();
}
/**
* Get key set entries for debugging
* @internal
*/
keySetEntries() {
return this.#keyToCacheKeySetMap.entries();
}
}
exports.BasicClientSideCache = BasicClientSideCache;
class PooledClientSideCacheProvider extends BasicClientSideCache {
#disabled = false;
disable() {
this.#disabled = true;
}
enable() {
this.#disabled = false;
}
get(cacheKey) {
if (this.#disabled) {
return undefined;
}
return super.get(cacheKey);
}
has(cacheKey) {
if (this.#disabled) {
return false;
}
return super.has(cacheKey);
}
onPoolClose() {
this.clear();
}
}
exports.PooledClientSideCacheProvider = PooledClientSideCacheProvider;
class BasicPooledClientSideCache extends PooledClientSideCacheProvider {
onError() {
this.clear(false);
}
onClose() {
this.clear(false);
}
}
exports.BasicPooledClientSideCache = BasicPooledClientSideCache;
class PooledClientSideCacheEntryValue extends ClientSideCacheEntryValue {
#creator;
constructor(ttl, creator, value) {
super(ttl, value);
this.#creator = creator;
}
validate() {
let ret = super.validate();
if (this.#creator) {
ret = ret && this.#creator.client.isReady && this.#creator.client.socketEpoch == this.#creator.epoch;
}
return ret;
}
}
class PooledClientSideCacheEntryPromise extends ClientSideCacheEntryPromise {
#creator;
constructor(ttl, creator, sendCommandPromise) {
super(ttl, sendCommandPromise);
this.#creator = creator;
}
validate() {
let ret = super.validate();
return ret && this.#creator.client.isReady && this.#creator.client.socketEpoch == this.#creator.epoch;
}
}
class PooledNoRedirectClientSideCache extends BasicPooledClientSideCache {
createValueEntry(client, value) {
const creator = {
epoch: client.socketEpoch,
client: client
};
return new PooledClientSideCacheEntryValue(this.ttl, creator, value);
}
createPromiseEntry(client, sendCommandPromise) {
const creator = {
epoch: client.socketEpoch,
client: client
};
return new PooledClientSideCacheEntryPromise(this.ttl, creator, sendCommandPromise);
}
onError() { }
onClose() { }
}
exports.PooledNoRedirectClientSideCache = PooledNoRedirectClientSideCache;
//# sourceMappingURL=cache.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,70 @@
/// <reference types="node" />
import { Decoder } from '../RESP/decoder';
import { TypeMapping, RespVersions, RedisArgument } from '../RESP/types';
import { ChannelListeners, PubSubListener, PubSubType, PubSubTypeListeners } from './pub-sub';
import { MonitorCallback } from '.';
export interface CommandOptions<T = TypeMapping> {
chainId?: symbol;
asap?: boolean;
abortSignal?: AbortSignal;
/**
* Maps between RESP and JavaScript types
*/
typeMapping?: T;
/**
* Timeout for the command in milliseconds
*/
timeout?: number;
}
export interface CommandToWrite extends CommandWaitingForReply {
args: ReadonlyArray<RedisArgument>;
chainId: symbol | undefined;
abort: {
signal: AbortSignal;
listener: () => unknown;
} | undefined;
timeout: {
signal: AbortSignal;
listener: () => unknown;
originalTimeout: number | undefined;
} | undefined;
}
interface CommandWaitingForReply {
resolve(reply?: unknown): void;
reject(err: unknown): void;
channelsCounter: number | undefined;
typeMapping: TypeMapping | undefined;
}
export type OnShardedChannelMoved = (channel: string, listeners: ChannelListeners) => void;
type PushHandler = (pushItems: Array<any>) => boolean;
export default class RedisCommandsQueue {
#private;
readonly decoder: Decoder;
setMaintenanceCommandTimeout(ms: number | undefined): void;
get isPubSubActive(): boolean;
constructor(respVersion: RespVersions, maxLength: number | null | undefined, onShardedChannelMoved: OnShardedChannelMoved);
addPushHandler(handler: PushHandler): void;
waitForInflightCommandsToComplete(): Promise<void>;
addCommand<T>(args: ReadonlyArray<RedisArgument>, options?: CommandOptions): Promise<T>;
subscribe<T extends boolean>(type: PubSubType, channels: string | Array<string>, listener: PubSubListener<T>, returnBuffers?: T): Promise<void> | undefined;
unsubscribe<T extends boolean>(type: PubSubType, channels?: string | Array<string>, listener?: PubSubListener<T>, returnBuffers?: T): Promise<void> | undefined;
removeAllPubSubListeners(): {
CHANNELS: PubSubTypeListeners;
PATTERNS: PubSubTypeListeners;
SHARDED: PubSubTypeListeners;
};
resubscribe(chainId?: symbol): Promise<void[]> | undefined;
extendPubSubChannelListeners(type: PubSubType, channel: string, listeners: ChannelListeners): Promise<void> | undefined;
extendPubSubListeners(type: PubSubType, listeners: PubSubTypeListeners): Promise<void> | undefined;
getPubSubListeners(type: PubSubType): PubSubTypeListeners;
monitor(callback: MonitorCallback, options?: CommandOptions): Promise<void>;
resetDecoder(): void;
reset<T extends TypeMapping>(chainId: symbol, typeMapping?: T): Promise<unknown>;
isWaitingToWrite(): boolean;
commandsToWrite(): Generator<readonly RedisArgument[], void, unknown>;
flushWaitingForReply(err: Error): void;
flushAll(err: Error): void;
isEmpty(): boolean;
}
export {};
//# sourceMappingURL=commands-queue.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"commands-queue.d.ts","sourceRoot":"","sources":["../../../lib/client/commands-queue.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,411 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const linked_list_1 = require("./linked-list");
const encoder_1 = __importDefault(require("../RESP/encoder"));
const decoder_1 = require("../RESP/decoder");
const pub_sub_1 = require("./pub-sub");
const errors_1 = require("../errors");
const enterprise_maintenance_manager_1 = require("./enterprise-maintenance-manager");
const PONG = Buffer.from('pong'), RESET = Buffer.from('RESET');
const RESP2_PUSH_TYPE_MAPPING = {
...decoder_1.PUSH_TYPE_MAPPING,
[decoder_1.RESP_TYPES.SIMPLE_STRING]: Buffer
};
class RedisCommandsQueue {
#respVersion;
#maxLength;
#toWrite = new linked_list_1.DoublyLinkedList();
#waitingForReply = new linked_list_1.EmptyAwareSinglyLinkedList();
#onShardedChannelMoved;
#chainInExecution;
decoder;
#pubSub = new pub_sub_1.PubSub();
#pushHandlers = [this.#onPush.bind(this)];
#maintenanceCommandTimeout;
setMaintenanceCommandTimeout(ms) {
// Prevent possible api misuse
if (this.#maintenanceCommandTimeout === ms) {
(0, enterprise_maintenance_manager_1.dbgMaintenance)(`Queue already set maintenanceCommandTimeout to ${ms}, skipping`);
return;
}
;
(0, enterprise_maintenance_manager_1.dbgMaintenance)(`Setting maintenance command timeout to ${ms}`);
this.#maintenanceCommandTimeout = ms;
if (this.#maintenanceCommandTimeout === undefined) {
(0, enterprise_maintenance_manager_1.dbgMaintenance)(`Queue will keep maintenanceCommandTimeout for exisitng commands, just to be on the safe side. New commands will receive normal timeouts`);
return;
}
let counter = 0;
const total = this.#toWrite.length;
// Overwrite timeouts of all eligible toWrite commands
for (const node of this.#toWrite.nodes()) {
const command = node.value;
// Remove timeout listener if it exists
RedisCommandsQueue.#removeTimeoutListener(command);
counter++;
const newTimeout = this.#maintenanceCommandTimeout;
// Overwrite the command's timeout
const signal = AbortSignal.timeout(newTimeout);
command.timeout = {
signal,
listener: () => {
this.#toWrite.remove(node);
command.reject(new errors_1.CommandTimeoutDuringMaintenanceError(newTimeout));
},
originalTimeout: command.timeout?.originalTimeout
};
signal.addEventListener('abort', command.timeout.listener, { once: true });
}
;
(0, enterprise_maintenance_manager_1.dbgMaintenance)(`Total of ${counter} of ${total} timeouts reset to ${ms}`);
}
get isPubSubActive() {
return this.#pubSub.isActive;
}
constructor(respVersion, maxLength, onShardedChannelMoved) {
this.#respVersion = respVersion;
this.#maxLength = maxLength;
this.#onShardedChannelMoved = onShardedChannelMoved;
this.decoder = this.#initiateDecoder();
}
#onReply(reply) {
this.#waitingForReply.shift().resolve(reply);
}
#onErrorReply(err) {
this.#waitingForReply.shift().reject(err);
}
#onPush(push) {
// TODO: type
if (this.#pubSub.handleMessageReply(push))
return true;
const isShardedUnsubscribe = pub_sub_1.PubSub.isShardedUnsubscribe(push);
if (isShardedUnsubscribe && !this.#waitingForReply.length) {
const channel = push[1].toString();
this.#onShardedChannelMoved(channel, this.#pubSub.removeShardedListeners(channel));
return true;
}
else if (isShardedUnsubscribe || pub_sub_1.PubSub.isStatusReply(push)) {
const head = this.#waitingForReply.head.value;
if ((Number.isNaN(head.channelsCounter) && push[2] === 0) ||
--head.channelsCounter === 0) {
this.#waitingForReply.shift().resolve();
}
return true;
}
return false;
}
#getTypeMapping() {
return this.#waitingForReply.head.value.typeMapping ?? {};
}
#initiateDecoder() {
return new decoder_1.Decoder({
onReply: reply => this.#onReply(reply),
onErrorReply: err => this.#onErrorReply(err),
//TODO: we can shave off a few cycles by not adding onPush handler at all if CSC is not used
onPush: push => {
for (const pushHandler of this.#pushHandlers) {
if (pushHandler(push))
return;
}
},
getTypeMapping: () => this.#getTypeMapping()
});
}
addPushHandler(handler) {
this.#pushHandlers.push(handler);
}
async waitForInflightCommandsToComplete() {
// In-flight commands already completed
if (this.#waitingForReply.length === 0) {
return;
}
;
// Otherwise wait for in-flight commands to fire `empty` event
return new Promise(resolve => {
this.#waitingForReply.events.on('empty', resolve);
});
}
addCommand(args, options) {
if (this.#maxLength && this.#toWrite.length + this.#waitingForReply.length >= this.#maxLength) {
return Promise.reject(new Error('The queue is full'));
}
else if (options?.abortSignal?.aborted) {
return Promise.reject(new errors_1.AbortError());
}
return new Promise((resolve, reject) => {
let node;
const value = {
args,
chainId: options?.chainId,
abort: undefined,
timeout: undefined,
resolve,
reject,
channelsCounter: undefined,
typeMapping: options?.typeMapping
};
// If #maintenanceCommandTimeout was explicitly set, we should
// use it instead of the timeout provided by the command
const timeout = this.#maintenanceCommandTimeout ?? options?.timeout;
const wasInMaintenance = this.#maintenanceCommandTimeout !== undefined;
if (timeout) {
const signal = AbortSignal.timeout(timeout);
value.timeout = {
signal,
listener: () => {
this.#toWrite.remove(node);
value.reject(wasInMaintenance ? new errors_1.CommandTimeoutDuringMaintenanceError(timeout) : new errors_1.TimeoutError());
},
originalTimeout: options?.timeout
};
signal.addEventListener('abort', value.timeout.listener, { once: true });
}
const signal = options?.abortSignal;
if (signal) {
value.abort = {
signal,
listener: () => {
this.#toWrite.remove(node);
value.reject(new errors_1.AbortError());
}
};
signal.addEventListener('abort', value.abort.listener, { once: true });
}
node = this.#toWrite.add(value, options?.asap);
});
}
#addPubSubCommand(command, asap = false, chainId) {
return new Promise((resolve, reject) => {
this.#toWrite.add({
args: command.args,
chainId,
abort: undefined,
timeout: undefined,
resolve() {
command.resolve();
resolve();
},
reject(err) {
command.reject?.();
reject(err);
},
channelsCounter: command.channelsCounter,
typeMapping: decoder_1.PUSH_TYPE_MAPPING
}, asap);
});
}
#setupPubSubHandler() {
// RESP3 uses `onPush` to handle PubSub, so no need to modify `onReply`
if (this.#respVersion !== 2)
return;
this.decoder.onReply = (reply => {
if (Array.isArray(reply)) {
if (this.#onPush(reply))
return;
if (PONG.equals(reply[0])) {
const { resolve, typeMapping } = this.#waitingForReply.shift(), buffer = (reply[1].length === 0 ? reply[0] : reply[1]);
resolve(typeMapping?.[decoder_1.RESP_TYPES.SIMPLE_STRING] === Buffer ? buffer : buffer.toString());
return;
}
}
return this.#onReply(reply);
});
this.decoder.getTypeMapping = () => RESP2_PUSH_TYPE_MAPPING;
}
subscribe(type, channels, listener, returnBuffers) {
const command = this.#pubSub.subscribe(type, channels, listener, returnBuffers);
if (!command)
return;
this.#setupPubSubHandler();
return this.#addPubSubCommand(command);
}
#resetDecoderCallbacks() {
this.decoder.onReply = (reply => this.#onReply(reply));
this.decoder.getTypeMapping = () => this.#getTypeMapping();
}
unsubscribe(type, channels, listener, returnBuffers) {
const command = this.#pubSub.unsubscribe(type, channels, listener, returnBuffers);
if (!command)
return;
if (command && this.#respVersion === 2) {
// RESP2 modifies `onReply` to handle PubSub (see #setupPubSubHandler)
const { resolve } = command;
command.resolve = () => {
if (!this.#pubSub.isActive) {
this.#resetDecoderCallbacks();
}
resolve();
};
}
return this.#addPubSubCommand(command);
}
removeAllPubSubListeners() {
return this.#pubSub.removeAllListeners();
}
resubscribe(chainId) {
const commands = this.#pubSub.resubscribe();
if (!commands.length)
return;
this.#setupPubSubHandler();
return Promise.all(commands.map(command => this.#addPubSubCommand(command, true, chainId)));
}
extendPubSubChannelListeners(type, channel, listeners) {
const command = this.#pubSub.extendChannelListeners(type, channel, listeners);
if (!command)
return;
this.#setupPubSubHandler();
return this.#addPubSubCommand(command);
}
extendPubSubListeners(type, listeners) {
const command = this.#pubSub.extendTypeListeners(type, listeners);
if (!command)
return;
this.#setupPubSubHandler();
return this.#addPubSubCommand(command);
}
getPubSubListeners(type) {
return this.#pubSub.listeners[type];
}
monitor(callback, options) {
return new Promise((resolve, reject) => {
const typeMapping = options?.typeMapping ?? {};
this.#toWrite.add({
args: ['MONITOR'],
chainId: options?.chainId,
abort: undefined,
timeout: undefined,
// using `resolve` instead of using `.then`/`await` to make sure it'll be called before processing the next reply
resolve: () => {
// after running `MONITOR` only `MONITOR` and `RESET` replies are expected
// any other command should cause an error
// if `RESET` already overrides `onReply`, set monitor as it's fallback
if (this.#resetFallbackOnReply) {
this.#resetFallbackOnReply = callback;
}
else {
this.decoder.onReply = callback;
}
this.decoder.getTypeMapping = () => typeMapping;
resolve();
},
reject,
channelsCounter: undefined,
typeMapping
}, options?.asap);
});
}
resetDecoder() {
this.#resetDecoderCallbacks();
this.decoder.reset();
}
#resetFallbackOnReply;
async reset(chainId, typeMapping) {
return new Promise((resolve, reject) => {
// overriding onReply to handle `RESET` while in `MONITOR` or PubSub mode
this.#resetFallbackOnReply = this.decoder.onReply;
this.decoder.onReply = (reply => {
if ((typeof reply === 'string' && reply === 'RESET') ||
(reply instanceof Buffer && RESET.equals(reply))) {
this.#resetDecoderCallbacks();
this.#resetFallbackOnReply = undefined;
this.#pubSub.reset();
this.#waitingForReply.shift().resolve(reply);
return;
}
this.#resetFallbackOnReply(reply);
});
this.#toWrite.push({
args: ['RESET'],
chainId,
abort: undefined,
timeout: undefined,
resolve,
reject,
channelsCounter: undefined,
typeMapping
});
});
}
isWaitingToWrite() {
return this.#toWrite.length > 0;
}
*commandsToWrite() {
let toSend = this.#toWrite.shift();
while (toSend) {
let encoded;
try {
encoded = (0, encoder_1.default)(toSend.args);
}
catch (err) {
toSend.reject(err);
toSend = this.#toWrite.shift();
continue;
}
// TODO reuse `toSend` or create new object?
toSend.args = undefined;
if (toSend.abort) {
RedisCommandsQueue.#removeAbortListener(toSend);
toSend.abort = undefined;
}
if (toSend.timeout) {
RedisCommandsQueue.#removeTimeoutListener(toSend);
toSend.timeout = undefined;
}
this.#chainInExecution = toSend.chainId;
toSend.chainId = undefined;
this.#waitingForReply.push(toSend);
yield encoded;
toSend = this.#toWrite.shift();
}
}
#flushWaitingForReply(err) {
for (const node of this.#waitingForReply) {
node.reject(err);
}
this.#waitingForReply.reset();
}
static #removeAbortListener(command) {
command.abort.signal.removeEventListener('abort', command.abort.listener);
}
static #removeTimeoutListener(command) {
command.timeout?.signal.removeEventListener('abort', command.timeout.listener);
}
static #flushToWrite(toBeSent, err) {
if (toBeSent.abort) {
RedisCommandsQueue.#removeAbortListener(toBeSent);
}
if (toBeSent.timeout) {
RedisCommandsQueue.#removeTimeoutListener(toBeSent);
}
toBeSent.reject(err);
}
flushWaitingForReply(err) {
this.resetDecoder();
this.#pubSub.reset();
this.#flushWaitingForReply(err);
if (!this.#chainInExecution)
return;
while (this.#toWrite.head?.value.chainId === this.#chainInExecution) {
RedisCommandsQueue.#flushToWrite(this.#toWrite.shift(), err);
}
this.#chainInExecution = undefined;
}
flushAll(err) {
this.resetDecoder();
this.#pubSub.reset();
this.#flushWaitingForReply(err);
for (const node of this.#toWrite) {
RedisCommandsQueue.#flushToWrite(node, err);
}
this.#toWrite.reset();
}
isEmpty() {
return (this.#toWrite.length === 0 &&
this.#waitingForReply.length === 0);
}
}
exports.default = RedisCommandsQueue;
//# sourceMappingURL=commands-queue.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,43 @@
import { RedisClientOptions } from ".";
import RedisCommandsQueue from "./commands-queue";
import { RedisArgument } from "../..";
import RedisSocket from "./socket";
export declare const MAINTENANCE_EVENTS: {
readonly PAUSE_WRITING: "pause-writing";
readonly RESUME_WRITING: "resume-writing";
readonly TIMEOUTS_UPDATE: "timeouts-update";
};
export type DiagnosticsEvent = {
type: string;
timestamp: number;
data?: Object;
};
export declare const dbgMaintenance: (...args: any[]) => void;
export declare const emitDiagnostics: (event: DiagnosticsEvent) => void;
export interface MaintenanceUpdate {
relaxedCommandTimeout?: number;
relaxedSocketTimeout?: number;
}
interface Client {
_ejectSocket: () => RedisSocket;
_insertSocket: (socket: RedisSocket) => void;
_pause: () => void;
_unpause: () => void;
_maintenanceUpdate: (update: MaintenanceUpdate) => void;
duplicate: () => Client;
connect: () => Promise<Client>;
destroy: () => void;
on: (event: string, callback: (value: unknown) => void) => void;
}
export default class EnterpriseMaintenanceManager {
#private;
static setupDefaultMaintOptions(options: RedisClientOptions): void;
static getHandshakeCommand(options: RedisClientOptions): Promise<{
cmd: Array<RedisArgument>;
errorHandler: (error: Error) => void;
} | undefined>;
constructor(commandsQueue: RedisCommandsQueue, client: Client, options: RedisClientOptions);
}
export type MovingEndpointType = "auto" | "internal-ip" | "internal-fqdn" | "external-ip" | "external-fqdn" | "none";
export {};
//# sourceMappingURL=enterprise-maintenance-manager.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"enterprise-maintenance-manager.d.ts","sourceRoot":"","sources":["../../../lib/client/enterprise-maintenance-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,GAAG,CAAC;AACvC,OAAO,kBAAkB,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAKtC,OAAO,WAAsC,MAAM,UAAU,CAAC;AAG9D,eAAO,MAAM,kBAAkB;;;;CAIrB,CAAC;AAUX,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,eAAO,MAAM,cAAc,YAAa,GAAG,EAAE,SAG5C,CAAC;AAEF,eAAO,MAAM,eAAe,UAAW,gBAAgB,SAKtD,CAAC;AAEF,MAAM,WAAW,iBAAiB;IAChC,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,UAAU,MAAM;IACd,YAAY,EAAE,MAAM,WAAW,CAAC;IAChC,aAAa,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,IAAI,CAAC;IAC7C,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,kBAAkB,EAAE,CAAC,MAAM,EAAE,iBAAiB,KAAK,IAAI,CAAC;IACxD,SAAS,EAAE,MAAM,MAAM,CAAC;IACxB,OAAO,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IAC/B,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,EAAE,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,KAAK,IAAI,CAAC;CACjE;AAED,MAAM,CAAC,OAAO,OAAO,4BAA4B;;IAM/C,MAAM,CAAC,wBAAwB,CAAC,OAAO,EAAE,kBAAkB;WAgB9C,mBAAmB,CAC9B,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CACN;QAAE,GAAG,EAAE,KAAK,CAAC,aAAa,CAAC,CAAC;QAAC,YAAY,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAA;KAAE,GACnE,SAAS,CACZ;gBA8BC,aAAa,EAAE,kBAAkB,EACjC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,kBAAkB;CAuL9B;AAED,MAAM,MAAM,kBAAkB,GAC1B,MAAM,GACN,aAAa,GACb,eAAe,GACf,aAAa,GACb,eAAe,GACf,MAAM,CAAC"}

View File

@@ -0,0 +1,280 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.emitDiagnostics = exports.dbgMaintenance = exports.MAINTENANCE_EVENTS = void 0;
const net_1 = require("net");
const promises_1 = require("dns/promises");
const node_assert_1 = __importDefault(require("node:assert"));
const promises_2 = require("node:timers/promises");
const node_diagnostics_channel_1 = __importDefault(require("node:diagnostics_channel"));
exports.MAINTENANCE_EVENTS = {
PAUSE_WRITING: "pause-writing",
RESUME_WRITING: "resume-writing",
TIMEOUTS_UPDATE: "timeouts-update",
};
const PN = {
MOVING: "MOVING",
MIGRATING: "MIGRATING",
MIGRATED: "MIGRATED",
FAILING_OVER: "FAILING_OVER",
FAILED_OVER: "FAILED_OVER",
};
const dbgMaintenance = (...args) => {
if (!process.env.REDIS_DEBUG_MAINTENANCE)
return;
return console.log("[MNT]", ...args);
};
exports.dbgMaintenance = dbgMaintenance;
const emitDiagnostics = (event) => {
if (!process.env.REDIS_EMIT_DIAGNOSTICS)
return;
const channel = node_diagnostics_channel_1.default.channel("redis.maintenance");
channel.publish(event);
};
exports.emitDiagnostics = emitDiagnostics;
class EnterpriseMaintenanceManager {
#commandsQueue;
#options;
#isMaintenance = 0;
#client;
static setupDefaultMaintOptions(options) {
if (options.maintNotifications === undefined) {
options.maintNotifications =
options?.RESP === 3 ? "auto" : "disabled";
}
if (options.maintEndpointType === undefined) {
options.maintEndpointType = "auto";
}
if (options.maintRelaxedSocketTimeout === undefined) {
options.maintRelaxedSocketTimeout = 10000;
}
if (options.maintRelaxedCommandTimeout === undefined) {
options.maintRelaxedCommandTimeout = 10000;
}
}
static async getHandshakeCommand(options) {
if (options.maintNotifications === "disabled")
return;
const host = options.url
? new URL(options.url).hostname
: options.socket?.host;
if (!host)
return;
const tls = options.socket?.tls ?? false;
const movingEndpointType = await determineEndpoint(tls, host, options);
return {
cmd: [
"CLIENT",
"MAINT_NOTIFICATIONS",
"ON",
"moving-endpoint-type",
movingEndpointType,
],
errorHandler: (error) => {
(0, exports.dbgMaintenance)("handshake failed:", error);
if (options.maintNotifications === "enabled") {
throw error;
}
},
};
}
constructor(commandsQueue, client, options) {
this.#commandsQueue = commandsQueue;
this.#options = options;
this.#client = client;
this.#commandsQueue.addPushHandler(this.#onPush);
}
#onPush = (push) => {
(0, exports.dbgMaintenance)("ONPUSH:", push.map(String));
if (!Array.isArray(push) || !["MOVING", "MIGRATING", "MIGRATED", "FAILING_OVER", "FAILED_OVER"].includes(String(push[0]))) {
return false;
}
const type = String(push[0]);
(0, exports.emitDiagnostics)({
type,
timestamp: Date.now(),
data: {
push: push.map(String),
},
});
switch (type) {
case PN.MOVING: {
// [ 'MOVING', '17', '15', '54.78.247.156:12075' ]
// ^seq ^after ^new ip
const afterSeconds = push[2];
const url = push[3] ? String(push[3]) : null;
(0, exports.dbgMaintenance)("Received MOVING:", afterSeconds, url);
this.#onMoving(afterSeconds, url);
return true;
}
case PN.MIGRATING:
case PN.FAILING_OVER: {
(0, exports.dbgMaintenance)("Received MIGRATING|FAILING_OVER");
this.#onMigrating();
return true;
}
case PN.MIGRATED:
case PN.FAILED_OVER: {
(0, exports.dbgMaintenance)("Received MIGRATED|FAILED_OVER");
this.#onMigrated();
return true;
}
}
return false;
};
// Queue:
// toWrite [ C D E ]
// waitingForReply [ A B ] - aka In-flight commands
//
// time: ---1-2---3-4-5-6---------------------------
//
// 1. [EVENT] MOVING PN received
// 2. [ACTION] Pause writing ( we need to wait for new socket to connect and for all in-flight commands to complete )
// 3. [EVENT] New socket connected
// 4. [EVENT] In-flight commands completed
// 5. [ACTION] Destroy old socket
// 6. [ACTION] Resume writing -> we are going to write to the new socket from now on
#onMoving = async (afterSeconds, url) => {
// 1 [EVENT] MOVING PN received
this.#onMigrating();
let host;
let port;
// The special value `none` indicates that the `MOVING` message doesnt need
// to contain an endpoint. Instead it contains the value `null` then. In
// such a corner case, the client is expected to schedule a graceful
// reconnect to its currently configured endpoint after half of the grace
// period that was communicated by the server is over.
if (url === null) {
(0, node_assert_1.default)(this.#options.maintEndpointType === "none");
(0, node_assert_1.default)(this.#options.socket !== undefined);
(0, node_assert_1.default)("host" in this.#options.socket);
(0, node_assert_1.default)(typeof this.#options.socket.host === "string");
host = this.#options.socket.host;
(0, node_assert_1.default)(typeof this.#options.socket.port === "number");
port = this.#options.socket.port;
const waitTime = (afterSeconds * 1000) / 2;
(0, exports.dbgMaintenance)(`Wait for ${waitTime}ms`);
await (0, promises_2.setTimeout)(waitTime);
}
else {
const split = url.split(":");
host = split[0];
port = Number(split[1]);
}
// 2 [ACTION] Pause writing
(0, exports.dbgMaintenance)("Pausing writing of new commands to old socket");
this.#client._pause();
(0, exports.dbgMaintenance)("Creating new tmp client");
let start = performance.now();
// If the URL is provided, it takes precedense
// the options object could just be mutated
if (this.#options.url) {
const u = new URL(this.#options.url);
u.hostname = host;
u.port = String(port);
this.#options.url = u.toString();
}
else {
this.#options.socket = {
...this.#options.socket,
host,
port
};
}
const tmpClient = this.#client.duplicate();
tmpClient.on('error', (error) => {
//We dont know how to handle tmp client errors
(0, exports.dbgMaintenance)(`[ERR]`, error);
});
(0, exports.dbgMaintenance)(`Tmp client created in ${(performance.now() - start).toFixed(2)}ms`);
(0, exports.dbgMaintenance)(`Set timeout for tmp client to ${this.#options.maintRelaxedSocketTimeout}`);
tmpClient._maintenanceUpdate({
relaxedCommandTimeout: this.#options.maintRelaxedCommandTimeout,
relaxedSocketTimeout: this.#options.maintRelaxedSocketTimeout,
});
(0, exports.dbgMaintenance)(`Connecting tmp client: ${host}:${port}`);
start = performance.now();
await tmpClient.connect();
(0, exports.dbgMaintenance)(`Connected to tmp client in ${(performance.now() - start).toFixed(2)}ms`);
// 3 [EVENT] New socket connected
(0, exports.dbgMaintenance)(`Wait for all in-flight commands to complete`);
await this.#commandsQueue.waitForInflightCommandsToComplete();
(0, exports.dbgMaintenance)(`In-flight commands completed`);
// 4 [EVENT] In-flight commands completed
(0, exports.dbgMaintenance)("Swap client sockets...");
const oldSocket = this.#client._ejectSocket();
const newSocket = tmpClient._ejectSocket();
this.#client._insertSocket(newSocket);
tmpClient._insertSocket(oldSocket);
tmpClient.destroy();
(0, exports.dbgMaintenance)("Swap client sockets done.");
// 5 + 6
(0, exports.dbgMaintenance)("Resume writing");
this.#client._unpause();
this.#onMigrated();
};
#onMigrating = () => {
this.#isMaintenance++;
if (this.#isMaintenance > 1) {
(0, exports.dbgMaintenance)(`Timeout relaxation already done`);
return;
}
const update = {
relaxedCommandTimeout: this.#options.maintRelaxedCommandTimeout,
relaxedSocketTimeout: this.#options.maintRelaxedSocketTimeout,
};
this.#client._maintenanceUpdate(update);
};
#onMigrated = () => {
//ensure that #isMaintenance doesnt go under 0
this.#isMaintenance = Math.max(this.#isMaintenance - 1, 0);
if (this.#isMaintenance > 0) {
(0, exports.dbgMaintenance)(`Not ready to unrelax timeouts yet`);
return;
}
const update = {
relaxedCommandTimeout: undefined,
relaxedSocketTimeout: undefined
};
this.#client._maintenanceUpdate(update);
};
}
exports.default = EnterpriseMaintenanceManager;
function isPrivateIP(ip) {
const version = (0, net_1.isIP)(ip);
if (version === 4) {
const octets = ip.split(".").map(Number);
return (octets[0] === 10 ||
(octets[0] === 172 && octets[1] >= 16 && octets[1] <= 31) ||
(octets[0] === 192 && octets[1] === 168));
}
if (version === 6) {
return (ip.startsWith("fc") || // Unique local
ip.startsWith("fd") || // Unique local
ip === "::1" || // Loopback
ip.startsWith("fe80") // Link-local unicast
);
}
return false;
}
async function determineEndpoint(tlsEnabled, host, options) {
(0, node_assert_1.default)(options.maintEndpointType !== undefined);
if (options.maintEndpointType !== "auto") {
(0, exports.dbgMaintenance)(`Determine endpoint type: ${options.maintEndpointType}`);
return options.maintEndpointType;
}
const ip = (0, net_1.isIP)(host) ? host : (await (0, promises_1.lookup)(host, { family: 0 })).address;
const isPrivate = isPrivateIP(ip);
let result;
if (tlsEnabled) {
result = isPrivate ? "internal-fqdn" : "external-fqdn";
}
else {
result = isPrivate ? "internal-ip" : "external-ip";
}
(0, exports.dbgMaintenance)(`Determine endpoint type: ${result}`);
return result;
}
//# sourceMappingURL=enterprise-maintenance-manager.js.map

File diff suppressed because one or more lines are too long

367
node_modules/@redis/client/dist/lib/client/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,367 @@
/// <reference types="node" />
/// <reference types="node" />
/// <reference types="node" />
import COMMANDS from '../commands';
import RedisSocket, { RedisSocketOptions } from './socket';
import { CredentialsProvider } from '../authx';
import { CommandOptions } from './commands-queue';
import { EventEmitter } from 'node:events';
import { PubSubType, PubSubListener, PubSubTypeListeners, ChannelListeners } from './pub-sub';
import { Command, CommandSignature, TypeMapping, CommanderConfig, RedisFunctions, RedisModules, RedisScript, RedisScripts, ReplyUnion, RespVersions, RedisArgument, ReplyWithTypeMapping, SimpleStringReply, TransformReply } from '../RESP/types';
import { RedisClientMultiCommandType } from './multi-command';
import { MULTI_MODE, MultiMode, RedisMultiQueuedCommand } from '../multi-command';
import { ScanOptions, ScanCommonOptions } from '../commands/SCAN';
import { RedisLegacyClientType } from './legacy-mode';
import { RedisPoolOptions } from './pool';
import { RedisVariadicArgument } from '../commands/generic-transformers';
import { ClientSideCacheConfig, ClientSideCacheProvider } from './cache';
import { CommandParser } from './parser';
import { MaintenanceUpdate, MovingEndpointType } from './enterprise-maintenance-manager';
export interface RedisClientOptions<M extends RedisModules = RedisModules, F extends RedisFunctions = RedisFunctions, S extends RedisScripts = RedisScripts, RESP extends RespVersions = RespVersions, TYPE_MAPPING extends TypeMapping = TypeMapping, SocketOptions extends RedisSocketOptions = RedisSocketOptions> extends CommanderConfig<M, F, S, RESP> {
/**
* `redis[s]://[[username][:password]@][host][:port][/db-number]`
* See [`redis`](https://www.iana.org/assignments/uri-schemes/prov/redis) and [`rediss`](https://www.iana.org/assignments/uri-schemes/prov/rediss) IANA registration for more details
*/
url?: string;
/**
* Socket connection properties
*/
socket?: SocketOptions;
/**
* ACL username ([see ACL guide](https://redis.io/topics/acl))
*/
username?: string;
/**
* ACL password or the old "--requirepass" password
*/
password?: string;
/**
* Provides credentials for authentication. Can be set directly or will be created internally
* if username/password are provided instead. If both are supplied, this credentialsProvider
* takes precedence over username/password.
*/
credentialsProvider?: CredentialsProvider;
/**
* Client name ([see `CLIENT SETNAME`](https://redis.io/commands/client-setname))
*/
name?: string;
/**
* Redis database number (see [`SELECT`](https://redis.io/commands/select) command)
*/
database?: number;
/**
* Maximum length of the client's internal command queue
*/
commandsQueueMaxLength?: number;
/**
* When `true`, commands are rejected when the client is reconnecting.
* When `false`, commands are queued for execution after reconnection.
*/
disableOfflineQueue?: boolean;
/**
* Connect in [`READONLY`](https://redis.io/commands/readonly) mode
*/
readonly?: boolean;
/**
* Send `PING` command at interval (in ms).
* Useful with Redis deployments that do not honor TCP Keep-Alive.
*/
pingInterval?: number;
/**
* Default command options to be applied to all commands executed through this client.
*
* These options can be overridden on a per-command basis when calling specific commands.
*
* @property {symbol} [chainId] - Identifier for chaining commands together
* @property {boolean} [asap] - When true, the command is executed as soon as possible
* @property {AbortSignal} [abortSignal] - AbortSignal to cancel the command
* @property {TypeMapping} [typeMapping] - Custom type mappings between RESP and JavaScript types
*
* @example Setting default command options
* ```
* const client = createClient({
* commandOptions: {
* asap: true,
* typeMapping: {
* // Custom type mapping configuration
* }
* }
* });
* ```
*/
commandOptions?: CommandOptions<TYPE_MAPPING>;
/**
* Client Side Caching configuration.
*
* Enables Redis Servers and Clients to work together to cache results from commands
* sent to a server. The server will notify the client when cached results are no longer valid.
*
* Note: Client Side Caching is only supported with RESP3.
*
* @example Anonymous cache configuration
* ```
* const client = createClient({
* RESP: 3,
* clientSideCache: {
* ttl: 0,
* maxEntries: 0,
* evictPolicy: "LRU"
* }
* });
* ```
*
* @example Using a controllable cache
* ```
* const cache = new BasicClientSideCache({
* ttl: 0,
* maxEntries: 0,
* evictPolicy: "LRU"
* });
* const client = createClient({
* RESP: 3,
* clientSideCache: cache
* });
* ```
*/
clientSideCache?: ClientSideCacheProvider | ClientSideCacheConfig;
/**
* If set to true, disables sending client identifier (user-agent like message) to the redis server
*/
disableClientInfo?: boolean;
/**
* Tag to append to library name that is sent to the Redis server
*/
clientInfoTag?: string;
/**
* When set to true, client tracking is turned on and the client emits `invalidate` events when it receives invalidation messages from the redis server.
* Mutually exclusive with `clientSideCache` option.
*/
emitInvalidate?: boolean;
/**
* Controls how the client handles Redis Enterprise maintenance push notifications.
*
* - `disabled`: The feature is not used by the client.
* - `enabled`: The client attempts to enable the feature on the server. If the server responds with an error, the connection is interrupted.
* - `auto`: The client attempts to enable the feature on the server. If the server returns an error, the client disables the feature and continues.
*
* The default is `auto`.
*/
maintNotifications?: 'disabled' | 'enabled' | 'auto';
/**
* Controls how the client requests the endpoint to reconnect to during a MOVING notification in Redis Enterprise maintenance.
*
* - `auto`: If the connection is opened to a name or IP address that is from/resolves to a reserved private IP range, request an internal endpoint (e.g., internal-ip), otherwise an external one. If TLS is enabled, then request a FQDN.
* - `internal-ip`: Enforce requesting the internal IP.
* - `internal-fqdn`: Enforce requesting the internal FQDN.
* - `external-ip`: Enforce requesting the external IP address.
* - `external-fqdn`: Enforce requesting the external FQDN.
* - `none`: Used to request a null endpoint, which tells the client to reconnect based on its current config
* The default is `auto`.
*/
maintEndpointType?: MovingEndpointType;
/**
* Specifies a more relaxed timeout (in milliseconds) for commands during a maintenance window.
* This helps minimize command timeouts during maintenance. Timeouts during maintenance period result
* in a `CommandTimeoutDuringMaintenance` error.
*
* The default is 10000
*/
maintRelaxedCommandTimeout?: number;
/**
* Specifies a more relaxed timeout (in milliseconds) for the socket during a maintenance window.
* This helps minimize socket timeouts during maintenance. Timeouts during maintenance period result
* in a `SocketTimeoutDuringMaintenance` error.
*
* The default is 10000
*/
maintRelaxedSocketTimeout?: number;
}
export type WithCommands<RESP extends RespVersions, TYPE_MAPPING extends TypeMapping> = {
[P in keyof typeof COMMANDS]: CommandSignature<(typeof COMMANDS)[P], RESP, TYPE_MAPPING>;
};
export type WithModules<M extends RedisModules, RESP extends RespVersions, TYPE_MAPPING extends TypeMapping> = {
[P in keyof M]: {
[C in keyof M[P]]: CommandSignature<M[P][C], RESP, TYPE_MAPPING>;
};
};
export type WithFunctions<F extends RedisFunctions, RESP extends RespVersions, TYPE_MAPPING extends TypeMapping> = {
[L in keyof F]: {
[C in keyof F[L]]: CommandSignature<F[L][C], RESP, TYPE_MAPPING>;
};
};
export type WithScripts<S extends RedisScripts, RESP extends RespVersions, TYPE_MAPPING extends TypeMapping> = {
[P in keyof S]: CommandSignature<S[P], RESP, TYPE_MAPPING>;
};
export type RedisClientExtensions<M extends RedisModules = {}, F extends RedisFunctions = {}, S extends RedisScripts = {}, RESP extends RespVersions = 2, TYPE_MAPPING extends TypeMapping = {}> = (WithCommands<RESP, TYPE_MAPPING> & WithModules<M, RESP, TYPE_MAPPING> & WithFunctions<F, RESP, TYPE_MAPPING> & WithScripts<S, RESP, TYPE_MAPPING>);
export type RedisClientType<M extends RedisModules = {}, F extends RedisFunctions = {}, S extends RedisScripts = {}, RESP extends RespVersions = 2, TYPE_MAPPING extends TypeMapping = {}> = (RedisClient<M, F, S, RESP, TYPE_MAPPING> & RedisClientExtensions<M, F, S, RESP, TYPE_MAPPING>);
interface ScanIteratorOptions {
cursor?: RedisArgument;
}
export type MonitorCallback<TYPE_MAPPING extends TypeMapping = TypeMapping> = (reply: ReplyWithTypeMapping<SimpleStringReply, TYPE_MAPPING>) => unknown;
export default class RedisClient<M extends RedisModules, F extends RedisFunctions, S extends RedisScripts, RESP extends RespVersions, TYPE_MAPPING extends TypeMapping> extends EventEmitter {
#private;
static factory<M extends RedisModules = {}, F extends RedisFunctions = {}, S extends RedisScripts = {}, RESP extends RespVersions = 2>(config?: CommanderConfig<M, F, S, RESP>): <TYPE_MAPPING extends TypeMapping = {}>(options?: Omit<RedisClientOptions<M, F, S, RESP, TYPE_MAPPING, RedisSocketOptions>, keyof CommanderConfig<M, F, S, RESP>> | undefined) => RedisClientType<M, F, S, RESP, TYPE_MAPPING>;
static create<M extends RedisModules = {}, F extends RedisFunctions = {}, S extends RedisScripts = {}, RESP extends RespVersions = 2, TYPE_MAPPING extends TypeMapping = {}>(this: void, options?: RedisClientOptions<M, F, S, RESP, TYPE_MAPPING>): RedisClientType<M, F, S, RESP, TYPE_MAPPING>;
static parseOptions<O extends RedisClientOptions>(options: O): O;
static parseURL(url: string): RedisClientOptions & {
socket: Exclude<RedisClientOptions['socket'], undefined> & {
tls: boolean;
};
};
private _self;
private _commandOptions?;
get clientSideCache(): ClientSideCacheProvider | undefined;
get options(): RedisClientOptions<M, F, S, RESP>;
get isOpen(): boolean;
get isReady(): boolean;
get isPubSubActive(): boolean;
get socketEpoch(): number;
get isWatching(): boolean;
/**
* Indicates whether the client's WATCH command has been invalidated by a topology change.
* When this returns true, any transaction using WATCH will fail with a WatchError.
* @returns true if the watched keys have been modified, false otherwise
*/
get isDirtyWatch(): boolean;
/**
* Marks the client's WATCH command as invalidated due to a topology change.
* This will cause any subsequent EXEC in a transaction to fail with a WatchError.
* @param msg - The error message explaining why the WATCH is dirty
*/
setDirtyWatch(msg: string): void;
constructor(options?: RedisClientOptions<M, F, S, RESP, TYPE_MAPPING>);
/**
* @param credentials
*/
private reAuthenticate;
withCommandOptions<OPTIONS extends CommandOptions<TYPE_MAPPING>, TYPE_MAPPING extends TypeMapping>(options: OPTIONS): RedisClientType<M, F, S, RESP, TYPE_MAPPING extends TypeMapping ? TYPE_MAPPING : {}>;
private _commandOptionsProxy;
/**
* Override the `typeMapping` command option
*/
withTypeMapping<TYPE_MAPPING extends TypeMapping>(typeMapping: TYPE_MAPPING): RedisClientType<M, F, S, RESP, TYPE_MAPPING extends TypeMapping ? TYPE_MAPPING : {}>;
/**
* Override the `abortSignal` command option
*/
withAbortSignal(abortSignal: AbortSignal): RedisClientType<M, F, S, RESP, TYPE_MAPPING>;
/**
* Override the `asap` command option to `true`
*/
asap(): RedisClientType<M, F, S, RESP, TYPE_MAPPING>;
/**
* Create the "legacy" (v3/callback) interface
*/
legacy(): RedisLegacyClientType;
/**
* Create {@link RedisClientPool `RedisClientPool`} using this client as a prototype
*/
createPool(options?: Partial<RedisPoolOptions>): import("./pool").RedisClientPoolType<M, F, S, RESP, TYPE_MAPPING>;
duplicate<_M extends RedisModules = M, _F extends RedisFunctions = F, _S extends RedisScripts = S, _RESP extends RespVersions = RESP, _TYPE_MAPPING extends TypeMapping = TYPE_MAPPING>(overrides?: Partial<RedisClientOptions<_M, _F, _S, _RESP, _TYPE_MAPPING>>): RedisClientType<_M, _F, _S, _RESP, _TYPE_MAPPING>;
connect(): Promise<RedisClientType<M, F, S, RESP, TYPE_MAPPING>>;
/**
* @internal
*/
_ejectSocket(): RedisSocket;
/**
* @internal
*/
_insertSocket(socket: RedisSocket): void;
/**
* @internal
*/
_maintenanceUpdate(update: MaintenanceUpdate): void;
/**
* @internal
*/
_pause(): void;
/**
* @internal
*/
_unpause(): void;
/**
* @internal
*/
_executeCommand(command: Command, parser: CommandParser, commandOptions: CommandOptions<TYPE_MAPPING> | undefined, transformReply: TransformReply | undefined): Promise<any>;
/**
* @internal
*/
_executeScript(script: RedisScript, parser: CommandParser, options: CommandOptions | undefined, transformReply: TransformReply | undefined): Promise<any>;
sendCommand<T = ReplyUnion>(args: ReadonlyArray<RedisArgument>, options?: CommandOptions): Promise<T>;
SELECT(db: number): Promise<void>;
select: (db: number) => Promise<void>;
SUBSCRIBE<T extends boolean = false>(channels: string | Array<string>, listener: PubSubListener<T>, bufferMode?: T): Promise<void>;
subscribe: <T extends boolean = false>(channels: string | Array<string>, listener: PubSubListener<T>, bufferMode?: T | undefined) => Promise<void>;
UNSUBSCRIBE<T extends boolean = false>(channels?: string | Array<string>, listener?: PubSubListener<T>, bufferMode?: T): Promise<void>;
unsubscribe: <T extends boolean = false>(channels?: string | Array<string>, listener?: PubSubListener<T> | undefined, bufferMode?: T | undefined) => Promise<void>;
PSUBSCRIBE<T extends boolean = false>(patterns: string | Array<string>, listener: PubSubListener<T>, bufferMode?: T): Promise<void>;
pSubscribe: <T extends boolean = false>(patterns: string | Array<string>, listener: PubSubListener<T>, bufferMode?: T | undefined) => Promise<void>;
PUNSUBSCRIBE<T extends boolean = false>(patterns?: string | Array<string>, listener?: PubSubListener<T>, bufferMode?: T): Promise<void>;
pUnsubscribe: <T extends boolean = false>(patterns?: string | Array<string>, listener?: PubSubListener<T> | undefined, bufferMode?: T | undefined) => Promise<void>;
SSUBSCRIBE<T extends boolean = false>(channels: string | Array<string>, listener: PubSubListener<T>, bufferMode?: T): Promise<void>;
sSubscribe: <T extends boolean = false>(channels: string | Array<string>, listener: PubSubListener<T>, bufferMode?: T | undefined) => Promise<void>;
SUNSUBSCRIBE<T extends boolean = false>(channels?: string | Array<string>, listener?: PubSubListener<T>, bufferMode?: T): Promise<void>;
sUnsubscribe: <T extends boolean = false>(channels?: string | Array<string>, listener?: PubSubListener<T> | undefined, bufferMode?: T | undefined) => Promise<void>;
WATCH(key: RedisVariadicArgument): Promise<TYPE_MAPPING[43] extends import("../RESP/types").MappedType<infer T> ? ReplyWithTypeMapping<Extract<"OK", T>, TYPE_MAPPING> | ReplyWithTypeMapping<Extract<Buffer, T>, TYPE_MAPPING> : "OK">;
watch: (key: RedisVariadicArgument) => Promise<TYPE_MAPPING[43] extends import("../RESP/types").MappedType<infer T> ? ReplyWithTypeMapping<Extract<"OK", T>, TYPE_MAPPING> | ReplyWithTypeMapping<Extract<Buffer, T>, TYPE_MAPPING> : "OK">;
UNWATCH(): Promise<TYPE_MAPPING[43] extends import("../RESP/types").MappedType<infer T> ? ReplyWithTypeMapping<Extract<"OK", T>, TYPE_MAPPING> | ReplyWithTypeMapping<Extract<Buffer, T>, TYPE_MAPPING> : "OK">;
unwatch: () => Promise<TYPE_MAPPING[43] extends import("../RESP/types").MappedType<infer T> ? ReplyWithTypeMapping<Extract<"OK", T>, TYPE_MAPPING> | ReplyWithTypeMapping<Extract<Buffer, T>, TYPE_MAPPING> : "OK">;
getPubSubListeners(type: PubSubType): PubSubTypeListeners;
extendPubSubChannelListeners(type: PubSubType, channel: string, listeners: ChannelListeners): Promise<void>;
extendPubSubListeners(type: PubSubType, listeners: PubSubTypeListeners): Promise<void>;
/**
* @internal
*/
_executePipeline(commands: Array<RedisMultiQueuedCommand>, selectedDB?: number): Promise<unknown[]>;
/**
* @internal
*/
_executeMulti(commands: Array<RedisMultiQueuedCommand>, selectedDB?: number): Promise<unknown[]>;
MULTI<isTyped extends MultiMode = MULTI_MODE['TYPED']>(): RedisClientMultiCommandType<isTyped, [], M, F, S, RESP, TYPE_MAPPING>;
multi: <isTyped extends MultiMode = "typed">() => RedisClientMultiCommandType<isTyped, [], M, F, S, RESP, TYPE_MAPPING>;
scanIterator(this: RedisClientType<M, F, S, RESP, TYPE_MAPPING>, options?: ScanOptions & ScanIteratorOptions): AsyncGenerator<TYPE_MAPPING[42] extends import("../RESP/types").MappedType<infer T> ? ReplyWithTypeMapping<Extract<import("../RESP/types").BlobStringReply<string>[], T>, TYPE_MAPPING> : (TYPE_MAPPING[36] extends import("../RESP/types").MappedType<infer T> ? ReplyWithTypeMapping<Extract<Buffer, T>, TYPE_MAPPING> | ReplyWithTypeMapping<Extract<string, T>, TYPE_MAPPING> : string)[], void, unknown>;
hScanIterator(this: RedisClientType<M, F, S, RESP, TYPE_MAPPING>, key: RedisArgument, options?: ScanCommonOptions & ScanIteratorOptions): AsyncGenerator<{
field: TYPE_MAPPING[36] extends import("../RESP/types").MappedType<infer T> ? ReplyWithTypeMapping<Extract<Buffer, T>, TYPE_MAPPING> | ReplyWithTypeMapping<Extract<string, T>, TYPE_MAPPING> : string;
value: TYPE_MAPPING[36] extends import("../RESP/types").MappedType<infer T> ? ReplyWithTypeMapping<Extract<Buffer, T>, TYPE_MAPPING> | ReplyWithTypeMapping<Extract<string, T>, TYPE_MAPPING> : string;
}[], void, unknown>;
hScanValuesIterator(this: RedisClientType<M, F, S, RESP, TYPE_MAPPING>, key: RedisArgument, options?: ScanCommonOptions & ScanIteratorOptions): AsyncGenerator<(TYPE_MAPPING[36] extends import("../RESP/types").MappedType<infer T> ? ReplyWithTypeMapping<Extract<Buffer, T>, TYPE_MAPPING> | ReplyWithTypeMapping<Extract<string, T>, TYPE_MAPPING> : string)[], void, unknown>;
hScanNoValuesIterator(this: RedisClientType<M, F, S, RESP, TYPE_MAPPING>, key: RedisArgument, options?: ScanCommonOptions & ScanIteratorOptions): AsyncGenerator<(TYPE_MAPPING[36] extends import("../RESP/types").MappedType<infer T> ? ReplyWithTypeMapping<Extract<Buffer, T>, TYPE_MAPPING> | ReplyWithTypeMapping<Extract<string, T>, TYPE_MAPPING> : string)[], void, unknown>;
sScanIterator(this: RedisClientType<M, F, S, RESP, TYPE_MAPPING>, key: RedisArgument, options?: ScanCommonOptions & ScanIteratorOptions): AsyncGenerator<(TYPE_MAPPING[36] extends import("../RESP/types").MappedType<infer T> ? ReplyWithTypeMapping<Extract<Buffer, T>, TYPE_MAPPING> | ReplyWithTypeMapping<Extract<string, T>, TYPE_MAPPING> : string)[], void, unknown>;
zScanIterator(this: RedisClientType<M, F, S, RESP, TYPE_MAPPING>, key: RedisArgument, options?: ScanCommonOptions & ScanIteratorOptions): AsyncGenerator<{
value: TYPE_MAPPING[36] extends import("../RESP/types").MappedType<infer T> ? ReplyWithTypeMapping<Extract<Buffer, T>, TYPE_MAPPING> | ReplyWithTypeMapping<Extract<string, T>, TYPE_MAPPING> : string;
score: TYPE_MAPPING[44] extends import("../RESP/types").MappedType<infer T> ? ReplyWithTypeMapping<Extract<number, T>, TYPE_MAPPING> | ReplyWithTypeMapping<Extract<`${number}`, T>, TYPE_MAPPING> : number;
}[], void, unknown>;
MONITOR(callback: MonitorCallback<TYPE_MAPPING>): Promise<void>;
monitor: (callback: MonitorCallback<TYPE_MAPPING>) => Promise<void>;
/**
* Reset the client to its default state (i.e. stop PubSub, stop monitoring, select default DB, etc.)
*/
reset(): Promise<void>;
/**
* If the client has state, reset it.
* An internal function to be used by wrapper class such as `RedisClientPool`.
* @internal
*/
resetIfDirty(): Promise<void> | undefined;
/**
* @deprecated use .close instead
*/
QUIT(): Promise<string>;
quit: () => Promise<string>;
/**
* @deprecated use .destroy instead
*/
disconnect(): Promise<void>;
/**
* Close the client. Wait for pending commands.
*/
close(): Promise<void>;
/**
* Destroy the client. Rejects all commands immediately.
*/
destroy(): void;
ref(): void;
unref(): void;
}
export {};
//# sourceMappingURL=index.d.ts.map

File diff suppressed because one or more lines are too long

953
node_modules/@redis/client/dist/lib/client/index.js generated vendored Normal file
View File

@@ -0,0 +1,953 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
var _a;
Object.defineProperty(exports, "__esModule", { value: true });
const commands_1 = __importDefault(require("../commands"));
const socket_1 = __importDefault(require("./socket"));
const authx_1 = require("../authx");
const commands_queue_1 = __importDefault(require("./commands-queue"));
const node_events_1 = require("node:events");
const commander_1 = require("../commander");
const errors_1 = require("../errors");
const node_url_1 = require("node:url");
const pub_sub_1 = require("./pub-sub");
const multi_command_1 = __importDefault(require("./multi-command"));
const HELLO_1 = __importDefault(require("../commands/HELLO"));
const legacy_mode_1 = require("./legacy-mode");
const pool_1 = require("./pool");
const generic_transformers_1 = require("../commands/generic-transformers");
const cache_1 = require("./cache");
const parser_1 = require("./parser");
const single_entry_cache_1 = __importDefault(require("../single-entry-cache"));
const package_json_1 = require("../../package.json");
const enterprise_maintenance_manager_1 = __importDefault(require("./enterprise-maintenance-manager"));
;
class RedisClient extends node_events_1.EventEmitter {
static #createCommand(command, resp) {
const transformReply = (0, commander_1.getTransformReply)(command, resp);
return async function (...args) {
const parser = new parser_1.BasicCommandParser();
command.parseCommand(parser, ...args);
return this._self._executeCommand(command, parser, this._commandOptions, transformReply);
};
}
static #createModuleCommand(command, resp) {
const transformReply = (0, commander_1.getTransformReply)(command, resp);
return async function (...args) {
const parser = new parser_1.BasicCommandParser();
command.parseCommand(parser, ...args);
return this._self._executeCommand(command, parser, this._self._commandOptions, transformReply);
};
}
static #createFunctionCommand(name, fn, resp) {
const prefix = (0, commander_1.functionArgumentsPrefix)(name, fn);
const transformReply = (0, commander_1.getTransformReply)(fn, resp);
return async function (...args) {
const parser = new parser_1.BasicCommandParser();
parser.push(...prefix);
fn.parseCommand(parser, ...args);
return this._self._executeCommand(fn, parser, this._self._commandOptions, transformReply);
};
}
static #createScriptCommand(script, resp) {
const prefix = (0, commander_1.scriptArgumentsPrefix)(script);
const transformReply = (0, commander_1.getTransformReply)(script, resp);
return async function (...args) {
const parser = new parser_1.BasicCommandParser();
parser.push(...prefix);
script.parseCommand(parser, ...args);
return this._executeScript(script, parser, this._commandOptions, transformReply);
};
}
static #SingleEntryCache = new single_entry_cache_1.default();
static factory(config) {
let Client = _a.#SingleEntryCache.get(config);
if (!Client) {
Client = (0, commander_1.attachConfig)({
BaseClass: _a,
commands: commands_1.default,
createCommand: _a.#createCommand,
createModuleCommand: _a.#createModuleCommand,
createFunctionCommand: _a.#createFunctionCommand,
createScriptCommand: _a.#createScriptCommand,
config
});
Client.prototype.Multi = multi_command_1.default.extend(config);
_a.#SingleEntryCache.set(config, Client);
}
return (options) => {
// returning a "proxy" to prevent the namespaces._self to leak between "proxies"
return Object.create(new Client(options));
};
}
static create(options) {
return _a.factory(options)(options);
}
static parseOptions(options) {
if (options?.url) {
const parsed = _a.parseURL(options.url);
if (options.socket) {
if (options.socket.tls !== undefined && options.socket.tls !== parsed.socket.tls) {
throw new TypeError(`tls socket option is set to ${options.socket.tls} which is mismatch with protocol or the URL ${options.url} passed`);
}
parsed.socket = Object.assign(options.socket, parsed.socket);
}
Object.assign(options, parsed);
}
return options;
}
static parseURL(url) {
// https://www.iana.org/assignments/uri-schemes/prov/redis
const { hostname, port, protocol, username, password, pathname } = new node_url_1.URL(url), parsed = {
socket: {
host: hostname,
tls: false
}
};
if (protocol !== 'redis:' && protocol !== 'rediss:') {
throw new TypeError('Invalid protocol');
}
parsed.socket.tls = protocol === 'rediss:';
if (port) {
parsed.socket.port = Number(port);
}
if (username) {
parsed.username = decodeURIComponent(username);
}
if (password) {
parsed.password = decodeURIComponent(password);
}
if (username || password) {
parsed.credentialsProvider = {
type: 'async-credentials-provider',
credentials: async () => ({
username: username ? decodeURIComponent(username) : undefined,
password: password ? decodeURIComponent(password) : undefined
})
};
}
if (pathname.length > 1) {
const database = Number(pathname.substring(1));
if (isNaN(database)) {
throw new TypeError('Invalid pathname');
}
parsed.database = database;
}
return parsed;
}
#options;
#socket;
#queue;
#selectedDB = 0;
#monitorCallback;
_self = this;
_commandOptions;
// flag used to annotate that the client
// was in a watch transaction when
// a topology change occured
#dirtyWatch;
#watchEpoch;
#clientSideCache;
#credentialsSubscription = null;
// Flag used to pause writing to the socket during maintenance windows.
// When true, prevents new commands from being written while waiting for:
// 1. New socket to be ready after maintenance redirect
// 2. In-flight commands on the old socket to complete
#paused = false;
get clientSideCache() {
return this._self.#clientSideCache;
}
get options() {
return this._self.#options;
}
get isOpen() {
return this._self.#socket.isOpen;
}
get isReady() {
return this._self.#socket.isReady;
}
get isPubSubActive() {
return this._self.#queue.isPubSubActive;
}
get socketEpoch() {
return this._self.#socket.socketEpoch;
}
get isWatching() {
return this._self.#watchEpoch !== undefined;
}
/**
* Indicates whether the client's WATCH command has been invalidated by a topology change.
* When this returns true, any transaction using WATCH will fail with a WatchError.
* @returns true if the watched keys have been modified, false otherwise
*/
get isDirtyWatch() {
return this._self.#dirtyWatch !== undefined;
}
/**
* Marks the client's WATCH command as invalidated due to a topology change.
* This will cause any subsequent EXEC in a transaction to fail with a WatchError.
* @param msg - The error message explaining why the WATCH is dirty
*/
setDirtyWatch(msg) {
this._self.#dirtyWatch = msg;
}
constructor(options) {
super();
this.#validateOptions(options);
this.#options = this.#initiateOptions(options);
this.#queue = this.#initiateQueue();
this.#socket = this.#initiateSocket();
if (this.#options.maintNotifications !== 'disabled') {
new enterprise_maintenance_manager_1.default(this.#queue, this, this.#options);
}
;
if (this.#options.clientSideCache) {
if (this.#options.clientSideCache instanceof cache_1.ClientSideCacheProvider) {
this.#clientSideCache = this.#options.clientSideCache;
}
else {
const cscConfig = this.#options.clientSideCache;
this.#clientSideCache = new cache_1.BasicClientSideCache(cscConfig);
}
this.#queue.addPushHandler((push) => {
if (push[0].toString() !== 'invalidate')
return false;
if (push[1] !== null) {
for (const key of push[1]) {
this.#clientSideCache?.invalidate(key);
}
}
else {
this.#clientSideCache?.invalidate(null);
}
return true;
});
}
else if (options?.emitInvalidate) {
this.#queue.addPushHandler((push) => {
if (push[0].toString() !== 'invalidate')
return false;
if (push[1] !== null) {
for (const key of push[1]) {
this.emit('invalidate', key);
}
}
else {
this.emit('invalidate', null);
}
return true;
});
}
}
#validateOptions(options) {
if (options?.clientSideCache && options?.RESP !== 3) {
throw new Error('Client Side Caching is only supported with RESP3');
}
if (options?.emitInvalidate && options?.RESP !== 3) {
throw new Error('emitInvalidate is only supported with RESP3');
}
if (options?.clientSideCache && options?.emitInvalidate) {
throw new Error('emitInvalidate is not supported (or necessary) when clientSideCache is enabled');
}
if (options?.maintNotifications && options?.maintNotifications !== 'disabled' && options?.RESP !== 3) {
throw new Error('Graceful Maintenance is only supported with RESP3');
}
}
#initiateOptions(options = {}) {
// Convert username/password to credentialsProvider if no credentialsProvider is already in place
if (!options.credentialsProvider && (options.username || options.password)) {
options.credentialsProvider = {
type: 'async-credentials-provider',
credentials: async () => ({
username: options.username,
password: options.password
})
};
}
if (options.database) {
this._self.#selectedDB = options.database;
}
if (options.commandOptions) {
this._commandOptions = options.commandOptions;
}
if (options.maintNotifications !== 'disabled') {
enterprise_maintenance_manager_1.default.setupDefaultMaintOptions(options);
}
if (options.url) {
const parsedOptions = _a.parseOptions(options);
if (parsedOptions?.database) {
this._self.#selectedDB = parsedOptions.database;
}
return parsedOptions;
}
return options;
}
#initiateQueue() {
return new commands_queue_1.default(this.#options.RESP ?? 2, this.#options.commandsQueueMaxLength, (channel, listeners) => this.emit('sharded-channel-moved', channel, listeners));
}
/**
* @param credentials
*/
reAuthenticate = async (credentials) => {
// Re-authentication is not supported on RESP2 with PubSub active
if (!(this.isPubSubActive && !this.#options.RESP)) {
await this.sendCommand((0, generic_transformers_1.parseArgs)(commands_1.default.AUTH, {
username: credentials.username,
password: credentials.password ?? ''
}));
}
};
#subscribeForStreamingCredentials(cp) {
return cp.subscribe({
onNext: credentials => {
this.reAuthenticate(credentials).catch(error => {
const errorMessage = error instanceof Error ? error.message : String(error);
cp.onReAuthenticationError(new authx_1.CredentialsError(errorMessage));
});
},
onError: (e) => {
const errorMessage = `Error from streaming credentials provider: ${e.message}`;
cp.onReAuthenticationError(new authx_1.UnableToObtainNewCredentialsError(errorMessage));
}
});
}
async #handshake(chainId, asap) {
const promises = [];
const commandsWithErrorHandlers = await this.#getHandshakeCommands();
if (asap)
commandsWithErrorHandlers.reverse();
for (const { cmd, errorHandler } of commandsWithErrorHandlers) {
promises.push(this.#queue
.addCommand(cmd, {
chainId,
asap
})
.catch(errorHandler));
}
return promises;
}
async #getHandshakeCommands() {
const commands = [];
const cp = this.#options.credentialsProvider;
if (this.#options.RESP) {
const hello = {};
if (cp && cp.type === 'async-credentials-provider') {
const credentials = await cp.credentials();
if (credentials.password) {
hello.AUTH = {
username: credentials.username ?? 'default',
password: credentials.password
};
}
}
if (cp && cp.type === 'streaming-credentials-provider') {
const [credentials, disposable] = await this.#subscribeForStreamingCredentials(cp);
this.#credentialsSubscription = disposable;
if (credentials.password) {
hello.AUTH = {
username: credentials.username ?? 'default',
password: credentials.password
};
}
}
if (this.#options.name) {
hello.SETNAME = this.#options.name;
}
commands.push({ cmd: (0, generic_transformers_1.parseArgs)(HELLO_1.default, this.#options.RESP, hello) });
}
else {
if (cp && cp.type === 'async-credentials-provider') {
const credentials = await cp.credentials();
if (credentials.username || credentials.password) {
commands.push({
cmd: (0, generic_transformers_1.parseArgs)(commands_1.default.AUTH, {
username: credentials.username,
password: credentials.password ?? ''
})
});
}
}
if (cp && cp.type === 'streaming-credentials-provider') {
const [credentials, disposable] = await this.#subscribeForStreamingCredentials(cp);
this.#credentialsSubscription = disposable;
if (credentials.username || credentials.password) {
commands.push({
cmd: (0, generic_transformers_1.parseArgs)(commands_1.default.AUTH, {
username: credentials.username,
password: credentials.password ?? ''
})
});
}
}
if (this.#options.name) {
commands.push({
cmd: (0, generic_transformers_1.parseArgs)(commands_1.default.CLIENT_SETNAME, this.#options.name)
});
}
}
if (this.#selectedDB !== 0) {
commands.push({ cmd: ['SELECT', this.#selectedDB.toString()] });
}
if (this.#options.readonly) {
commands.push({ cmd: (0, generic_transformers_1.parseArgs)(commands_1.default.READONLY) });
}
if (!this.#options.disableClientInfo) {
commands.push({
cmd: ['CLIENT', 'SETINFO', 'LIB-VER', package_json_1.version],
errorHandler: () => {
// Client libraries are expected to pipeline this command
// after authentication on all connections and ignore failures
// since they could be connected to an older version that doesn't support them.
}
});
commands.push({
cmd: [
'CLIENT',
'SETINFO',
'LIB-NAME',
this.#options.clientInfoTag
? `node-redis(${this.#options.clientInfoTag})`
: 'node-redis'
],
errorHandler: () => {
// Client libraries are expected to pipeline this command
// after authentication on all connections and ignore failures
// since they could be connected to an older version that doesn't support them.
}
});
}
if (this.#clientSideCache) {
commands.push({ cmd: this.#clientSideCache.trackingOn() });
}
if (this.#options?.emitInvalidate) {
commands.push({ cmd: ['CLIENT', 'TRACKING', 'ON'] });
}
const maintenanceHandshakeCmd = await enterprise_maintenance_manager_1.default.getHandshakeCommand(this.#options);
if (maintenanceHandshakeCmd) {
commands.push(maintenanceHandshakeCmd);
}
;
return commands;
}
#attachListeners(socket) {
socket.on('data', chunk => {
try {
this.#queue.decoder.write(chunk);
}
catch (err) {
this.#queue.resetDecoder();
this.emit('error', err);
}
})
.on('error', err => {
this.emit('error', err);
this.#clientSideCache?.onError();
if (this.#socket.isOpen && !this.#options.disableOfflineQueue) {
this.#queue.flushWaitingForReply(err);
}
else {
this.#queue.flushAll(err);
}
})
.on('connect', () => this.emit('connect'))
.on('ready', () => {
this.emit('ready');
this.#setPingTimer();
this.#maybeScheduleWrite();
})
.on('reconnecting', () => this.emit('reconnecting'))
.on('drain', () => this.#maybeScheduleWrite())
.on('end', () => this.emit('end'));
}
#initiateSocket() {
const socketInitiator = async () => {
const promises = [], chainId = Symbol('Socket Initiator');
const resubscribePromise = this.#queue.resubscribe(chainId);
resubscribePromise?.catch(error => {
if (error.message && error.message.startsWith('MOVED')) {
this.emit('__MOVED', this._self.#queue.removeAllPubSubListeners());
}
});
if (resubscribePromise) {
promises.push(resubscribePromise);
}
if (this.#monitorCallback) {
promises.push(this.#queue.monitor(this.#monitorCallback, {
typeMapping: this._commandOptions?.typeMapping,
chainId,
asap: true
}));
}
promises.push(...(await this.#handshake(chainId, true)));
if (promises.length) {
this.#write();
return Promise.all(promises);
}
};
const socket = new socket_1.default(socketInitiator, this.#options.socket);
this.#attachListeners(socket);
return socket;
}
#pingTimer;
#setPingTimer() {
if (!this.#options.pingInterval || !this.#socket.isReady)
return;
clearTimeout(this.#pingTimer);
this.#pingTimer = setTimeout(() => {
if (!this.#socket.isReady)
return;
this.sendCommand(['PING'])
.then(reply => this.emit('ping-interval', reply))
.catch(err => this.emit('error', err))
.finally(() => this.#setPingTimer());
}, this.#options.pingInterval);
}
withCommandOptions(options) {
const proxy = Object.create(this._self);
proxy._commandOptions = options;
return proxy;
}
_commandOptionsProxy(key, value) {
const proxy = Object.create(this._self);
proxy._commandOptions = Object.create(this._commandOptions ?? null);
proxy._commandOptions[key] = value;
return proxy;
}
/**
* Override the `typeMapping` command option
*/
withTypeMapping(typeMapping) {
return this._commandOptionsProxy('typeMapping', typeMapping);
}
/**
* Override the `abortSignal` command option
*/
withAbortSignal(abortSignal) {
return this._commandOptionsProxy('abortSignal', abortSignal);
}
/**
* Override the `asap` command option to `true`
*/
asap() {
return this._commandOptionsProxy('asap', true);
}
/**
* Create the "legacy" (v3/callback) interface
*/
legacy() {
return new legacy_mode_1.RedisLegacyClient(this);
}
/**
* Create {@link RedisClientPool `RedisClientPool`} using this client as a prototype
*/
createPool(options) {
return pool_1.RedisClientPool.create(this._self.#options, options);
}
duplicate(overrides) {
return new (Object.getPrototypeOf(this).constructor)({
...this._self.#options,
commandOptions: this._commandOptions,
...overrides
});
}
async connect() {
await this._self.#socket.connect();
return this;
}
/**
* @internal
*/
_ejectSocket() {
const socket = this._self.#socket;
// @ts-ignore
this._self.#socket = null;
socket.removeAllListeners();
return socket;
}
/**
* @internal
*/
_insertSocket(socket) {
if (this._self.#socket) {
this._self._ejectSocket().destroy();
}
this._self.#socket = socket;
this._self.#attachListeners(this._self.#socket);
}
/**
* @internal
*/
_maintenanceUpdate(update) {
this._self.#socket.setMaintenanceTimeout(update.relaxedSocketTimeout);
this._self.#queue.setMaintenanceCommandTimeout(update.relaxedCommandTimeout);
}
/**
* @internal
*/
_pause() {
this._self.#paused = true;
}
/**
* @internal
*/
_unpause() {
this._self.#paused = false;
this._self.#maybeScheduleWrite();
}
/**
* @internal
*/
async _executeCommand(command, parser, commandOptions, transformReply) {
const csc = this._self.#clientSideCache;
const defaultTypeMapping = this._self.#options.commandOptions === commandOptions;
const fn = () => { return this.sendCommand(parser.redisArgs, commandOptions); };
if (csc && command.CACHEABLE && defaultTypeMapping) {
return await csc.handleCache(this._self, parser, fn, transformReply, commandOptions?.typeMapping);
}
else {
const reply = await fn();
if (transformReply) {
return transformReply(reply, parser.preserve, commandOptions?.typeMapping);
}
return reply;
}
}
/**
* @internal
*/
async _executeScript(script, parser, options, transformReply) {
const args = parser.redisArgs;
let reply;
try {
reply = await this.sendCommand(args, options);
}
catch (err) {
if (!err?.message?.startsWith?.('NOSCRIPT'))
throw err;
args[0] = 'EVAL';
args[1] = script.SCRIPT;
reply = await this.sendCommand(args, options);
}
return transformReply ?
transformReply(reply, parser.preserve, options?.typeMapping) :
reply;
}
sendCommand(args, options) {
if (!this._self.#socket.isOpen) {
return Promise.reject(new errors_1.ClientClosedError());
}
else if (!this._self.#socket.isReady && this._self.#options.disableOfflineQueue) {
return Promise.reject(new errors_1.ClientOfflineError());
}
// Merge global options with provided options
const opts = {
...this._self._commandOptions,
...options
};
const promise = this._self.#queue.addCommand(args, opts);
this._self.#scheduleWrite();
return promise;
}
async SELECT(db) {
await this.sendCommand(['SELECT', db.toString()]);
this._self.#selectedDB = db;
}
select = this.SELECT;
#pubSubCommand(promise) {
if (promise === undefined)
return Promise.resolve();
this.#scheduleWrite();
return promise;
}
SUBSCRIBE(channels, listener, bufferMode) {
return this._self.#pubSubCommand(this._self.#queue.subscribe(pub_sub_1.PUBSUB_TYPE.CHANNELS, channels, listener, bufferMode));
}
subscribe = this.SUBSCRIBE;
UNSUBSCRIBE(channels, listener, bufferMode) {
return this._self.#pubSubCommand(this._self.#queue.unsubscribe(pub_sub_1.PUBSUB_TYPE.CHANNELS, channels, listener, bufferMode));
}
unsubscribe = this.UNSUBSCRIBE;
PSUBSCRIBE(patterns, listener, bufferMode) {
return this._self.#pubSubCommand(this._self.#queue.subscribe(pub_sub_1.PUBSUB_TYPE.PATTERNS, patterns, listener, bufferMode));
}
pSubscribe = this.PSUBSCRIBE;
PUNSUBSCRIBE(patterns, listener, bufferMode) {
return this._self.#pubSubCommand(this._self.#queue.unsubscribe(pub_sub_1.PUBSUB_TYPE.PATTERNS, patterns, listener, bufferMode));
}
pUnsubscribe = this.PUNSUBSCRIBE;
SSUBSCRIBE(channels, listener, bufferMode) {
return this._self.#pubSubCommand(this._self.#queue.subscribe(pub_sub_1.PUBSUB_TYPE.SHARDED, channels, listener, bufferMode));
}
sSubscribe = this.SSUBSCRIBE;
SUNSUBSCRIBE(channels, listener, bufferMode) {
return this._self.#pubSubCommand(this._self.#queue.unsubscribe(pub_sub_1.PUBSUB_TYPE.SHARDED, channels, listener, bufferMode));
}
sUnsubscribe = this.SUNSUBSCRIBE;
async WATCH(key) {
const reply = await this._self.sendCommand((0, generic_transformers_1.pushVariadicArguments)(['WATCH'], key));
this._self.#watchEpoch ??= this._self.socketEpoch;
return reply;
}
watch = this.WATCH;
async UNWATCH() {
const reply = await this._self.sendCommand(['UNWATCH']);
this._self.#watchEpoch = undefined;
return reply;
}
unwatch = this.UNWATCH;
getPubSubListeners(type) {
return this._self.#queue.getPubSubListeners(type);
}
extendPubSubChannelListeners(type, channel, listeners) {
return this._self.#pubSubCommand(this._self.#queue.extendPubSubChannelListeners(type, channel, listeners));
}
extendPubSubListeners(type, listeners) {
return this._self.#pubSubCommand(this._self.#queue.extendPubSubListeners(type, listeners));
}
#write() {
if (this.#paused) {
return;
}
this.#socket.write(this.#queue.commandsToWrite());
}
#scheduledWrite;
#scheduleWrite() {
if (!this.#socket.isReady || this.#scheduledWrite)
return;
this.#scheduledWrite = setImmediate(() => {
this.#write();
this.#scheduledWrite = undefined;
});
}
#maybeScheduleWrite() {
if (!this.#queue.isWaitingToWrite())
return;
this.#scheduleWrite();
}
/**
* @internal
*/
async _executePipeline(commands, selectedDB) {
if (!this._self.#socket.isOpen) {
return Promise.reject(new errors_1.ClientClosedError());
}
const chainId = Symbol('Pipeline Chain'), promise = Promise.all(commands.map(({ args }) => this._self.#queue.addCommand(args, {
chainId,
typeMapping: this._commandOptions?.typeMapping
})));
this._self.#scheduleWrite();
const result = await promise;
if (selectedDB !== undefined) {
this._self.#selectedDB = selectedDB;
}
return result;
}
/**
* @internal
*/
async _executeMulti(commands, selectedDB) {
const dirtyWatch = this._self.#dirtyWatch;
this._self.#dirtyWatch = undefined;
const watchEpoch = this._self.#watchEpoch;
this._self.#watchEpoch = undefined;
if (!this._self.#socket.isOpen) {
throw new errors_1.ClientClosedError();
}
if (dirtyWatch) {
throw new errors_1.WatchError(dirtyWatch);
}
if (watchEpoch && watchEpoch !== this._self.socketEpoch) {
throw new errors_1.WatchError('Client reconnected after WATCH');
}
const typeMapping = this._commandOptions?.typeMapping;
const chainId = Symbol('MULTI Chain');
const promises = [
this._self.#queue.addCommand(['MULTI'], { chainId }),
];
for (const { args } of commands) {
promises.push(this._self.#queue.addCommand(args, {
chainId,
typeMapping
}));
}
promises.push(this._self.#queue.addCommand(['EXEC'], { chainId }));
this._self.#scheduleWrite();
const results = await Promise.all(promises), execResult = results[results.length - 1];
if (execResult === null) {
throw new errors_1.WatchError();
}
if (selectedDB !== undefined) {
this._self.#selectedDB = selectedDB;
}
return execResult;
}
MULTI() {
return new this.Multi(this._executeMulti.bind(this), this._executePipeline.bind(this), this._commandOptions?.typeMapping);
}
multi = this.MULTI;
async *scanIterator(options) {
let cursor = options?.cursor ?? '0';
do {
const reply = await this.scan(cursor, options);
cursor = reply.cursor;
yield reply.keys;
} while (cursor !== '0');
}
async *hScanIterator(key, options) {
let cursor = options?.cursor ?? '0';
do {
const reply = await this.hScan(key, cursor, options);
cursor = reply.cursor;
yield reply.entries;
} while (cursor !== '0');
}
async *hScanValuesIterator(key, options) {
let cursor = options?.cursor ?? '0';
do {
const reply = await this.hScanNoValues(key, cursor, options);
cursor = reply.cursor;
yield reply.fields;
} while (cursor !== '0');
}
async *hScanNoValuesIterator(key, options) {
let cursor = options?.cursor ?? '0';
do {
const reply = await this.hScanNoValues(key, cursor, options);
cursor = reply.cursor;
yield reply.fields;
} while (cursor !== '0');
}
async *sScanIterator(key, options) {
let cursor = options?.cursor ?? '0';
do {
const reply = await this.sScan(key, cursor, options);
cursor = reply.cursor;
yield reply.members;
} while (cursor !== '0');
}
async *zScanIterator(key, options) {
let cursor = options?.cursor ?? '0';
do {
const reply = await this.zScan(key, cursor, options);
cursor = reply.cursor;
yield reply.members;
} while (cursor !== '0');
}
async MONITOR(callback) {
const promise = this._self.#queue.monitor(callback, {
typeMapping: this._commandOptions?.typeMapping
});
this._self.#scheduleWrite();
await promise;
this._self.#monitorCallback = callback;
}
monitor = this.MONITOR;
/**
* Reset the client to its default state (i.e. stop PubSub, stop monitoring, select default DB, etc.)
*/
async reset() {
const chainId = Symbol('Reset Chain'), promises = [this._self.#queue.reset(chainId)], selectedDB = this._self.#options?.database ?? 0;
this._self.#credentialsSubscription?.dispose();
this._self.#credentialsSubscription = null;
promises.push(...(await this._self.#handshake(chainId, false)));
this._self.#scheduleWrite();
await Promise.all(promises);
this._self.#selectedDB = selectedDB;
this._self.#monitorCallback = undefined;
this._self.#dirtyWatch = undefined;
this._self.#watchEpoch = undefined;
}
/**
* If the client has state, reset it.
* An internal function to be used by wrapper class such as `RedisClientPool`.
* @internal
*/
resetIfDirty() {
let shouldReset = false;
if (this._self.#selectedDB !== (this._self.#options?.database ?? 0)) {
console.warn('Returning a client with a different selected DB');
shouldReset = true;
}
if (this._self.#monitorCallback) {
console.warn('Returning a client with active MONITOR');
shouldReset = true;
}
if (this._self.#queue.isPubSubActive) {
console.warn('Returning a client with active PubSub');
shouldReset = true;
}
if (this._self.#dirtyWatch || this._self.#watchEpoch) {
console.warn('Returning a client with active WATCH');
shouldReset = true;
}
if (shouldReset) {
return this.reset();
}
}
/**
* @deprecated use .close instead
*/
QUIT() {
this._self.#credentialsSubscription?.dispose();
this._self.#credentialsSubscription = null;
return this._self.#socket.quit(async () => {
clearTimeout(this._self.#pingTimer);
const quitPromise = this._self.#queue.addCommand(['QUIT']);
this._self.#scheduleWrite();
return quitPromise;
});
}
quit = this.QUIT;
/**
* @deprecated use .destroy instead
*/
disconnect() {
return Promise.resolve(this.destroy());
}
/**
* Close the client. Wait for pending commands.
*/
close() {
return new Promise(resolve => {
clearTimeout(this._self.#pingTimer);
this._self.#socket.close();
this._self.#clientSideCache?.onClose();
if (this._self.#queue.isEmpty()) {
this._self.#socket.destroySocket();
return resolve();
}
const maybeClose = () => {
if (!this._self.#queue.isEmpty())
return;
this._self.#socket.off('data', maybeClose);
this._self.#socket.destroySocket();
resolve();
};
this._self.#socket.on('data', maybeClose);
this._self.#credentialsSubscription?.dispose();
this._self.#credentialsSubscription = null;
});
}
/**
* Destroy the client. Rejects all commands immediately.
*/
destroy() {
clearTimeout(this._self.#pingTimer);
this._self.#queue.flushAll(new errors_1.DisconnectsClientError());
this._self.#socket.destroy();
this._self.#clientSideCache?.onClose();
this._self.#credentialsSubscription?.dispose();
this._self.#credentialsSubscription = null;
}
ref() {
this._self.#socket.ref();
}
unref() {
this._self.#socket.unref();
}
}
_a = RedisClient;
exports.default = RedisClient;
//# sourceMappingURL=index.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,37 @@
/// <reference types="node" />
import { RedisModules, RedisFunctions, RedisScripts, RespVersions, Command, CommandArguments, ReplyUnion } from '../RESP/types';
import { RedisClientType } from '.';
import { ErrorReply } from '../errors';
import COMMANDS from '../commands';
type LegacyArgument = string | Buffer | number | Date;
type LegacyArguments = Array<LegacyArgument | LegacyArguments>;
type LegacyCallback = (err: ErrorReply | null, reply?: ReplyUnion) => unknown;
type LegacyCommandArguments = LegacyArguments | [
...args: LegacyArguments,
callback: LegacyCallback
];
type WithCommands = {
[P in keyof typeof COMMANDS]: (...args: LegacyCommandArguments) => void;
};
export type RedisLegacyClientType = RedisLegacyClient & WithCommands;
export declare class RedisLegacyClient {
#private;
static pushArguments(redisArgs: CommandArguments, args: LegacyArguments): void;
static getTransformReply(command: Command, resp: RespVersions): import("../RESP/types").TransformReply | undefined;
constructor(client: RedisClientType<RedisModules, RedisFunctions, RedisScripts>);
sendCommand(...args: LegacyCommandArguments): void;
multi(): RedisLegacyMultiType;
}
type MultiWithCommands = {
[P in keyof typeof COMMANDS]: (...args: LegacyCommandArguments) => RedisLegacyMultiType;
};
export type RedisLegacyMultiType = LegacyMultiCommand & MultiWithCommands;
declare class LegacyMultiCommand {
#private;
static factory(resp: RespVersions): (client: RedisClientType<RedisModules, RedisFunctions, RedisScripts>) => RedisLegacyMultiType;
constructor(client: RedisClientType<RedisModules, RedisFunctions, RedisScripts>);
sendCommand(...args: LegacyArguments): this;
exec(cb?: (err: ErrorReply | null, replies?: Array<unknown>) => unknown): void;
}
export {};
//# sourceMappingURL=legacy-mode.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"legacy-mode.d.ts","sourceRoot":"","sources":["../../../lib/client/legacy-mode.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,YAAY,EAAE,OAAO,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAChI,OAAO,EAAE,eAAe,EAAE,MAAM,GAAG,CAAC;AAEpC,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,QAAQ,MAAM,aAAa,CAAC;AAGnC,KAAK,cAAc,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;AAEtD,KAAK,eAAe,GAAG,KAAK,CAAC,cAAc,GAAG,eAAe,CAAC,CAAC;AAE/D,KAAK,cAAc,GAAG,CAAC,GAAG,EAAE,UAAU,GAAG,IAAI,EAAE,KAAK,CAAC,EAAE,UAAU,KAAK,OAAO,CAAA;AAE7E,KAAK,sBAAsB,GAAG,eAAe,GAAG;IAC9C,GAAG,IAAI,EAAE,eAAe;IACxB,QAAQ,EAAE,cAAc;CACzB,CAAC;AAEF,KAAK,YAAY,GAAG;KACjB,CAAC,IAAI,MAAM,OAAO,QAAQ,GAAG,CAAC,GAAG,IAAI,EAAE,sBAAsB,KAAK,IAAI;CACxE,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG,iBAAiB,GAAG,YAAY,CAAC;AAErE,qBAAa,iBAAiB;;IAY5B,MAAM,CAAC,aAAa,CAAC,SAAS,EAAE,gBAAgB,EAAE,IAAI,EAAE,eAAe;IAevE,MAAM,CAAC,iBAAiB,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY;gBA4B3D,MAAM,EAAE,eAAe,CAAC,YAAY,EAAE,cAAc,EAAE,YAAY,CAAC;IAiBrE,WAAW,CAAC,GAAG,IAAI,EAAE,sBAAsB;IAe3C,KAAK;CAGN;AAED,KAAK,iBAAiB,GAAG;KACtB,CAAC,IAAI,MAAM,OAAO,QAAQ,GAAG,CAAC,GAAG,IAAI,EAAE,sBAAsB,KAAK,oBAAoB;CACxF,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG,kBAAkB,GAAG,iBAAiB,CAAC;AAE1E,cAAM,kBAAkB;;IAWtB,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,YAAY,YAYf,gBAAgB,YAAY,EAAE,cAAc,EAAE,YAAY,CAAC;gBAQjE,MAAM,EAAE,eAAe,CAAC,YAAY,EAAE,cAAc,EAAE,YAAY,CAAC;IAI/E,WAAW,CAAC,GAAG,IAAI,EAAE,eAAe;IAOpC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,EAAE,UAAU,GAAG,IAAI,EAAE,OAAO,CAAC,EAAE,KAAK,CAAC,OAAO,CAAC,KAAK,OAAO;CAYxE"}

View File

@@ -0,0 +1,119 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.RedisLegacyClient = void 0;
const commander_1 = require("../commander");
const commands_1 = __importDefault(require("../commands"));
const multi_command_1 = __importDefault(require("../multi-command"));
class RedisLegacyClient {
static #transformArguments(redisArgs, args) {
let callback;
if (typeof args[args.length - 1] === 'function') {
callback = args.pop();
}
RedisLegacyClient.pushArguments(redisArgs, args);
return callback;
}
static pushArguments(redisArgs, args) {
for (let i = 0; i < args.length; ++i) {
const arg = args[i];
if (Array.isArray(arg)) {
RedisLegacyClient.pushArguments(redisArgs, arg);
}
else {
redisArgs.push(typeof arg === 'number' || arg instanceof Date ?
arg.toString() :
arg);
}
}
}
static getTransformReply(command, resp) {
return command.TRANSFORM_LEGACY_REPLY ?
(0, commander_1.getTransformReply)(command, resp) :
undefined;
}
static #createCommand(name, command, resp) {
const transformReply = RedisLegacyClient.getTransformReply(command, resp);
return function (...args) {
const redisArgs = [name], callback = RedisLegacyClient.#transformArguments(redisArgs, args), promise = this.#client.sendCommand(redisArgs);
if (!callback) {
promise.catch(err => this.#client.emit('error', err));
return;
}
promise
.then(reply => callback(null, transformReply ? transformReply(reply) : reply))
.catch(err => callback(err));
};
}
#client;
#Multi;
constructor(client) {
this.#client = client;
const RESP = client.options?.RESP ?? 2;
for (const [name, command] of Object.entries(commands_1.default)) {
// TODO: as any?
this[name] = RedisLegacyClient.#createCommand(name, command, RESP);
}
this.#Multi = LegacyMultiCommand.factory(RESP);
}
sendCommand(...args) {
const redisArgs = [], callback = RedisLegacyClient.#transformArguments(redisArgs, args), promise = this.#client.sendCommand(redisArgs);
if (!callback) {
promise.catch(err => this.#client.emit('error', err));
return;
}
promise
.then(reply => callback(null, reply))
.catch(err => callback(err));
}
multi() {
return this.#Multi(this.#client);
}
}
exports.RedisLegacyClient = RedisLegacyClient;
class LegacyMultiCommand {
static #createCommand(name, command, resp) {
const transformReply = RedisLegacyClient.getTransformReply(command, resp);
return function (...args) {
const redisArgs = [name];
RedisLegacyClient.pushArguments(redisArgs, args);
this.#multi.addCommand(redisArgs, transformReply);
return this;
};
}
static factory(resp) {
const Multi = class extends LegacyMultiCommand {
};
for (const [name, command] of Object.entries(commands_1.default)) {
// TODO: as any?
Multi.prototype[name] = LegacyMultiCommand.#createCommand(name, command, resp);
}
return (client) => {
return new Multi(client);
};
}
#multi = new multi_command_1.default();
#client;
constructor(client) {
this.#client = client;
}
sendCommand(...args) {
const redisArgs = [];
RedisLegacyClient.pushArguments(redisArgs, args);
this.#multi.addCommand(redisArgs);
return this;
}
exec(cb) {
const promise = this.#client._executeMulti(this.#multi.queue);
if (!cb) {
promise.catch(err => this.#client.emit('error', err));
return;
}
promise
.then(results => cb(null, this.#multi.transformReplies(results)))
.catch(err => cb?.(err));
}
}
//# sourceMappingURL=legacy-mode.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"legacy-mode.js","sourceRoot":"","sources":["../../../lib/client/legacy-mode.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,68 @@
/// <reference types="node" />
import EventEmitter from "events";
export interface DoublyLinkedNode<T> {
value: T;
previous: DoublyLinkedNode<T> | undefined;
next: DoublyLinkedNode<T> | undefined;
}
export declare class DoublyLinkedList<T> {
#private;
get length(): number;
get head(): DoublyLinkedNode<T> | undefined;
get tail(): DoublyLinkedNode<T> | undefined;
push(value: T): {
previous: DoublyLinkedNode<T> | undefined;
next: undefined;
value: T;
};
unshift(value: T): {
previous: undefined;
next: undefined;
value: T;
} | {
previous: undefined;
next: DoublyLinkedNode<T>;
value: T;
};
add(value: T, prepend?: boolean): {
previous: DoublyLinkedNode<T> | undefined;
next: undefined;
value: T;
} | {
previous: undefined;
next: DoublyLinkedNode<T>;
value: T;
};
shift(): T | undefined;
remove(node: DoublyLinkedNode<T>): void;
reset(): void;
[Symbol.iterator](): Generator<T, void, unknown>;
nodes(): Generator<DoublyLinkedNode<T>, void, unknown>;
}
export interface SinglyLinkedNode<T> {
value: T;
next: SinglyLinkedNode<T> | undefined;
removed: boolean;
}
export declare class SinglyLinkedList<T> {
#private;
get length(): number;
get head(): SinglyLinkedNode<T> | undefined;
get tail(): SinglyLinkedNode<T> | undefined;
push(value: T): {
value: T;
next: undefined;
removed: boolean;
};
remove(node: SinglyLinkedNode<T>, parent: SinglyLinkedNode<T> | undefined): void;
shift(): T | undefined;
reset(): void;
[Symbol.iterator](): Generator<T, void, unknown>;
}
export declare class EmptyAwareSinglyLinkedList<T> extends SinglyLinkedList<T> {
readonly events: EventEmitter;
reset(): void;
shift(): T | undefined;
remove(node: SinglyLinkedNode<T>, parent: SinglyLinkedNode<T> | undefined): void;
}
//# sourceMappingURL=linked-list.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"linked-list.d.ts","sourceRoot":"","sources":["../../../lib/client/linked-list.ts"],"names":[],"mappings":";AAAA,OAAO,YAAY,MAAM,QAAQ,CAAC;AAElC,MAAM,WAAW,gBAAgB,CAAC,CAAC;IACjC,KAAK,EAAE,CAAC,CAAC;IACT,QAAQ,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IAC1C,IAAI,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;CACvC;AAED,qBAAa,gBAAgB,CAAC,CAAC;;IAG7B,IAAI,MAAM,WAET;IAID,IAAI,IAAI,oCAEP;IAID,IAAI,IAAI,oCAEP;IAED,IAAI,CAAC,KAAK,EAAE,CAAC;;;;;IAkBb,OAAO,CAAC,KAAK,EAAE,CAAC;;;;;;;;;IAkBhB,GAAG,CAAC,KAAK,EAAE,CAAC,EAAE,OAAO,UAAQ;;;;;;;;;IAM7B,KAAK;IAeL,MAAM,CAAC,IAAI,EAAE,gBAAgB,CAAC,CAAC,CAAC;IAqBhC,KAAK;IAKJ,CAAC,MAAM,CAAC,QAAQ,CAAC;IAQjB,KAAK;CAQP;AAED,MAAM,WAAW,gBAAgB,CAAC,CAAC;IACjC,KAAK,EAAE,CAAC,CAAC;IACT,IAAI,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IACtC,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,qBAAa,gBAAgB,CAAC,CAAC;;IAG7B,IAAI,MAAM,WAET;IAID,IAAI,IAAI,oCAEP;IAID,IAAI,IAAI,oCAEP;IAED,IAAI,CAAC,KAAK,EAAE,CAAC;;;;;IAgBb,MAAM,CAAC,IAAI,EAAE,gBAAgB,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,SAAS;IAsBzE,KAAK;IAcL,KAAK;IAKJ,CAAC,MAAM,CAAC,QAAQ,CAAC;CAOnB;AAED,qBAAa,0BAA0B,CAAC,CAAC,CAAE,SAAQ,gBAAgB,CAAC,CAAC,CAAC;IACpE,QAAQ,CAAC,MAAM,eAAsB;IACrC,KAAK;IAOL,KAAK,IAAI,CAAC,GAAG,SAAS;IAQtB,MAAM,CAAC,IAAI,EAAE,gBAAgB,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,SAAS;CAQ1E"}

View File

@@ -0,0 +1,212 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.EmptyAwareSinglyLinkedList = exports.SinglyLinkedList = exports.DoublyLinkedList = void 0;
const events_1 = __importDefault(require("events"));
class DoublyLinkedList {
#length = 0;
get length() {
return this.#length;
}
#head;
get head() {
return this.#head;
}
#tail;
get tail() {
return this.#tail;
}
push(value) {
++this.#length;
if (this.#tail === undefined) {
return this.#head = this.#tail = {
previous: this.#head,
next: undefined,
value
};
}
return this.#tail = this.#tail.next = {
previous: this.#tail,
next: undefined,
value
};
}
unshift(value) {
++this.#length;
if (this.#head === undefined) {
return this.#head = this.#tail = {
previous: undefined,
next: undefined,
value
};
}
return this.#head = this.#head.previous = {
previous: undefined,
next: this.#head,
value
};
}
add(value, prepend = false) {
return prepend ?
this.unshift(value) :
this.push(value);
}
shift() {
if (this.#head === undefined)
return undefined;
--this.#length;
const node = this.#head;
if (node.next) {
node.next.previous = undefined;
this.#head = node.next;
node.next = undefined;
}
else {
this.#head = this.#tail = undefined;
}
return node.value;
}
remove(node) {
if (this.#length === 0)
return;
--this.#length;
if (this.#tail === node) {
this.#tail = node.previous;
}
if (this.#head === node) {
this.#head = node.next;
}
else {
if (node.previous) {
node.previous.next = node.next;
}
if (node.next) {
node.next.previous = node.previous;
}
}
node.previous = undefined;
node.next = undefined;
}
reset() {
this.#length = 0;
this.#head = this.#tail = undefined;
}
*[Symbol.iterator]() {
let node = this.#head;
while (node !== undefined) {
yield node.value;
node = node.next;
}
}
*nodes() {
let node = this.#head;
while (node) {
const next = node.next;
yield node;
node = next;
}
}
}
exports.DoublyLinkedList = DoublyLinkedList;
class SinglyLinkedList {
#length = 0;
get length() {
return this.#length;
}
#head;
get head() {
return this.#head;
}
#tail;
get tail() {
return this.#tail;
}
push(value) {
++this.#length;
const node = {
value,
next: undefined,
removed: false
};
if (this.#head === undefined) {
return this.#head = this.#tail = node;
}
return this.#tail.next = this.#tail = node;
}
remove(node, parent) {
if (node.removed) {
throw new Error("node already removed");
}
--this.#length;
if (this.#head === node) {
if (this.#tail === node) {
this.#head = this.#tail = undefined;
}
else {
this.#head = node.next;
}
}
else if (this.#tail === node) {
this.#tail = parent;
parent.next = undefined;
}
else {
parent.next = node.next;
}
node.removed = true;
}
shift() {
if (this.#head === undefined)
return undefined;
const node = this.#head;
if (--this.#length === 0) {
this.#head = this.#tail = undefined;
}
else {
this.#head = node.next;
}
node.removed = true;
return node.value;
}
reset() {
this.#length = 0;
this.#head = this.#tail = undefined;
}
*[Symbol.iterator]() {
let node = this.#head;
while (node !== undefined) {
yield node.value;
node = node.next;
}
}
}
exports.SinglyLinkedList = SinglyLinkedList;
class EmptyAwareSinglyLinkedList extends SinglyLinkedList {
events = new events_1.default();
reset() {
const old = this.length;
super.reset();
if (old !== this.length && this.length === 0) {
this.events.emit('empty');
}
}
shift() {
const old = this.length;
const ret = super.shift();
if (old !== this.length && this.length === 0) {
this.events.emit('empty');
}
return ret;
}
remove(node, parent) {
const old = this.length;
super.remove(node, parent);
if (old !== this.length && this.length === 0) {
this.events.emit('empty');
}
}
}
exports.EmptyAwareSinglyLinkedList = EmptyAwareSinglyLinkedList;
//# sourceMappingURL=linked-list.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,45 @@
import COMMANDS from '../commands';
import { MULTI_MODE, MULTI_REPLY, MultiMode, MultiReply, MultiReplyType, RedisMultiQueuedCommand } from '../multi-command';
import { ReplyWithTypeMapping, CommandReply, Command, CommandArguments, CommanderConfig, RedisFunctions, RedisModules, RedisScripts, RespVersions, TransformReply, TypeMapping } from '../RESP/types';
import { Tail } from '../commands/generic-transformers';
type CommandSignature<REPLIES extends Array<unknown>, C extends Command, M extends RedisModules, F extends RedisFunctions, S extends RedisScripts, RESP extends RespVersions, TYPE_MAPPING extends TypeMapping> = (...args: Tail<Parameters<C['parseCommand']>>) => InternalRedisClientMultiCommandType<[
...REPLIES,
ReplyWithTypeMapping<CommandReply<C, RESP>, TYPE_MAPPING>
], M, F, S, RESP, TYPE_MAPPING>;
type WithCommands<REPLIES extends Array<unknown>, M extends RedisModules, F extends RedisFunctions, S extends RedisScripts, RESP extends RespVersions, TYPE_MAPPING extends TypeMapping> = {
[P in keyof typeof COMMANDS]: CommandSignature<REPLIES, (typeof COMMANDS)[P], M, F, S, RESP, TYPE_MAPPING>;
};
type WithModules<REPLIES extends Array<unknown>, M extends RedisModules, F extends RedisFunctions, S extends RedisScripts, RESP extends RespVersions, TYPE_MAPPING extends TypeMapping> = {
[P in keyof M]: {
[C in keyof M[P]]: CommandSignature<REPLIES, M[P][C], M, F, S, RESP, TYPE_MAPPING>;
};
};
type WithFunctions<REPLIES extends Array<unknown>, M extends RedisModules, F extends RedisFunctions, S extends RedisScripts, RESP extends RespVersions, TYPE_MAPPING extends TypeMapping> = {
[L in keyof F]: {
[C in keyof F[L]]: CommandSignature<REPLIES, F[L][C], M, F, S, RESP, TYPE_MAPPING>;
};
};
type WithScripts<REPLIES extends Array<unknown>, M extends RedisModules, F extends RedisFunctions, S extends RedisScripts, RESP extends RespVersions, TYPE_MAPPING extends TypeMapping> = {
[P in keyof S]: CommandSignature<REPLIES, S[P], M, F, S, RESP, TYPE_MAPPING>;
};
type InternalRedisClientMultiCommandType<REPLIES extends Array<any>, M extends RedisModules, F extends RedisFunctions, S extends RedisScripts, RESP extends RespVersions, TYPE_MAPPING extends TypeMapping> = (RedisClientMultiCommand<REPLIES> & WithCommands<REPLIES, M, F, S, RESP, TYPE_MAPPING> & WithModules<REPLIES, M, F, S, RESP, TYPE_MAPPING> & WithFunctions<REPLIES, M, F, S, RESP, TYPE_MAPPING> & WithScripts<REPLIES, M, F, S, RESP, TYPE_MAPPING>);
type TypedOrAny<Flag extends MultiMode, T> = [
Flag
] extends [MULTI_MODE['TYPED']] ? T : any;
export type RedisClientMultiCommandType<isTyped extends MultiMode, REPLIES extends Array<any>, M extends RedisModules, F extends RedisFunctions, S extends RedisScripts, RESP extends RespVersions, TYPE_MAPPING extends TypeMapping> = TypedOrAny<isTyped, InternalRedisClientMultiCommandType<REPLIES, M, F, S, RESP, TYPE_MAPPING>>;
type ExecuteMulti = (commands: Array<RedisMultiQueuedCommand>, selectedDB?: number) => Promise<Array<unknown>>;
export default class RedisClientMultiCommand<REPLIES = []> {
#private;
static extend<M extends RedisModules = Record<string, never>, F extends RedisFunctions = Record<string, never>, S extends RedisScripts = Record<string, never>, RESP extends RespVersions = 2>(config?: CommanderConfig<M, F, S, RESP>): any;
constructor(executeMulti: ExecuteMulti, executePipeline: ExecuteMulti, typeMapping?: TypeMapping);
SELECT(db: number, transformReply?: TransformReply): this;
select: (db: number, transformReply?: TransformReply) => this;
addCommand(args: CommandArguments, transformReply?: TransformReply): this;
exec<T extends MultiReply = MULTI_REPLY['GENERIC']>(execAsPipeline?: boolean): Promise<MultiReplyType<T, REPLIES>>;
EXEC: <T extends MultiReply = "generic">(execAsPipeline?: boolean) => Promise<MultiReplyType<T, REPLIES>>;
execTyped(execAsPipeline?: boolean): Promise<REPLIES>;
execAsPipeline<T extends MultiReply = MULTI_REPLY['GENERIC']>(): Promise<MultiReplyType<T, REPLIES>>;
execAsPipelineTyped(): Promise<REPLIES>;
}
export {};
//# sourceMappingURL=multi-command.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"multi-command.d.ts","sourceRoot":"","sources":["../../../lib/client/multi-command.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,106 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const commands_1 = __importDefault(require("../commands"));
const multi_command_1 = __importDefault(require("../multi-command"));
const commander_1 = require("../commander");
const parser_1 = require("./parser");
class RedisClientMultiCommand {
static #createCommand(command, resp) {
const transformReply = (0, commander_1.getTransformReply)(command, resp);
return function (...args) {
const parser = new parser_1.BasicCommandParser();
command.parseCommand(parser, ...args);
const redisArgs = parser.redisArgs;
redisArgs.preserve = parser.preserve;
return this.addCommand(redisArgs, transformReply);
};
}
static #createModuleCommand(command, resp) {
const transformReply = (0, commander_1.getTransformReply)(command, resp);
return function (...args) {
const parser = new parser_1.BasicCommandParser();
command.parseCommand(parser, ...args);
const redisArgs = parser.redisArgs;
redisArgs.preserve = parser.preserve;
return this._self.addCommand(redisArgs, transformReply);
};
}
static #createFunctionCommand(name, fn, resp) {
const prefix = (0, commander_1.functionArgumentsPrefix)(name, fn);
const transformReply = (0, commander_1.getTransformReply)(fn, resp);
return function (...args) {
const parser = new parser_1.BasicCommandParser();
parser.push(...prefix);
fn.parseCommand(parser, ...args);
const redisArgs = parser.redisArgs;
redisArgs.preserve = parser.preserve;
return this._self.addCommand(redisArgs, transformReply);
};
}
static #createScriptCommand(script, resp) {
const transformReply = (0, commander_1.getTransformReply)(script, resp);
return function (...args) {
const parser = new parser_1.BasicCommandParser();
script.parseCommand(parser, ...args);
const redisArgs = parser.redisArgs;
redisArgs.preserve = parser.preserve;
return this.#addScript(script, redisArgs, transformReply);
};
}
static extend(config) {
return (0, commander_1.attachConfig)({
BaseClass: RedisClientMultiCommand,
commands: commands_1.default,
createCommand: RedisClientMultiCommand.#createCommand,
createModuleCommand: RedisClientMultiCommand.#createModuleCommand,
createFunctionCommand: RedisClientMultiCommand.#createFunctionCommand,
createScriptCommand: RedisClientMultiCommand.#createScriptCommand,
config
});
}
#multi;
#executeMulti;
#executePipeline;
#selectedDB;
constructor(executeMulti, executePipeline, typeMapping) {
this.#multi = new multi_command_1.default(typeMapping);
this.#executeMulti = executeMulti;
this.#executePipeline = executePipeline;
}
SELECT(db, transformReply) {
this.#selectedDB = db;
this.#multi.addCommand(['SELECT', db.toString()], transformReply);
return this;
}
select = this.SELECT;
addCommand(args, transformReply) {
this.#multi.addCommand(args, transformReply);
return this;
}
#addScript(script, args, transformReply) {
this.#multi.addScript(script, args, transformReply);
return this;
}
async exec(execAsPipeline = false) {
if (execAsPipeline)
return this.execAsPipeline();
return this.#multi.transformReplies(await this.#executeMulti(this.#multi.queue, this.#selectedDB));
}
EXEC = this.exec;
execTyped(execAsPipeline = false) {
return this.exec(execAsPipeline);
}
async execAsPipeline() {
if (this.#multi.queue.length === 0)
return [];
return this.#multi.transformReplies(await this.#executePipeline(this.#multi.queue, this.#selectedDB));
}
execAsPipelineTyped() {
return this.execAsPipeline();
}
}
exports.default = RedisClientMultiCommand;
//# sourceMappingURL=multi-command.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"multi-command.js","sourceRoot":"","sources":["../../../lib/client/multi-command.ts"],"names":[],"mappings":"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"}

31
node_modules/@redis/client/dist/lib/client/parser.d.ts generated vendored Normal file
View File

@@ -0,0 +1,31 @@
import { RedisArgument } from '../RESP/types';
import { RedisVariadicArgument } from '../commands/generic-transformers';
export interface CommandParser {
redisArgs: ReadonlyArray<RedisArgument>;
keys: ReadonlyArray<RedisArgument>;
firstKey: RedisArgument | undefined;
preserve: unknown;
push: (...arg: Array<RedisArgument>) => unknown;
pushVariadic: (vals: RedisVariadicArgument) => unknown;
pushVariadicWithLength: (vals: RedisVariadicArgument) => unknown;
pushVariadicNumber: (vals: number | Array<number>) => unknown;
pushKey: (key: RedisArgument) => unknown;
pushKeys: (keys: RedisVariadicArgument) => unknown;
pushKeysLength: (keys: RedisVariadicArgument) => unknown;
}
export declare class BasicCommandParser implements CommandParser {
#private;
preserve: unknown;
get redisArgs(): RedisArgument[];
get keys(): RedisArgument[];
get firstKey(): RedisArgument;
get cacheKey(): string;
push(...arg: Array<RedisArgument>): void;
pushVariadic(vals: RedisVariadicArgument): void;
pushVariadicWithLength(vals: RedisVariadicArgument): void;
pushVariadicNumber(vals: number | number[]): void;
pushKey(key: RedisArgument): void;
pushKeysLength(keys: RedisVariadicArgument): void;
pushKeys(keys: RedisVariadicArgument): void;
}
//# sourceMappingURL=parser.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../../lib/client/parser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAC;AAEzE,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,aAAa,CAAC,aAAa,CAAC,CAAC;IACxC,IAAI,EAAE,aAAa,CAAC,aAAa,CAAC,CAAC;IACnC,QAAQ,EAAE,aAAa,GAAG,SAAS,CAAC;IACpC,QAAQ,EAAE,OAAO,CAAC;IAElB,IAAI,EAAE,CAAC,GAAG,GAAG,EAAE,KAAK,CAAC,aAAa,CAAC,KAAK,OAAO,CAAC;IAChD,YAAY,EAAE,CAAC,IAAI,EAAE,qBAAqB,KAAK,OAAO,CAAC;IACvD,sBAAsB,EAAE,CAAC,IAAI,EAAE,qBAAqB,KAAK,OAAO,CAAC;IACjE,kBAAkB,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,KAAK,OAAO,CAAC;IAC9D,OAAO,EAAE,CAAC,GAAG,EAAE,aAAa,KAAK,OAAO,CAAC;IACzC,QAAQ,EAAE,CAAC,IAAI,EAAE,qBAAqB,KAAK,OAAO,CAAC;IACnD,cAAc,EAAE,CAAC,IAAI,EAAE,qBAAqB,KAAK,OAAO,CAAC;CAC1D;AAED,qBAAa,kBAAmB,YAAW,aAAa;;IAGtD,QAAQ,EAAE,OAAO,CAAC;IAElB,IAAI,SAAS,oBAEZ;IAED,IAAI,IAAI,oBAEP;IAED,IAAI,QAAQ,kBAEX;IAED,IAAI,QAAQ,WASX;IAED,IAAI,CAAC,GAAG,GAAG,EAAE,KAAK,CAAC,aAAa,CAAC;IAIjC,YAAY,CAAC,IAAI,EAAE,qBAAqB;IAUxC,sBAAsB,CAAC,IAAI,EAAE,qBAAqB;IASlD,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE;IAU1C,OAAO,CAAC,GAAG,EAAE,aAAa;IAK1B,cAAc,CAAC,IAAI,EAAE,qBAAqB;IAS1C,QAAQ,CAAC,IAAI,EAAE,qBAAqB;CASrC"}

83
node_modules/@redis/client/dist/lib/client/parser.js generated vendored Normal file
View File

@@ -0,0 +1,83 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.BasicCommandParser = void 0;
class BasicCommandParser {
#redisArgs = [];
#keys = [];
preserve;
get redisArgs() {
return this.#redisArgs;
}
get keys() {
return this.#keys;
}
get firstKey() {
return this.#keys[0];
}
get cacheKey() {
const tmp = new Array(this.#redisArgs.length * 2);
for (let i = 0; i < this.#redisArgs.length; i++) {
tmp[i] = this.#redisArgs[i].length;
tmp[i + this.#redisArgs.length] = this.#redisArgs[i];
}
return tmp.join('_');
}
push(...arg) {
this.#redisArgs.push(...arg);
}
;
pushVariadic(vals) {
if (Array.isArray(vals)) {
for (const val of vals) {
this.push(val);
}
}
else {
this.push(vals);
}
}
pushVariadicWithLength(vals) {
if (Array.isArray(vals)) {
this.#redisArgs.push(vals.length.toString());
}
else {
this.#redisArgs.push('1');
}
this.pushVariadic(vals);
}
pushVariadicNumber(vals) {
if (Array.isArray(vals)) {
for (const val of vals) {
this.push(val.toString());
}
}
else {
this.push(vals.toString());
}
}
pushKey(key) {
this.#keys.push(key);
this.#redisArgs.push(key);
}
pushKeysLength(keys) {
if (Array.isArray(keys)) {
this.#redisArgs.push(keys.length.toString());
}
else {
this.#redisArgs.push('1');
}
this.pushKeys(keys);
}
pushKeys(keys) {
if (Array.isArray(keys)) {
this.#keys.push(...keys);
this.#redisArgs.push(...keys);
}
else {
this.#keys.push(keys);
this.#redisArgs.push(keys);
}
}
}
exports.BasicCommandParser = BasicCommandParser;
//# sourceMappingURL=parser.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"parser.js","sourceRoot":"","sources":["../../../lib/client/parser.ts"],"names":[],"mappings":"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"}

138
node_modules/@redis/client/dist/lib/client/pool.d.ts generated vendored Normal file
View File

@@ -0,0 +1,138 @@
/// <reference types="node" />
/// <reference types="node" />
/// <reference types="node" />
import { RedisArgument, RedisFunctions, RedisModules, RedisScripts, RespVersions, TypeMapping } from '../RESP/types';
import { RedisClientType, RedisClientOptions, RedisClientExtensions } from '.';
import { EventEmitter } from 'node:events';
import { CommandOptions } from './commands-queue';
import { RedisClientMultiCommandType } from './multi-command';
import { ClientSideCacheConfig, PooledClientSideCacheProvider } from './cache';
import { MULTI_MODE, MultiMode } from '../multi-command';
export interface RedisPoolOptions {
/**
* The minimum number of clients to keep in the pool (>= 1).
*/
minimum: number;
/**
* The maximum number of clients to keep in the pool (>= {@link RedisPoolOptions.minimum} >= 1).
*/
maximum: number;
/**
* The maximum time a task can wait for a client to become available (>= 0).
*/
acquireTimeout: number;
/**
* The delay in milliseconds before a cleanup operation is performed on idle clients.
*
* After this delay, the pool will check if there are too many idle clients and destroy
* excess ones to maintain optimal pool size.
*/
cleanupDelay: number;
/**
* Client Side Caching configuration for the pool.
*
* Enables Redis Servers and Clients to work together to cache results from commands
* sent to a server. The server will notify the client when cached results are no longer valid.
* In pooled mode, the cache is shared across all clients in the pool.
*
* Note: Client Side Caching is only supported with RESP3.
*
* @example Anonymous cache configuration
* ```
* const client = createClientPool({RESP: 3}, {
* clientSideCache: {
* ttl: 0,
* maxEntries: 0,
* evictPolicy: "LRU"
* },
* minimum: 5
* });
* ```
*
* @example Using a controllable cache
* ```
* const cache = new BasicPooledClientSideCache({
* ttl: 0,
* maxEntries: 0,
* evictPolicy: "LRU"
* });
* const client = createClientPool({RESP: 3}, {
* clientSideCache: cache,
* minimum: 5
* });
* ```
*/
clientSideCache?: PooledClientSideCacheProvider | ClientSideCacheConfig;
/**
* Enable experimental support for RESP3 module commands.
*
* When enabled, allows the use of module commands that have been adapted
* for the RESP3 protocol. This is an unstable feature and may change in
* future versions.
*
* @default false
*/
unstableResp3Modules?: boolean;
}
export type PoolTask<M extends RedisModules, F extends RedisFunctions, S extends RedisScripts, RESP extends RespVersions, TYPE_MAPPING extends TypeMapping, T = unknown> = (client: RedisClientType<M, F, S, RESP, TYPE_MAPPING>) => T;
export type RedisClientPoolType<M extends RedisModules = {}, F extends RedisFunctions = {}, S extends RedisScripts = {}, RESP extends RespVersions = 2, TYPE_MAPPING extends TypeMapping = {}> = (RedisClientPool<M, F, S, RESP, TYPE_MAPPING> & RedisClientExtensions<M, F, S, RESP, TYPE_MAPPING>);
export declare class RedisClientPool<M extends RedisModules = {}, F extends RedisFunctions = {}, S extends RedisScripts = {}, RESP extends RespVersions = 2, TYPE_MAPPING extends TypeMapping = {}> extends EventEmitter {
#private;
static create<M extends RedisModules, F extends RedisFunctions, S extends RedisScripts, RESP extends RespVersions, TYPE_MAPPING extends TypeMapping = {}>(clientOptions?: Omit<RedisClientOptions<M, F, S, RESP, TYPE_MAPPING>, "clientSideCache">, options?: Partial<RedisPoolOptions>): RedisClientPoolType<M, F, S, RESP, TYPE_MAPPING>;
/**
* The number of idle clients.
*/
get idleClients(): number;
/**
* The number of clients in use.
*/
get clientsInUse(): number;
/**
* The total number of clients in the pool (including connecting, idle, and in use).
*/
get totalClients(): number;
/**
* The number of tasks waiting for a client to become available.
*/
get tasksQueueLength(): number;
/**
* Whether the pool is open (either connecting or connected).
*/
get isOpen(): boolean;
/**
* Whether the pool is closing (*not* closed).
*/
get isClosing(): boolean;
get clientSideCache(): PooledClientSideCacheProvider | undefined;
/**
* You are probably looking for {@link RedisClient.createPool `RedisClient.createPool`},
* {@link RedisClientPool.fromClient `RedisClientPool.fromClient`},
* or {@link RedisClientPool.fromOptions `RedisClientPool.fromOptions`}...
*/
constructor(clientOptions?: RedisClientOptions<M, F, S, RESP, TYPE_MAPPING>, options?: Partial<RedisPoolOptions>);
private _self;
private _commandOptions?;
withCommandOptions<OPTIONS extends CommandOptions<TYPE_MAPPING>, TYPE_MAPPING extends TypeMapping>(options: OPTIONS): RedisClientPoolType<M, F, S, RESP, TYPE_MAPPING extends TypeMapping ? TYPE_MAPPING : {}>;
/**
* Override the `typeMapping` command option
*/
withTypeMapping<TYPE_MAPPING extends TypeMapping>(typeMapping: TYPE_MAPPING): RedisClientPoolType<M, F, S, RESP, TYPE_MAPPING extends TypeMapping ? TYPE_MAPPING : {}>;
/**
* Override the `abortSignal` command option
*/
withAbortSignal(abortSignal: AbortSignal): RedisClientPoolType<M, F, S, RESP, TYPE_MAPPING>;
/**
* Override the `asap` command option to `true`
* TODO: remove?
*/
asap(): RedisClientPoolType<M, F, S, RESP, TYPE_MAPPING>;
connect(): Promise<RedisClientPoolType<M, F, S, RESP, TYPE_MAPPING> | undefined>;
execute<T>(fn: PoolTask<M, F, S, RESP, TYPE_MAPPING, T>): Promise<Awaited<T>>;
cleanupTimeout?: NodeJS.Timeout;
sendCommand(args: Array<RedisArgument>, options?: CommandOptions): Promise<import("../RESP/types").ReplyUnion>;
MULTI<isTyped extends MultiMode = MULTI_MODE['TYPED']>(): RedisClientMultiCommandType<isTyped, [], M, F, S, RESP, TYPE_MAPPING>;
multi: <isTyped extends MultiMode = "typed">() => RedisClientMultiCommandType<isTyped, [], M, F, S, RESP, TYPE_MAPPING>;
close(): Promise<void>;
destroy(): void;
}
//# sourceMappingURL=pool.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"pool.d.ts","sourceRoot":"","sources":["../../../lib/client/pool.ts"],"names":[],"mappings":"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"}

327
node_modules/@redis/client/dist/lib/client/pool.js generated vendored Normal file
View File

@@ -0,0 +1,327 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.RedisClientPool = void 0;
const commands_1 = __importDefault(require("../commands"));
const _1 = __importDefault(require("."));
const node_events_1 = require("node:events");
const linked_list_1 = require("./linked-list");
const errors_1 = require("../errors");
const commander_1 = require("../commander");
const multi_command_1 = __importDefault(require("./multi-command"));
const cache_1 = require("./cache");
const parser_1 = require("./parser");
const single_entry_cache_1 = __importDefault(require("../single-entry-cache"));
class RedisClientPool extends node_events_1.EventEmitter {
static #createCommand(command, resp) {
const transformReply = (0, commander_1.getTransformReply)(command, resp);
return async function (...args) {
const parser = new parser_1.BasicCommandParser();
command.parseCommand(parser, ...args);
return this.execute(client => client._executeCommand(command, parser, this._commandOptions, transformReply));
};
}
static #createModuleCommand(command, resp) {
const transformReply = (0, commander_1.getTransformReply)(command, resp);
return async function (...args) {
const parser = new parser_1.BasicCommandParser();
command.parseCommand(parser, ...args);
return this._self.execute(client => client._executeCommand(command, parser, this._self._commandOptions, transformReply));
};
}
static #createFunctionCommand(name, fn, resp) {
const prefix = (0, commander_1.functionArgumentsPrefix)(name, fn);
const transformReply = (0, commander_1.getTransformReply)(fn, resp);
return async function (...args) {
const parser = new parser_1.BasicCommandParser();
parser.push(...prefix);
fn.parseCommand(parser, ...args);
return this._self.execute(client => client._executeCommand(fn, parser, this._self._commandOptions, transformReply));
};
}
static #createScriptCommand(script, resp) {
const prefix = (0, commander_1.scriptArgumentsPrefix)(script);
const transformReply = (0, commander_1.getTransformReply)(script, resp);
return async function (...args) {
const parser = new parser_1.BasicCommandParser();
parser.pushVariadic(prefix);
script.parseCommand(parser, ...args);
return this.execute(client => client._executeScript(script, parser, this._commandOptions, transformReply));
};
}
static #SingleEntryCache = new single_entry_cache_1.default();
static create(clientOptions, options) {
let Pool = RedisClientPool.#SingleEntryCache.get(clientOptions);
if (!Pool) {
Pool = (0, commander_1.attachConfig)({
BaseClass: RedisClientPool,
commands: commands_1.default,
createCommand: RedisClientPool.#createCommand,
createModuleCommand: RedisClientPool.#createModuleCommand,
createFunctionCommand: RedisClientPool.#createFunctionCommand,
createScriptCommand: RedisClientPool.#createScriptCommand,
config: clientOptions
});
Pool.prototype.Multi = multi_command_1.default.extend(clientOptions);
RedisClientPool.#SingleEntryCache.set(clientOptions, Pool);
}
// returning a "proxy" to prevent the namespaces._self to leak between "proxies"
return Object.create(new Pool(clientOptions, options));
}
// TODO: defaults
static #DEFAULTS = {
minimum: 1,
maximum: 100,
acquireTimeout: 3000,
cleanupDelay: 3000
};
#clientFactory;
#options;
#idleClients = new linked_list_1.SinglyLinkedList();
/**
* The number of idle clients.
*/
get idleClients() {
return this._self.#idleClients.length;
}
#clientsInUse = new linked_list_1.DoublyLinkedList();
/**
* The number of clients in use.
*/
get clientsInUse() {
return this._self.#clientsInUse.length;
}
/**
* The total number of clients in the pool (including connecting, idle, and in use).
*/
get totalClients() {
return this._self.#idleClients.length + this._self.#clientsInUse.length;
}
#tasksQueue = new linked_list_1.SinglyLinkedList();
/**
* The number of tasks waiting for a client to become available.
*/
get tasksQueueLength() {
return this._self.#tasksQueue.length;
}
#isOpen = false;
/**
* Whether the pool is open (either connecting or connected).
*/
get isOpen() {
return this._self.#isOpen;
}
#isClosing = false;
/**
* Whether the pool is closing (*not* closed).
*/
get isClosing() {
return this._self.#isClosing;
}
#clientSideCache;
get clientSideCache() {
return this._self.#clientSideCache;
}
/**
* You are probably looking for {@link RedisClient.createPool `RedisClient.createPool`},
* {@link RedisClientPool.fromClient `RedisClientPool.fromClient`},
* or {@link RedisClientPool.fromOptions `RedisClientPool.fromOptions`}...
*/
constructor(clientOptions, options) {
super();
this.#options = {
...RedisClientPool.#DEFAULTS,
...options
};
if (options?.clientSideCache) {
if (clientOptions === undefined) {
clientOptions = {};
}
if (options.clientSideCache instanceof cache_1.PooledClientSideCacheProvider) {
this.#clientSideCache = clientOptions.clientSideCache = options.clientSideCache;
}
else {
const cscConfig = options.clientSideCache;
this.#clientSideCache = clientOptions.clientSideCache = new cache_1.BasicPooledClientSideCache(cscConfig);
// this.#clientSideCache = clientOptions.clientSideCache = new PooledNoRedirectClientSideCache(cscConfig);
}
}
this.#clientFactory = _1.default.factory(clientOptions).bind(undefined, clientOptions);
}
_self = this;
_commandOptions;
withCommandOptions(options) {
const proxy = Object.create(this._self);
proxy._commandOptions = options;
return proxy;
}
#commandOptionsProxy(key, value) {
const proxy = Object.create(this._self);
proxy._commandOptions = Object.create(this._commandOptions ?? null);
proxy._commandOptions[key] = value;
return proxy;
}
/**
* Override the `typeMapping` command option
*/
withTypeMapping(typeMapping) {
return this._self.#commandOptionsProxy('typeMapping', typeMapping);
}
/**
* Override the `abortSignal` command option
*/
withAbortSignal(abortSignal) {
return this._self.#commandOptionsProxy('abortSignal', abortSignal);
}
/**
* Override the `asap` command option to `true`
* TODO: remove?
*/
asap() {
return this._self.#commandOptionsProxy('asap', true);
}
async connect() {
if (this._self.#isOpen)
return; // TODO: throw error?
this._self.#isOpen = true;
const promises = [];
while (promises.length < this._self.#options.minimum) {
promises.push(this._self.#create());
}
try {
await Promise.all(promises);
}
catch (err) {
this.destroy();
throw err;
}
return this;
}
async #create() {
const node = this._self.#clientsInUse.push(this._self.#clientFactory()
.on('error', (err) => this.emit('error', err)));
try {
const client = node.value;
await client.connect();
}
catch (err) {
this._self.#clientsInUse.remove(node);
throw err;
}
this._self.#returnClient(node);
}
execute(fn) {
return new Promise((resolve, reject) => {
const client = this._self.#idleClients.shift(), { tail } = this._self.#tasksQueue;
if (!client) {
let timeout;
if (this._self.#options.acquireTimeout > 0) {
timeout = setTimeout(() => {
this._self.#tasksQueue.remove(task, tail);
reject(new errors_1.TimeoutError('Timeout waiting for a client')); // TODO: message
}, this._self.#options.acquireTimeout);
}
const task = this._self.#tasksQueue.push({
timeout,
// @ts-ignore
resolve,
reject,
fn
});
if (this.totalClients < this._self.#options.maximum) {
this._self.#create();
}
return;
}
const node = this._self.#clientsInUse.push(client);
// @ts-ignore
this._self.#executeTask(node, resolve, reject, fn);
});
}
#executeTask(node, resolve, reject, fn) {
const result = fn(node.value);
if (result instanceof Promise) {
result
.then(resolve, reject)
.finally(() => this.#returnClient(node));
}
else {
resolve(result);
this.#returnClient(node);
}
}
#returnClient(node) {
const task = this.#tasksQueue.shift();
if (task) {
clearTimeout(task.timeout);
this.#executeTask(node, task.resolve, task.reject, task.fn);
return;
}
this.#clientsInUse.remove(node);
this.#idleClients.push(node.value);
this.#scheduleCleanup();
}
cleanupTimeout;
#scheduleCleanup() {
if (this.totalClients <= this.#options.minimum)
return;
clearTimeout(this.cleanupTimeout);
this.cleanupTimeout = setTimeout(() => this.#cleanup(), this.#options.cleanupDelay);
}
#cleanup() {
const toDestroy = Math.min(this.#idleClients.length, this.totalClients - this.#options.minimum);
for (let i = 0; i < toDestroy; i++) {
// TODO: shift vs pop
const client = this.#idleClients.shift();
client.destroy();
}
}
sendCommand(args, options) {
return this.execute(client => client.sendCommand(args, options));
}
MULTI() {
return new this.Multi((commands, selectedDB) => this.execute(client => client._executeMulti(commands, selectedDB)), commands => this.execute(client => client._executePipeline(commands)), this._commandOptions?.typeMapping);
}
multi = this.MULTI;
async close() {
if (this._self.#isClosing)
return; // TODO: throw err?
if (!this._self.#isOpen)
return; // TODO: throw err?
this._self.#isClosing = true;
try {
const promises = [];
for (const client of this._self.#idleClients) {
promises.push(client.close());
}
for (const client of this._self.#clientsInUse) {
promises.push(client.close());
}
await Promise.all(promises);
this.#clientSideCache?.onPoolClose();
this._self.#idleClients.reset();
this._self.#clientsInUse.reset();
}
catch (err) {
}
finally {
this._self.#isClosing = false;
}
}
destroy() {
for (const client of this._self.#idleClients) {
client.destroy();
}
this._self.#idleClients.reset();
for (const client of this._self.#clientsInUse) {
client.destroy();
}
this._self.#clientSideCache?.onPoolClose();
this._self.#clientsInUse.reset();
this._self.#isOpen = false;
}
}
exports.RedisClientPool = RedisClientPool;
//# sourceMappingURL=pool.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,62 @@
/// <reference types="node" />
import { RedisArgument } from '../RESP/types';
import { CommandToWrite } from './commands-queue';
export declare const PUBSUB_TYPE: {
readonly CHANNELS: "CHANNELS";
readonly PATTERNS: "PATTERNS";
readonly SHARDED: "SHARDED";
};
export type PUBSUB_TYPE = typeof PUBSUB_TYPE;
export type PubSubType = PUBSUB_TYPE[keyof PUBSUB_TYPE];
export type PubSubListener<RETURN_BUFFERS extends boolean = false> = <T extends RETURN_BUFFERS extends true ? Buffer : string>(message: T, channel: T) => unknown;
export interface ChannelListeners {
unsubscribing: boolean;
buffers: Set<PubSubListener<true>>;
strings: Set<PubSubListener<false>>;
}
export type PubSubTypeListeners = Map<string, ChannelListeners>;
export type PubSubListeners = Record<PubSubType, PubSubTypeListeners>;
export type PubSubCommand = (Required<Pick<CommandToWrite, 'args' | 'channelsCounter' | 'resolve'>> & {
reject: undefined | (() => unknown);
});
export declare class PubSub {
#private;
static isStatusReply(reply: Array<Buffer>): boolean;
static isShardedUnsubscribe(reply: Array<Buffer>): boolean;
get isActive(): boolean;
readonly listeners: PubSubListeners;
subscribe<T extends boolean>(type: PubSubType, channels: string | Array<string>, listener: PubSubListener<T>, returnBuffers?: T): {
args: RedisArgument[];
channelsCounter: number;
resolve: () => void;
reject: () => void;
} | undefined;
extendChannelListeners(type: PubSubType, channel: string, listeners: ChannelListeners): {
args: (string | Buffer)[];
channelsCounter: number;
resolve: () => number;
reject: () => void;
} | undefined;
extendTypeListeners(type: PubSubType, listeners: PubSubTypeListeners): {
args: RedisArgument[];
channelsCounter: number;
resolve: () => number;
reject: () => void;
} | undefined;
unsubscribe<T extends boolean>(type: PubSubType, channels?: string | Array<string>, listener?: PubSubListener<T>, returnBuffers?: T): {
args: RedisArgument[];
channelsCounter: number;
resolve: () => void;
reject: undefined;
} | undefined;
reset(): void;
resubscribe(): PubSubCommand[];
handleMessageReply(reply: Array<Buffer>): boolean;
removeShardedListeners(channel: string): ChannelListeners;
removeAllListeners(): {
CHANNELS: PubSubTypeListeners;
PATTERNS: PubSubTypeListeners;
SHARDED: PubSubTypeListeners;
};
}
//# sourceMappingURL=pub-sub.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"pub-sub.d.ts","sourceRoot":"","sources":["../../../lib/client/pub-sub.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAElD,eAAO,MAAM,WAAW;;;;CAId,CAAC;AAEX,MAAM,MAAM,WAAW,GAAG,OAAO,WAAW,CAAC;AAE7C,MAAM,MAAM,UAAU,GAAG,WAAW,CAAC,MAAM,WAAW,CAAC,CAAC;AAoBxD,MAAM,MAAM,cAAc,CACxB,cAAc,SAAS,OAAO,GAAG,KAAK,IACpC,CAAC,CAAC,SAAS,cAAc,SAAS,IAAI,GAAG,MAAM,GAAG,MAAM,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,KAAK,OAAO,CAAC;AAEjG,MAAM,WAAW,gBAAgB;IAC/B,aAAa,EAAE,OAAO,CAAC;IACvB,OAAO,EAAE,GAAG,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC;IACnC,OAAO,EAAE,GAAG,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC;CACrC;AAED,MAAM,MAAM,mBAAmB,GAAG,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;AAEhE,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC,UAAU,EAAE,mBAAmB,CAAC,CAAC;AAEtE,MAAM,MAAM,aAAa,GAAG,CAC1B,QAAQ,CAAC,IAAI,CAAC,cAAc,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS,CAAC,CAAC,GAAG;IACvE,MAAM,EAAE,SAAS,GAAG,CAAC,MAAM,OAAO,CAAC,CAAC;CACrC,CACF,CAAC;AAEF,qBAAa,MAAM;;IACjB,MAAM,CAAC,aAAa,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,OAAO;IAUnD,MAAM,CAAC,oBAAoB,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,OAAO;IAmB1D,IAAI,QAAQ,YAEX;IAED,QAAQ,CAAC,SAAS,EAAE,eAAe,CAIjC;IAEF,SAAS,CAAC,CAAC,SAAS,OAAO,EACzB,IAAI,EAAE,UAAU,EAChB,QAAQ,EAAE,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,EAChC,QAAQ,EAAE,cAAc,CAAC,CAAC,CAAC,EAC3B,aAAa,CAAC,EAAE,CAAC;;;;;;IAkDnB,sBAAsB,CACpB,IAAI,EAAE,UAAU,EAChB,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,gBAAgB;;;;;;IA0C7B,mBAAmB,CAAC,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,mBAAmB;;;;;;IAuBpE,WAAW,CAAC,CAAC,SAAS,OAAO,EAC3B,IAAI,EAAE,UAAU,EAChB,QAAQ,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,EACjC,QAAQ,CAAC,EAAE,cAAc,CAAC,CAAC,CAAC,EAC5B,aAAa,CAAC,EAAE,CAAC;;;;;;IAsGnB,KAAK;IAKL,WAAW;IA+CX,kBAAkB,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,OAAO;IA4BjD,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,gBAAgB;IAOzD,kBAAkB;;;;;CA2CnB"}

320
node_modules/@redis/client/dist/lib/client/pub-sub.js generated vendored Normal file
View File

@@ -0,0 +1,320 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.PubSub = exports.PUBSUB_TYPE = void 0;
exports.PUBSUB_TYPE = {
CHANNELS: 'CHANNELS',
PATTERNS: 'PATTERNS',
SHARDED: 'SHARDED'
};
const COMMANDS = {
[exports.PUBSUB_TYPE.CHANNELS]: {
subscribe: Buffer.from('subscribe'),
unsubscribe: Buffer.from('unsubscribe'),
message: Buffer.from('message')
},
[exports.PUBSUB_TYPE.PATTERNS]: {
subscribe: Buffer.from('psubscribe'),
unsubscribe: Buffer.from('punsubscribe'),
message: Buffer.from('pmessage')
},
[exports.PUBSUB_TYPE.SHARDED]: {
subscribe: Buffer.from('ssubscribe'),
unsubscribe: Buffer.from('sunsubscribe'),
message: Buffer.from('smessage')
}
};
class PubSub {
static isStatusReply(reply) {
return (COMMANDS[exports.PUBSUB_TYPE.CHANNELS].subscribe.equals(reply[0]) ||
COMMANDS[exports.PUBSUB_TYPE.CHANNELS].unsubscribe.equals(reply[0]) ||
COMMANDS[exports.PUBSUB_TYPE.PATTERNS].subscribe.equals(reply[0]) ||
COMMANDS[exports.PUBSUB_TYPE.PATTERNS].unsubscribe.equals(reply[0]) ||
COMMANDS[exports.PUBSUB_TYPE.SHARDED].subscribe.equals(reply[0]));
}
static isShardedUnsubscribe(reply) {
return COMMANDS[exports.PUBSUB_TYPE.SHARDED].unsubscribe.equals(reply[0]);
}
static #channelsArray(channels) {
return (Array.isArray(channels) ? channels : [channels]);
}
static #listenersSet(listeners, returnBuffers) {
return (returnBuffers ? listeners.buffers : listeners.strings);
}
#subscribing = 0;
#isActive = false;
get isActive() {
return this.#isActive;
}
listeners = {
[exports.PUBSUB_TYPE.CHANNELS]: new Map(),
[exports.PUBSUB_TYPE.PATTERNS]: new Map(),
[exports.PUBSUB_TYPE.SHARDED]: new Map()
};
subscribe(type, channels, listener, returnBuffers) {
const args = [COMMANDS[type].subscribe], channelsArray = PubSub.#channelsArray(channels);
for (const channel of channelsArray) {
let channelListeners = this.listeners[type].get(channel);
if (!channelListeners || channelListeners.unsubscribing) {
args.push(channel);
}
}
if (args.length === 1) {
// all channels are already subscribed, add listeners without issuing a command
for (const channel of channelsArray) {
PubSub.#listenersSet(this.listeners[type].get(channel), returnBuffers).add(listener);
}
return;
}
this.#isActive = true;
this.#subscribing++;
return {
args,
channelsCounter: args.length - 1,
resolve: () => {
this.#subscribing--;
for (const channel of channelsArray) {
let listeners = this.listeners[type].get(channel);
if (!listeners) {
listeners = {
unsubscribing: false,
buffers: new Set(),
strings: new Set()
};
this.listeners[type].set(channel, listeners);
}
PubSub.#listenersSet(listeners, returnBuffers).add(listener);
}
},
reject: () => {
this.#subscribing--;
this.#updateIsActive();
}
};
}
extendChannelListeners(type, channel, listeners) {
if (!this.#extendChannelListeners(type, channel, listeners))
return;
this.#isActive = true;
this.#subscribing++;
return {
args: [
COMMANDS[type].subscribe,
channel
],
channelsCounter: 1,
resolve: () => this.#subscribing--,
reject: () => {
this.#subscribing--;
this.#updateIsActive();
}
};
}
#extendChannelListeners(type, channel, listeners) {
const existingListeners = this.listeners[type].get(channel);
if (!existingListeners) {
this.listeners[type].set(channel, listeners);
return true;
}
for (const listener of listeners.buffers) {
existingListeners.buffers.add(listener);
}
for (const listener of listeners.strings) {
existingListeners.strings.add(listener);
}
return false;
}
extendTypeListeners(type, listeners) {
const args = [COMMANDS[type].subscribe];
for (const [channel, channelListeners] of listeners) {
if (this.#extendChannelListeners(type, channel, channelListeners)) {
args.push(channel);
}
}
if (args.length === 1)
return;
this.#isActive = true;
this.#subscribing++;
return {
args,
channelsCounter: args.length - 1,
resolve: () => this.#subscribing--,
reject: () => {
this.#subscribing--;
this.#updateIsActive();
}
};
}
unsubscribe(type, channels, listener, returnBuffers) {
const listeners = this.listeners[type];
if (!channels) {
return this.#unsubscribeCommand([COMMANDS[type].unsubscribe],
// cannot use `this.#subscribed` because there might be some `SUBSCRIBE` commands in the queue
// cannot use `this.#subscribed + this.#subscribing` because some `SUBSCRIBE` commands might fail
NaN, () => listeners.clear());
}
const channelsArray = PubSub.#channelsArray(channels);
if (!listener) {
return this.#unsubscribeCommand([COMMANDS[type].unsubscribe, ...channelsArray], channelsArray.length, () => {
for (const channel of channelsArray) {
listeners.delete(channel);
}
});
}
const args = [COMMANDS[type].unsubscribe];
for (const channel of channelsArray) {
const sets = listeners.get(channel);
if (sets) {
let current, other;
if (returnBuffers) {
current = sets.buffers;
other = sets.strings;
}
else {
current = sets.strings;
other = sets.buffers;
}
const currentSize = current.has(listener) ? current.size - 1 : current.size;
if (currentSize !== 0 || other.size !== 0)
continue;
sets.unsubscribing = true;
}
args.push(channel);
}
if (args.length === 1) {
// all channels has other listeners,
// delete the listeners without issuing a command
for (const channel of channelsArray) {
PubSub.#listenersSet(listeners.get(channel), returnBuffers).delete(listener);
}
return;
}
return this.#unsubscribeCommand(args, args.length - 1, () => {
for (const channel of channelsArray) {
const sets = listeners.get(channel);
if (!sets)
continue;
(returnBuffers ? sets.buffers : sets.strings).delete(listener);
if (sets.buffers.size === 0 && sets.strings.size === 0) {
listeners.delete(channel);
}
}
});
}
#unsubscribeCommand(args, channelsCounter, removeListeners) {
return {
args,
channelsCounter,
resolve: () => {
removeListeners();
this.#updateIsActive();
},
reject: undefined
};
}
#updateIsActive() {
this.#isActive = (this.listeners[exports.PUBSUB_TYPE.CHANNELS].size !== 0 ||
this.listeners[exports.PUBSUB_TYPE.PATTERNS].size !== 0 ||
this.listeners[exports.PUBSUB_TYPE.SHARDED].size !== 0 ||
this.#subscribing !== 0);
}
reset() {
this.#isActive = false;
this.#subscribing = 0;
}
resubscribe() {
const commands = [];
for (const [type, listeners] of Object.entries(this.listeners)) {
if (!listeners.size)
continue;
this.#isActive = true;
if (type === exports.PUBSUB_TYPE.SHARDED) {
this.#shardedResubscribe(commands, listeners);
}
else {
this.#normalResubscribe(commands, type, listeners);
}
}
return commands;
}
#normalResubscribe(commands, type, listeners) {
this.#subscribing++;
const callback = () => this.#subscribing--;
commands.push({
args: [
COMMANDS[type].subscribe,
...listeners.keys()
],
channelsCounter: listeners.size,
resolve: callback,
reject: callback
});
}
#shardedResubscribe(commands, listeners) {
const callback = () => this.#subscribing--;
for (const channel of listeners.keys()) {
this.#subscribing++;
commands.push({
args: [
COMMANDS[exports.PUBSUB_TYPE.SHARDED].subscribe,
channel
],
channelsCounter: 1,
resolve: callback,
reject: callback
});
}
}
handleMessageReply(reply) {
if (COMMANDS[exports.PUBSUB_TYPE.CHANNELS].message.equals(reply[0])) {
this.#emitPubSubMessage(exports.PUBSUB_TYPE.CHANNELS, reply[2], reply[1]);
return true;
}
else if (COMMANDS[exports.PUBSUB_TYPE.PATTERNS].message.equals(reply[0])) {
this.#emitPubSubMessage(exports.PUBSUB_TYPE.PATTERNS, reply[3], reply[2], reply[1]);
return true;
}
else if (COMMANDS[exports.PUBSUB_TYPE.SHARDED].message.equals(reply[0])) {
this.#emitPubSubMessage(exports.PUBSUB_TYPE.SHARDED, reply[2], reply[1]);
return true;
}
return false;
}
removeShardedListeners(channel) {
const listeners = this.listeners[exports.PUBSUB_TYPE.SHARDED].get(channel);
this.listeners[exports.PUBSUB_TYPE.SHARDED].delete(channel);
this.#updateIsActive();
return listeners;
}
removeAllListeners() {
const result = {
[exports.PUBSUB_TYPE.CHANNELS]: this.listeners[exports.PUBSUB_TYPE.CHANNELS],
[exports.PUBSUB_TYPE.PATTERNS]: this.listeners[exports.PUBSUB_TYPE.PATTERNS],
[exports.PUBSUB_TYPE.SHARDED]: this.listeners[exports.PUBSUB_TYPE.SHARDED]
};
this.#updateIsActive();
this.listeners[exports.PUBSUB_TYPE.CHANNELS] = new Map();
this.listeners[exports.PUBSUB_TYPE.PATTERNS] = new Map();
this.listeners[exports.PUBSUB_TYPE.SHARDED] = new Map();
return result;
}
#emitPubSubMessage(type, message, channel, pattern) {
const keyString = (pattern ?? channel).toString(), listeners = this.listeners[type].get(keyString);
if (!listeners)
return;
for (const listener of listeners.buffers) {
listener(message, channel);
}
if (!listeners.strings.size)
return;
const channelString = pattern ? channel.toString() : keyString, messageString = channelString === '__redis__:invalidate' ?
// https://github.com/redis/redis/pull/7469
// https://github.com/redis/redis/issues/7463
(message === null ? null : message.map(x => x.toString())) :
message.toString();
for (const listener of listeners.strings) {
listener(messageString, channelString);
}
}
}
exports.PubSub = PubSub;
//# sourceMappingURL=pub-sub.js.map

File diff suppressed because one or more lines are too long

59
node_modules/@redis/client/dist/lib/client/socket.d.ts generated vendored Normal file
View File

@@ -0,0 +1,59 @@
/// <reference types="node" />
/// <reference types="node" />
/// <reference types="node" />
import { EventEmitter } from 'node:events';
import net from 'node:net';
import tls from 'node:tls';
import { RedisArgument } from '../RESP/types';
type NetOptions = {
tls?: false;
};
type ReconnectStrategyFunction = (retries: number, cause: Error) => false | Error | number;
type RedisSocketOptionsCommon = {
/**
* Connection timeout (in milliseconds)
*/
connectTimeout?: number;
/**
* When the socket closes unexpectedly (without calling `.close()`/`.destroy()`), the client uses `reconnectStrategy` to decide what to do. The following values are supported:
* 1. `false` -> do not reconnect, close the client and flush the command queue.
* 2. `number` -> wait for `X` milliseconds before reconnecting.
* 3. `(retries: number, cause: Error) => false | number | Error` -> `number` is the same as configuring a `number` directly, `Error` is the same as `false`, but with a custom error.
*/
reconnectStrategy?: false | number | ReconnectStrategyFunction;
/**
* The timeout (in milliseconds) after which the socket will be closed. `undefined` means no timeout.
*/
socketTimeout?: number;
};
type RedisTcpOptions = RedisSocketOptionsCommon & NetOptions & Omit<net.TcpNetConnectOpts, 'timeout' | 'onread' | 'readable' | 'writable' | 'port'> & {
port?: number;
};
type RedisTlsOptions = RedisSocketOptionsCommon & tls.ConnectionOptions & {
tls: true;
};
type RedisIpcOptions = RedisSocketOptionsCommon & Omit<net.IpcNetConnectOpts, 'timeout' | 'onread' | 'readable' | 'writable'> & {
tls: false;
};
export type RedisTcpSocketOptions = RedisTcpOptions | RedisTlsOptions;
export type RedisSocketOptions = RedisTcpSocketOptions | RedisIpcOptions;
export type RedisSocketInitiator = () => void | Promise<unknown>;
export default class RedisSocket extends EventEmitter {
#private;
get isOpen(): boolean;
get isReady(): boolean;
get socketEpoch(): number;
constructor(initiator: RedisSocketInitiator, options?: RedisSocketOptions);
connect(): Promise<void>;
setMaintenanceTimeout(ms?: number): void;
write(iterable: Iterable<ReadonlyArray<RedisArgument>>): void;
quit<T>(fn: () => Promise<T>): Promise<T>;
close(): void;
destroy(): void;
destroySocket(): void;
ref(): void;
unref(): void;
defaultReconnectStrategy(retries: number, cause: unknown): number | false;
}
export {};
//# sourceMappingURL=socket.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"socket.d.ts","sourceRoot":"","sources":["../../../lib/client/socket.ts"],"names":[],"mappings":";;;AAAA,OAAO,EAAE,YAAY,EAAQ,MAAM,aAAa,CAAC;AACjD,OAAO,GAAG,MAAM,UAAU,CAAC;AAC3B,OAAO,GAAG,MAAM,UAAU,CAAC;AAG3B,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAG9C,KAAK,UAAU,GAAG;IAChB,GAAG,CAAC,EAAE,KAAK,CAAC;CACb,CAAC;AAEF,KAAK,yBAAyB,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,KAAK,KAAK,GAAG,KAAK,GAAG,MAAM,CAAC;AAE3F,KAAK,wBAAwB,GAAG;IAC9B;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,yBAAyB,CAAC;IAC/D;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAA;AAED,KAAK,eAAe,GAAG,wBAAwB,GAAG,UAAU,GAAG,IAAI,CACjE,GAAG,CAAC,iBAAiB,EACrB,SAAS,GAAG,QAAQ,GAAG,UAAU,GAAG,UAAU,GAAG,MAAM,CACxD,GAAG;IACF,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,KAAK,eAAe,GAAG,wBAAwB,GAAG,GAAG,CAAC,iBAAiB,GAAG;IACxE,GAAG,EAAE,IAAI,CAAC;CACX,CAAA;AAED,KAAK,eAAe,GAAG,wBAAwB,GAAG,IAAI,CACpD,GAAG,CAAC,iBAAiB,EACrB,SAAS,GAAG,QAAQ,GAAG,UAAU,GAAG,UAAU,CAC/C,GAAG;IACF,GAAG,EAAE,KAAK,CAAC;CACZ,CAAA;AAED,MAAM,MAAM,qBAAqB,GAAG,eAAe,GAAG,eAAe,CAAC;AAEtE,MAAM,MAAM,kBAAkB,GAAG,qBAAqB,GAAG,eAAe,CAAC;AAEzE,MAAM,MAAM,oBAAoB,GAAG,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AAEjE,MAAM,CAAC,OAAO,OAAO,WAAY,SAAQ,YAAY;;IAanD,IAAI,MAAM,YAET;IAID,IAAI,OAAO,YAEV;IAMD,IAAI,WAAW,WAEd;gBAEW,SAAS,EAAE,oBAAoB,EAAE,OAAO,CAAC,EAAE,kBAAkB;IAqHnE,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAuC9B,qBAAqB,CAAC,EAAE,CAAC,EAAE,MAAM;IAuEjC,KAAK,CAAC,QAAQ,EAAE,QAAQ,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC;IAchD,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAW/C,KAAK;IAQL,OAAO;IASP,aAAa;IAWb,GAAG;IAKH,KAAK;IAKL,wBAAwB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO;CAazD"}

303
node_modules/@redis/client/dist/lib/client/socket.js generated vendored Normal file
View File

@@ -0,0 +1,303 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const node_events_1 = require("node:events");
const node_net_1 = __importDefault(require("node:net"));
const node_tls_1 = __importDefault(require("node:tls"));
const errors_1 = require("../errors");
const promises_1 = require("node:timers/promises");
const enterprise_maintenance_manager_1 = require("./enterprise-maintenance-manager");
class RedisSocket extends node_events_1.EventEmitter {
#initiator;
#connectTimeout;
#reconnectStrategy;
#socketFactory;
#socketTimeout;
#maintenanceTimeout;
#socket;
#isOpen = false;
get isOpen() {
return this.#isOpen;
}
#isReady = false;
get isReady() {
return this.#isReady;
}
#isSocketUnrefed = false;
#socketEpoch = 0;
get socketEpoch() {
return this.#socketEpoch;
}
constructor(initiator, options) {
super();
this.#initiator = initiator;
this.#connectTimeout = options?.connectTimeout ?? 5000;
this.#reconnectStrategy = this.#createReconnectStrategy(options);
this.#socketFactory = this.#createSocketFactory(options);
this.#socketTimeout = options?.socketTimeout;
}
#createReconnectStrategy(options) {
const strategy = options?.reconnectStrategy;
if (strategy === false || typeof strategy === 'number') {
return () => strategy;
}
if (strategy) {
return (retries, cause) => {
try {
const retryIn = strategy(retries, cause);
if (retryIn !== false && !(retryIn instanceof Error) && typeof retryIn !== 'number') {
throw new TypeError(`Reconnect strategy should return \`false | Error | number\`, got ${retryIn} instead`);
}
return retryIn;
}
catch (err) {
this.emit('error', err);
return this.defaultReconnectStrategy(retries, err);
}
};
}
return this.defaultReconnectStrategy;
}
#createSocketFactory(options) {
// TLS
if (options?.tls === true) {
const withDefaults = {
...options,
port: options?.port ?? 6379,
// https://nodejs.org/api/tls.html#tlsconnectoptions-callback "Any socket.connect() option not already listed"
// @types/node is... incorrect...
// @ts-expect-error
noDelay: options?.noDelay ?? true,
// https://nodejs.org/api/tls.html#tlsconnectoptions-callback "Any socket.connect() option not already listed"
// @types/node is... incorrect...
// @ts-expect-error
keepAlive: options?.keepAlive ?? true,
// https://nodejs.org/api/tls.html#tlsconnectoptions-callback "Any socket.connect() option not already listed"
// @types/node is... incorrect...
// @ts-expect-error
keepAliveInitialDelay: options?.keepAliveInitialDelay ?? 5000,
timeout: undefined,
onread: undefined,
readable: true,
writable: true
};
return {
create() {
return node_tls_1.default.connect(withDefaults);
},
event: 'secureConnect'
};
}
// IPC
if (options && 'path' in options) {
const withDefaults = {
...options,
timeout: undefined,
onread: undefined,
readable: true,
writable: true
};
return {
create() {
return node_net_1.default.createConnection(withDefaults);
},
event: 'connect'
};
}
// TCP
const withDefaults = {
...options,
port: options?.port ?? 6379,
noDelay: options?.noDelay ?? true,
keepAlive: options?.keepAlive ?? true,
keepAliveInitialDelay: options?.keepAliveInitialDelay ?? 5000,
timeout: undefined,
onread: undefined,
readable: true,
writable: true
};
return {
create() {
return node_net_1.default.createConnection(withDefaults);
},
event: 'connect'
};
}
#shouldReconnect(retries, cause) {
const retryIn = this.#reconnectStrategy(retries, cause);
if (retryIn === false) {
this.#isOpen = false;
this.emit('error', cause);
return cause;
}
else if (retryIn instanceof Error) {
this.#isOpen = false;
this.emit('error', cause);
return new errors_1.ReconnectStrategyError(retryIn, cause);
}
return retryIn;
}
async connect() {
if (this.#isOpen) {
throw new Error('Socket already opened');
}
this.#isOpen = true;
return this.#connect();
}
async #connect() {
let retries = 0;
do {
try {
this.#socket = await this.#createSocket();
this.emit('connect');
try {
await this.#initiator();
}
catch (err) {
this.#socket.destroy();
this.#socket = undefined;
throw err;
}
this.#isReady = true;
this.#socketEpoch++;
this.emit('ready');
}
catch (err) {
const retryIn = this.#shouldReconnect(retries++, err);
if (typeof retryIn !== 'number') {
throw retryIn;
}
this.emit('error', err);
await (0, promises_1.setTimeout)(retryIn);
this.emit('reconnecting');
}
} while (this.#isOpen && !this.#isReady);
}
setMaintenanceTimeout(ms) {
(0, enterprise_maintenance_manager_1.dbgMaintenance)(`Set socket timeout to ${ms}`);
if (this.#maintenanceTimeout === ms) {
(0, enterprise_maintenance_manager_1.dbgMaintenance)(`Socket already set maintenanceCommandTimeout to ${ms}, skipping`);
return;
}
;
this.#maintenanceTimeout = ms;
if (ms !== undefined) {
this.#socket?.setTimeout(ms);
}
else {
this.#socket?.setTimeout(this.#socketTimeout ?? 0);
}
}
async #createSocket() {
const socket = this.#socketFactory.create();
let onTimeout;
if (this.#connectTimeout !== undefined) {
onTimeout = () => socket.destroy(new errors_1.ConnectionTimeoutError());
socket.once('timeout', onTimeout);
socket.setTimeout(this.#connectTimeout);
}
if (this.#isSocketUnrefed) {
socket.unref();
}
await (0, node_events_1.once)(socket, this.#socketFactory.event);
if (onTimeout) {
socket.removeListener('timeout', onTimeout);
}
if (this.#socketTimeout) {
socket.once('timeout', () => {
const error = this.#maintenanceTimeout
? new errors_1.SocketTimeoutDuringMaintenanceError(this.#maintenanceTimeout)
: new errors_1.SocketTimeoutError(this.#socketTimeout);
socket.destroy(error);
});
socket.setTimeout(this.#socketTimeout);
}
socket
.once('error', err => this.#onSocketError(err))
.once('close', hadError => {
if (hadError || !this.#isOpen || this.#socket !== socket)
return;
this.#onSocketError(new errors_1.SocketClosedUnexpectedlyError());
})
.on('drain', () => this.emit('drain'))
.on('data', data => this.emit('data', data));
return socket;
}
#onSocketError(err) {
const wasReady = this.#isReady;
this.#isReady = false;
this.emit('error', err);
if (!wasReady || !this.#isOpen || typeof this.#shouldReconnect(0, err) !== 'number')
return;
this.emit('reconnecting');
this.#connect().catch(() => {
// the error was already emitted, silently ignore it
});
}
write(iterable) {
if (!this.#socket)
return;
this.#socket.cork();
for (const args of iterable) {
for (const toWrite of args) {
this.#socket.write(toWrite);
}
if (this.#socket.writableNeedDrain)
break;
}
this.#socket.uncork();
}
async quit(fn) {
if (!this.#isOpen) {
throw new errors_1.ClientClosedError();
}
this.#isOpen = false;
const reply = await fn();
this.destroySocket();
return reply;
}
close() {
if (!this.#isOpen) {
throw new errors_1.ClientClosedError();
}
this.#isOpen = false;
}
destroy() {
if (!this.#isOpen) {
throw new errors_1.ClientClosedError();
}
this.#isOpen = false;
this.destroySocket();
}
destroySocket() {
this.#isReady = false;
if (this.#socket) {
this.#socket.destroy();
this.#socket = undefined;
}
this.emit('end');
}
ref() {
this.#isSocketUnrefed = false;
this.#socket?.ref();
}
unref() {
this.#isSocketUnrefed = true;
this.#socket?.unref();
}
defaultReconnectStrategy(retries, cause) {
// By default, do not reconnect on socket timeout.
if (cause instanceof errors_1.SocketTimeoutError) {
return false;
}
// Generate a random jitter between 0 200 ms:
const jitter = Math.floor(Math.random() * 200);
// Delay is an exponential back off, (times^2) * 50 ms, with a maximum value of 2000 ms:
const delay = Math.min(Math.pow(2, retries) * 50, 2000);
return delay + jitter;
}
}
exports.default = RedisSocket;
//# sourceMappingURL=socket.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,72 @@
/// <reference types="node" />
/// <reference types="node" />
import { RedisClusterOptions } from '.';
import { RedisClientType } from '../client';
import { EventEmitter } from 'node:stream';
import { ChannelListeners } from '../client/pub-sub';
import { RedisArgument, RedisFunctions, RedisModules, RedisScripts, RespVersions, TypeMapping } from '../RESP/types';
import { PooledClientSideCacheProvider } from '../client/cache';
interface NodeAddress {
host: string;
port: number;
}
export type NodeAddressMap = {
[address: string]: NodeAddress;
} | ((address: string) => NodeAddress | undefined);
export interface Node<M extends RedisModules, F extends RedisFunctions, S extends RedisScripts, RESP extends RespVersions, TYPE_MAPPING extends TypeMapping> {
address: string;
client?: RedisClientType<M, F, S, RESP, TYPE_MAPPING>;
connectPromise?: Promise<RedisClientType<M, F, S, RESP, TYPE_MAPPING>>;
}
export interface ShardNode<M extends RedisModules, F extends RedisFunctions, S extends RedisScripts, RESP extends RespVersions, TYPE_MAPPING extends TypeMapping> extends Node<M, F, S, RESP, TYPE_MAPPING>, NodeAddress {
id: string;
readonly: boolean;
}
export interface MasterNode<M extends RedisModules, F extends RedisFunctions, S extends RedisScripts, RESP extends RespVersions, TYPE_MAPPING extends TypeMapping> extends ShardNode<M, F, S, RESP, TYPE_MAPPING> {
pubSub?: {
connectPromise?: Promise<RedisClientType<M, F, S, RESP, TYPE_MAPPING>>;
client: RedisClientType<M, F, S, RESP, TYPE_MAPPING>;
};
}
export interface Shard<M extends RedisModules, F extends RedisFunctions, S extends RedisScripts, RESP extends RespVersions, TYPE_MAPPING extends TypeMapping> {
master: MasterNode<M, F, S, RESP, TYPE_MAPPING>;
replicas?: Array<ShardNode<M, F, S, RESP, TYPE_MAPPING>>;
nodesIterator?: IterableIterator<ShardNode<M, F, S, RESP, TYPE_MAPPING>>;
}
type PubSubNode<M extends RedisModules, F extends RedisFunctions, S extends RedisScripts, RESP extends RespVersions, TYPE_MAPPING extends TypeMapping> = (Omit<Node<M, F, S, RESP, TYPE_MAPPING>, 'client'> & Required<Pick<Node<M, F, S, RESP, TYPE_MAPPING>, 'client'>>);
export type OnShardedChannelMovedError = (err: unknown, channel: string, listeners?: ChannelListeners) => void;
export default class RedisClusterSlots<M extends RedisModules, F extends RedisFunctions, S extends RedisScripts, RESP extends RespVersions, TYPE_MAPPING extends TypeMapping> {
#private;
slots: Shard<M, F, S, RESP, TYPE_MAPPING>[];
masters: MasterNode<M, F, S, RESP, TYPE_MAPPING>[];
replicas: ShardNode<M, F, S, RESP, TYPE_MAPPING>[];
readonly nodeByAddress: Map<string, ShardNode<M, F, S, RESP, TYPE_MAPPING> | MasterNode<M, F, S, RESP, TYPE_MAPPING>>;
pubSubNode?: PubSubNode<M, F, S, RESP, TYPE_MAPPING>;
clientSideCache?: PooledClientSideCacheProvider;
get isOpen(): boolean;
constructor(options: RedisClusterOptions<M, F, S, RESP, TYPE_MAPPING>, emit: EventEmitter['emit']);
connect(): Promise<void>;
nodeClient(node: ShardNode<M, F, S, RESP, TYPE_MAPPING>): RedisClientType<M, F, S, RESP, TYPE_MAPPING> | Promise<RedisClientType<M, F, S, RESP, TYPE_MAPPING>>;
rediscover(startWith: RedisClientType<M, F, S, RESP>): Promise<void>;
/**
* @deprecated Use `close` instead.
*/
quit(): Promise<void>;
/**
* @deprecated Use `destroy` instead.
*/
disconnect(): Promise<void>;
close(): Promise<void>;
destroy(): void;
getClient(firstKey: RedisArgument | undefined, isReadonly: boolean | undefined): RedisClientType<M, F, S, RESP, TYPE_MAPPING> | Promise<RedisClientType<M, F, S, RESP, TYPE_MAPPING>>;
_randomNodeIterator?: IterableIterator<ShardNode<M, F, S, RESP, TYPE_MAPPING>>;
getRandomNode(): ShardNode<M, F, S, RESP, TYPE_MAPPING>;
getSlotRandomNode(slotNumber: number): ShardNode<M, F, S, RESP, TYPE_MAPPING>;
getMasterByAddress(address: string): RedisClientType<M, F, S, RESP, TYPE_MAPPING> | Promise<RedisClientType<M, F, S, RESP, TYPE_MAPPING>> | undefined;
getPubSubClient(): RedisClientType<M, F, S, RESP, TYPE_MAPPING> | Promise<RedisClientType<M, F, S, RESP, TYPE_MAPPING>>;
executeUnsubscribeCommand(unsubscribe: (client: RedisClientType<M, F, S, RESP>) => Promise<void>): Promise<void>;
getShardedPubSubClient(channel: string): RedisClientType<M, F, S, RESP, TYPE_MAPPING> | Promise<RedisClientType<M, F, S, RESP, TYPE_MAPPING>>;
executeShardedUnsubscribeCommand(channel: string, unsubscribe: (client: RedisClientType<M, F, S, RESP, TYPE_MAPPING>) => Promise<void>): Promise<void>;
}
export {};
//# sourceMappingURL=cluster-slots.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"cluster-slots.d.ts","sourceRoot":"","sources":["../../../lib/cluster/cluster-slots.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,462 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
var _a;
Object.defineProperty(exports, "__esModule", { value: true });
const errors_1 = require("../errors");
const client_1 = __importDefault(require("../client"));
const pub_sub_1 = require("../client/pub-sub");
const cluster_key_slot_1 = __importDefault(require("cluster-key-slot"));
const cache_1 = require("../client/cache");
class RedisClusterSlots {
static #SLOTS = 16384;
#options;
#clientFactory;
#emit;
slots = new Array(_a.#SLOTS);
masters = new Array();
replicas = new Array();
nodeByAddress = new Map();
pubSubNode;
clientSideCache;
#isOpen = false;
get isOpen() {
return this.#isOpen;
}
#validateOptions(options) {
if (options?.clientSideCache && options?.RESP !== 3) {
throw new Error('Client Side Caching is only supported with RESP3');
}
}
constructor(options, emit) {
this.#validateOptions(options);
this.#options = options;
if (options?.clientSideCache) {
if (options.clientSideCache instanceof cache_1.PooledClientSideCacheProvider) {
this.clientSideCache = options.clientSideCache;
}
else {
this.clientSideCache = new cache_1.BasicPooledClientSideCache(options.clientSideCache);
}
}
this.#clientFactory = client_1.default.factory(this.#options);
this.#emit = emit;
}
async connect() {
if (this.#isOpen) {
throw new Error('Cluster already open');
}
this.#isOpen = true;
try {
await this.#discoverWithRootNodes();
this.#emit('connect');
}
catch (err) {
this.#isOpen = false;
throw err;
}
}
async #discoverWithRootNodes() {
let start = Math.floor(Math.random() * this.#options.rootNodes.length);
for (let i = start; i < this.#options.rootNodes.length; i++) {
if (!this.#isOpen)
throw new Error('Cluster closed');
if (await this.#discover(this.#options.rootNodes[i]))
return;
}
for (let i = 0; i < start; i++) {
if (!this.#isOpen)
throw new Error('Cluster closed');
if (await this.#discover(this.#options.rootNodes[i]))
return;
}
throw new errors_1.RootNodesUnavailableError();
}
#resetSlots() {
this.slots = new Array(_a.#SLOTS);
this.masters = [];
this.replicas = [];
this._randomNodeIterator = undefined;
}
async #discover(rootNode) {
this.clientSideCache?.clear();
this.clientSideCache?.disable();
try {
const addressesInUse = new Set(), promises = [], eagerConnect = this.#options.minimizeConnections !== true;
const shards = await this.#getShards(rootNode);
this.#resetSlots(); // Reset slots AFTER shards have been fetched to prevent a race condition
for (const { from, to, master, replicas } of shards) {
const shard = {
master: this.#initiateSlotNode(master, false, eagerConnect, addressesInUse, promises)
};
if (this.#options.useReplicas) {
shard.replicas = replicas.map(replica => this.#initiateSlotNode(replica, true, eagerConnect, addressesInUse, promises));
}
for (let i = from; i <= to; i++) {
this.slots[i] = shard;
}
}
if (this.pubSubNode && !addressesInUse.has(this.pubSubNode.address)) {
const channelsListeners = this.pubSubNode.client.getPubSubListeners(pub_sub_1.PUBSUB_TYPE.CHANNELS), patternsListeners = this.pubSubNode.client.getPubSubListeners(pub_sub_1.PUBSUB_TYPE.PATTERNS);
this.pubSubNode.client.destroy();
if (channelsListeners.size || patternsListeners.size) {
promises.push(this.#initiatePubSubClient({
[pub_sub_1.PUBSUB_TYPE.CHANNELS]: channelsListeners,
[pub_sub_1.PUBSUB_TYPE.PATTERNS]: patternsListeners
}));
}
}
//Keep only the nodes that are still in use
for (const [address, node] of this.nodeByAddress.entries()) {
if (addressesInUse.has(address))
continue;
if (node.client) {
node.client.destroy();
}
const { pubSub } = node;
if (pubSub) {
pubSub.client.destroy();
}
this.nodeByAddress.delete(address);
}
await Promise.all(promises);
this.clientSideCache?.enable();
return true;
}
catch (err) {
this.#emit('error', err);
return false;
}
}
async #getShards(rootNode) {
const options = this.#clientOptionsDefaults(rootNode);
options.socket ??= {};
options.socket.reconnectStrategy = false;
options.RESP = this.#options.RESP;
options.commandOptions = undefined;
// TODO: find a way to avoid type casting
const client = await this.#clientFactory(options)
.on('error', err => this.#emit('error', err))
.connect();
try {
// switch to `CLUSTER SHARDS` when Redis 7.0 will be the minimum supported version
return await client.clusterSlots();
}
finally {
client.destroy();
}
}
#getNodeAddress(address) {
switch (typeof this.#options.nodeAddressMap) {
case 'object':
return this.#options.nodeAddressMap[address];
case 'function':
return this.#options.nodeAddressMap(address);
}
}
#clientOptionsDefaults(options) {
if (!this.#options.defaults)
return options;
let socket;
if (this.#options.defaults.socket) {
socket = {
...this.#options.defaults.socket,
...options?.socket
};
}
else {
socket = options?.socket;
}
return {
...this.#options.defaults,
...options,
socket: socket
};
}
#initiateSlotNode(shard, readonly, eagerConnent, addressesInUse, promises) {
const address = `${shard.host}:${shard.port}`;
let node = this.nodeByAddress.get(address);
if (!node) {
node = {
...shard,
address,
readonly,
client: undefined,
connectPromise: undefined
};
if (eagerConnent) {
promises.push(this.#createNodeClient(node));
}
this.nodeByAddress.set(address, node);
}
if (!addressesInUse.has(address)) {
addressesInUse.add(address);
(readonly ? this.replicas : this.masters).push(node);
}
return node;
}
#createClient(node, readonly = node.readonly) {
const socket = this.#getNodeAddress(node.address) ??
{ host: node.host, port: node.port, };
const clientInfo = Object.freeze({
host: socket.host,
port: socket.port,
});
const emit = this.#emit;
const client = this.#clientFactory(this.#clientOptionsDefaults({
clientSideCache: this.clientSideCache,
RESP: this.#options.RESP,
socket,
readonly,
}))
.on('error', error => emit('node-error', error, clientInfo))
.on('reconnecting', () => emit('node-reconnecting', clientInfo))
.once('ready', () => emit('node-ready', clientInfo))
.once('connect', () => emit('node-connect', clientInfo))
.once('end', () => emit('node-disconnect', clientInfo))
.on('__MOVED', async (allPubSubListeners) => {
await this.rediscover(client);
this.#emit('__resubscribeAllPubSubListeners', allPubSubListeners);
});
return client;
}
#createNodeClient(node, readonly) {
const client = node.client = this.#createClient(node, readonly);
return node.connectPromise = client.connect()
.finally(() => node.connectPromise = undefined);
}
nodeClient(node) {
return (node.connectPromise ?? // if the node is connecting
node.client ?? // if the node is connected
this.#createNodeClient(node) // if the not is disconnected
);
}
#runningRediscoverPromise;
async rediscover(startWith) {
this.#runningRediscoverPromise ??= this.#rediscover(startWith)
.finally(() => {
this.#runningRediscoverPromise = undefined;
});
return this.#runningRediscoverPromise;
}
async #rediscover(startWith) {
if (await this.#discover(startWith.options))
return;
return this.#discoverWithRootNodes();
}
/**
* @deprecated Use `close` instead.
*/
quit() {
return this.#destroy(client => client.quit());
}
/**
* @deprecated Use `destroy` instead.
*/
disconnect() {
return this.#destroy(client => client.disconnect());
}
close() {
return this.#destroy(client => client.close());
}
destroy() {
this.#isOpen = false;
for (const client of this.#clients()) {
client.destroy();
}
if (this.pubSubNode) {
this.pubSubNode.client.destroy();
this.pubSubNode = undefined;
}
this.#resetSlots();
this.nodeByAddress.clear();
this.#emit('disconnect');
}
*#clients() {
for (const master of this.masters) {
if (master.client) {
yield master.client;
}
if (master.pubSub) {
yield master.pubSub.client;
}
}
for (const replica of this.replicas) {
if (replica.client) {
yield replica.client;
}
}
}
async #destroy(fn) {
this.#isOpen = false;
const promises = [];
for (const client of this.#clients()) {
promises.push(fn(client));
}
if (this.pubSubNode) {
promises.push(fn(this.pubSubNode.client));
this.pubSubNode = undefined;
}
this.#resetSlots();
this.nodeByAddress.clear();
await Promise.allSettled(promises);
this.#emit('disconnect');
}
getClient(firstKey, isReadonly) {
if (!firstKey) {
return this.nodeClient(this.getRandomNode());
}
const slotNumber = (0, cluster_key_slot_1.default)(firstKey);
if (!isReadonly) {
return this.nodeClient(this.slots[slotNumber].master);
}
return this.nodeClient(this.getSlotRandomNode(slotNumber));
}
*#iterateAllNodes() {
if (this.masters.length + this.replicas.length === 0)
return;
let i = Math.floor(Math.random() * (this.masters.length + this.replicas.length));
if (i < this.masters.length) {
do {
yield this.masters[i];
} while (++i < this.masters.length);
for (const replica of this.replicas) {
yield replica;
}
}
else {
i -= this.masters.length;
do {
yield this.replicas[i];
} while (++i < this.replicas.length);
}
while (true) {
for (const master of this.masters) {
yield master;
}
for (const replica of this.replicas) {
yield replica;
}
}
}
_randomNodeIterator;
getRandomNode() {
this._randomNodeIterator ??= this.#iterateAllNodes();
return this._randomNodeIterator.next().value;
}
*#slotNodesIterator(slot) {
let i = Math.floor(Math.random() * (1 + slot.replicas.length));
if (i < slot.replicas.length) {
do {
yield slot.replicas[i];
} while (++i < slot.replicas.length);
}
while (true) {
yield slot.master;
for (const replica of slot.replicas) {
yield replica;
}
}
}
getSlotRandomNode(slotNumber) {
const slot = this.slots[slotNumber];
if (!slot.replicas?.length) {
return slot.master;
}
slot.nodesIterator ??= this.#slotNodesIterator(slot);
return slot.nodesIterator.next().value;
}
getMasterByAddress(address) {
const master = this.nodeByAddress.get(address);
if (!master)
return;
return this.nodeClient(master);
}
getPubSubClient() {
if (!this.pubSubNode)
return this.#initiatePubSubClient();
return this.pubSubNode.connectPromise ?? this.pubSubNode.client;
}
async #initiatePubSubClient(toResubscribe) {
const index = Math.floor(Math.random() * (this.masters.length + this.replicas.length)), node = index < this.masters.length ?
this.masters[index] :
this.replicas[index - this.masters.length], client = this.#createClient(node, false);
this.pubSubNode = {
address: node.address,
client,
connectPromise: client.connect()
.then(async (client) => {
if (toResubscribe) {
await Promise.all([
client.extendPubSubListeners(pub_sub_1.PUBSUB_TYPE.CHANNELS, toResubscribe[pub_sub_1.PUBSUB_TYPE.CHANNELS]),
client.extendPubSubListeners(pub_sub_1.PUBSUB_TYPE.PATTERNS, toResubscribe[pub_sub_1.PUBSUB_TYPE.PATTERNS])
]);
}
this.pubSubNode.connectPromise = undefined;
return client;
})
.catch(err => {
this.pubSubNode = undefined;
throw err;
})
};
return this.pubSubNode.connectPromise;
}
async executeUnsubscribeCommand(unsubscribe) {
const client = await this.getPubSubClient();
await unsubscribe(client);
if (!client.isPubSubActive) {
client.destroy();
this.pubSubNode = undefined;
}
}
getShardedPubSubClient(channel) {
const { master } = this.slots[(0, cluster_key_slot_1.default)(channel)];
if (!master.pubSub)
return this.#initiateShardedPubSubClient(master);
return master.pubSub.connectPromise ?? master.pubSub.client;
}
async #initiateShardedPubSubClient(master) {
const client = this.#createClient(master, false)
.on('server-sunsubscribe', async (channel, listeners) => {
try {
await this.rediscover(client);
const redirectTo = await this.getShardedPubSubClient(channel);
await redirectTo.extendPubSubChannelListeners(pub_sub_1.PUBSUB_TYPE.SHARDED, channel, listeners);
}
catch (err) {
this.#emit('sharded-shannel-moved-error', err, channel, listeners);
}
});
master.pubSub = {
client,
connectPromise: client.connect()
.then(client => {
master.pubSub.connectPromise = undefined;
return client;
})
.catch(err => {
master.pubSub = undefined;
throw err;
})
};
return master.pubSub.connectPromise;
}
async executeShardedUnsubscribeCommand(channel, unsubscribe) {
const { master } = this.slots[(0, cluster_key_slot_1.default)(channel)];
if (!master.pubSub)
return;
const client = master.pubSub.connectPromise ?
await master.pubSub.connectPromise :
master.pubSub.client;
await unsubscribe(client);
if (!client.isPubSubActive) {
client.destroy();
master.pubSub = undefined;
}
}
}
_a = RedisClusterSlots;
exports.default = RedisClusterSlots;
//# sourceMappingURL=cluster-slots.js.map

File diff suppressed because one or more lines are too long

178
node_modules/@redis/client/dist/lib/cluster/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,178 @@
/// <reference types="node" />
import { RedisClientOptions, RedisClientType } from '../client';
import { CommandOptions } from '../client/commands-queue';
import { CommandArguments, CommanderConfig, TypeMapping, RedisArgument, RedisFunctions, RedisModules, RedisScripts, ReplyUnion, RespVersions } from '../RESP/types';
import { EventEmitter } from 'node:events';
import RedisClusterSlots, { NodeAddressMap, ShardNode } from './cluster-slots';
import { RedisClusterMultiCommandType } from './multi-command';
import { PubSubListener, PubSubListeners } from '../client/pub-sub';
import { RedisTcpSocketOptions } from '../client/socket';
import { ClientSideCacheConfig, PooledClientSideCacheProvider } from '../client/cache';
import { WithCommands, WithFunctions, WithModules, WithScripts } from '../client';
interface ClusterCommander<M extends RedisModules, F extends RedisFunctions, S extends RedisScripts, RESP extends RespVersions, TYPE_MAPPING extends TypeMapping> extends CommanderConfig<M, F, S, RESP> {
commandOptions?: ClusterCommandOptions<TYPE_MAPPING>;
}
export type RedisClusterClientOptions = Omit<RedisClientOptions<RedisModules, RedisFunctions, RedisScripts, RespVersions, TypeMapping, RedisTcpSocketOptions>, keyof ClusterCommander<RedisModules, RedisFunctions, RedisScripts, RespVersions, TypeMapping>>;
export interface RedisClusterOptions<M extends RedisModules = RedisModules, F extends RedisFunctions = RedisFunctions, S extends RedisScripts = RedisScripts, RESP extends RespVersions = RespVersions, TYPE_MAPPING extends TypeMapping = TypeMapping> extends ClusterCommander<M, F, S, RESP, TYPE_MAPPING> {
/**
* Should contain details for some of the cluster nodes that the client will use to discover
* the "cluster topology". We recommend including details for at least 3 nodes here.
*/
rootNodes: Array<RedisClusterClientOptions>;
/**
* Default values used for every client in the cluster. Use this to specify global values,
* for example: ACL credentials, timeouts, TLS configuration etc.
*/
defaults?: Partial<RedisClusterClientOptions>;
/**
* When `true`, `.connect()` will only discover the cluster topology, without actually connecting to all the nodes.
* Useful for short-term or PubSub-only connections.
*/
minimizeConnections?: boolean;
/**
* When `true`, distribute load by executing readonly commands (such as `GET`, `GEOSEARCH`, etc.) across all cluster nodes. When `false`, only use master nodes.
*/
useReplicas?: boolean;
/**
* The maximum number of times a command will be redirected due to `MOVED` or `ASK` errors.
*/
maxCommandRedirections?: number;
/**
* Mapping between the addresses in the cluster (see `CLUSTER SHARDS`) and the addresses the client should connect to
* Useful when the cluster is running on another network
*/
nodeAddressMap?: NodeAddressMap;
/**
* Client Side Caching configuration for the pool.
*
* Enables Redis Servers and Clients to work together to cache results from commands
* sent to a server. The server will notify the client when cached results are no longer valid.
* In pooled mode, the cache is shared across all clients in the pool.
*
* Note: Client Side Caching is only supported with RESP3.
*
* @example Anonymous cache configuration
* ```
* const client = createCluster({
* clientSideCache: {
* ttl: 0,
* maxEntries: 0,
* evictPolicy: "LRU"
* },
* minimum: 5
* });
* ```
*
* @example Using a controllable cache
* ```
* const cache = new BasicPooledClientSideCache({
* ttl: 0,
* maxEntries: 0,
* evictPolicy: "LRU"
* });
* const client = createCluster({
* clientSideCache: cache,
* minimum: 5
* });
* ```
*/
clientSideCache?: PooledClientSideCacheProvider | ClientSideCacheConfig;
}
export type RedisClusterType<M extends RedisModules = {}, F extends RedisFunctions = {}, S extends RedisScripts = {}, RESP extends RespVersions = 2, TYPE_MAPPING extends TypeMapping = {}> = (RedisCluster<M, F, S, RESP, TYPE_MAPPING> & WithCommands<RESP, TYPE_MAPPING> & WithModules<M, RESP, TYPE_MAPPING> & WithFunctions<F, RESP, TYPE_MAPPING> & WithScripts<S, RESP, TYPE_MAPPING>);
export interface ClusterCommandOptions<TYPE_MAPPING extends TypeMapping = TypeMapping> extends CommandOptions<TYPE_MAPPING> {
}
export default class RedisCluster<M extends RedisModules, F extends RedisFunctions, S extends RedisScripts, RESP extends RespVersions, TYPE_MAPPING extends TypeMapping> extends EventEmitter {
#private;
static factory<M extends RedisModules = {}, F extends RedisFunctions = {}, S extends RedisScripts = {}, RESP extends RespVersions = 2, TYPE_MAPPING extends TypeMapping = {}>(config?: ClusterCommander<M, F, S, RESP, TYPE_MAPPING>): (options?: Omit<RedisClusterOptions, keyof Exclude<typeof config, undefined>>) => RedisClusterType<M, F, S, RESP, TYPE_MAPPING>;
static create<M extends RedisModules = {}, F extends RedisFunctions = {}, S extends RedisScripts = {}, RESP extends RespVersions = 2, TYPE_MAPPING extends TypeMapping = {}>(options?: RedisClusterOptions<M, F, S, RESP, TYPE_MAPPING>): RedisClusterType<M, F, S, RESP, TYPE_MAPPING>;
readonly _options: RedisClusterOptions<M, F, S, RESP, TYPE_MAPPING>;
readonly _slots: RedisClusterSlots<M, F, S, RESP, TYPE_MAPPING>;
private _self;
private _commandOptions?;
/**
* An array of the cluster slots, each slot contain its `master` and `replicas`.
* Use with {@link RedisCluster.prototype.nodeClient} to get the client for a specific node (master or replica).
*/
get slots(): import("./cluster-slots").Shard<M, F, S, RESP, TYPE_MAPPING>[];
get clientSideCache(): PooledClientSideCacheProvider | undefined;
/**
* An array of the cluster masters.
* Use with {@link RedisCluster.prototype.nodeClient} to get the client for a specific master node.
*/
get masters(): import("./cluster-slots").MasterNode<M, F, S, RESP, TYPE_MAPPING>[];
/**
* An array of the cluster replicas.
* Use with {@link RedisCluster.prototype.nodeClient} to get the client for a specific replica node.
*/
get replicas(): ShardNode<M, F, S, RESP, TYPE_MAPPING>[];
/**
* A map form a node address (`<host>:<port>`) to its shard, each shard contain its `master` and `replicas`.
* Use with {@link RedisCluster.prototype.nodeClient} to get the client for a specific node (master or replica).
*/
get nodeByAddress(): Map<string, import("./cluster-slots").MasterNode<M, F, S, RESP, TYPE_MAPPING> | ShardNode<M, F, S, RESP, TYPE_MAPPING>>;
/**
* The current pub/sub node.
*/
get pubSubNode(): (Omit<import("./cluster-slots").Node<M, F, S, RESP, TYPE_MAPPING>, "client"> & Required<Pick<import("./cluster-slots").Node<M, F, S, RESP, TYPE_MAPPING>, "client">>) | undefined;
get isOpen(): boolean;
constructor(options: RedisClusterOptions<M, F, S, RESP, TYPE_MAPPING>);
duplicate<_M extends RedisModules = M, _F extends RedisFunctions = F, _S extends RedisScripts = S, _RESP extends RespVersions = RESP, _TYPE_MAPPING extends TypeMapping = TYPE_MAPPING>(overrides?: Partial<RedisClusterOptions<_M, _F, _S, _RESP, _TYPE_MAPPING>>): RedisClusterType<_M, _F, _S, _RESP, _TYPE_MAPPING>;
connect(): Promise<RedisClusterType<M, F, S, RESP, TYPE_MAPPING>>;
withCommandOptions<OPTIONS extends ClusterCommandOptions<TYPE_MAPPING>, TYPE_MAPPING extends TypeMapping>(options: OPTIONS): RedisClusterType<M, F, S, RESP, TYPE_MAPPING extends TypeMapping ? TYPE_MAPPING : {}>;
private _commandOptionsProxy;
/**
* Override the `typeMapping` command option
*/
withTypeMapping<TYPE_MAPPING extends TypeMapping>(typeMapping: TYPE_MAPPING): RedisClusterType<M, F, S, RESP, TYPE_MAPPING extends TypeMapping ? TYPE_MAPPING : {}>;
_handleAsk<T>(fn: (client: RedisClientType<M, F, S, RESP, TYPE_MAPPING>, opts?: ClusterCommandOptions) => Promise<T>): (client: RedisClientType<M, F, S, RESP, TYPE_MAPPING>, options?: ClusterCommandOptions) => Promise<T>;
_execute<T>(firstKey: RedisArgument | undefined, isReadonly: boolean | undefined, options: ClusterCommandOptions | undefined, fn: (client: RedisClientType<M, F, S, RESP, TYPE_MAPPING>, opts?: ClusterCommandOptions) => Promise<T>): Promise<T>;
sendCommand<T = ReplyUnion>(firstKey: RedisArgument | undefined, isReadonly: boolean | undefined, args: CommandArguments, options?: ClusterCommandOptions): Promise<T>;
MULTI(routing?: RedisArgument): RedisClusterMultiCommandType<[], M, F, S, RESP, TYPE_MAPPING>;
multi: (routing?: RedisArgument) => RedisClusterMultiCommandType<[], M, F, S, RESP, TYPE_MAPPING>;
SUBSCRIBE<T extends boolean = false>(channels: string | Array<string>, listener: PubSubListener<T>, bufferMode?: T): Promise<void>;
subscribe: <T extends boolean = false>(channels: string | Array<string>, listener: PubSubListener<T>, bufferMode?: T | undefined) => Promise<void>;
UNSUBSCRIBE<T extends boolean = false>(channels?: string | Array<string>, listener?: PubSubListener<boolean>, bufferMode?: T): Promise<void>;
unsubscribe: <T extends boolean = false>(channels?: string | Array<string>, listener?: PubSubListener<boolean>, bufferMode?: T | undefined) => Promise<void>;
PSUBSCRIBE<T extends boolean = false>(patterns: string | Array<string>, listener: PubSubListener<T>, bufferMode?: T): Promise<void>;
pSubscribe: <T extends boolean = false>(patterns: string | Array<string>, listener: PubSubListener<T>, bufferMode?: T | undefined) => Promise<void>;
PUNSUBSCRIBE<T extends boolean = false>(patterns?: string | Array<string>, listener?: PubSubListener<T>, bufferMode?: T): Promise<void>;
pUnsubscribe: <T extends boolean = false>(patterns?: string | Array<string>, listener?: PubSubListener<T> | undefined, bufferMode?: T | undefined) => Promise<void>;
SSUBSCRIBE<T extends boolean = false>(channels: string | Array<string>, listener: PubSubListener<T>, bufferMode?: T): Promise<void>;
sSubscribe: <T extends boolean = false>(channels: string | Array<string>, listener: PubSubListener<T>, bufferMode?: T | undefined) => Promise<void>;
SUNSUBSCRIBE<T extends boolean = false>(channels: string | Array<string>, listener?: PubSubListener<T>, bufferMode?: T): Promise<void>;
resubscribeAllPubSubListeners(allListeners: PubSubListeners): void;
sUnsubscribe: <T extends boolean = false>(channels: string | Array<string>, listener?: PubSubListener<T> | undefined, bufferMode?: T | undefined) => Promise<void>;
/**
* @deprecated Use `close` instead.
*/
quit(): Promise<void>;
/**
* @deprecated Use `destroy` instead.
*/
disconnect(): Promise<void>;
close(): Promise<void>;
destroy(): void;
nodeClient(node: ShardNode<M, F, S, RESP, TYPE_MAPPING>): RedisClientType<M, F, S, RESP, TYPE_MAPPING> | Promise<RedisClientType<M, F, S, RESP, TYPE_MAPPING>>;
/**
* Returns a random node from the cluster.
* Userful for running "forward" commands (like PUBLISH) on a random node.
*/
getRandomNode(): ShardNode<M, F, S, RESP, TYPE_MAPPING>;
/**
* Get a random node from a slot.
* Useful for running readonly commands on a slot.
*/
getSlotRandomNode(slot: number): ShardNode<M, F, S, RESP, TYPE_MAPPING>;
/**
* @deprecated use `.masters` instead
* TODO
*/
getMasters(): import("./cluster-slots").MasterNode<M, F, S, RESP, TYPE_MAPPING>[];
/**
* @deprecated use `.slots[<SLOT>]` instead
* TODO
*/
getSlotMaster(slot: number): import("./cluster-slots").MasterNode<M, F, S, RESP, TYPE_MAPPING>;
}
export {};
//# sourceMappingURL=index.d.ts.map

File diff suppressed because one or more lines are too long

357
node_modules/@redis/client/dist/lib/cluster/index.js generated vendored Normal file
View File

@@ -0,0 +1,357 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const commands_1 = __importDefault(require("../commands"));
const node_events_1 = require("node:events");
const commander_1 = require("../commander");
const cluster_slots_1 = __importDefault(require("./cluster-slots"));
const multi_command_1 = __importDefault(require("./multi-command"));
const errors_1 = require("../errors");
const parser_1 = require("../client/parser");
const ASKING_1 = require("../commands/ASKING");
const single_entry_cache_1 = __importDefault(require("../single-entry-cache"));
class RedisCluster extends node_events_1.EventEmitter {
static #createCommand(command, resp) {
const transformReply = (0, commander_1.getTransformReply)(command, resp);
return async function (...args) {
const parser = new parser_1.BasicCommandParser();
command.parseCommand(parser, ...args);
return this._self._execute(parser.firstKey, command.IS_READ_ONLY, this._commandOptions, (client, opts) => client._executeCommand(command, parser, opts, transformReply));
};
}
static #createModuleCommand(command, resp) {
const transformReply = (0, commander_1.getTransformReply)(command, resp);
return async function (...args) {
const parser = new parser_1.BasicCommandParser();
command.parseCommand(parser, ...args);
return this._self._execute(parser.firstKey, command.IS_READ_ONLY, this._self._commandOptions, (client, opts) => client._executeCommand(command, parser, opts, transformReply));
};
}
static #createFunctionCommand(name, fn, resp) {
const prefix = (0, commander_1.functionArgumentsPrefix)(name, fn);
const transformReply = (0, commander_1.getTransformReply)(fn, resp);
return async function (...args) {
const parser = new parser_1.BasicCommandParser();
parser.push(...prefix);
fn.parseCommand(parser, ...args);
return this._self._execute(parser.firstKey, fn.IS_READ_ONLY, this._self._commandOptions, (client, opts) => client._executeCommand(fn, parser, opts, transformReply));
};
}
static #createScriptCommand(script, resp) {
const prefix = (0, commander_1.scriptArgumentsPrefix)(script);
const transformReply = (0, commander_1.getTransformReply)(script, resp);
return async function (...args) {
const parser = new parser_1.BasicCommandParser();
parser.push(...prefix);
script.parseCommand(parser, ...args);
return this._self._execute(parser.firstKey, script.IS_READ_ONLY, this._commandOptions, (client, opts) => client._executeScript(script, parser, opts, transformReply));
};
}
static #SingleEntryCache = new single_entry_cache_1.default();
static factory(config) {
let Cluster = RedisCluster.#SingleEntryCache.get(config);
if (!Cluster) {
Cluster = (0, commander_1.attachConfig)({
BaseClass: RedisCluster,
commands: commands_1.default,
createCommand: RedisCluster.#createCommand,
createModuleCommand: RedisCluster.#createModuleCommand,
createFunctionCommand: RedisCluster.#createFunctionCommand,
createScriptCommand: RedisCluster.#createScriptCommand,
config
});
Cluster.prototype.Multi = multi_command_1.default.extend(config);
RedisCluster.#SingleEntryCache.set(config, Cluster);
}
return (options) => {
// returning a "proxy" to prevent the namespaces._self to leak between "proxies"
return Object.create(new Cluster(options));
};
}
static create(options) {
return RedisCluster.factory(options)(options);
}
_options;
_slots;
_self = this;
_commandOptions;
/**
* An array of the cluster slots, each slot contain its `master` and `replicas`.
* Use with {@link RedisCluster.prototype.nodeClient} to get the client for a specific node (master or replica).
*/
get slots() {
return this._self._slots.slots;
}
get clientSideCache() {
return this._self._slots.clientSideCache;
}
/**
* An array of the cluster masters.
* Use with {@link RedisCluster.prototype.nodeClient} to get the client for a specific master node.
*/
get masters() {
return this._self._slots.masters;
}
/**
* An array of the cluster replicas.
* Use with {@link RedisCluster.prototype.nodeClient} to get the client for a specific replica node.
*/
get replicas() {
return this._self._slots.replicas;
}
/**
* A map form a node address (`<host>:<port>`) to its shard, each shard contain its `master` and `replicas`.
* Use with {@link RedisCluster.prototype.nodeClient} to get the client for a specific node (master or replica).
*/
get nodeByAddress() {
return this._self._slots.nodeByAddress;
}
/**
* The current pub/sub node.
*/
get pubSubNode() {
return this._self._slots.pubSubNode;
}
get isOpen() {
return this._self._slots.isOpen;
}
constructor(options) {
super();
this._options = options;
this._slots = new cluster_slots_1.default(options, this.emit.bind(this));
this.on('__resubscribeAllPubSubListeners', this.resubscribeAllPubSubListeners.bind(this));
if (options?.commandOptions) {
this._commandOptions = options.commandOptions;
}
}
duplicate(overrides) {
return new (Object.getPrototypeOf(this).constructor)({
...this._self._options,
commandOptions: this._commandOptions,
...overrides
});
}
async connect() {
await this._self._slots.connect();
return this;
}
withCommandOptions(options) {
const proxy = Object.create(this);
proxy._commandOptions = options;
return proxy;
}
_commandOptionsProxy(key, value) {
const proxy = Object.create(this);
proxy._commandOptions = Object.create(this._commandOptions ?? null);
proxy._commandOptions[key] = value;
return proxy;
}
/**
* Override the `typeMapping` command option
*/
withTypeMapping(typeMapping) {
return this._commandOptionsProxy('typeMapping', typeMapping);
}
// /**
// * Override the `policies` command option
// * TODO
// */
// withPolicies<POLICIES extends CommandPolicies> (policies: POLICIES) {
// return this._commandOptionsProxy('policies', policies);
// }
_handleAsk(fn) {
return async (client, options) => {
const chainId = Symbol("asking chain");
const opts = options ? { ...options } : {};
opts.chainId = chainId;
const ret = await Promise.all([
client.sendCommand([ASKING_1.ASKING_CMD], { chainId: chainId }),
fn(client, opts)
]);
return ret[1];
};
}
async _execute(firstKey, isReadonly, options, fn) {
const maxCommandRedirections = this._options.maxCommandRedirections ?? 16;
let client = await this._slots.getClient(firstKey, isReadonly);
let i = 0;
let myFn = fn;
while (true) {
try {
return await myFn(client, options);
}
catch (err) {
myFn = fn;
// TODO: error class
if (++i > maxCommandRedirections || !(err instanceof Error)) {
throw err;
}
if (err.message.startsWith('ASK')) {
const address = err.message.substring(err.message.lastIndexOf(' ') + 1);
let redirectTo = await this._slots.getMasterByAddress(address);
if (!redirectTo) {
await this._slots.rediscover(client);
redirectTo = await this._slots.getMasterByAddress(address);
}
if (!redirectTo) {
throw new Error(`Cannot find node ${address}`);
}
client = redirectTo;
myFn = this._handleAsk(fn);
continue;
}
if (err.message.startsWith('MOVED')) {
await this._slots.rediscover(client);
client = await this._slots.getClient(firstKey, isReadonly);
continue;
}
throw err;
}
}
}
async sendCommand(firstKey, isReadonly, args, options) {
// Merge global options with local options
const opts = {
...this._self._commandOptions,
...options
};
return this._self._execute(firstKey, isReadonly, opts, (client, opts) => client.sendCommand(args, opts));
}
MULTI(routing) {
return new this.Multi(async (firstKey, isReadonly, commands) => {
const client = await this._self._slots.getClient(firstKey, isReadonly);
return client._executeMulti(commands);
}, async (firstKey, isReadonly, commands) => {
const client = await this._self._slots.getClient(firstKey, isReadonly);
return client._executePipeline(commands);
}, routing, this._commandOptions?.typeMapping);
}
multi = this.MULTI;
async SUBSCRIBE(channels, listener, bufferMode) {
return (await this._self._slots.getPubSubClient())
.SUBSCRIBE(channels, listener, bufferMode);
}
subscribe = this.SUBSCRIBE;
async UNSUBSCRIBE(channels, listener, bufferMode) {
return this._self._slots.executeUnsubscribeCommand(client => client.UNSUBSCRIBE(channels, listener, bufferMode));
}
unsubscribe = this.UNSUBSCRIBE;
async PSUBSCRIBE(patterns, listener, bufferMode) {
return (await this._self._slots.getPubSubClient())
.PSUBSCRIBE(patterns, listener, bufferMode);
}
pSubscribe = this.PSUBSCRIBE;
async PUNSUBSCRIBE(patterns, listener, bufferMode) {
return this._self._slots.executeUnsubscribeCommand(client => client.PUNSUBSCRIBE(patterns, listener, bufferMode));
}
pUnsubscribe = this.PUNSUBSCRIBE;
async SSUBSCRIBE(channels, listener, bufferMode) {
const maxCommandRedirections = this._self._options.maxCommandRedirections ?? 16, firstChannel = Array.isArray(channels) ? channels[0] : channels;
let client = await this._self._slots.getShardedPubSubClient(firstChannel);
for (let i = 0;; i++) {
try {
return await client.SSUBSCRIBE(channels, listener, bufferMode);
}
catch (err) {
if (++i > maxCommandRedirections || !(err instanceof errors_1.ErrorReply)) {
throw err;
}
if (err.message.startsWith('MOVED')) {
await this._self._slots.rediscover(client);
client = await this._self._slots.getShardedPubSubClient(firstChannel);
continue;
}
throw err;
}
}
}
sSubscribe = this.SSUBSCRIBE;
SUNSUBSCRIBE(channels, listener, bufferMode) {
return this._self._slots.executeShardedUnsubscribeCommand(Array.isArray(channels) ? channels[0] : channels, client => client.SUNSUBSCRIBE(channels, listener, bufferMode));
}
resubscribeAllPubSubListeners(allListeners) {
for (const [channel, listeners] of allListeners.CHANNELS) {
listeners.buffers.forEach(bufListener => {
this.subscribe(channel, bufListener, true);
});
listeners.strings.forEach(strListener => {
this.subscribe(channel, strListener);
});
}
;
for (const [channel, listeners] of allListeners.PATTERNS) {
listeners.buffers.forEach(bufListener => {
this.pSubscribe(channel, bufListener, true);
});
listeners.strings.forEach(strListener => {
this.pSubscribe(channel, strListener);
});
}
;
for (const [channel, listeners] of allListeners.SHARDED) {
listeners.buffers.forEach(bufListener => {
this.sSubscribe(channel, bufListener, true);
});
listeners.strings.forEach(strListener => {
this.sSubscribe(channel, strListener);
});
}
;
}
sUnsubscribe = this.SUNSUBSCRIBE;
/**
* @deprecated Use `close` instead.
*/
quit() {
return this._self._slots.quit();
}
/**
* @deprecated Use `destroy` instead.
*/
disconnect() {
return this._self._slots.disconnect();
}
close() {
this._self._slots.clientSideCache?.onPoolClose();
return this._self._slots.close();
}
destroy() {
this._self._slots.clientSideCache?.onPoolClose();
return this._self._slots.destroy();
}
nodeClient(node) {
return this._self._slots.nodeClient(node);
}
/**
* Returns a random node from the cluster.
* Userful for running "forward" commands (like PUBLISH) on a random node.
*/
getRandomNode() {
return this._self._slots.getRandomNode();
}
/**
* Get a random node from a slot.
* Useful for running readonly commands on a slot.
*/
getSlotRandomNode(slot) {
return this._self._slots.getSlotRandomNode(slot);
}
/**
* @deprecated use `.masters` instead
* TODO
*/
getMasters() {
return this.masters;
}
/**
* @deprecated use `.slots[<SLOT>]` instead
* TODO
*/
getSlotMaster(slot) {
return this.slots[slot].master;
}
}
exports.default = RedisCluster;
//# sourceMappingURL=index.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,39 @@
import COMMANDS from '../commands';
import { MULTI_REPLY, MultiReply, MultiReplyType, RedisMultiQueuedCommand } from '../multi-command';
import { ReplyWithTypeMapping, CommandReply, Command, CommandArguments, CommanderConfig, RedisFunctions, RedisModules, RedisScripts, RespVersions, TransformReply, TypeMapping, RedisArgument } from '../RESP/types';
import { Tail } from '../commands/generic-transformers';
type CommandSignature<REPLIES extends Array<unknown>, C extends Command, M extends RedisModules, F extends RedisFunctions, S extends RedisScripts, RESP extends RespVersions, TYPE_MAPPING extends TypeMapping> = (...args: Tail<Parameters<C['parseCommand']>>) => RedisClusterMultiCommandType<[
...REPLIES,
ReplyWithTypeMapping<CommandReply<C, RESP>, TYPE_MAPPING>
], M, F, S, RESP, TYPE_MAPPING>;
type WithCommands<REPLIES extends Array<unknown>, M extends RedisModules, F extends RedisFunctions, S extends RedisScripts, RESP extends RespVersions, TYPE_MAPPING extends TypeMapping> = {
[P in keyof typeof COMMANDS]: CommandSignature<REPLIES, (typeof COMMANDS)[P], M, F, S, RESP, TYPE_MAPPING>;
};
type WithModules<REPLIES extends Array<unknown>, M extends RedisModules, F extends RedisFunctions, S extends RedisScripts, RESP extends RespVersions, TYPE_MAPPING extends TypeMapping> = {
[P in keyof M]: {
[C in keyof M[P]]: CommandSignature<REPLIES, M[P][C], M, F, S, RESP, TYPE_MAPPING>;
};
};
type WithFunctions<REPLIES extends Array<unknown>, M extends RedisModules, F extends RedisFunctions, S extends RedisScripts, RESP extends RespVersions, TYPE_MAPPING extends TypeMapping> = {
[L in keyof F]: {
[C in keyof F[L]]: CommandSignature<REPLIES, F[L][C], M, F, S, RESP, TYPE_MAPPING>;
};
};
type WithScripts<REPLIES extends Array<unknown>, M extends RedisModules, F extends RedisFunctions, S extends RedisScripts, RESP extends RespVersions, TYPE_MAPPING extends TypeMapping> = {
[P in keyof S]: CommandSignature<REPLIES, S[P], M, F, S, RESP, TYPE_MAPPING>;
};
export type RedisClusterMultiCommandType<REPLIES extends Array<any>, M extends RedisModules, F extends RedisFunctions, S extends RedisScripts, RESP extends RespVersions, TYPE_MAPPING extends TypeMapping> = (RedisClusterMultiCommand<REPLIES> & WithCommands<REPLIES, M, F, S, RESP, TYPE_MAPPING> & WithModules<REPLIES, M, F, S, RESP, TYPE_MAPPING> & WithFunctions<REPLIES, M, F, S, RESP, TYPE_MAPPING> & WithScripts<REPLIES, M, F, S, RESP, TYPE_MAPPING>);
export type ClusterMultiExecute = (firstKey: RedisArgument | undefined, isReadonly: boolean | undefined, commands: Array<RedisMultiQueuedCommand>) => Promise<Array<unknown>>;
export default class RedisClusterMultiCommand<REPLIES = []> {
#private;
static extend<M extends RedisModules = Record<string, never>, F extends RedisFunctions = Record<string, never>, S extends RedisScripts = Record<string, never>, RESP extends RespVersions = 2>(config?: CommanderConfig<M, F, S, RESP>): any;
constructor(executeMulti: ClusterMultiExecute, executePipeline: ClusterMultiExecute, routing: RedisArgument | undefined, typeMapping?: TypeMapping);
addCommand(firstKey: RedisArgument | undefined, isReadonly: boolean | undefined, args: CommandArguments, transformReply?: TransformReply): this;
exec<T extends MultiReply = MULTI_REPLY['GENERIC']>(execAsPipeline?: boolean): Promise<MultiReplyType<T, REPLIES>>;
EXEC: <T extends MultiReply = "generic">(execAsPipeline?: boolean) => Promise<MultiReplyType<T, REPLIES>>;
execTyped(execAsPipeline?: boolean): Promise<REPLIES>;
execAsPipeline<T extends MultiReply = MULTI_REPLY['GENERIC']>(): Promise<MultiReplyType<T, REPLIES>>;
execAsPipelineTyped(): Promise<REPLIES>;
}
export {};
//# sourceMappingURL=multi-command.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"multi-command.d.ts","sourceRoot":"","sources":["../../../lib/cluster/multi-command.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,112 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const commands_1 = __importDefault(require("../commands"));
const multi_command_1 = __importDefault(require("../multi-command"));
const commander_1 = require("../commander");
const parser_1 = require("../client/parser");
class RedisClusterMultiCommand {
static #createCommand(command, resp) {
const transformReply = (0, commander_1.getTransformReply)(command, resp);
return function (...args) {
const parser = new parser_1.BasicCommandParser();
command.parseCommand(parser, ...args);
const redisArgs = parser.redisArgs;
redisArgs.preserve = parser.preserve;
const firstKey = parser.firstKey;
return this.addCommand(firstKey, command.IS_READ_ONLY, redisArgs, transformReply);
};
}
static #createModuleCommand(command, resp) {
const transformReply = (0, commander_1.getTransformReply)(command, resp);
return function (...args) {
const parser = new parser_1.BasicCommandParser();
command.parseCommand(parser, ...args);
const redisArgs = parser.redisArgs;
redisArgs.preserve = parser.preserve;
const firstKey = parser.firstKey;
return this._self.addCommand(firstKey, command.IS_READ_ONLY, redisArgs, transformReply);
};
}
static #createFunctionCommand(name, fn, resp) {
const prefix = (0, commander_1.functionArgumentsPrefix)(name, fn);
const transformReply = (0, commander_1.getTransformReply)(fn, resp);
return function (...args) {
const parser = new parser_1.BasicCommandParser();
parser.push(...prefix);
fn.parseCommand(parser, ...args);
const redisArgs = parser.redisArgs;
redisArgs.preserve = parser.preserve;
const firstKey = parser.firstKey;
return this._self.addCommand(firstKey, fn.IS_READ_ONLY, redisArgs, transformReply);
};
}
static #createScriptCommand(script, resp) {
const transformReply = (0, commander_1.getTransformReply)(script, resp);
return function (...args) {
const parser = new parser_1.BasicCommandParser();
script.parseCommand(parser, ...args);
const scriptArgs = parser.redisArgs;
scriptArgs.preserve = parser.preserve;
const firstKey = parser.firstKey;
return this.#addScript(firstKey, script.IS_READ_ONLY, script, scriptArgs, transformReply);
};
}
static extend(config) {
return (0, commander_1.attachConfig)({
BaseClass: RedisClusterMultiCommand,
commands: commands_1.default,
createCommand: RedisClusterMultiCommand.#createCommand,
createModuleCommand: RedisClusterMultiCommand.#createModuleCommand,
createFunctionCommand: RedisClusterMultiCommand.#createFunctionCommand,
createScriptCommand: RedisClusterMultiCommand.#createScriptCommand,
config
});
}
#multi;
#executeMulti;
#executePipeline;
#firstKey;
#isReadonly = true;
constructor(executeMulti, executePipeline, routing, typeMapping) {
this.#multi = new multi_command_1.default(typeMapping);
this.#executeMulti = executeMulti;
this.#executePipeline = executePipeline;
this.#firstKey = routing;
}
#setState(firstKey, isReadonly) {
this.#firstKey ??= firstKey;
this.#isReadonly &&= isReadonly;
}
addCommand(firstKey, isReadonly, args, transformReply) {
this.#setState(firstKey, isReadonly);
this.#multi.addCommand(args, transformReply);
return this;
}
#addScript(firstKey, isReadonly, script, args, transformReply) {
this.#setState(firstKey, isReadonly);
this.#multi.addScript(script, args, transformReply);
return this;
}
async exec(execAsPipeline = false) {
if (execAsPipeline)
return this.execAsPipeline();
return this.#multi.transformReplies(await this.#executeMulti(this.#firstKey, this.#isReadonly, this.#multi.queue));
}
EXEC = this.exec;
execTyped(execAsPipeline = false) {
return this.exec(execAsPipeline);
}
async execAsPipeline() {
if (this.#multi.queue.length === 0)
return [];
return this.#multi.transformReplies(await this.#executePipeline(this.#firstKey, this.#isReadonly, this.#multi.queue));
}
execAsPipelineTyped() {
return this.execAsPipeline();
}
}
exports.default = RedisClusterMultiCommand;
//# sourceMappingURL=multi-command.js.map

Some files were not shown because too many files have changed in this diff Show More