diff --git a/packages/client/lib/client/index.spec.ts b/packages/client/lib/client/index.spec.ts index 51dded18b1..3f0bca45e2 100644 --- a/packages/client/lib/client/index.spec.ts +++ b/packages/client/lib/client/index.spec.ts @@ -1,6 +1,6 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL, waitTillBeenCalled } from '../test-utils'; -import RedisClient, { RedisClientType } from '.'; +import RedisClient, { ClientLegacyCommandArguments, RedisClientType } from '.'; import { RedisClientMultiCommandType } from './multi-command'; import { RedisCommandArguments, RedisCommandRawReply, RedisModules, RedisScripts } from '../commands'; import { AbortError, ClientClosedError, ConnectionTimeoutError, DisconnectsClientError, SocketClosedUnexpectedlyError, WatchError } from '../errors'; @@ -170,7 +170,7 @@ describe('Client', () => { } }); - function setAsync(client: RedisClientType, ...args: Array): Promise { + function setAsync(client: RedisClientType, ...args: ClientLegacyCommandArguments): Promise { return new Promise((resolve, reject) => { (client as any).set(...args, (err: Error | undefined, reply: RedisCommandRawReply) => { if (err) return reject(err); @@ -204,10 +204,10 @@ describe('Client', () => { } }); - testUtils.testWithClient('client.{command} should accept mix of strings and array of strings', async client => { + testUtils.testWithClient('client.{command} should accept mix of arrays and arguments', async client => { assert.equal( - await setAsync(client, ['a'], 'b', ['XX']), - null + await setAsync(client, ['a'], 'b', ['EX', 1]), + 'OK' ); }, { ...GLOBAL.SERVERS.OPEN, diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index 40499cbf7c..dd1bf83334 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -9,7 +9,7 @@ import { CommandOptions, commandOptions, isCommandOptions } from '../command-opt import { ScanOptions, ZMember } from '../commands/generic-transformers'; import { ScanCommandOptions } from '../commands/SCAN'; import { HScanTuple } from '../commands/HSCAN'; -import { extendWithCommands, extendWithModulesAndScripts, transformCommandArguments, transformCommandReply } from '../commander'; +import { extendWithCommands, extendWithModulesAndScripts, LegacyCommandArguments, transformCommandArguments, transformCommandReply, transformLegacyCommandArguments } from '../commander'; import { Pool, Options as PoolOptions, createPool } from 'generic-pool'; import { ClientClosedError, DisconnectsClientError } from '../errors'; import { URL } from 'url'; @@ -55,6 +55,7 @@ export interface ClientCommandOptions extends QueueCommandOptions { type ClientLegacyCallback = (err: Error | null, reply?: RedisCommandRawReply) => void; +export type ClientLegacyCommandArguments = LegacyCommandArguments | [...LegacyCommandArguments, ClientLegacyCallback]; export default class RedisClient extends EventEmitter { static commandOptions(options: ClientCommandOptions): CommandOptions { return commandOptions(options); @@ -246,12 +247,13 @@ export default class RedisClient if (!this.#options?.legacyMode) return; (this as any).#v4.sendCommand = this.#sendCommand.bind(this); - (this as any).sendCommand = (...args: Array): void => { - const callback = typeof args[args.length - 1] === 'function' ? - args[args.length - 1] as ClientLegacyCallback : - undefined, - actualArgs = !callback ? args : args.slice(0, -1); - this.#sendCommand(actualArgs.flat() as Array) + (this as any).sendCommand = (...args: ClientLegacyCommandArguments): void => { + let callback: ClientLegacyCallback; + if (typeof args[args.length - 1] === 'function') { + callback = args.pop() as ClientLegacyCallback; + } + + this.#sendCommand(transformLegacyCommandArguments(args as LegacyCommandArguments)) .then((reply: RedisCommandRawReply) => { if (!callback) return; diff --git a/packages/client/lib/client/multi-command.ts b/packages/client/lib/client/multi-command.ts index e4b2c165c8..9c19d3d068 100644 --- a/packages/client/lib/client/multi-command.ts +++ b/packages/client/lib/client/multi-command.ts @@ -1,7 +1,7 @@ import COMMANDS from './commands'; import { RedisCommand, RedisCommandArguments, RedisCommandRawReply, RedisModules, RedisPlugins, RedisScript, RedisScripts } from '../commands'; import RedisMultiCommand, { RedisMultiQueuedCommand } from '../multi-command'; -import { extendWithCommands, extendWithModulesAndScripts } from '../commander'; +import { extendWithCommands, extendWithModulesAndScripts, LegacyCommandArguments, transformLegacyCommandArguments } from '../commander'; type RedisClientMultiCommandSignature = (...args: Parameters) => RedisClientMultiCommandType; @@ -52,8 +52,8 @@ export default class RedisClientMultiCommand { #legacyMode(): void { this.v4.addCommand = this.addCommand.bind(this); - (this as any).addCommand = (...args: Array>): this => { - this.#multi.addCommand(args.flat()); + (this as any).addCommand = (...args: LegacyCommandArguments): this => { + this.#multi.addCommand(transformLegacyCommandArguments(args)); return this; }; this.v4.exec = this.exec.bind(this); diff --git a/packages/client/lib/commander.ts b/packages/client/lib/commander.ts index 5871c2f235..50d416f5b5 100644 --- a/packages/client/lib/commander.ts +++ b/packages/client/lib/commander.ts @@ -113,3 +113,18 @@ export function transformCommandReply( return command.transformReply(rawReply, preserved); } + +export type LegacyCommandArguments = Array; + +export function transformLegacyCommandArguments(args: LegacyCommandArguments, flat: RedisCommandArguments = []): RedisCommandArguments { + for (const arg of args) { + if (Array.isArray(arg)) { + transformLegacyCommandArguments(arg, flat); + continue; + } + + flat.push(typeof arg === 'number' ? arg.toString() : arg); + } + + return flat; +}