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'; type RedisClientMultiCommandSignature = (...args: Parameters) => RedisClientMultiCommandType; type WithCommands = { [P in keyof typeof COMMANDS]: RedisClientMultiCommandSignature<(typeof COMMANDS)[P], M, S> }; type WithModules = { [P in keyof M]: { [C in keyof M[P]]: RedisClientMultiCommandSignature; }; }; type WithScripts = { [P in keyof S]: RedisClientMultiCommandSignature }; export type RedisClientMultiCommandType = RedisClientMultiCommand & WithCommands & WithModules & WithScripts; export type RedisClientMultiExecutor = (queue: Array, chainId?: symbol) => Promise>; export default class RedisClientMultiCommand { readonly #multi = new RedisMultiCommand(); readonly #executor: RedisClientMultiExecutor; static extend( plugins?: RedisPlugins ): new (...args: ConstructorParameters) => RedisClientMultiCommandType { return extendWithModulesAndScripts({ BaseClass: RedisClientMultiCommand, modules: plugins?.modules, modulesCommandsExecutor: RedisClientMultiCommand.prototype.commandsExecutor, scripts: plugins?.scripts, scriptsExecutor: RedisClientMultiCommand.prototype.scriptsExecutor }); } readonly v4: Record = {}; constructor(executor: RedisClientMultiExecutor, legacyMode = false) { this.#executor = executor; if (legacyMode) { this.#legacyMode(); } } #legacyMode(): void { this.v4.addCommand = this.addCommand.bind(this); (this as any).addCommand = (...args: Array>): this => { this.#multi.addCommand(args.flat()); return this; }; this.v4.exec = this.exec.bind(this); (this as any).exec = (callback?: (err: Error | null, replies?: Array) => unknown): void => { this.v4.exec() .then((reply: Array) => { if (!callback) return; callback(null, reply); }) .catch((err: Error) => { if (!callback) { // this.emit('error', err); return; } callback(err); }); }; for (const name of Object.keys(COMMANDS)) { this.#defineLegacyCommand(name); } } #defineLegacyCommand(name: string): void { (this as any).v4[name] = (this as any)[name].bind(this.v4); (this as any)[name] = (...args: Array): void => (this as any).addCommand(name, args); } commandsExecutor(command: RedisCommand, args: Array): this { return this.addCommand( command.transformArguments(...args), command.transformReply ); } addCommand(args: RedisCommandArguments, transformReply?: RedisCommand['transformReply']): this { this.#multi.addCommand(args, transformReply); return this; } scriptsExecutor(script: RedisScript, args: Array): this { this.#multi.addScript(script, args); return this; } async exec(execAsPipeline = false): Promise> { if (execAsPipeline) { return this.execAsPipeline(); } const commands = this.#multi.exec(); if (!commands) return []; return this.#multi.handleExecReplies( await this.#executor(commands, RedisMultiCommand.generateChainId()) ); } EXEC = this.exec; async execAsPipeline(): Promise> { if (!this.#multi.queue.length) return []; return this.#multi.transformReplies( await this.#executor(this.#multi.queue) ); } } extendWithCommands({ BaseClass: RedisClientMultiCommand, commands: COMMANDS, executor: RedisClientMultiCommand.prototype.commandsExecutor });