You've already forked node-redis
mirror of
https://github.com/redis/node-redis.git
synced 2025-08-17 19:41:06 +03:00
Add support for lua scripts in client & muilti, fix client socket initiator, implement simple cluster nodes discovery strategy
This commit is contained in:
5
TODO.md
5
TODO.md
@@ -29,9 +29,8 @@
|
|||||||
* optionally filtered master(RW) & optionally filtered slaves(R) (?)
|
* optionally filtered master(RW) & optionally filtered slaves(R) (?)
|
||||||
|
|
||||||
## Lua Scripts
|
## Lua Scripts
|
||||||
* Create `RedisLuaScript` class
|
* ~~In `RedisClient` (with TypeScript mapping)~~
|
||||||
* In `RedisClient` (with TypeScript mapping)
|
* ~~In `RedisMultiCommand` (with TypeScript mapping)~~
|
||||||
* In `RedisMultiCommand` (with TypeScript mapping)
|
|
||||||
* In `RedisCluster` (with TypeScript mapping)
|
* In `RedisCluster` (with TypeScript mapping)
|
||||||
|
|
||||||
## Multi
|
## Multi
|
||||||
|
@@ -3,6 +3,7 @@ import { once } from 'events';
|
|||||||
import { itWithClient, TEST_REDIS_SERVERS, TestRedisServers } from './test-utils';
|
import { itWithClient, TEST_REDIS_SERVERS, TestRedisServers } from './test-utils';
|
||||||
import RedisClient from './client';
|
import RedisClient from './client';
|
||||||
import { AbortError } from './errors';
|
import { AbortError } from './errors';
|
||||||
|
import { defineScript } from './lua-script';
|
||||||
|
|
||||||
describe('Client', () => {
|
describe('Client', () => {
|
||||||
describe('authentication', () => {
|
describe('authentication', () => {
|
||||||
@@ -142,6 +143,64 @@ describe('Client', () => {
|
|||||||
.exec()
|
.exec()
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('with script', async () => {
|
||||||
|
const client = RedisClient.create({
|
||||||
|
scripts: {
|
||||||
|
add: defineScript({
|
||||||
|
NUMBER_OF_KEYS: 0,
|
||||||
|
SCRIPT: 'return ARGV[1] + 1;',
|
||||||
|
transformArguments(number: number): Array<string> {
|
||||||
|
return [number.toString()];
|
||||||
|
},
|
||||||
|
transformReply(reply: number): number {
|
||||||
|
return reply;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await client.connect();
|
||||||
|
|
||||||
|
try {
|
||||||
|
assert.deepEqual(
|
||||||
|
await client.multi()
|
||||||
|
.add(1)
|
||||||
|
.exec(),
|
||||||
|
[2]
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
await client.disconnect();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('scripts', async () => {
|
||||||
|
const client = RedisClient.create({
|
||||||
|
scripts: {
|
||||||
|
add: defineScript({
|
||||||
|
NUMBER_OF_KEYS: 0,
|
||||||
|
SCRIPT: 'return ARGV[1] + 1;',
|
||||||
|
transformArguments(number: number): Array<string> {
|
||||||
|
return [number.toString()];
|
||||||
|
},
|
||||||
|
transformReply(reply: number): number {
|
||||||
|
return reply;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await client.connect();
|
||||||
|
|
||||||
|
try {
|
||||||
|
assert.equal(
|
||||||
|
await client.add(1),
|
||||||
|
2
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
await client.disconnect();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
itWithClient(TestRedisServers.OPEN, 'should reconnect after DEBUG RESTART', async client => {
|
itWithClient(TestRedisServers.OPEN, 'should reconnect after DEBUG RESTART', async client => {
|
||||||
@@ -160,8 +219,10 @@ describe('Client', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await client.select(1);
|
await client.select(1);
|
||||||
await client.set('key', 'value');
|
|
||||||
await assert.rejects(client.sendCommand(['DEBUG', 'RESTART']));
|
await assert.rejects(client.sendCommand(['DEBUG', 'RESTART']));
|
||||||
// assert.equal(await client.get('key'), 'value');
|
assert.equal(
|
||||||
|
(await client.clientInfo()).db,
|
||||||
|
1
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
@@ -1,14 +1,16 @@
|
|||||||
import RedisSocket, { RedisSocketOptions } from './socket';
|
import RedisSocket, { RedisSocketOptions } from './socket';
|
||||||
import RedisCommandsQueue, { QueueCommandOptions } from './commands-queue';
|
import RedisCommandsQueue, { QueueCommandOptions } from './commands-queue';
|
||||||
import COMMANDS from './commands/client';
|
import COMMANDS from './commands/client';
|
||||||
import { RedisCommand, RedisModules, RedisModule, RedisReply } from './commands';
|
import { RedisCommand, RedisModules, RedisReply } from './commands';
|
||||||
import RedisMultiCommand, { MultiQueuedCommand, RedisMultiCommandType } from './multi-command';
|
import RedisMultiCommand, { MultiQueuedCommand, RedisMultiCommandType } from './multi-command';
|
||||||
import EventEmitter from 'events';
|
import EventEmitter from 'events';
|
||||||
import { CommandOptions, commandOptions, isCommandOptions } from './command-options';
|
import { CommandOptions, commandOptions, isCommandOptions } from './command-options';
|
||||||
|
import { RedisLuaScript, RedisLuaScripts } from './lua-script';
|
||||||
|
|
||||||
export interface RedisClientOptions<M = RedisModules> {
|
export interface RedisClientOptions<M = RedisModules, S = RedisLuaScripts> {
|
||||||
socket?: RedisSocketOptions;
|
socket?: RedisSocketOptions;
|
||||||
modules?: M;
|
modules?: M;
|
||||||
|
scripts?: S;
|
||||||
commandsQueueMaxLength?: number;
|
commandsQueueMaxLength?: number;
|
||||||
readOnly?: boolean;
|
readOnly?: boolean;
|
||||||
callbackify?: boolean;
|
callbackify?: boolean;
|
||||||
@@ -21,17 +23,21 @@ type WithCommands = {
|
|||||||
[P in keyof typeof COMMANDS]: RedisCommandSignature<(typeof COMMANDS)[P]>;
|
[P in keyof typeof COMMANDS]: RedisCommandSignature<(typeof COMMANDS)[P]>;
|
||||||
};
|
};
|
||||||
|
|
||||||
type WithModules<M extends Array<RedisModule>> = {
|
type WithModules<M extends RedisModules> = {
|
||||||
[P in keyof M[number]]: RedisCommandSignature<M[number][P]>;
|
[P in keyof M[number]]: RedisCommandSignature<M[number][P]>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type RedisClientType<M extends RedisModules> = WithCommands & WithModules<M> & RedisClient<M>;
|
type WithScripts<S extends RedisLuaScripts> = {
|
||||||
|
[P in keyof S]: RedisCommandSignature<S[P]>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type RedisClientType<M extends RedisModules, S extends RedisLuaScripts> = WithCommands & WithModules<M> & WithScripts<S> & RedisClient<M, S>;
|
||||||
|
|
||||||
export interface ClientCommandOptions extends QueueCommandOptions {
|
export interface ClientCommandOptions extends QueueCommandOptions {
|
||||||
duplicateConnection?: boolean;
|
duplicateConnection?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class RedisClient<M extends RedisModules = RedisModules> extends EventEmitter {
|
export default class RedisClient<M extends RedisModules = RedisModules, S extends RedisLuaScripts = RedisLuaScripts> extends EventEmitter {
|
||||||
static defineCommand(on: any, name: string, command: RedisCommand): void {
|
static defineCommand(on: any, name: string, command: RedisCommand): void {
|
||||||
on[name] = async function (...args: Array<unknown>): Promise<unknown> {
|
on[name] = async function (...args: Array<unknown>): Promise<unknown> {
|
||||||
const options = isCommandOptions(args[0]) && args.shift();
|
const options = isCommandOptions(args[0]) && args.shift();
|
||||||
@@ -62,31 +68,36 @@ export default class RedisClient<M extends RedisModules = RedisModules> extends
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static create<M extends RedisModules>(options?: RedisClientOptions<M>): RedisClientType<M> {
|
static create<M extends RedisModules, S extends RedisLuaScripts>(options?: RedisClientOptions<M, S>): RedisClientType<M, S> {
|
||||||
return <any>new RedisClient<M>(options);
|
return <any>new RedisClient<M, S>(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
static commandOptions(options: ClientCommandOptions): CommandOptions<ClientCommandOptions> {
|
static commandOptions(options: ClientCommandOptions): CommandOptions<ClientCommandOptions> {
|
||||||
return commandOptions(options);
|
return commandOptions(options);
|
||||||
};
|
};
|
||||||
|
|
||||||
readonly #options?: RedisClientOptions<M>;
|
readonly #options?: RedisClientOptions<M, S>;
|
||||||
readonly #socket: RedisSocket;
|
readonly #socket: RedisSocket;
|
||||||
readonly #queue: RedisCommandsQueue;
|
readonly #queue: RedisCommandsQueue;
|
||||||
readonly #Multi: typeof RedisMultiCommand & { new(): RedisMultiCommandType<M> };
|
readonly #Multi: typeof RedisMultiCommand & { new(): RedisMultiCommandType<M, S> };
|
||||||
#selectedDB = 0;
|
#selectedDB = 0;
|
||||||
|
|
||||||
|
get options(): RedisClientOptions<M> | null | undefined {
|
||||||
|
return this.#options;
|
||||||
|
}
|
||||||
|
|
||||||
get isOpen(): boolean {
|
get isOpen(): boolean {
|
||||||
return this.#socket.isOpen;
|
return this.#socket.isOpen;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(options?: RedisClientOptions<M>) {
|
constructor(options?: RedisClientOptions<M, S>) {
|
||||||
super();
|
super();
|
||||||
this.#options = options;
|
this.#options = options;
|
||||||
this.#socket = this.#initiateSocket();
|
this.#socket = this.#initiateSocket();
|
||||||
this.#queue = this.#initiateQueue();
|
this.#queue = this.#initiateQueue();
|
||||||
this.#Multi = this.#initiateMulti();
|
this.#Multi = this.#initiateMulti();
|
||||||
this.#initiateModules();
|
this.#initiateModules();
|
||||||
|
this.#initiateScripts();
|
||||||
this.#callbackify();
|
this.#callbackify();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,15 +106,15 @@ export default class RedisClient<M extends RedisModules = RedisModules> extends
|
|||||||
const promises = [];
|
const promises = [];
|
||||||
|
|
||||||
if (this.#options?.socket?.password) {
|
if (this.#options?.socket?.password) {
|
||||||
promises.push((this as any).auth(this.#options?.socket));
|
promises.push((this as any).auth(RedisClient.commandOptions({ asap: true }), this.#options?.socket));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.#options?.readOnly) {
|
if (this.#options?.readOnly) {
|
||||||
promises.push((this as any).readOnly());
|
promises.push((this as any).readOnly(RedisClient.commandOptions({ asap: true })));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.#selectedDB !== 0) {
|
if (this.#selectedDB !== 0) {
|
||||||
promises.push((this as any).select(this.#selectedDB));
|
promises.push((this as any).select(RedisClient.commandOptions({ asap: true }), this.#selectedDB));
|
||||||
}
|
}
|
||||||
|
|
||||||
await Promise.all(promises);
|
await Promise.all(promises);
|
||||||
@@ -131,7 +142,7 @@ export default class RedisClient<M extends RedisModules = RedisModules> extends
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#initiateMulti(): typeof RedisMultiCommand & { new(): RedisMultiCommandType<M> } {
|
#initiateMulti(): typeof RedisMultiCommand & { new(): RedisMultiCommandType<M, S> } {
|
||||||
const executor = async (commands: Array<MultiQueuedCommand>): Promise<Array<RedisReply>> => {
|
const executor = async (commands: Array<MultiQueuedCommand>): Promise<Array<RedisReply>> => {
|
||||||
const promise = Promise.all(
|
const promise = Promise.all(
|
||||||
commands.map(({encodedCommand}) => {
|
commands.map(({encodedCommand}) => {
|
||||||
@@ -145,11 +156,10 @@ export default class RedisClient<M extends RedisModules = RedisModules> extends
|
|||||||
return (replies[replies.length - 1] as Array<RedisReply>);
|
return (replies[replies.length - 1] as Array<RedisReply>);
|
||||||
};
|
};
|
||||||
|
|
||||||
const modules = this.#options?.modules;
|
const options = this.#options;
|
||||||
|
|
||||||
return <any>class extends RedisMultiCommand {
|
return <any>class extends RedisMultiCommand {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(executor, modules);
|
super(executor, options?.modules, options?.scripts);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -165,6 +175,34 @@ export default class RedisClient<M extends RedisModules = RedisModules> extends
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#initiateScripts(): void {
|
||||||
|
if (!this.#options?.scripts) return;
|
||||||
|
|
||||||
|
for (const [name, script] of Object.entries(this.#options.scripts)) {
|
||||||
|
(this as any)[name] = async function (...args: Parameters<typeof script.transformArguments>): Promise<ReturnType<typeof script.transformReply>> {
|
||||||
|
const options = isCommandOptions(args[0]) && args[0];
|
||||||
|
return script.transformReply(
|
||||||
|
await this.#executeScript(script, [
|
||||||
|
script.NUMBER_OF_KEYS.toString(),
|
||||||
|
...script.transformArguments(options ? args.slice(1) : args)
|
||||||
|
])
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async #executeScript<S extends RedisLuaScript>(script: S, args: Array<string>): Promise<ReturnType<S['transformReply']>> {
|
||||||
|
try {
|
||||||
|
return await this.sendCommand(['EVALSHA', script.SHA, ...args]);
|
||||||
|
} catch (err: any) {
|
||||||
|
if (!err?.message?.startsWith?.('NOSCRIPT')) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return await this.sendCommand(['EVAL', script.SCRIPT, ...args]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#callbackify(): void {
|
#callbackify(): void {
|
||||||
if (!this.#options?.callbackify) return;
|
if (!this.#options?.callbackify) return;
|
||||||
|
|
||||||
@@ -183,7 +221,7 @@ export default class RedisClient<M extends RedisModules = RedisModules> extends
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
duplicate(): RedisClientType<M> {
|
duplicate(): RedisClientType<M, S> {
|
||||||
return RedisClient.create(this.#options);
|
return RedisClient.create(this.#options);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,8 +229,15 @@ export default class RedisClient<M extends RedisModules = RedisModules> extends
|
|||||||
await this.#socket.connect();
|
await this.#socket.connect();
|
||||||
}
|
}
|
||||||
|
|
||||||
async SELECT(db: number): Promise<void> {
|
async SELECT(db: number): Promise<void>;
|
||||||
await this.sendCommand(['SELECT', db.toString()]);
|
async SELECT(options: CommandOptions<ClientCommandOptions>, db: number): Promise<void>;
|
||||||
|
async SELECT(options?: any, db?: any): Promise<void> {
|
||||||
|
if (!isCommandOptions(options)) {
|
||||||
|
db = options;
|
||||||
|
options = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.sendCommand(['SELECT', db.toString()], options);
|
||||||
this.#selectedDB = db;
|
this.#selectedDB = db;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,7 +263,7 @@ export default class RedisClient<M extends RedisModules = RedisModules> extends
|
|||||||
return await promise;
|
return await promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
multi(): RedisMultiCommandType<M> {
|
multi(): RedisMultiCommandType<M, S> {
|
||||||
return new this.#Multi();
|
return new this.#Multi();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -33,11 +33,20 @@ export default class RedisClusterSlots {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async discover(): Promise<void> {
|
async discover(): Promise<void> {
|
||||||
// TODO
|
// TODO: shuffle?
|
||||||
throw new Error('None of the cluster node is available');
|
for (const client of this.#clientByKey.values()) {
|
||||||
|
try {
|
||||||
|
await this.#discoverNodes(client.options?.socket);
|
||||||
|
return;
|
||||||
|
} catch (err) {
|
||||||
|
// this.emit('error', err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error('None of the cluster nodes is available');
|
||||||
}
|
}
|
||||||
|
|
||||||
async #discoverNodes(socketOptions: RedisSocketOptions) {
|
async #discoverNodes(socketOptions?: RedisSocketOptions) {
|
||||||
const client = RedisClient.create({
|
const client = RedisClient.create({
|
||||||
socket: socketOptions
|
socket: socketOptions
|
||||||
});
|
});
|
||||||
|
85
lib/commands/CLIENT_INFO.ts
Normal file
85
lib/commands/CLIENT_INFO.ts
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
export function transformArguments(): Array<string> {
|
||||||
|
return ['CLIENT', 'INFO'];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ClientInfoReply {
|
||||||
|
id: number;
|
||||||
|
addr: string;
|
||||||
|
laddr: string;
|
||||||
|
fd: number;
|
||||||
|
name: string;
|
||||||
|
age: number;
|
||||||
|
idle: number;
|
||||||
|
flags: string;
|
||||||
|
db: number;
|
||||||
|
sub: number;
|
||||||
|
psub: number;
|
||||||
|
multi: number;
|
||||||
|
qbuf: number;
|
||||||
|
qbufFree: number;
|
||||||
|
argvMem: number;
|
||||||
|
obl: number;
|
||||||
|
oll: number;
|
||||||
|
omem: number;
|
||||||
|
totMem: number;
|
||||||
|
events: string;
|
||||||
|
cmd: string;
|
||||||
|
user: string;
|
||||||
|
redir: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const REGEX = /=([^\s]*)/g;
|
||||||
|
|
||||||
|
export function transformReply(reply: string): ClientInfoReply {
|
||||||
|
const [
|
||||||
|
[, id],
|
||||||
|
[, addr],
|
||||||
|
[, laddr],
|
||||||
|
[, fd],
|
||||||
|
[, name],
|
||||||
|
[, age],
|
||||||
|
[, idle],
|
||||||
|
[, flags],
|
||||||
|
[, db],
|
||||||
|
[, sub],
|
||||||
|
[, psub],
|
||||||
|
[, multi],
|
||||||
|
[, qbuf],
|
||||||
|
[, qbufFree],
|
||||||
|
[, argvMem],
|
||||||
|
[, obl],
|
||||||
|
[, oll],
|
||||||
|
[, omem],
|
||||||
|
[, totMem],
|
||||||
|
[, events],
|
||||||
|
[, cmd],
|
||||||
|
[, user],
|
||||||
|
[, redir]
|
||||||
|
] = [...reply.matchAll(REGEX)];
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: Number(id),
|
||||||
|
addr,
|
||||||
|
laddr,
|
||||||
|
fd: Number(fd),
|
||||||
|
name,
|
||||||
|
age: Number(age),
|
||||||
|
idle: Number(idle),
|
||||||
|
flags,
|
||||||
|
db: Number(db),
|
||||||
|
sub: Number(sub),
|
||||||
|
psub: Number(psub),
|
||||||
|
multi: Number(multi),
|
||||||
|
qbuf: Number(qbuf),
|
||||||
|
qbufFree: Number(qbufFree),
|
||||||
|
argvMem: Number(argvMem),
|
||||||
|
obl: Number(obl),
|
||||||
|
oll: Number(oll),
|
||||||
|
omem: Number(omem),
|
||||||
|
totMem: Number(totMem),
|
||||||
|
events,
|
||||||
|
cmd,
|
||||||
|
user,
|
||||||
|
redir: Number(redir)
|
||||||
|
};
|
||||||
|
}
|
@@ -1,6 +1,7 @@
|
|||||||
import * as APPEND from './APPEND';
|
import * as APPEND from './APPEND';
|
||||||
import * as AUTH from './AUTH';
|
import * as AUTH from './AUTH';
|
||||||
import * as BLPOP from './BLPOP';
|
import * as BLPOP from './BLPOP';
|
||||||
|
import * as CLIENT_INFO from './CLIENT_INFO';
|
||||||
import * as CLUSTER_NODES from './CLUSTER_NODES';
|
import * as CLUSTER_NODES from './CLUSTER_NODES';
|
||||||
import * as COPY from './COPY';
|
import * as COPY from './COPY';
|
||||||
import * as DECR from './DECR';
|
import * as DECR from './DECR';
|
||||||
@@ -42,6 +43,8 @@ export default {
|
|||||||
auth: AUTH,
|
auth: AUTH,
|
||||||
BLPOP,
|
BLPOP,
|
||||||
blPop: BLPOP,
|
blPop: BLPOP,
|
||||||
|
CLIENT_INFO,
|
||||||
|
clientInfo: CLIENT_INFO,
|
||||||
CLUSTER_NODES,
|
CLUSTER_NODES,
|
||||||
clusterNodes: CLUSTER_NODES,
|
clusterNodes: CLUSTER_NODES,
|
||||||
COPY,
|
COPY,
|
||||||
|
24
lib/lua-script.ts
Normal file
24
lib/lua-script.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { createHash } from 'crypto';
|
||||||
|
import { RedisCommand } from './commands';
|
||||||
|
|
||||||
|
export interface RedisLuaScriptConfig extends RedisCommand {
|
||||||
|
SCRIPT: string;
|
||||||
|
NUMBER_OF_KEYS: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SHA {
|
||||||
|
SHA: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type RedisLuaScript = RedisLuaScriptConfig & SHA;
|
||||||
|
|
||||||
|
export interface RedisLuaScripts {
|
||||||
|
[key: string]: RedisLuaScript;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function defineScript<S extends RedisLuaScriptConfig>(script: S): S & SHA {
|
||||||
|
return {
|
||||||
|
...script,
|
||||||
|
SHA: createHash('sha1').update(script.SCRIPT).digest('hex')
|
||||||
|
};
|
||||||
|
}
|
@@ -1,18 +1,23 @@
|
|||||||
import COMMANDS from './commands/client';
|
import COMMANDS from './commands/client';
|
||||||
import { RedisCommand, RedisModule, RedisModules, RedisReply } from './commands';
|
import { RedisCommand, RedisModules, RedisReply } from './commands';
|
||||||
import RedisCommandsQueue from './commands-queue';
|
import RedisCommandsQueue from './commands-queue';
|
||||||
|
import { RedisLuaScript, RedisLuaScripts } from './lua-script';
|
||||||
|
|
||||||
type RedisMultiCommandSignature<C extends RedisCommand, M extends RedisModules> = (...args: Parameters<C['transformArguments']>) => RedisMultiCommandType<M>;
|
type RedisMultiCommandSignature<C extends RedisCommand, M extends RedisModules, S extends RedisLuaScripts> = (...args: Parameters<C['transformArguments']>) => RedisMultiCommandType<M, S>;
|
||||||
|
|
||||||
type RedisMultiWithCommands<M extends RedisModules> = {
|
type WithCommands<M extends RedisModules, S extends RedisLuaScripts> = {
|
||||||
[P in keyof typeof COMMANDS]: RedisMultiCommandSignature<(typeof COMMANDS)[P], M>
|
[P in keyof typeof COMMANDS]: RedisMultiCommandSignature<(typeof COMMANDS)[P], M, S>
|
||||||
};
|
};
|
||||||
|
|
||||||
type RedisMultiWithModules<M extends Array<RedisModule>> = {
|
type WithModules<M extends RedisModules, S extends RedisLuaScripts> = {
|
||||||
[P in keyof M[number]]: RedisMultiCommandSignature<M[number][P], M>
|
[P in keyof M[number]]: RedisMultiCommandSignature<M[number][P], M, S>
|
||||||
};
|
};
|
||||||
|
|
||||||
export type RedisMultiCommandType<M extends RedisModules> = RedisMultiCommand & RedisMultiWithCommands<M> & RedisMultiWithModules<M>;
|
type WithScripts<M extends RedisModules, S extends RedisLuaScripts> = {
|
||||||
|
[P in keyof S]: RedisMultiCommandSignature<S[P], M, S>
|
||||||
|
};
|
||||||
|
|
||||||
|
export type RedisMultiCommandType<M extends RedisModules, S extends RedisLuaScripts> = RedisMultiCommand & WithCommands<M, S> & WithModules<M, S> & WithScripts<M, S>;
|
||||||
|
|
||||||
export interface MultiQueuedCommand {
|
export interface MultiQueuedCommand {
|
||||||
encodedCommand: string;
|
encodedCommand: string;
|
||||||
@@ -21,27 +26,57 @@ export interface MultiQueuedCommand {
|
|||||||
|
|
||||||
export type RedisMultiExecutor = (queue: Array<MultiQueuedCommand>, chainId: Symbol) => Promise<Array<RedisReply>>;
|
export type RedisMultiExecutor = (queue: Array<MultiQueuedCommand>, chainId: Symbol) => Promise<Array<RedisReply>>;
|
||||||
|
|
||||||
export default class RedisMultiCommand {
|
export default class RedisMultiCommand<M extends RedisModules = RedisModules, S extends RedisLuaScripts = RedisLuaScripts> {
|
||||||
static defineCommand(on: any, name: string, command: RedisCommand) {
|
static defineCommand(on: any, name: string, command: RedisCommand): void {
|
||||||
on[name] = function (...args: Array<unknown>) {
|
on[name] = function (...args: Parameters<typeof command.transformArguments>) {
|
||||||
return this.addCommand(command.transformArguments(...args), command.transformReply);
|
return this.addCommand(command.transformArguments(...args), command.transformReply);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static create<M extends RedisModules>(executor: RedisMultiExecutor, modules?: M): RedisMultiCommandType<M> {
|
static defineLuaScript(on: any, name: string, script: RedisLuaScript): void {
|
||||||
return <any>new RedisMultiCommand(executor, modules);
|
on[name] = function (...args: Array<unknown>) {
|
||||||
|
let evalArgs;
|
||||||
|
if (this.#scriptsInUse.has(name)) {
|
||||||
|
evalArgs = [
|
||||||
|
'EVALSHA',
|
||||||
|
script.SHA
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
this.#scriptsInUse.add(name);
|
||||||
|
evalArgs = [
|
||||||
|
'EVAL',
|
||||||
|
script.SCRIPT
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.addCommand(
|
||||||
|
[
|
||||||
|
...evalArgs,
|
||||||
|
script.NUMBER_OF_KEYS,
|
||||||
|
...script.transformArguments(...args)
|
||||||
|
],
|
||||||
|
script.transformReply
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static create<M extends RedisModules, S extends RedisLuaScripts>(executor: RedisMultiExecutor, modules?: M, scripts?: S): RedisMultiCommandType<M, S> {
|
||||||
|
return <any>new RedisMultiCommand<M, S>(executor, modules, scripts);
|
||||||
}
|
}
|
||||||
|
|
||||||
readonly #executor: RedisMultiExecutor;
|
readonly #executor: RedisMultiExecutor;
|
||||||
|
|
||||||
readonly #queue: Array<MultiQueuedCommand> = [];
|
readonly #queue: Array<MultiQueuedCommand> = [];
|
||||||
|
|
||||||
constructor(executor: RedisMultiExecutor, modules?: RedisModules) {
|
readonly #scriptsInUse = new Set<string>();
|
||||||
|
|
||||||
|
constructor(executor: RedisMultiExecutor, modules?: RedisModules, scripts?: RedisLuaScripts) {
|
||||||
this.#executor = executor;
|
this.#executor = executor;
|
||||||
this.#initiateModules(modules);
|
this.#initiateModules(modules);
|
||||||
|
this.#initiateScripts(scripts);
|
||||||
}
|
}
|
||||||
|
|
||||||
#initiateModules(modules?: RedisModules) {
|
#initiateModules(modules?: RedisModules): void {
|
||||||
if (!modules) return;
|
if (!modules) return;
|
||||||
|
|
||||||
for (const m of modules) {
|
for (const m of modules) {
|
||||||
@@ -51,6 +86,14 @@ export default class RedisMultiCommand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#initiateScripts(scripts?: RedisLuaScripts): void {
|
||||||
|
if (!scripts) return;
|
||||||
|
|
||||||
|
for (const [name, script] of Object.entries(scripts)) {
|
||||||
|
RedisMultiCommand.defineLuaScript(this, name, script);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
addCommand(args: Array<string>, transformReply?: RedisCommand['transformReply']): this {
|
addCommand(args: Array<string>, transformReply?: RedisCommand['transformReply']): this {
|
||||||
this.#queue.push({
|
this.#queue.push({
|
||||||
encodedCommand: RedisCommandsQueue.encodeCommand(args),
|
encodedCommand: RedisCommandsQueue.encodeCommand(args),
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
import assert from 'assert/strict';
|
import assert from 'assert/strict';
|
||||||
import RedisClient, { RedisClientType } from './client';
|
import RedisClient, { RedisClientType } from './client';
|
||||||
import { RedisModules } from './commands';
|
import { RedisModules } from './commands';
|
||||||
|
import { RedisLuaScripts } from './lua-script';
|
||||||
import { spawn } from 'child_process';
|
import { spawn } from 'child_process';
|
||||||
import { once } from 'events';
|
import { once } from 'events';
|
||||||
import tcpPortUsed from 'tcp-port-used';
|
import tcpPortUsed from 'tcp-port-used';
|
||||||
@@ -35,7 +36,7 @@ async function spawnPasswordServer(): Promise<void> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function itWithClient(type: TestRedisServers, title: string, fn: (client: RedisClientType<RedisModules>) => Promise<void>) {
|
export function itWithClient(type: TestRedisServers, title: string, fn: (client: RedisClientType<RedisModules, RedisLuaScripts>) => Promise<void>) {
|
||||||
it(title, async () => {
|
it(title, async () => {
|
||||||
const client = RedisClient.create({
|
const client = RedisClient.create({
|
||||||
socket: TEST_REDIS_SERVERS[type]
|
socket: TEST_REDIS_SERVERS[type]
|
||||||
|
128
package-lock.json
generated
128
package-lock.json
generated
@@ -16,7 +16,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@istanbuljs/nyc-config-typescript": "^1.0.1",
|
"@istanbuljs/nyc-config-typescript": "^1.0.1",
|
||||||
"@types/mocha": "^8.2.2",
|
"@types/mocha": "^8.2.2",
|
||||||
"@types/node": "^15.3.1",
|
"@types/node": "^15.6.1",
|
||||||
"@types/tcp-port-used": "^1.0.0",
|
"@types/tcp-port-used": "^1.0.0",
|
||||||
"@types/which": "^2.0.0",
|
"@types/which": "^2.0.0",
|
||||||
"@types/yallist": "^4.0.0",
|
"@types/yallist": "^4.0.0",
|
||||||
@@ -24,8 +24,8 @@
|
|||||||
"nyc": "^15.1.0",
|
"nyc": "^15.1.0",
|
||||||
"source-map-support": "^0.5.19",
|
"source-map-support": "^0.5.19",
|
||||||
"tcp-port-used": "^1.0.2",
|
"tcp-port-used": "^1.0.2",
|
||||||
"ts-node": "^9.1.1",
|
"ts-node": "^10.0.0",
|
||||||
"typescript": "^4.3.1-rc",
|
"typescript": "^4.3.2",
|
||||||
"which": "^2.0.2"
|
"which": "^2.0.2"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -470,6 +470,30 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@tsconfig/node10": {
|
||||||
|
"version": "1.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.7.tgz",
|
||||||
|
"integrity": "sha512-aBvUmXLQbayM4w3A8TrjwrXs4DZ8iduJnuJLLRGdkWlyakCf1q6uHZJBzXoRA/huAEknG5tcUyQxN3A+In5euQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/@tsconfig/node12": {
|
||||||
|
"version": "1.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.7.tgz",
|
||||||
|
"integrity": "sha512-dgasobK/Y0wVMswcipr3k0HpevxFJLijN03A8mYfEPvWvOs14v0ZlYTR4kIgMx8g4+fTyTFv8/jLCIfRqLDJ4A==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/@tsconfig/node14": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-RKkL8eTdPv6t5EHgFKIVQgsDapugbuOptNd9OOunN/HAkzmmTnZELx1kNCK0rSdUYGmiFMM3rRQMAWiyp023LQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/@tsconfig/node16": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-FTgBI767POY/lKNDNbIzgAX6miIDBs6NTCbdlDb8TrWovHsSvaVIZDlTqym29C6UqhzwcJx4CYr+AlrMywA0cA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/@types/mocha": {
|
"node_modules/@types/mocha": {
|
||||||
"version": "8.2.2",
|
"version": "8.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.2.2.tgz",
|
||||||
@@ -480,7 +504,8 @@
|
|||||||
"version": "15.6.1",
|
"version": "15.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-15.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-15.6.1.tgz",
|
||||||
"integrity": "sha512-7EIraBEyRHEe7CH+Fm1XvgqU6uwZN8Q7jppJGcqjROMT29qhAuuOxYB1uEY5UMYQKEmA5D+5tBnhdaPXSsLONA==",
|
"integrity": "sha512-7EIraBEyRHEe7CH+Fm1XvgqU6uwZN8Q7jppJGcqjROMT29qhAuuOxYB1uEY5UMYQKEmA5D+5tBnhdaPXSsLONA==",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@types/tcp-port-used": {
|
"node_modules/@types/tcp-port-used": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
@@ -692,9 +717,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/caniuse-lite": {
|
"node_modules/caniuse-lite": {
|
||||||
"version": "1.0.30001228",
|
"version": "1.0.30001230",
|
||||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001228.tgz",
|
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001230.tgz",
|
||||||
"integrity": "sha512-QQmLOGJ3DEgokHbMSA8cj2a+geXqmnpyOFT0lhQV6P3/YOJvGDEwoedcwxEQ30gJIwIIunHIicunJ2rzK5gB2A==",
|
"integrity": "sha512-5yBd5nWCBS+jWKTcHOzXwo5xzcj4ePE/yjtkZyUV1BTUmrBaA9MRGC+e7mxnqXSA90CmCA8L3eKLaSUkt099IQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
@@ -947,9 +972,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/electron-to-chromium": {
|
"node_modules/electron-to-chromium": {
|
||||||
"version": "1.3.738",
|
"version": "1.3.740",
|
||||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.738.tgz",
|
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.740.tgz",
|
||||||
"integrity": "sha512-vCMf4gDOpEylPSLPLSwAEsz+R3ShP02Y3cAKMZvTqule3XcPp7tgc/0ESI7IS6ZeyBlGClE50N53fIOkcIVnpw==",
|
"integrity": "sha512-Mi2m55JrX2BFbNZGKYR+2ItcGnR4O5HhrvgoRRyZQlaMGQULqDhoGkLWHzJoshSzi7k1PUofxcDbNhlFrDZNhg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/emoji-regex": {
|
"node_modules/emoji-regex": {
|
||||||
@@ -2402,11 +2427,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ts-node": {
|
"node_modules/ts-node": {
|
||||||
"version": "9.1.1",
|
"version": "10.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.0.0.tgz",
|
||||||
"integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==",
|
"integrity": "sha512-ROWeOIUvfFbPZkoDis0L/55Fk+6gFQNZwwKPLinacRl6tsxstTF1DbAcLKkovwnpKMVvOMHP1TIbnwXwtLg1gg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@tsconfig/node10": "^1.0.7",
|
||||||
|
"@tsconfig/node12": "^1.0.7",
|
||||||
|
"@tsconfig/node14": "^1.0.0",
|
||||||
|
"@tsconfig/node16": "^1.0.1",
|
||||||
"arg": "^4.1.0",
|
"arg": "^4.1.0",
|
||||||
"create-require": "^1.1.0",
|
"create-require": "^1.1.0",
|
||||||
"diff": "^4.0.1",
|
"diff": "^4.0.1",
|
||||||
@@ -2416,15 +2445,27 @@
|
|||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"ts-node": "dist/bin.js",
|
"ts-node": "dist/bin.js",
|
||||||
|
"ts-node-cwd": "dist/bin-cwd.js",
|
||||||
"ts-node-script": "dist/bin-script.js",
|
"ts-node-script": "dist/bin-script.js",
|
||||||
"ts-node-transpile-only": "dist/bin-transpile.js",
|
"ts-node-transpile-only": "dist/bin-transpile.js",
|
||||||
"ts-script": "dist/bin-script-deprecated.js"
|
"ts-script": "dist/bin-script-deprecated.js"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10.0.0"
|
"node": ">=12.0.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
|
"@swc/core": ">=1.2.45",
|
||||||
|
"@swc/wasm": ">=1.2.45",
|
||||||
|
"@types/node": "*",
|
||||||
"typescript": ">=2.7"
|
"typescript": ">=2.7"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@swc/core": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@swc/wasm": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ts-node/node_modules/diff": {
|
"node_modules/ts-node/node_modules/diff": {
|
||||||
@@ -2455,10 +2496,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/typescript": {
|
"node_modules/typescript": {
|
||||||
"version": "4.3.1-rc",
|
"version": "4.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.1-rc.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.2.tgz",
|
||||||
"integrity": "sha512-L3uJ0gcntaRaKni9aV2amYB+pCDVodKe/B5+IREyvtKGsDOF7cYjchHb/B894skqkgD52ykRuWatIZMqEsHIqA==",
|
"integrity": "sha512-zZ4hShnmnoVnAHpVHWpTcxdv7dWP60S2FsydQLV8V5PbS3FifjWFFRiHSWpDJahly88PRyV5teTSLoq4eG7mKw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
"tsserver": "bin/tsserver"
|
"tsserver": "bin/tsserver"
|
||||||
@@ -3113,6 +3155,30 @@
|
|||||||
"integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==",
|
"integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"@tsconfig/node10": {
|
||||||
|
"version": "1.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.7.tgz",
|
||||||
|
"integrity": "sha512-aBvUmXLQbayM4w3A8TrjwrXs4DZ8iduJnuJLLRGdkWlyakCf1q6uHZJBzXoRA/huAEknG5tcUyQxN3A+In5euQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"@tsconfig/node12": {
|
||||||
|
"version": "1.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.7.tgz",
|
||||||
|
"integrity": "sha512-dgasobK/Y0wVMswcipr3k0HpevxFJLijN03A8mYfEPvWvOs14v0ZlYTR4kIgMx8g4+fTyTFv8/jLCIfRqLDJ4A==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"@tsconfig/node14": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-RKkL8eTdPv6t5EHgFKIVQgsDapugbuOptNd9OOunN/HAkzmmTnZELx1kNCK0rSdUYGmiFMM3rRQMAWiyp023LQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"@tsconfig/node16": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-FTgBI767POY/lKNDNbIzgAX6miIDBs6NTCbdlDb8TrWovHsSvaVIZDlTqym29C6UqhzwcJx4CYr+AlrMywA0cA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"@types/mocha": {
|
"@types/mocha": {
|
||||||
"version": "8.2.2",
|
"version": "8.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.2.2.tgz",
|
||||||
@@ -3292,9 +3358,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"caniuse-lite": {
|
"caniuse-lite": {
|
||||||
"version": "1.0.30001228",
|
"version": "1.0.30001230",
|
||||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001228.tgz",
|
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001230.tgz",
|
||||||
"integrity": "sha512-QQmLOGJ3DEgokHbMSA8cj2a+geXqmnpyOFT0lhQV6P3/YOJvGDEwoedcwxEQ30gJIwIIunHIicunJ2rzK5gB2A==",
|
"integrity": "sha512-5yBd5nWCBS+jWKTcHOzXwo5xzcj4ePE/yjtkZyUV1BTUmrBaA9MRGC+e7mxnqXSA90CmCA8L3eKLaSUkt099IQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"chalk": {
|
"chalk": {
|
||||||
@@ -3494,9 +3560,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"electron-to-chromium": {
|
"electron-to-chromium": {
|
||||||
"version": "1.3.738",
|
"version": "1.3.740",
|
||||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.738.tgz",
|
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.740.tgz",
|
||||||
"integrity": "sha512-vCMf4gDOpEylPSLPLSwAEsz+R3ShP02Y3cAKMZvTqule3XcPp7tgc/0ESI7IS6ZeyBlGClE50N53fIOkcIVnpw==",
|
"integrity": "sha512-Mi2m55JrX2BFbNZGKYR+2ItcGnR4O5HhrvgoRRyZQlaMGQULqDhoGkLWHzJoshSzi7k1PUofxcDbNhlFrDZNhg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"emoji-regex": {
|
"emoji-regex": {
|
||||||
@@ -4575,11 +4641,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ts-node": {
|
"ts-node": {
|
||||||
"version": "9.1.1",
|
"version": "10.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.0.0.tgz",
|
||||||
"integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==",
|
"integrity": "sha512-ROWeOIUvfFbPZkoDis0L/55Fk+6gFQNZwwKPLinacRl6tsxstTF1DbAcLKkovwnpKMVvOMHP1TIbnwXwtLg1gg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
|
"@tsconfig/node10": "^1.0.7",
|
||||||
|
"@tsconfig/node12": "^1.0.7",
|
||||||
|
"@tsconfig/node14": "^1.0.0",
|
||||||
|
"@tsconfig/node16": "^1.0.1",
|
||||||
"arg": "^4.1.0",
|
"arg": "^4.1.0",
|
||||||
"create-require": "^1.1.0",
|
"create-require": "^1.1.0",
|
||||||
"diff": "^4.0.1",
|
"diff": "^4.0.1",
|
||||||
@@ -4612,9 +4682,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"typescript": {
|
"typescript": {
|
||||||
"version": "4.3.1-rc",
|
"version": "4.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.1-rc.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.2.tgz",
|
||||||
"integrity": "sha512-L3uJ0gcntaRaKni9aV2amYB+pCDVodKe/B5+IREyvtKGsDOF7cYjchHb/B894skqkgD52ykRuWatIZMqEsHIqA==",
|
"integrity": "sha512-zZ4hShnmnoVnAHpVHWpTcxdv7dWP60S2FsydQLV8V5PbS3FifjWFFRiHSWpDJahly88PRyV5teTSLoq4eG7mKw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"uuid": {
|
"uuid": {
|
||||||
|
@@ -34,7 +34,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@istanbuljs/nyc-config-typescript": "^1.0.1",
|
"@istanbuljs/nyc-config-typescript": "^1.0.1",
|
||||||
"@types/mocha": "^8.2.2",
|
"@types/mocha": "^8.2.2",
|
||||||
"@types/node": "^15.3.1",
|
"@types/node": "^15.6.1",
|
||||||
"@types/tcp-port-used": "^1.0.0",
|
"@types/tcp-port-used": "^1.0.0",
|
||||||
"@types/which": "^2.0.0",
|
"@types/which": "^2.0.0",
|
||||||
"@types/yallist": "^4.0.0",
|
"@types/yallist": "^4.0.0",
|
||||||
@@ -42,8 +42,8 @@
|
|||||||
"nyc": "^15.1.0",
|
"nyc": "^15.1.0",
|
||||||
"source-map-support": "^0.5.19",
|
"source-map-support": "^0.5.19",
|
||||||
"tcp-port-used": "^1.0.2",
|
"tcp-port-used": "^1.0.2",
|
||||||
"ts-node": "^9.1.1",
|
"ts-node": "^10.0.0",
|
||||||
"typescript": "^4.3.1-rc",
|
"typescript": "^4.3.2",
|
||||||
"which": "^2.0.2"
|
"which": "^2.0.2"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
Reference in New Issue
Block a user