You've already forked node-redis
mirror of
https://github.com/redis/node-redis.git
synced 2025-08-03 04:01:40 +03:00
164 lines
4.6 KiB
TypeScript
164 lines
4.6 KiB
TypeScript
|
|
import { ClientCommandOptions } from './client';
|
|
import { CommandOptions, isCommandOptions } from './command-options';
|
|
import { RedisCommand, RedisCommandArgument, RedisCommandArguments, RedisCommandReply, RedisFunction, RedisFunctions, RedisModules, RedisScript, RedisScripts } from './commands';
|
|
|
|
type Instantiable<T = any> = new (...args: Array<any>) => T;
|
|
|
|
type CommandsExecutor<C extends RedisCommand = RedisCommand> =
|
|
(command: C, args: Array<unknown>, name: string) => unknown;
|
|
|
|
interface AttachCommandsConfig<C extends RedisCommand> {
|
|
BaseClass: Instantiable;
|
|
commands: Record<string, C>;
|
|
executor: CommandsExecutor<C>;
|
|
}
|
|
|
|
export function attachCommands<C extends RedisCommand>({
|
|
BaseClass,
|
|
commands,
|
|
executor
|
|
}: AttachCommandsConfig<C>): void {
|
|
for (const [name, command] of Object.entries(commands)) {
|
|
BaseClass.prototype[name] = function (...args: Array<unknown>): unknown {
|
|
return executor.call(this, command, args, name);
|
|
};
|
|
}
|
|
}
|
|
|
|
interface AttachExtensionsConfig<T extends Instantiable = Instantiable> {
|
|
BaseClass: T;
|
|
modulesExecutor: CommandsExecutor;
|
|
modules?: RedisModules;
|
|
functionsExecutor: CommandsExecutor<RedisFunction>;
|
|
functions?: RedisFunctions;
|
|
scriptsExecutor: CommandsExecutor<RedisScript>;
|
|
scripts?: RedisScripts;
|
|
}
|
|
|
|
export function attachExtensions(config: AttachExtensionsConfig): any {
|
|
let Commander;
|
|
|
|
if (config.modules) {
|
|
Commander = attachWithNamespaces({
|
|
BaseClass: config.BaseClass,
|
|
namespaces: config.modules,
|
|
executor: config.modulesExecutor
|
|
});
|
|
}
|
|
|
|
if (config.functions) {
|
|
Commander = attachWithNamespaces({
|
|
BaseClass: Commander ?? config.BaseClass,
|
|
namespaces: config.functions,
|
|
executor: config.functionsExecutor
|
|
});
|
|
}
|
|
|
|
if (config.scripts) {
|
|
Commander ??= class extends config.BaseClass {};
|
|
attachCommands({
|
|
BaseClass: Commander,
|
|
commands: config.scripts,
|
|
executor: config.scriptsExecutor
|
|
});
|
|
}
|
|
|
|
return Commander ?? config.BaseClass;
|
|
}
|
|
|
|
interface AttachWithNamespacesConfig<C extends RedisCommand> {
|
|
BaseClass: Instantiable;
|
|
namespaces: Record<string, Record<string, C>>;
|
|
executor: CommandsExecutor<C>;
|
|
}
|
|
|
|
function attachWithNamespaces<C extends RedisCommand>({
|
|
BaseClass,
|
|
namespaces,
|
|
executor
|
|
}: AttachWithNamespacesConfig<C>): any {
|
|
const Commander = class extends BaseClass {
|
|
constructor(...args: Array<any>) {
|
|
super(...args);
|
|
|
|
for (const namespace of Object.keys(namespaces)) {
|
|
this[namespace] = Object.create(this[namespace], {
|
|
self: {
|
|
value: this
|
|
}
|
|
});
|
|
}
|
|
}
|
|
};
|
|
|
|
for (const [namespace, commands] of Object.entries(namespaces)) {
|
|
Commander.prototype[namespace] = {};
|
|
for (const [name, command] of Object.entries(commands)) {
|
|
Commander.prototype[namespace][name] = function (...args: Array<unknown>): unknown {
|
|
return executor.call(this.self, command, args, name);
|
|
};
|
|
}
|
|
}
|
|
|
|
return Commander;
|
|
}
|
|
|
|
export function transformCommandArguments<T = ClientCommandOptions>(
|
|
command: RedisCommand,
|
|
args: Array<unknown>
|
|
): {
|
|
args: RedisCommandArguments;
|
|
options: CommandOptions<T> | undefined;
|
|
} {
|
|
let options;
|
|
if (isCommandOptions<T>(args[0])) {
|
|
options = args[0];
|
|
args = args.slice(1);
|
|
}
|
|
|
|
return {
|
|
args: command.transformArguments(...args),
|
|
options
|
|
};
|
|
}
|
|
|
|
export function transformLegacyCommandArguments(args: Array<any>): Array<any> {
|
|
return args.flat().map(arg => {
|
|
return typeof arg === 'number' || arg instanceof Date ?
|
|
arg.toString() :
|
|
arg;
|
|
});
|
|
}
|
|
|
|
export function transformCommandReply<C extends RedisCommand>(
|
|
command: C,
|
|
rawReply: unknown,
|
|
preserved: unknown
|
|
): RedisCommandReply<C> {
|
|
if (!command.transformReply) {
|
|
return rawReply as RedisCommandReply<C>;
|
|
}
|
|
|
|
return command.transformReply(rawReply, preserved);
|
|
}
|
|
|
|
export function fCallArguments(
|
|
name: RedisCommandArgument,
|
|
fn: RedisFunction,
|
|
args: RedisCommandArguments
|
|
): RedisCommandArguments {
|
|
const actualArgs: RedisCommandArguments = [
|
|
fn.IS_READ_ONLY ? 'FCALL_RO' : 'FCALL',
|
|
name
|
|
];
|
|
|
|
if (fn.NUMBER_OF_KEYS !== undefined) {
|
|
actualArgs.push(fn.NUMBER_OF_KEYS.toString());
|
|
}
|
|
|
|
actualArgs.push(...args);
|
|
|
|
return actualArgs;
|
|
}
|