ioredis-om 5.10.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +1571 -0
- package/built/Command.d.ts +166 -0
- package/built/Command.js +450 -0
- package/built/DataHandler.d.ts +37 -0
- package/built/DataHandler.js +224 -0
- package/built/Pipeline.d.ts +31 -0
- package/built/Pipeline.js +342 -0
- package/built/Redis.d.ts +243 -0
- package/built/Redis.js +800 -0
- package/built/ScanStream.d.ts +23 -0
- package/built/ScanStream.js +51 -0
- package/built/Script.d.ts +11 -0
- package/built/Script.js +62 -0
- package/built/SubscriptionSet.d.ts +14 -0
- package/built/SubscriptionSet.js +41 -0
- package/built/autoPipelining.d.ts +8 -0
- package/built/autoPipelining.js +167 -0
- package/built/cluster/ClusterOptions.d.ts +172 -0
- package/built/cluster/ClusterOptions.js +22 -0
- package/built/cluster/ClusterSubscriber.d.ts +29 -0
- package/built/cluster/ClusterSubscriber.js +223 -0
- package/built/cluster/ClusterSubscriberGroup.d.ts +108 -0
- package/built/cluster/ClusterSubscriberGroup.js +373 -0
- package/built/cluster/ConnectionPool.d.ts +37 -0
- package/built/cluster/ConnectionPool.js +154 -0
- package/built/cluster/DelayQueue.d.ts +20 -0
- package/built/cluster/DelayQueue.js +53 -0
- package/built/cluster/ShardedSubscriber.d.ts +36 -0
- package/built/cluster/ShardedSubscriber.js +147 -0
- package/built/cluster/index.d.ts +163 -0
- package/built/cluster/index.js +937 -0
- package/built/cluster/util.d.ts +25 -0
- package/built/cluster/util.js +100 -0
- package/built/connectors/AbstractConnector.d.ts +12 -0
- package/built/connectors/AbstractConnector.js +26 -0
- package/built/connectors/ConnectorConstructor.d.ts +5 -0
- package/built/connectors/ConnectorConstructor.js +2 -0
- package/built/connectors/SentinelConnector/FailoverDetector.d.ts +11 -0
- package/built/connectors/SentinelConnector/FailoverDetector.js +45 -0
- package/built/connectors/SentinelConnector/SentinelIterator.d.ts +13 -0
- package/built/connectors/SentinelConnector/SentinelIterator.js +37 -0
- package/built/connectors/SentinelConnector/index.d.ts +72 -0
- package/built/connectors/SentinelConnector/index.js +305 -0
- package/built/connectors/SentinelConnector/types.d.ts +21 -0
- package/built/connectors/SentinelConnector/types.js +2 -0
- package/built/connectors/StandaloneConnector.d.ts +17 -0
- package/built/connectors/StandaloneConnector.js +69 -0
- package/built/connectors/index.d.ts +3 -0
- package/built/connectors/index.js +7 -0
- package/built/constants/TLSProfiles.d.ts +9 -0
- package/built/constants/TLSProfiles.js +149 -0
- package/built/errors/ClusterAllFailedError.d.ts +7 -0
- package/built/errors/ClusterAllFailedError.js +15 -0
- package/built/errors/MaxRetriesPerRequestError.d.ts +5 -0
- package/built/errors/MaxRetriesPerRequestError.js +14 -0
- package/built/errors/index.d.ts +2 -0
- package/built/errors/index.js +5 -0
- package/built/index.d.ts +44 -0
- package/built/index.js +62 -0
- package/built/redis/RedisOptions.d.ts +197 -0
- package/built/redis/RedisOptions.js +58 -0
- package/built/redis/event_handler.d.ts +4 -0
- package/built/redis/event_handler.js +315 -0
- package/built/tracing.d.ts +26 -0
- package/built/tracing.js +96 -0
- package/built/transaction.d.ts +13 -0
- package/built/transaction.js +100 -0
- package/built/types.d.ts +33 -0
- package/built/types.js +2 -0
- package/built/utils/Commander.d.ts +50 -0
- package/built/utils/Commander.js +117 -0
- package/built/utils/RedisCommander.d.ts +8950 -0
- package/built/utils/RedisCommander.js +7 -0
- package/built/utils/applyMixin.d.ts +3 -0
- package/built/utils/applyMixin.js +8 -0
- package/built/utils/argumentParsers.d.ts +14 -0
- package/built/utils/argumentParsers.js +74 -0
- package/built/utils/debug.d.ts +16 -0
- package/built/utils/debug.js +95 -0
- package/built/utils/index.d.ts +124 -0
- package/built/utils/index.js +332 -0
- package/built/utils/lodash.d.ts +4 -0
- package/built/utils/lodash.js +9 -0
- package/package.json +103 -0
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { Callback, Respondable, CommandParameter } from "./types";
|
|
3
|
+
export declare type ArgumentType = string | Buffer | number | (string | Buffer | number | any[])[];
|
|
4
|
+
interface CommandOptions {
|
|
5
|
+
/**
|
|
6
|
+
* Set the encoding of the reply, by default buffer will be returned.
|
|
7
|
+
*/
|
|
8
|
+
replyEncoding?: BufferEncoding | null;
|
|
9
|
+
errorStack?: Error;
|
|
10
|
+
keyPrefix?: string;
|
|
11
|
+
/**
|
|
12
|
+
* Force the command to be readOnly so it will also execute on slaves
|
|
13
|
+
*/
|
|
14
|
+
readOnly?: boolean;
|
|
15
|
+
}
|
|
16
|
+
declare type ArgumentTransformer = (args: any[]) => any[];
|
|
17
|
+
declare type ReplyTransformer = (reply: any) => any;
|
|
18
|
+
export interface CommandNameFlags {
|
|
19
|
+
VALID_IN_SUBSCRIBER_MODE: [
|
|
20
|
+
"subscribe",
|
|
21
|
+
"psubscribe",
|
|
22
|
+
"unsubscribe",
|
|
23
|
+
"punsubscribe",
|
|
24
|
+
"ssubscribe",
|
|
25
|
+
"sunsubscribe",
|
|
26
|
+
"ping",
|
|
27
|
+
"quit"
|
|
28
|
+
];
|
|
29
|
+
VALID_IN_MONITOR_MODE: ["monitor", "auth"];
|
|
30
|
+
ENTER_SUBSCRIBER_MODE: ["subscribe", "psubscribe", "ssubscribe"];
|
|
31
|
+
EXIT_SUBSCRIBER_MODE: ["unsubscribe", "punsubscribe", "sunsubscribe"];
|
|
32
|
+
WILL_DISCONNECT: ["quit"];
|
|
33
|
+
HANDSHAKE_COMMANDS: ["auth", "select", "client", "readonly", "info"];
|
|
34
|
+
IGNORE_RECONNECT_ON_ERROR: ["client"];
|
|
35
|
+
BLOCKING_COMMANDS: [
|
|
36
|
+
"blpop",
|
|
37
|
+
"brpop",
|
|
38
|
+
"brpoplpush",
|
|
39
|
+
"blmove",
|
|
40
|
+
"bzpopmin",
|
|
41
|
+
"bzpopmax",
|
|
42
|
+
"bzmpop",
|
|
43
|
+
"blmpop",
|
|
44
|
+
"xread",
|
|
45
|
+
"xreadgroup"
|
|
46
|
+
];
|
|
47
|
+
LAST_ARG_TIMEOUT_COMMANDS: [
|
|
48
|
+
"blpop",
|
|
49
|
+
"brpop",
|
|
50
|
+
"brpoplpush",
|
|
51
|
+
"blmove",
|
|
52
|
+
"bzpopmin",
|
|
53
|
+
"bzpopmax"
|
|
54
|
+
];
|
|
55
|
+
FIRST_ARG_TIMEOUT_COMMANDS: ["bzmpop", "blmpop"];
|
|
56
|
+
BLOCK_OPTION_COMMANDS: ["xread", "xreadgroup"];
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Command instance
|
|
60
|
+
*
|
|
61
|
+
* It's rare that you need to create a Command instance yourself.
|
|
62
|
+
*
|
|
63
|
+
* ```js
|
|
64
|
+
* var infoCommand = new Command('info', null, function (err, result) {
|
|
65
|
+
* console.log('result', result);
|
|
66
|
+
* });
|
|
67
|
+
*
|
|
68
|
+
* redis.sendCommand(infoCommand);
|
|
69
|
+
*
|
|
70
|
+
* // When no callback provided, Command instance will have a `promise` property,
|
|
71
|
+
* // which will resolve/reject with the result of the command.
|
|
72
|
+
* var getCommand = new Command('get', ['foo']);
|
|
73
|
+
* getCommand.promise.then(function (result) {
|
|
74
|
+
* console.log('result', result);
|
|
75
|
+
* });
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
export default class Command implements Respondable {
|
|
79
|
+
name: string;
|
|
80
|
+
static FLAGS: {
|
|
81
|
+
[key in keyof CommandNameFlags]: CommandNameFlags[key];
|
|
82
|
+
};
|
|
83
|
+
private static flagMap?;
|
|
84
|
+
private static _transformer;
|
|
85
|
+
/**
|
|
86
|
+
* Check whether the command has the flag
|
|
87
|
+
*/
|
|
88
|
+
static checkFlag<T extends keyof CommandNameFlags>(flagName: T, commandName: string): commandName is CommandNameFlags[T][number];
|
|
89
|
+
static setArgumentTransformer(name: string, func: ArgumentTransformer): void;
|
|
90
|
+
static setReplyTransformer(name: string, func: ReplyTransformer): void;
|
|
91
|
+
private static getFlagMap;
|
|
92
|
+
ignore?: boolean;
|
|
93
|
+
isReadOnly?: boolean;
|
|
94
|
+
args: CommandParameter[];
|
|
95
|
+
inTransaction: boolean;
|
|
96
|
+
pipelineIndex?: number;
|
|
97
|
+
isTraced: boolean;
|
|
98
|
+
isResolved: boolean;
|
|
99
|
+
reject: (err: Error) => void;
|
|
100
|
+
resolve: (result: any) => void;
|
|
101
|
+
promise: Promise<any>;
|
|
102
|
+
private replyEncoding;
|
|
103
|
+
private errorStack;
|
|
104
|
+
private bufferMode;
|
|
105
|
+
private callback;
|
|
106
|
+
private transformed;
|
|
107
|
+
private _commandTimeoutTimer?;
|
|
108
|
+
private _blockingTimeoutTimer?;
|
|
109
|
+
private _blockingDeadline?;
|
|
110
|
+
private slot?;
|
|
111
|
+
private keys?;
|
|
112
|
+
/**
|
|
113
|
+
* Creates an instance of Command.
|
|
114
|
+
* @param name Command name
|
|
115
|
+
* @param args An array of command arguments
|
|
116
|
+
* @param options
|
|
117
|
+
* @param callback The callback that handles the response.
|
|
118
|
+
* If omit, the response will be handled via Promise
|
|
119
|
+
*/
|
|
120
|
+
constructor(name: string, args?: Array<ArgumentType>, options?: CommandOptions, callback?: Callback);
|
|
121
|
+
getSlot(): number;
|
|
122
|
+
getKeys(): Array<string | Buffer>;
|
|
123
|
+
/**
|
|
124
|
+
* Convert command to writable buffer or string
|
|
125
|
+
*/
|
|
126
|
+
toWritable(_socket: object): string | Buffer;
|
|
127
|
+
stringifyArguments(): void;
|
|
128
|
+
/**
|
|
129
|
+
* Convert buffer/buffer[] to string/string[],
|
|
130
|
+
* and apply reply transformer.
|
|
131
|
+
*/
|
|
132
|
+
transformReply(result: Buffer | Buffer[]): string | string[] | Buffer | Buffer[];
|
|
133
|
+
/**
|
|
134
|
+
* Set the wait time before terminating the attempt to execute a command
|
|
135
|
+
* and generating an error.
|
|
136
|
+
*/
|
|
137
|
+
setTimeout(ms: number): void;
|
|
138
|
+
/**
|
|
139
|
+
* Set a timeout for blocking commands.
|
|
140
|
+
* When the timeout expires, the command resolves with null (matching Redis behavior).
|
|
141
|
+
* This handles the case of undetectable network failures (e.g., docker network disconnect)
|
|
142
|
+
* where the TCP connection becomes a zombie and no close event fires.
|
|
143
|
+
*/
|
|
144
|
+
setBlockingTimeout(ms: number): void;
|
|
145
|
+
/**
|
|
146
|
+
* Extract the blocking timeout from the command arguments.
|
|
147
|
+
*
|
|
148
|
+
* @returns The timeout in seconds, null for indefinite blocking (timeout of 0),
|
|
149
|
+
* or undefined if this is not a blocking command
|
|
150
|
+
*/
|
|
151
|
+
extractBlockingTimeout(): number | null | undefined;
|
|
152
|
+
/**
|
|
153
|
+
* Clear the command and blocking timers
|
|
154
|
+
*/
|
|
155
|
+
private _clearTimers;
|
|
156
|
+
private initPromise;
|
|
157
|
+
/**
|
|
158
|
+
* Iterate through the command arguments that are considered keys.
|
|
159
|
+
*/
|
|
160
|
+
private _iterateKeys;
|
|
161
|
+
/**
|
|
162
|
+
* Convert the value from buffer to the target encoding.
|
|
163
|
+
*/
|
|
164
|
+
private _convertValue;
|
|
165
|
+
}
|
|
166
|
+
export {};
|
package/built/Command.js
ADDED
|
@@ -0,0 +1,450 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const commands_1 = require("@ioredis/commands");
|
|
4
|
+
const calculateSlot = require("cluster-key-slot");
|
|
5
|
+
const standard_as_callback_1 = require("standard-as-callback");
|
|
6
|
+
const utils_1 = require("./utils");
|
|
7
|
+
const argumentParsers_1 = require("./utils/argumentParsers");
|
|
8
|
+
/**
|
|
9
|
+
* Command instance
|
|
10
|
+
*
|
|
11
|
+
* It's rare that you need to create a Command instance yourself.
|
|
12
|
+
*
|
|
13
|
+
* ```js
|
|
14
|
+
* var infoCommand = new Command('info', null, function (err, result) {
|
|
15
|
+
* console.log('result', result);
|
|
16
|
+
* });
|
|
17
|
+
*
|
|
18
|
+
* redis.sendCommand(infoCommand);
|
|
19
|
+
*
|
|
20
|
+
* // When no callback provided, Command instance will have a `promise` property,
|
|
21
|
+
* // which will resolve/reject with the result of the command.
|
|
22
|
+
* var getCommand = new Command('get', ['foo']);
|
|
23
|
+
* getCommand.promise.then(function (result) {
|
|
24
|
+
* console.log('result', result);
|
|
25
|
+
* });
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
class Command {
|
|
29
|
+
/**
|
|
30
|
+
* Creates an instance of Command.
|
|
31
|
+
* @param name Command name
|
|
32
|
+
* @param args An array of command arguments
|
|
33
|
+
* @param options
|
|
34
|
+
* @param callback The callback that handles the response.
|
|
35
|
+
* If omit, the response will be handled via Promise
|
|
36
|
+
*/
|
|
37
|
+
constructor(name, args = [], options = {}, callback) {
|
|
38
|
+
this.name = name;
|
|
39
|
+
this.inTransaction = false;
|
|
40
|
+
this.isTraced = false;
|
|
41
|
+
this.isResolved = false;
|
|
42
|
+
this.transformed = false;
|
|
43
|
+
this.replyEncoding = options.replyEncoding;
|
|
44
|
+
this.errorStack = options.errorStack;
|
|
45
|
+
this.args = args.flat();
|
|
46
|
+
this.callback = callback;
|
|
47
|
+
this.initPromise();
|
|
48
|
+
if (options.keyPrefix) {
|
|
49
|
+
// @ts-expect-error
|
|
50
|
+
const isBufferKeyPrefix = options.keyPrefix instanceof Buffer;
|
|
51
|
+
// @ts-expect-error
|
|
52
|
+
let keyPrefixBuffer = isBufferKeyPrefix
|
|
53
|
+
? options.keyPrefix
|
|
54
|
+
: null;
|
|
55
|
+
this._iterateKeys((key) => {
|
|
56
|
+
if (key instanceof Buffer) {
|
|
57
|
+
if (keyPrefixBuffer === null) {
|
|
58
|
+
keyPrefixBuffer = Buffer.from(options.keyPrefix);
|
|
59
|
+
}
|
|
60
|
+
return Buffer.concat([keyPrefixBuffer, key]);
|
|
61
|
+
}
|
|
62
|
+
else if (isBufferKeyPrefix) {
|
|
63
|
+
// @ts-expect-error
|
|
64
|
+
return Buffer.concat([options.keyPrefix, Buffer.from(String(key))]);
|
|
65
|
+
}
|
|
66
|
+
return options.keyPrefix + key;
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
if (options.readOnly) {
|
|
70
|
+
this.isReadOnly = true;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Check whether the command has the flag
|
|
75
|
+
*/
|
|
76
|
+
static checkFlag(flagName, commandName) {
|
|
77
|
+
commandName = commandName.toLowerCase();
|
|
78
|
+
return !!this.getFlagMap()[flagName][commandName];
|
|
79
|
+
}
|
|
80
|
+
static setArgumentTransformer(name, func) {
|
|
81
|
+
this._transformer.argument[name] = func;
|
|
82
|
+
}
|
|
83
|
+
static setReplyTransformer(name, func) {
|
|
84
|
+
this._transformer.reply[name] = func;
|
|
85
|
+
}
|
|
86
|
+
static getFlagMap() {
|
|
87
|
+
if (!this.flagMap) {
|
|
88
|
+
this.flagMap = Object.keys(Command.FLAGS).reduce((map, flagName) => {
|
|
89
|
+
map[flagName] = {};
|
|
90
|
+
Command.FLAGS[flagName].forEach((commandName) => {
|
|
91
|
+
map[flagName][commandName] = true;
|
|
92
|
+
});
|
|
93
|
+
return map;
|
|
94
|
+
}, {});
|
|
95
|
+
}
|
|
96
|
+
return this.flagMap;
|
|
97
|
+
}
|
|
98
|
+
getSlot() {
|
|
99
|
+
if (typeof this.slot === "undefined") {
|
|
100
|
+
const key = this.getKeys()[0];
|
|
101
|
+
this.slot = key == null ? null : calculateSlot(key);
|
|
102
|
+
}
|
|
103
|
+
return this.slot;
|
|
104
|
+
}
|
|
105
|
+
getKeys() {
|
|
106
|
+
return this._iterateKeys();
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Convert command to writable buffer or string
|
|
110
|
+
*/
|
|
111
|
+
toWritable(_socket) {
|
|
112
|
+
let result;
|
|
113
|
+
const commandStr = "*" +
|
|
114
|
+
(this.args.length + 1) +
|
|
115
|
+
"\r\n$" +
|
|
116
|
+
Buffer.byteLength(this.name) +
|
|
117
|
+
"\r\n" +
|
|
118
|
+
this.name +
|
|
119
|
+
"\r\n";
|
|
120
|
+
if (this.bufferMode) {
|
|
121
|
+
const buffers = new MixedBuffers();
|
|
122
|
+
buffers.push(commandStr);
|
|
123
|
+
for (let i = 0; i < this.args.length; ++i) {
|
|
124
|
+
const arg = this.args[i];
|
|
125
|
+
if (arg instanceof Buffer) {
|
|
126
|
+
if (arg.length === 0) {
|
|
127
|
+
buffers.push("$0\r\n\r\n");
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
buffers.push("$" + arg.length + "\r\n");
|
|
131
|
+
buffers.push(arg);
|
|
132
|
+
buffers.push("\r\n");
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
buffers.push("$" +
|
|
137
|
+
Buffer.byteLength(arg) +
|
|
138
|
+
"\r\n" +
|
|
139
|
+
arg +
|
|
140
|
+
"\r\n");
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
result = buffers.toBuffer();
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
result = commandStr;
|
|
147
|
+
for (let i = 0; i < this.args.length; ++i) {
|
|
148
|
+
const arg = this.args[i];
|
|
149
|
+
result +=
|
|
150
|
+
"$" +
|
|
151
|
+
Buffer.byteLength(arg) +
|
|
152
|
+
"\r\n" +
|
|
153
|
+
arg +
|
|
154
|
+
"\r\n";
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return result;
|
|
158
|
+
}
|
|
159
|
+
stringifyArguments() {
|
|
160
|
+
for (let i = 0; i < this.args.length; ++i) {
|
|
161
|
+
const arg = this.args[i];
|
|
162
|
+
if (typeof arg === "string") {
|
|
163
|
+
// buffers and strings don't need any transformation
|
|
164
|
+
}
|
|
165
|
+
else if (arg instanceof Buffer) {
|
|
166
|
+
this.bufferMode = true;
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
this.args[i] = (0, utils_1.toArg)(arg);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Convert buffer/buffer[] to string/string[],
|
|
175
|
+
* and apply reply transformer.
|
|
176
|
+
*/
|
|
177
|
+
transformReply(result) {
|
|
178
|
+
if (this.replyEncoding) {
|
|
179
|
+
result = (0, utils_1.convertBufferToString)(result, this.replyEncoding);
|
|
180
|
+
}
|
|
181
|
+
const transformer = Command._transformer.reply[this.name];
|
|
182
|
+
if (transformer) {
|
|
183
|
+
result = transformer(result);
|
|
184
|
+
}
|
|
185
|
+
return result;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Set the wait time before terminating the attempt to execute a command
|
|
189
|
+
* and generating an error.
|
|
190
|
+
*/
|
|
191
|
+
setTimeout(ms) {
|
|
192
|
+
if (!this._commandTimeoutTimer) {
|
|
193
|
+
this._commandTimeoutTimer = setTimeout(() => {
|
|
194
|
+
if (!this.isResolved) {
|
|
195
|
+
this.reject(new Error("Command timed out"));
|
|
196
|
+
}
|
|
197
|
+
}, ms);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Set a timeout for blocking commands.
|
|
202
|
+
* When the timeout expires, the command resolves with null (matching Redis behavior).
|
|
203
|
+
* This handles the case of undetectable network failures (e.g., docker network disconnect)
|
|
204
|
+
* where the TCP connection becomes a zombie and no close event fires.
|
|
205
|
+
*/
|
|
206
|
+
setBlockingTimeout(ms) {
|
|
207
|
+
if (ms <= 0) {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
// Clear existing timer if any (can happen when command moves from offline to command queue)
|
|
211
|
+
if (this._blockingTimeoutTimer) {
|
|
212
|
+
clearTimeout(this._blockingTimeoutTimer);
|
|
213
|
+
this._blockingTimeoutTimer = undefined;
|
|
214
|
+
}
|
|
215
|
+
const now = Date.now();
|
|
216
|
+
// First call: establish absolute deadline
|
|
217
|
+
if (this._blockingDeadline === undefined) {
|
|
218
|
+
this._blockingDeadline = now + ms;
|
|
219
|
+
}
|
|
220
|
+
// Check if we've already exceeded the deadline
|
|
221
|
+
const remaining = this._blockingDeadline - now;
|
|
222
|
+
if (remaining <= 0) {
|
|
223
|
+
// Resolve with null to indicate timeout (same as Redis behavior)
|
|
224
|
+
this.resolve(null);
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
this._blockingTimeoutTimer = setTimeout(() => {
|
|
228
|
+
if (this.isResolved) {
|
|
229
|
+
this._blockingTimeoutTimer = undefined;
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
this._blockingTimeoutTimer = undefined;
|
|
233
|
+
// Timeout expired - resolve with null (same as Redis behavior when blocking command times out)
|
|
234
|
+
this.resolve(null);
|
|
235
|
+
}, remaining);
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Extract the blocking timeout from the command arguments.
|
|
239
|
+
*
|
|
240
|
+
* @returns The timeout in seconds, null for indefinite blocking (timeout of 0),
|
|
241
|
+
* or undefined if this is not a blocking command
|
|
242
|
+
*/
|
|
243
|
+
extractBlockingTimeout() {
|
|
244
|
+
const args = this.args;
|
|
245
|
+
if (!args || args.length === 0) {
|
|
246
|
+
return undefined;
|
|
247
|
+
}
|
|
248
|
+
const name = this.name.toLowerCase();
|
|
249
|
+
if (Command.checkFlag("LAST_ARG_TIMEOUT_COMMANDS", name)) {
|
|
250
|
+
return (0, argumentParsers_1.parseSecondsArgument)(args[args.length - 1]);
|
|
251
|
+
}
|
|
252
|
+
if (Command.checkFlag("FIRST_ARG_TIMEOUT_COMMANDS", name)) {
|
|
253
|
+
return (0, argumentParsers_1.parseSecondsArgument)(args[0]);
|
|
254
|
+
}
|
|
255
|
+
if (Command.checkFlag("BLOCK_OPTION_COMMANDS", name)) {
|
|
256
|
+
return (0, argumentParsers_1.parseBlockOption)(args);
|
|
257
|
+
}
|
|
258
|
+
return undefined;
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Clear the command and blocking timers
|
|
262
|
+
*/
|
|
263
|
+
_clearTimers() {
|
|
264
|
+
const existingTimer = this._commandTimeoutTimer;
|
|
265
|
+
if (existingTimer) {
|
|
266
|
+
clearTimeout(existingTimer);
|
|
267
|
+
delete this._commandTimeoutTimer;
|
|
268
|
+
}
|
|
269
|
+
const blockingTimer = this._blockingTimeoutTimer;
|
|
270
|
+
if (blockingTimer) {
|
|
271
|
+
clearTimeout(blockingTimer);
|
|
272
|
+
delete this._blockingTimeoutTimer;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
initPromise() {
|
|
276
|
+
const promise = new Promise((resolve, reject) => {
|
|
277
|
+
if (!this.transformed) {
|
|
278
|
+
this.transformed = true;
|
|
279
|
+
const transformer = Command._transformer.argument[this.name];
|
|
280
|
+
if (transformer) {
|
|
281
|
+
this.args = transformer(this.args);
|
|
282
|
+
}
|
|
283
|
+
this.stringifyArguments();
|
|
284
|
+
}
|
|
285
|
+
this.resolve = this._convertValue(resolve);
|
|
286
|
+
this.reject = (err) => {
|
|
287
|
+
this._clearTimers();
|
|
288
|
+
if (this.errorStack) {
|
|
289
|
+
reject((0, utils_1.optimizeErrorStack)(err, this.errorStack.stack, __dirname));
|
|
290
|
+
}
|
|
291
|
+
else {
|
|
292
|
+
reject(err);
|
|
293
|
+
}
|
|
294
|
+
};
|
|
295
|
+
});
|
|
296
|
+
this.promise = (0, standard_as_callback_1.default)(promise, this.callback);
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Iterate through the command arguments that are considered keys.
|
|
300
|
+
*/
|
|
301
|
+
_iterateKeys(transform = (key) => key) {
|
|
302
|
+
if (typeof this.keys === "undefined") {
|
|
303
|
+
this.keys = [];
|
|
304
|
+
if ((0, commands_1.exists)(this.name, { caseInsensitive: true })) {
|
|
305
|
+
// @ts-expect-error
|
|
306
|
+
const keyIndexes = (0, commands_1.getKeyIndexes)(this.name, this.args, {
|
|
307
|
+
nameCaseInsensitive: true,
|
|
308
|
+
});
|
|
309
|
+
for (const index of keyIndexes) {
|
|
310
|
+
this.args[index] = transform(this.args[index]);
|
|
311
|
+
this.keys.push(this.args[index]);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
return this.keys;
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Convert the value from buffer to the target encoding.
|
|
319
|
+
*/
|
|
320
|
+
_convertValue(resolve) {
|
|
321
|
+
return (value) => {
|
|
322
|
+
try {
|
|
323
|
+
this._clearTimers();
|
|
324
|
+
resolve(this.transformReply(value));
|
|
325
|
+
this.isResolved = true;
|
|
326
|
+
}
|
|
327
|
+
catch (err) {
|
|
328
|
+
this.reject(err);
|
|
329
|
+
}
|
|
330
|
+
return this.promise;
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
exports.default = Command;
|
|
335
|
+
Command.FLAGS = {
|
|
336
|
+
VALID_IN_SUBSCRIBER_MODE: [
|
|
337
|
+
"subscribe",
|
|
338
|
+
"psubscribe",
|
|
339
|
+
"unsubscribe",
|
|
340
|
+
"punsubscribe",
|
|
341
|
+
"ssubscribe",
|
|
342
|
+
"sunsubscribe",
|
|
343
|
+
"ping",
|
|
344
|
+
"quit",
|
|
345
|
+
],
|
|
346
|
+
VALID_IN_MONITOR_MODE: ["monitor", "auth"],
|
|
347
|
+
ENTER_SUBSCRIBER_MODE: ["subscribe", "psubscribe", "ssubscribe"],
|
|
348
|
+
EXIT_SUBSCRIBER_MODE: ["unsubscribe", "punsubscribe", "sunsubscribe"],
|
|
349
|
+
WILL_DISCONNECT: ["quit"],
|
|
350
|
+
HANDSHAKE_COMMANDS: ["auth", "select", "client", "readonly", "info"],
|
|
351
|
+
IGNORE_RECONNECT_ON_ERROR: ["client"],
|
|
352
|
+
BLOCKING_COMMANDS: [
|
|
353
|
+
"blpop",
|
|
354
|
+
"brpop",
|
|
355
|
+
"brpoplpush",
|
|
356
|
+
"blmove",
|
|
357
|
+
"bzpopmin",
|
|
358
|
+
"bzpopmax",
|
|
359
|
+
"bzmpop",
|
|
360
|
+
"blmpop",
|
|
361
|
+
"xread",
|
|
362
|
+
"xreadgroup",
|
|
363
|
+
],
|
|
364
|
+
LAST_ARG_TIMEOUT_COMMANDS: [
|
|
365
|
+
"blpop",
|
|
366
|
+
"brpop",
|
|
367
|
+
"brpoplpush",
|
|
368
|
+
"blmove",
|
|
369
|
+
"bzpopmin",
|
|
370
|
+
"bzpopmax",
|
|
371
|
+
],
|
|
372
|
+
FIRST_ARG_TIMEOUT_COMMANDS: ["bzmpop", "blmpop"],
|
|
373
|
+
BLOCK_OPTION_COMMANDS: ["xread", "xreadgroup"],
|
|
374
|
+
};
|
|
375
|
+
Command._transformer = {
|
|
376
|
+
argument: {},
|
|
377
|
+
reply: {},
|
|
378
|
+
};
|
|
379
|
+
const msetArgumentTransformer = function (args) {
|
|
380
|
+
if (args.length === 1) {
|
|
381
|
+
if (args[0] instanceof Map) {
|
|
382
|
+
return (0, utils_1.convertMapToArray)(args[0]);
|
|
383
|
+
}
|
|
384
|
+
if (typeof args[0] === "object" && args[0] !== null) {
|
|
385
|
+
return (0, utils_1.convertObjectToArray)(args[0]);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
return args;
|
|
389
|
+
};
|
|
390
|
+
const hsetArgumentTransformer = function (args) {
|
|
391
|
+
if (args.length === 2) {
|
|
392
|
+
if (args[1] instanceof Map) {
|
|
393
|
+
return [args[0]].concat((0, utils_1.convertMapToArray)(args[1]));
|
|
394
|
+
}
|
|
395
|
+
if (typeof args[1] === "object" && args[1] !== null) {
|
|
396
|
+
return [args[0]].concat((0, utils_1.convertObjectToArray)(args[1]));
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
return args;
|
|
400
|
+
};
|
|
401
|
+
Command.setArgumentTransformer("mset", msetArgumentTransformer);
|
|
402
|
+
Command.setArgumentTransformer("msetnx", msetArgumentTransformer);
|
|
403
|
+
Command.setArgumentTransformer("hset", hsetArgumentTransformer);
|
|
404
|
+
Command.setArgumentTransformer("hmset", hsetArgumentTransformer);
|
|
405
|
+
Command.setReplyTransformer("hgetall", function (result) {
|
|
406
|
+
if (Array.isArray(result)) {
|
|
407
|
+
const obj = {};
|
|
408
|
+
for (let i = 0; i < result.length; i += 2) {
|
|
409
|
+
const key = result[i];
|
|
410
|
+
const value = result[i + 1];
|
|
411
|
+
if (key in obj) {
|
|
412
|
+
// can only be truthy if the property is special somehow, like '__proto__' or 'constructor'
|
|
413
|
+
// https://github.com/luin/ioredis/issues/1267
|
|
414
|
+
Object.defineProperty(obj, key, {
|
|
415
|
+
value,
|
|
416
|
+
configurable: true,
|
|
417
|
+
enumerable: true,
|
|
418
|
+
writable: true,
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
else {
|
|
422
|
+
obj[key] = value;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
return obj;
|
|
426
|
+
}
|
|
427
|
+
return result;
|
|
428
|
+
});
|
|
429
|
+
class MixedBuffers {
|
|
430
|
+
constructor() {
|
|
431
|
+
this.length = 0;
|
|
432
|
+
this.items = [];
|
|
433
|
+
}
|
|
434
|
+
push(x) {
|
|
435
|
+
this.length += Buffer.byteLength(x);
|
|
436
|
+
this.items.push(x);
|
|
437
|
+
}
|
|
438
|
+
toBuffer() {
|
|
439
|
+
const result = Buffer.allocUnsafe(this.length);
|
|
440
|
+
let offset = 0;
|
|
441
|
+
for (const item of this.items) {
|
|
442
|
+
const length = Buffer.byteLength(item);
|
|
443
|
+
Buffer.isBuffer(item)
|
|
444
|
+
? item.copy(result, offset)
|
|
445
|
+
: result.write(item, offset, length);
|
|
446
|
+
offset += length;
|
|
447
|
+
}
|
|
448
|
+
return result;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { NetStream, CommandItem } from "./types";
|
|
3
|
+
import Deque = require("denque");
|
|
4
|
+
import { EventEmitter } from "events";
|
|
5
|
+
import SubscriptionSet from "./SubscriptionSet";
|
|
6
|
+
export interface Condition {
|
|
7
|
+
select: number;
|
|
8
|
+
auth?: string | [string, string];
|
|
9
|
+
subscriber: false | SubscriptionSet;
|
|
10
|
+
}
|
|
11
|
+
export declare type FlushQueueOptions = {
|
|
12
|
+
offlineQueue?: boolean;
|
|
13
|
+
commandQueue?: boolean;
|
|
14
|
+
};
|
|
15
|
+
export interface DataHandledable extends EventEmitter {
|
|
16
|
+
stream: NetStream;
|
|
17
|
+
status: string;
|
|
18
|
+
condition: Condition | null;
|
|
19
|
+
commandQueue: Deque<CommandItem>;
|
|
20
|
+
disconnect(reconnect: boolean): void;
|
|
21
|
+
recoverFromFatalError(commandError: Error, err: Error, options: FlushQueueOptions): void;
|
|
22
|
+
handleReconnection(err: Error, item: CommandItem): void;
|
|
23
|
+
}
|
|
24
|
+
interface ParserOptions {
|
|
25
|
+
stringNumbers: boolean;
|
|
26
|
+
}
|
|
27
|
+
export default class DataHandler {
|
|
28
|
+
private redis;
|
|
29
|
+
constructor(redis: DataHandledable, parserOptions: ParserOptions);
|
|
30
|
+
private returnFatalError;
|
|
31
|
+
private returnError;
|
|
32
|
+
private returnReply;
|
|
33
|
+
private handleSubscriberReply;
|
|
34
|
+
private handleMonitorReply;
|
|
35
|
+
private shiftCommand;
|
|
36
|
+
}
|
|
37
|
+
export {};
|