Skip to content

Commit 4894c26

Browse files
committed
wip
1 parent f22879d commit 4894c26

35 files changed

+845
-659
lines changed

docs/v4-to-v5.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,9 +161,15 @@ Some command arguments/replies have changed to align more closely to data types
161161
- `HELLO`: `protover` moved from the options object to it's own argument, `auth` -> `AUTH`, `clientName` -> `SETNAME`
162162
- `MODULE LIST`: `version` -> `ver` [^map-keys]
163163
- `MEMORY STATS`: [^map-keys]
164+
- `CLIENT TRACKINGINFO`: `flags` in RESP2 - `Set<string>` -> `Array<string>` (to match RESP3 default type mapping)
165+
- `CLUSETER SETSLOT`: `ClusterSlotStates` -> `CLUSTER_SLOT_STATES` [^enum-to-constants]
166+
- `FUNCTION RESTORE`: the second argument is `{ mode: string; }` instead of `string` [^future-proofing]
167+
- `CLUSTER RESET`: the second argument is `{ mode: string; }` instead of `string` [^future-proofing]
164168

165169
[^enum-to-constants]: TODO
166170

167171
[^boolean-to-number]: TODO
168172

169-
[^map-keys]: [TODO](https://github.com/redis/node-redis/discussions/2506)
173+
[^map-keys]: [TODO](https://github.com/redis/node-redis/discussions/2506)
174+
175+
[^future-proofing]: TODO

packages/client/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export { RedisModules, RedisFunctions, RedisScripts, RespVersions } from './lib/RESP/types';
1+
export { RedisModules, RedisFunctions, RedisScripts, RespVersions, TypeMapping, CommandPolicies } from './lib/RESP/types';
22
export { RESP_TYPES } from './lib/RESP/decoder';
33
export { VerbatimString } from './lib/RESP/verbatim-string';
44
export { defineScript } from './lib/lua-script';

packages/client/lib/client/commands-queue.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,14 @@ import { ChannelListeners, PubSub, PubSubCommand, PubSubListener, PubSubType, Pu
66
import { AbortError, ErrorReply } from '../errors';
77
import { EventEmitter } from 'stream';
88

9-
export interface CommandOptions {
9+
export interface CommandOptions<T = TypeMapping> {
1010
chainId?: symbol;
1111
asap?: boolean;
1212
abortSignal?: AbortSignal;
13-
typeMapping?: TypeMapping;
13+
/**
14+
* Maps bettween RESP and JavaScript types
15+
*/
16+
typeMapping?: T;
1417
}
1518

1619
export interface CommandWaitingToBeSent extends CommandWaitingForReply {

packages/client/lib/client/index.spec.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -122,10 +122,10 @@ describe('Client', () => {
122122
client.connect()
123123
]);
124124

125-
const promise = once(client, 'end');
126-
console.log('listen to end', client.listeners('end'));
127-
client.close();
128-
await promise;
125+
await Promise.all([
126+
once(client, 'end'),
127+
client.close()
128+
]);
129129
}, {
130130
...GLOBAL.SERVERS.OPEN,
131131
disableClientSetup: true
@@ -335,9 +335,7 @@ describe('Client', () => {
335335
});
336336

337337
testUtils.testWithClient('duplicate should reuse command options', async client => {
338-
const duplicate = client.withTypeMapping({
339-
[RESP_TYPES.SIMPLE_STRING]: Buffer
340-
}).duplicate();
338+
const duplicate = client.duplicate();
341339

342340
await duplicate.connect();
343341

@@ -351,6 +349,13 @@ describe('Client', () => {
351349
}
352350
}, {
353351
...GLOBAL.SERVERS.OPEN,
352+
clientOptions: {
353+
commandOptions: {
354+
typeMapping: {
355+
[RESP_TYPES.SIMPLE_STRING]: Buffer
356+
}
357+
}
358+
},
354359
disableClientSetup: true,
355360
});
356361

packages/client/lib/client/index.ts

Lines changed: 36 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,27 @@ import { Command, CommandArguments, CommandSignature, TypeMapping, CommanderConf
1111
import RedisClientMultiCommand, { RedisClientMultiCommandType } from './multi-command';
1212
import { RedisMultiQueuedCommand } from '../multi-command';
1313
import HELLO, { HelloOptions } from '../commands/HELLO';
14-
import { ReplyWithTypeMapping, CommandReply } from '../RESP/types';
1514
import { ScanOptions, ScanCommonOptions } from '../commands/SCAN';
1615
import { RedisLegacyClient, RedisLegacyClientType } from './legacy-mode';
1716
// import { RedisClientPool } from './pool';
1817

18+
interface ClientCommander<
19+
M extends RedisModules,
20+
F extends RedisFunctions,
21+
S extends RedisScripts,
22+
RESP extends RespVersions,
23+
TYPE_MAPPING extends TypeMapping
24+
> extends CommanderConfig<M, F, S, RESP>{
25+
commandOptions?: CommandOptions<TYPE_MAPPING>;
26+
}
27+
1928
export interface RedisClientOptions<
2029
M extends RedisModules = RedisModules,
2130
F extends RedisFunctions = RedisFunctions,
2231
S extends RedisScripts = RedisScripts,
2332
RESP extends RespVersions = RespVersions,
2433
TYPE_MAPPING extends TypeMapping = TypeMapping
25-
> extends CommanderConfig<M, F, S, RESP>, TypeMappingOption<TYPE_MAPPING> {
34+
> extends ClientCommander<M, F, S, RESP, TYPE_MAPPING> {
2635
/**
2736
* `redis[s]://[[username][:password]@][host][:port][/db-number]`
2837
* 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
@@ -68,13 +77,6 @@ export interface RedisClientOptions<
6877
pingInterval?: number;
6978
}
7079

71-
export interface TypeMappingOption<TYPE_MAPPING extends TypeMapping> {
72-
/**
73-
* Maps bettwen RESP types to JavaScript types
74-
*/
75-
typeMapping?: TYPE_MAPPING;
76-
}
77-
7880
type WithCommands<
7981
RESP extends RespVersions,
8082
TYPE_MAPPING extends TypeMapping
@@ -134,7 +136,7 @@ export type RedisClientType<
134136
RedisClientExtensions<M, F, S, RESP, TYPE_MAPPING>
135137
);
136138

137-
type ProxyClient = RedisClient<any, any, any, any, any> & { commandOptions?: CommandOptions };
139+
type ProxyClient = RedisClient<any, any, any, any, any>;
138140

139141
type NamespaceProxyClient = { self: ProxyClient };
140142

@@ -153,7 +155,7 @@ export default class RedisClient<
153155
const transformReply = getTransformReply(command, resp);
154156
return async function (this: ProxyClient, ...args: Array<unknown>) {
155157
const redisArgs = command.transformArguments(...args),
156-
reply = await this.sendCommand(redisArgs, this.commandOptions);
158+
reply = await this.sendCommand(redisArgs, this._commandOptions);
157159
return transformReply ?
158160
transformReply(reply, redisArgs.preserve) :
159161
reply;
@@ -164,7 +166,7 @@ export default class RedisClient<
164166
const transformReply = getTransformReply(command, resp);
165167
return async function (this: NamespaceProxyClient, ...args: Array<unknown>) {
166168
const redisArgs = command.transformArguments(...args),
167-
reply = await this.self.sendCommand(redisArgs, this.self.commandOptions);
169+
reply = await this.self.sendCommand(redisArgs, this.self._commandOptions);
168170
return transformReply ?
169171
transformReply(reply, redisArgs.preserve) :
170172
reply;
@@ -178,7 +180,7 @@ export default class RedisClient<
178180
const fnArgs = fn.transformArguments(...args),
179181
reply = await this.self.sendCommand(
180182
prefix.concat(fnArgs),
181-
this.self.commandOptions
183+
this.self._commandOptions
182184
);
183185
return transformReply ?
184186
transformReply(reply, fnArgs.preserve) :
@@ -192,12 +194,12 @@ export default class RedisClient<
192194
return async function (this: ProxyClient, ...args: Array<unknown>) {
193195
const scriptArgs = script.transformArguments(...args),
194196
redisArgs = prefix.concat(scriptArgs),
195-
reply = await this.sendCommand(redisArgs, this.commandOptions).catch((err: unknown) => {
197+
reply = await this.sendCommand(redisArgs, this._commandOptions).catch((err: unknown) => {
196198
if (!(err as Error)?.message?.startsWith?.('NOSCRIPT')) throw err;
197199

198200
redisArgs[0] = 'EVAL';
199201
redisArgs[1] = script.SCRIPT;
200-
return this.sendCommand(redisArgs, this.commandOptions);
202+
return this.sendCommand(redisArgs, this._commandOptions);
201203
});
202204
return transformReply ?
203205
transformReply(reply, scriptArgs.preserve) :
@@ -211,7 +213,7 @@ export default class RedisClient<
211213
S extends RedisScripts = {},
212214
RESP extends RespVersions = 2,
213215
TYPE_MAPPING extends TypeMapping = {}
214-
>(config?: CommanderConfig<M, F, S, RESP> & TypeMappingOption<TYPE_MAPPING>) {
216+
>(config?: ClientCommander<M, F, S, RESP, TYPE_MAPPING>) {
215217
const Client = attachConfig({
216218
BaseClass: RedisClient,
217219
commands: COMMANDS,
@@ -282,10 +284,11 @@ export default class RedisClient<
282284

283285
self = this;
284286

285-
private readonly _options?: RedisClientOptions<M, F, S, RESP>;
287+
private readonly _options?: RedisClientOptions<M, F, S, RESP, TYPE_MAPPING>;
286288
private readonly _socket: RedisSocket;
287289
private readonly _queue: RedisCommandsQueue;
288290
private _selectedDB = 0;
291+
private _commandOptions?: CommandOptions<TYPE_MAPPING>;
289292

290293
get options(): RedisClientOptions<M, F, S, RESP> | undefined {
291294
return this._options;
@@ -303,14 +306,14 @@ export default class RedisClient<
303306
return this._queue.isPubSubActive;
304307
}
305308

306-
constructor(options?: RedisClientOptions<M, F, S, RESP>) {
309+
constructor(options?: RedisClientOptions<M, F, S, RESP, TYPE_MAPPING>) {
307310
super();
308311
this._options = this._initiateOptions(options);
309312
this._queue = this._initiateQueue();
310313
this._socket = this._initiateSocket();
311314
}
312315

313-
private _initiateOptions(options?: RedisClientOptions<M, F, S, RESP>): RedisClientOptions<M, F, S, RESP> | undefined {
316+
private _initiateOptions(options?: RedisClientOptions<M, F, S, RESP, TYPE_MAPPING>): RedisClientOptions<M, F, S, RESP, TYPE_MAPPING> | undefined {
314317
if (options?.url) {
315318
const parsed = RedisClient.parseURL(options.url);
316319
if (options.socket) {
@@ -324,10 +327,8 @@ export default class RedisClient<
324327
this._selectedDB = options.database;
325328
}
326329

327-
if (options?.typeMapping) {
328-
(this as unknown as ProxyClient).commandOptions = {
329-
typeMapping: options.typeMapping
330-
};
330+
if (options?.commandOptions) {
331+
this._commandOptions = options.commandOptions;
331332
}
332333

333334
return options;
@@ -462,15 +463,18 @@ export default class RedisClient<
462463
}, this._options.pingInterval);
463464
}
464465

465-
withCommandOptions<T extends CommandOptions>(options: T) {
466+
withCommandOptions<
467+
OPTIONS extends CommandOptions<TYPE_MAPPING>,
468+
TYPE_MAPPING extends TypeMapping
469+
>(options: OPTIONS) {
466470
const proxy = Object.create(this.self);
467-
proxy.commandOptions = options;
471+
proxy._commandOptions = options;
468472
return proxy as RedisClientType<
469473
M,
470474
F,
471475
S,
472476
RESP,
473-
T['typeMapping'] extends TypeMapping ? T['typeMapping'] : {}
477+
TYPE_MAPPING extends TypeMapping ? TYPE_MAPPING : {}
474478
>;
475479
}
476480

@@ -482,8 +486,8 @@ export default class RedisClient<
482486
value: V
483487
) {
484488
const proxy = Object.create(this.self);
485-
proxy.commandOptions = Object.create((this as unknown as ProxyClient).commandOptions ?? null);
486-
proxy.commandOptions[key] = value;
489+
proxy._commandOptions = Object.create(this._commandOptions ?? null);
490+
proxy._commandOptions[key] = value;
487491
return proxy as RedisClientType<
488492
M,
489493
F,
@@ -539,17 +543,11 @@ export default class RedisClient<
539543
_RESP extends RespVersions = RESP,
540544
_TYPE_MAPPING extends TypeMapping = TYPE_MAPPING
541545
>(overrides?: Partial<RedisClientOptions<_M, _F, _S, _RESP, _TYPE_MAPPING>>) {
542-
const client = new (Object.getPrototypeOf(this).constructor)({
546+
return new (Object.getPrototypeOf(this).constructor)({
543547
...this._options,
548+
commandOptions: this._commandOptions,
544549
...overrides
545550
}) as RedisClientType<_M, _F, _S, _RESP, _TYPE_MAPPING>;
546-
547-
const { commandOptions } = this as ProxyClient;
548-
if (commandOptions) {
549-
return client.withCommandOptions(commandOptions);
550-
}
551-
552-
return client;
553551
}
554552

555553
connect() {
@@ -732,7 +730,7 @@ export default class RedisClient<
732730

733731
const promise = Promise.all(
734732
commands.map(({ args }) => this._queue.addCommand(args, {
735-
typeMapping: (this as ProxyClient).commandOptions?.typeMapping
733+
typeMapping: this._commandOptions?.typeMapping
736734
}))
737735
);
738736
this._scheduleWrite();
@@ -750,7 +748,7 @@ export default class RedisClient<
750748
return Promise.reject(new ClientClosedError());
751749
}
752750

753-
const typeMapping = (this as ProxyClient).commandOptions?.typeMapping,
751+
const typeMapping = this._commandOptions?.typeMapping,
754752
chainId = Symbol('MULTI Chain'),
755753
promises = [
756754
this._queue.addCommand(['MULTI'], { chainId }),

0 commit comments

Comments
 (0)