You've already forked node-redis
mirror of
https://github.com/redis/node-redis.git
synced 2025-08-07 13:22:56 +03:00
WIP
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
export { RedisModules, RedisFunctions, RedisScripts, RespVersions } from './lib/RESP/types';
|
export { RedisModules, RedisFunctions, RedisScripts, RespVersions } from './lib/RESP/types';
|
||||||
|
export { RESP_TYPES } from './lib/RESP/decoder';
|
||||||
export { VerbatimString } from './lib/RESP/verbatim-string';
|
export { VerbatimString } from './lib/RESP/verbatim-string';
|
||||||
export { defineScript } from './lib/lua-script';
|
export { defineScript } from './lib/lua-script';
|
||||||
// export * from './lib/errors';
|
// export * from './lib/errors';
|
||||||
|
@@ -4,7 +4,7 @@ import { SimpleError, BlobError, ErrorReply } from '../errors';
|
|||||||
import { Flags } from './types';
|
import { Flags } from './types';
|
||||||
|
|
||||||
// https://github.com/redis/redis-specifications/blob/master/protocol/RESP3.md
|
// https://github.com/redis/redis-specifications/blob/master/protocol/RESP3.md
|
||||||
export const TYPES = {
|
export const RESP_TYPES = {
|
||||||
NULL: 95, // _
|
NULL: 95, // _
|
||||||
BOOLEAN: 35, // #
|
BOOLEAN: 35, // #
|
||||||
NUMBER: 58, // :
|
NUMBER: 58, // :
|
||||||
@@ -35,7 +35,7 @@ const ASCII = {
|
|||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const PUSH_FLAGS = {
|
export const PUSH_FLAGS = {
|
||||||
[TYPES.BLOB_STRING]: Buffer
|
[RESP_TYPES.BLOB_STRING]: Buffer
|
||||||
};
|
};
|
||||||
|
|
||||||
// this was written with performance in mind, so it's not very readable... sorry :(
|
// this was written with performance in mind, so it's not very readable... sorry :(
|
||||||
@@ -98,98 +98,98 @@ export class Decoder {
|
|||||||
|
|
||||||
private _decodeTypeValue(type, chunk) {
|
private _decodeTypeValue(type, chunk) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case TYPES.NULL:
|
case RESP_TYPES.NULL:
|
||||||
this._config.onReply(this._decodeNull());
|
this._config.onReply(this._decodeNull());
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
case TYPES.BOOLEAN:
|
case RESP_TYPES.BOOLEAN:
|
||||||
return this._handleDecodedValue(
|
return this._handleDecodedValue(
|
||||||
this._config.onReply,
|
this._config.onReply,
|
||||||
this._decodeBoolean(chunk)
|
this._decodeBoolean(chunk)
|
||||||
);
|
);
|
||||||
|
|
||||||
case TYPES.NUMBER:
|
case RESP_TYPES.NUMBER:
|
||||||
return this._handleDecodedValue(
|
return this._handleDecodedValue(
|
||||||
this._config.onReply,
|
this._config.onReply,
|
||||||
this._decodeNumber(chunk)
|
this._decodeNumber(chunk)
|
||||||
);
|
);
|
||||||
|
|
||||||
case TYPES.BIG_NUMBER:
|
case RESP_TYPES.BIG_NUMBER:
|
||||||
return this._handleDecodedValue(
|
return this._handleDecodedValue(
|
||||||
this._config.onReply,
|
this._config.onReply,
|
||||||
this._decodeBigNumber(
|
this._decodeBigNumber(
|
||||||
this._config.getFlags()[TYPES.BIG_NUMBER],
|
this._config.getFlags()[RESP_TYPES.BIG_NUMBER],
|
||||||
chunk
|
chunk
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
case TYPES.DOUBLE:
|
case RESP_TYPES.DOUBLE:
|
||||||
return this._handleDecodedValue(
|
return this._handleDecodedValue(
|
||||||
this._config.onReply,
|
this._config.onReply,
|
||||||
this._decodeDouble(
|
this._decodeDouble(
|
||||||
this._config.getFlags()[TYPES.DOUBLE],
|
this._config.getFlags()[RESP_TYPES.DOUBLE],
|
||||||
chunk
|
chunk
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
case TYPES.SIMPLE_STRING:
|
case RESP_TYPES.SIMPLE_STRING:
|
||||||
return this._handleDecodedValue(
|
return this._handleDecodedValue(
|
||||||
this._config.onReply,
|
this._config.onReply,
|
||||||
this._decodeSimpleString(
|
this._decodeSimpleString(
|
||||||
this._config.getFlags()[TYPES.SIMPLE_STRING],
|
this._config.getFlags()[RESP_TYPES.SIMPLE_STRING],
|
||||||
chunk
|
chunk
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
case TYPES.BLOB_STRING:
|
case RESP_TYPES.BLOB_STRING:
|
||||||
return this._handleDecodedValue(
|
return this._handleDecodedValue(
|
||||||
this._config.onReply,
|
this._config.onReply,
|
||||||
this._decodeBlobString(
|
this._decodeBlobString(
|
||||||
this._config.getFlags()[TYPES.BLOB_STRING],
|
this._config.getFlags()[RESP_TYPES.BLOB_STRING],
|
||||||
chunk
|
chunk
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
case TYPES.VERBATIM_STRING:
|
case RESP_TYPES.VERBATIM_STRING:
|
||||||
return this._handleDecodedValue(
|
return this._handleDecodedValue(
|
||||||
this._config.onReply,
|
this._config.onReply,
|
||||||
this._decodeVerbatimString(
|
this._decodeVerbatimString(
|
||||||
this._config.getFlags()[TYPES.VERBATIM_STRING],
|
this._config.getFlags()[RESP_TYPES.VERBATIM_STRING],
|
||||||
chunk
|
chunk
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
case TYPES.SIMPLE_ERROR:
|
case RESP_TYPES.SIMPLE_ERROR:
|
||||||
return this._handleDecodedValue(
|
return this._handleDecodedValue(
|
||||||
this._config.onErrorReply,
|
this._config.onErrorReply,
|
||||||
this._decodeSimpleError(chunk)
|
this._decodeSimpleError(chunk)
|
||||||
);
|
);
|
||||||
|
|
||||||
case TYPES.BLOB_ERROR:
|
case RESP_TYPES.BLOB_ERROR:
|
||||||
return this._handleDecodedValue(
|
return this._handleDecodedValue(
|
||||||
this._config.onErrorReply,
|
this._config.onErrorReply,
|
||||||
this._decodeBlobError(chunk)
|
this._decodeBlobError(chunk)
|
||||||
);
|
);
|
||||||
|
|
||||||
case TYPES.ARRAY:
|
case RESP_TYPES.ARRAY:
|
||||||
return this._handleDecodedValue(
|
return this._handleDecodedValue(
|
||||||
this._config.onReply,
|
this._config.onReply,
|
||||||
this._decodeArray(this._config.getFlags(), chunk)
|
this._decodeArray(this._config.getFlags(), chunk)
|
||||||
);
|
);
|
||||||
|
|
||||||
case TYPES.SET:
|
case RESP_TYPES.SET:
|
||||||
return this._handleDecodedValue(
|
return this._handleDecodedValue(
|
||||||
this._config.onReply,
|
this._config.onReply,
|
||||||
this._decodeSet(this._config.getFlags(), chunk)
|
this._decodeSet(this._config.getFlags(), chunk)
|
||||||
);
|
);
|
||||||
|
|
||||||
case TYPES.MAP:
|
case RESP_TYPES.MAP:
|
||||||
return this._handleDecodedValue(
|
return this._handleDecodedValue(
|
||||||
this._config.onReply,
|
this._config.onReply,
|
||||||
this._decodeMap(this._config.getFlags(), chunk)
|
this._decodeMap(this._config.getFlags(), chunk)
|
||||||
);
|
);
|
||||||
|
|
||||||
case TYPES.PUSH:
|
case RESP_TYPES.PUSH:
|
||||||
return this._handleDecodedValue(
|
return this._handleDecodedValue(
|
||||||
this._config.onPush,
|
this._config.onPush,
|
||||||
this._decodeArray(PUSH_FLAGS, chunk)
|
this._decodeArray(PUSH_FLAGS, chunk)
|
||||||
@@ -664,43 +664,43 @@ export class Decoder {
|
|||||||
|
|
||||||
private _decodeNestedTypeValue(type, flags, chunk) {
|
private _decodeNestedTypeValue(type, flags, chunk) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case TYPES.NULL:
|
case RESP_TYPES.NULL:
|
||||||
return this._decodeNull();
|
return this._decodeNull();
|
||||||
|
|
||||||
case TYPES.BOOLEAN:
|
case RESP_TYPES.BOOLEAN:
|
||||||
return this._decodeBoolean(chunk);
|
return this._decodeBoolean(chunk);
|
||||||
|
|
||||||
case TYPES.NUMBER:
|
case RESP_TYPES.NUMBER:
|
||||||
return this._decodeNumber(chunk);
|
return this._decodeNumber(chunk);
|
||||||
|
|
||||||
case TYPES.BIG_NUMBER:
|
case RESP_TYPES.BIG_NUMBER:
|
||||||
return this._decodeBigNumber(flags[TYPES.BIG_NUMBER], chunk);
|
return this._decodeBigNumber(flags[RESP_TYPES.BIG_NUMBER], chunk);
|
||||||
|
|
||||||
case TYPES.DOUBLE:
|
case RESP_TYPES.DOUBLE:
|
||||||
return this._decodeDouble(flags[TYPES.DOUBLE], chunk);
|
return this._decodeDouble(flags[RESP_TYPES.DOUBLE], chunk);
|
||||||
|
|
||||||
case TYPES.SIMPLE_STRING:
|
case RESP_TYPES.SIMPLE_STRING:
|
||||||
return this._decodeSimpleString(flags[TYPES.SIMPLE_STRING], chunk);
|
return this._decodeSimpleString(flags[RESP_TYPES.SIMPLE_STRING], chunk);
|
||||||
|
|
||||||
case TYPES.BLOB_STRING:
|
case RESP_TYPES.BLOB_STRING:
|
||||||
return this._decodeBlobString(flags[TYPES.BLOB_STRING], chunk);
|
return this._decodeBlobString(flags[RESP_TYPES.BLOB_STRING], chunk);
|
||||||
|
|
||||||
case TYPES.VERBATIM_STRING:
|
case RESP_TYPES.VERBATIM_STRING:
|
||||||
return this._decodeVerbatimString(flags[TYPES.VERBATIM_STRING], chunk);
|
return this._decodeVerbatimString(flags[RESP_TYPES.VERBATIM_STRING], chunk);
|
||||||
|
|
||||||
case TYPES.SIMPLE_ERROR:
|
case RESP_TYPES.SIMPLE_ERROR:
|
||||||
return this._decodeSimpleError(chunk);
|
return this._decodeSimpleError(chunk);
|
||||||
|
|
||||||
case TYPES.BLOB_ERROR:
|
case RESP_TYPES.BLOB_ERROR:
|
||||||
return this._decodeBlobError(chunk);
|
return this._decodeBlobError(chunk);
|
||||||
|
|
||||||
case TYPES.ARRAY:
|
case RESP_TYPES.ARRAY:
|
||||||
return this._decodeArray(flags, chunk);
|
return this._decodeArray(flags, chunk);
|
||||||
|
|
||||||
case TYPES.SET:
|
case RESP_TYPES.SET:
|
||||||
return this._decodeSet(flags, chunk);
|
return this._decodeSet(flags, chunk);
|
||||||
|
|
||||||
case TYPES.MAP:
|
case RESP_TYPES.MAP:
|
||||||
return this._decodeMap(flags, chunk);
|
return this._decodeMap(flags, chunk);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -805,7 +805,7 @@ export class Decoder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _decodeSetItems(length, flags, chunk) {
|
private _decodeSetItems(length, flags, chunk) {
|
||||||
return flags[TYPES.SET] === Set ?
|
return flags[RESP_TYPES.SET] === Set ?
|
||||||
this._decodeSetAsSet(
|
this._decodeSetAsSet(
|
||||||
new Set(),
|
new Set(),
|
||||||
length,
|
length,
|
||||||
@@ -888,7 +888,7 @@ export class Decoder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _decodeMapItems(length, flags, chunk) {
|
private _decodeMapItems(length, flags, chunk) {
|
||||||
switch (flags[TYPES.MAP]) {
|
switch (flags[RESP_TYPES.MAP]) {
|
||||||
case Map:
|
case Map:
|
||||||
return this._decodeMapAsMap(
|
return this._decodeMapAsMap(
|
||||||
new Map(),
|
new Map(),
|
||||||
@@ -978,11 +978,11 @@ export class Decoder {
|
|||||||
private _decodeMapKeyValue(type, flags, chunk) {
|
private _decodeMapKeyValue(type, flags, chunk) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
// decode simple string map key as string (and not as buffer)
|
// decode simple string map key as string (and not as buffer)
|
||||||
case TYPES.SIMPLE_STRING:
|
case RESP_TYPES.SIMPLE_STRING:
|
||||||
return this._decodeSimpleString(String, chunk);
|
return this._decodeSimpleString(String, chunk);
|
||||||
|
|
||||||
// decode blob string map key as string (and not as buffer)
|
// decode blob string map key as string (and not as buffer)
|
||||||
case TYPES.BLOB_STRING:
|
case RESP_TYPES.BLOB_STRING:
|
||||||
return this._decodeBlobString(String, chunk);
|
return this._decodeBlobString(String, chunk);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@@ -1,13 +1,13 @@
|
|||||||
import { RedisScriptConfig, SHA1 } from '../lua-script';
|
import { RedisScriptConfig, SHA1 } from '../lua-script';
|
||||||
import { TYPES } from './decoder';
|
import { RESP_TYPES } from './decoder';
|
||||||
import { VerbatimString } from './verbatim-string';
|
import { VerbatimString } from './verbatim-string';
|
||||||
|
|
||||||
export type RespTypes = typeof TYPES;
|
export type RESP_TYPES = typeof RESP_TYPES;
|
||||||
|
|
||||||
export type RespTypesUnion = RespTypes[keyof RespTypes];
|
export type RespTypes = RESP_TYPES[keyof RESP_TYPES];
|
||||||
|
|
||||||
type RespType<
|
type RespType<
|
||||||
RESP_TYPE extends RespTypesUnion,
|
RESP_TYPE extends RespTypes,
|
||||||
DEFAULT,
|
DEFAULT,
|
||||||
TYPES = never,
|
TYPES = never,
|
||||||
FLAG_TYPES = DEFAULT | TYPES
|
FLAG_TYPES = DEFAULT | TYPES
|
||||||
@@ -19,19 +19,19 @@ type RespType<
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type NullReply = RespType<
|
export type NullReply = RespType<
|
||||||
RespTypes['NULL'],
|
RESP_TYPES['NULL'],
|
||||||
null
|
null
|
||||||
>;
|
>;
|
||||||
export type BooleanReply<
|
export type BooleanReply<
|
||||||
T extends boolean = boolean
|
T extends boolean = boolean
|
||||||
> = RespType<
|
> = RespType<
|
||||||
RespTypes['BOOLEAN'],
|
RESP_TYPES['BOOLEAN'],
|
||||||
T
|
T
|
||||||
>;
|
>;
|
||||||
export type NumberReply<
|
export type NumberReply<
|
||||||
T extends number = number
|
T extends number = number
|
||||||
> = RespType<
|
> = RespType<
|
||||||
RespTypes['NUMBER'],
|
RESP_TYPES['NUMBER'],
|
||||||
T,
|
T,
|
||||||
`${T}`,
|
`${T}`,
|
||||||
number | string
|
number | string
|
||||||
@@ -39,7 +39,7 @@ export type NumberReply<
|
|||||||
export type BigNumberReply<
|
export type BigNumberReply<
|
||||||
T extends bigint = bigint
|
T extends bigint = bigint
|
||||||
> = RespType<
|
> = RespType<
|
||||||
RespTypes['BIG_NUMBER'],
|
RESP_TYPES['BIG_NUMBER'],
|
||||||
T,
|
T,
|
||||||
number | `${T}`,
|
number | `${T}`,
|
||||||
bigint | number | string
|
bigint | number | string
|
||||||
@@ -47,7 +47,7 @@ export type BigNumberReply<
|
|||||||
export type DoubleReply<
|
export type DoubleReply<
|
||||||
T extends number = number
|
T extends number = number
|
||||||
> = RespType<
|
> = RespType<
|
||||||
RespTypes['DOUBLE'],
|
RESP_TYPES['DOUBLE'],
|
||||||
T,
|
T,
|
||||||
`${T}`,
|
`${T}`,
|
||||||
number | string
|
number | string
|
||||||
@@ -55,7 +55,7 @@ export type DoubleReply<
|
|||||||
export type SimpleStringReply<
|
export type SimpleStringReply<
|
||||||
T extends string = string
|
T extends string = string
|
||||||
> = RespType<
|
> = RespType<
|
||||||
RespTypes['SIMPLE_STRING'],
|
RESP_TYPES['SIMPLE_STRING'],
|
||||||
T,
|
T,
|
||||||
Buffer,
|
Buffer,
|
||||||
string | Buffer
|
string | Buffer
|
||||||
@@ -63,7 +63,7 @@ export type SimpleStringReply<
|
|||||||
export type BlobStringReply<
|
export type BlobStringReply<
|
||||||
T extends string = string
|
T extends string = string
|
||||||
> = RespType<
|
> = RespType<
|
||||||
RespTypes['BLOB_STRING'],
|
RESP_TYPES['BLOB_STRING'],
|
||||||
T,
|
T,
|
||||||
Buffer,
|
Buffer,
|
||||||
string | Buffer
|
string | Buffer
|
||||||
@@ -71,39 +71,39 @@ export type BlobStringReply<
|
|||||||
export type VerbatimStringReply<
|
export type VerbatimStringReply<
|
||||||
T extends string = string
|
T extends string = string
|
||||||
> = RespType<
|
> = RespType<
|
||||||
RespTypes['VERBATIM_STRING'],
|
RESP_TYPES['VERBATIM_STRING'],
|
||||||
T,
|
T,
|
||||||
Buffer | VerbatimString,
|
Buffer | VerbatimString,
|
||||||
string | Buffer | VerbatimString
|
string | Buffer | VerbatimString
|
||||||
>;
|
>;
|
||||||
export type SimpleErrorReply = RespType<
|
export type SimpleErrorReply = RespType<
|
||||||
RespTypes['SIMPLE_ERROR'],
|
RESP_TYPES['SIMPLE_ERROR'],
|
||||||
Buffer
|
Buffer
|
||||||
>;
|
>;
|
||||||
export type BlobErrorReply = RespType<
|
export type BlobErrorReply = RespType<
|
||||||
RespTypes['BLOB_ERROR'],
|
RESP_TYPES['BLOB_ERROR'],
|
||||||
Buffer
|
Buffer
|
||||||
>;
|
>;
|
||||||
export type ArrayReply<T> = RespType<
|
export type ArrayReply<T> = RespType<
|
||||||
RespTypes['ARRAY'],
|
RESP_TYPES['ARRAY'],
|
||||||
Array<T>,
|
Array<T>,
|
||||||
never,
|
never,
|
||||||
Array<any>
|
Array<any>
|
||||||
>;
|
>;
|
||||||
export type TuplesReply<T extends [...Array<unknown>]> = RespType<
|
export type TuplesReply<T extends [...Array<unknown>]> = RespType<
|
||||||
RespTypes['ARRAY'],
|
RESP_TYPES['ARRAY'],
|
||||||
T,
|
T,
|
||||||
never,
|
never,
|
||||||
Array<any>
|
Array<any>
|
||||||
>;
|
>;
|
||||||
export type SetReply<T> = RespType<
|
export type SetReply<T> = RespType<
|
||||||
RespTypes['SET'],
|
RESP_TYPES['SET'],
|
||||||
Array<T>,
|
Array<T>,
|
||||||
Set<T>,
|
Set<T>,
|
||||||
Array<any> | Set<any>
|
Array<any> | Set<any>
|
||||||
>;
|
>;
|
||||||
export type MapReply<K, V> = RespType<
|
export type MapReply<K, V> = RespType<
|
||||||
RespTypes['MAP'],
|
RESP_TYPES['MAP'],
|
||||||
{ [key: string]: V },
|
{ [key: string]: V },
|
||||||
Map<K, V> | Array<K | V>,
|
Map<K, V> | Array<K | V>,
|
||||||
Map<any, any> | Array<any>
|
Map<any, any> | Array<any>
|
||||||
@@ -114,7 +114,7 @@ type MapKeyValue = [key: BlobStringReply, value: unknown];
|
|||||||
type MapTuples = Array<MapKeyValue>;
|
type MapTuples = Array<MapKeyValue>;
|
||||||
|
|
||||||
export type TuplesToMapReply<T extends MapTuples> = RespType<
|
export type TuplesToMapReply<T extends MapTuples> = RespType<
|
||||||
RespTypes['MAP'],
|
RESP_TYPES['MAP'],
|
||||||
{
|
{
|
||||||
[P in T[number] as P[0] extends BlobStringReply<infer S> ? S : never]: P[1];
|
[P in T[number] as P[0] extends BlobStringReply<infer S> ? S : never]: P[1];
|
||||||
},
|
},
|
||||||
@@ -134,16 +134,16 @@ type FlattenTuples<T> = (
|
|||||||
export type ReplyUnion = NullReply | BooleanReply | NumberReply | BigNumberReply | DoubleReply | SimpleStringReply | BlobStringReply | VerbatimStringReply | SimpleErrorReply | BlobErrorReply |
|
export type ReplyUnion = NullReply | BooleanReply | NumberReply | BigNumberReply | DoubleReply | SimpleStringReply | BlobStringReply | VerbatimStringReply | SimpleErrorReply | BlobErrorReply |
|
||||||
// cannot reuse ArrayReply, SetReply and MapReply because of circular reference
|
// cannot reuse ArrayReply, SetReply and MapReply because of circular reference
|
||||||
RespType<
|
RespType<
|
||||||
RespTypes['ARRAY'],
|
RESP_TYPES['ARRAY'],
|
||||||
Array<ReplyUnion>
|
Array<ReplyUnion>
|
||||||
> |
|
> |
|
||||||
RespType<
|
RespType<
|
||||||
RespTypes['SET'],
|
RESP_TYPES['SET'],
|
||||||
Array<ReplyUnion>,
|
Array<ReplyUnion>,
|
||||||
Set<ReplyUnion>
|
Set<ReplyUnion>
|
||||||
> |
|
> |
|
||||||
RespType<
|
RespType<
|
||||||
RespTypes['MAP'],
|
RESP_TYPES['MAP'],
|
||||||
{ [key: string]: ReplyUnion },
|
{ [key: string]: ReplyUnion },
|
||||||
Map<ReplyUnion, ReplyUnion> | Array<ReplyUnion | ReplyUnion>
|
Map<ReplyUnion, ReplyUnion> | Array<ReplyUnion | ReplyUnion>
|
||||||
>;
|
>;
|
||||||
@@ -152,10 +152,10 @@ export type Reply = ReplyWithFlags<ReplyUnion, {}>;
|
|||||||
|
|
||||||
export type Flag<T> = ((...args: any) => T) | (new (...args: any) => T);
|
export type Flag<T> = ((...args: any) => T) | (new (...args: any) => T);
|
||||||
|
|
||||||
type RespTypeUnion<T> = T extends RespType<RespTypesUnion, unknown, unknown, infer FLAG_TYPES> ? FLAG_TYPES : never;
|
type RespTypeUnion<T> = T extends RespType<RespTypes, unknown, unknown, infer FLAG_TYPES> ? FLAG_TYPES : never;
|
||||||
|
|
||||||
export type Flags = {
|
export type Flags = {
|
||||||
[P in RespTypesUnion]?: Flag<RespTypeUnion<Extract<ReplyUnion, RespType<P, any, any, any>>>>;
|
[P in RespTypes]?: Flag<RespTypeUnion<Extract<ReplyUnion, RespType<P, any, any, any>>>>;
|
||||||
};
|
};
|
||||||
|
|
||||||
type MapKey<
|
type MapKey<
|
||||||
@@ -163,8 +163,8 @@ type MapKey<
|
|||||||
FLAGS extends Flags
|
FLAGS extends Flags
|
||||||
> = ReplyWithFlags<T, FLAGS & {
|
> = ReplyWithFlags<T, FLAGS & {
|
||||||
// simple and blob strings as map keys decoded as strings
|
// simple and blob strings as map keys decoded as strings
|
||||||
[TYPES.SIMPLE_STRING]: StringConstructor;
|
[RESP_TYPES.SIMPLE_STRING]: StringConstructor;
|
||||||
[TYPES.BLOB_STRING]: StringConstructor;
|
[RESP_TYPES.BLOB_STRING]: StringConstructor;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
export type ReplyWithFlags<
|
export type ReplyWithFlags<
|
||||||
@@ -299,13 +299,13 @@ type Resp2Array<T> = (
|
|||||||
export type Resp2Reply<RESP3REPLY> = (
|
export type Resp2Reply<RESP3REPLY> = (
|
||||||
RESP3REPLY extends RespType<infer RESP_TYPE, infer DEFAULT, infer TYPES, unknown> ?
|
RESP3REPLY extends RespType<infer RESP_TYPE, infer DEFAULT, infer TYPES, unknown> ?
|
||||||
// TODO: RESP3 only scalar types
|
// TODO: RESP3 only scalar types
|
||||||
RESP_TYPE extends RespTypes['DOUBLE'] ? BlobStringReply :
|
RESP_TYPE extends RESP_TYPES['DOUBLE'] ? BlobStringReply :
|
||||||
RESP_TYPE extends RespTypes['ARRAY'] | RespTypes['SET'] ? RespType<
|
RESP_TYPE extends RESP_TYPES['ARRAY'] | RESP_TYPES['SET'] ? RespType<
|
||||||
RESP_TYPE,
|
RESP_TYPE,
|
||||||
Resp2Array<DEFAULT>
|
Resp2Array<DEFAULT>
|
||||||
> :
|
> :
|
||||||
RESP_TYPE extends RespTypes['MAP'] ? RespType<
|
RESP_TYPE extends RESP_TYPES['MAP'] ? RespType<
|
||||||
RespTypes['ARRAY'],
|
RESP_TYPES['ARRAY'],
|
||||||
Resp2Array<Extract<TYPES, Array<any>>>
|
Resp2Array<Extract<TYPES, Array<any>>>
|
||||||
> :
|
> :
|
||||||
RespType<
|
RespType<
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import * as LinkedList from 'yallist';
|
import * as LinkedList from 'yallist';
|
||||||
import encodeCommand from '../RESP/encoder';
|
import encodeCommand from '../RESP/encoder';
|
||||||
import { Decoder, PUSH_FLAGS, TYPES } from '../RESP/decoder';
|
import { Decoder, PUSH_FLAGS, RESP_TYPES } from '../RESP/decoder';
|
||||||
import { CommandArguments, Flags, ReplyUnion, RespVersions } from '../RESP/types';
|
import { CommandArguments, Flags, ReplyUnion, RespVersions } from '../RESP/types';
|
||||||
import { ChannelListeners, PubSub, PubSubCommand, PubSubListener, PubSubType, PubSubTypeListeners } from './pub-sub';
|
import { ChannelListeners, PubSub, PubSubCommand, PubSubListener, PubSubType, PubSubTypeListeners } from './pub-sub';
|
||||||
import { AbortError, ErrorReply } from '../errors';
|
import { AbortError, ErrorReply } from '../errors';
|
||||||
@@ -32,7 +32,7 @@ const PONG = Buffer.from('pong');
|
|||||||
|
|
||||||
const RESP2_PUSH_FLAGS = {
|
const RESP2_PUSH_FLAGS = {
|
||||||
...PUSH_FLAGS,
|
...PUSH_FLAGS,
|
||||||
[TYPES.SIMPLE_STRING]: Buffer
|
[RESP_TYPES.SIMPLE_STRING]: Buffer
|
||||||
};
|
};
|
||||||
|
|
||||||
export default class RedisCommandsQueue {
|
export default class RedisCommandsQueue {
|
||||||
@@ -125,7 +125,7 @@ export default class RedisCommandsQueue {
|
|||||||
if (PONG.equals(reply[0] as Buffer)) {
|
if (PONG.equals(reply[0] as Buffer)) {
|
||||||
const { resolve, flags } = this._waitingForReply.shift()!,
|
const { resolve, flags } = this._waitingForReply.shift()!,
|
||||||
buffer = ((reply[1] as Buffer).length === 0 ? reply[0] : reply[1]) as Buffer;
|
buffer = ((reply[1] as Buffer).length === 0 ? reply[0] : reply[1]) as Buffer;
|
||||||
resolve(flags?.[TYPES.SIMPLE_STRING] === Buffer ? buffer : buffer.toString());
|
resolve(flags?.[RESP_TYPES.SIMPLE_STRING] === Buffer ? buffer : buffer.toString());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,338 +0,0 @@
|
|||||||
import { setTimeout } from 'timers/promises';
|
|
||||||
import { createClient, defineScript, createCluster } from './dist/index.js';
|
|
||||||
import { TYPES } from './dist/lib/RESP/decoder.js';
|
|
||||||
|
|
||||||
async function client() {
|
|
||||||
console.log(`!!! CLIENT !!!`);
|
|
||||||
|
|
||||||
const client = createClient({
|
|
||||||
RESP,
|
|
||||||
isolationPoolOptions: {
|
|
||||||
max: 5
|
|
||||||
},
|
|
||||||
modules: {
|
|
||||||
/**
|
|
||||||
* module jsdoc
|
|
||||||
*/
|
|
||||||
module: {
|
|
||||||
/**
|
|
||||||
* module ping jsdoc
|
|
||||||
*/
|
|
||||||
ping: {
|
|
||||||
/**
|
|
||||||
* @param {string} [message]
|
|
||||||
*/
|
|
||||||
transformArguments(message) {
|
|
||||||
const args = ['PING'];
|
|
||||||
if (message) {
|
|
||||||
args.push(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
return args;
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* @callback PingReply
|
|
||||||
* @returns {import('./lib/RESP/types').SimpleStringReply}
|
|
||||||
*
|
|
||||||
* @type {PingReply}
|
|
||||||
*/
|
|
||||||
transformReply: undefined
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* module square jsdoc
|
|
||||||
*/
|
|
||||||
square: {
|
|
||||||
/**
|
|
||||||
* @param {number} number
|
|
||||||
*/
|
|
||||||
transformArguments(number) {
|
|
||||||
return ['FCALL_RO', 'square', '0', number.toString()];
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* @callback SquareResp2
|
|
||||||
* @returns {import('./lib/RESP/types').BlobStringReply}
|
|
||||||
*
|
|
||||||
* @callback SquareResp3
|
|
||||||
* @returns {import('./lib/RESP/types').DoubleReply}
|
|
||||||
*
|
|
||||||
* @type {{ 2: SquareResp2, 3: SquareResp3 }}
|
|
||||||
*/
|
|
||||||
transformReply: undefined
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
functions: {
|
|
||||||
/**
|
|
||||||
* library jsdoc
|
|
||||||
*/
|
|
||||||
library: {
|
|
||||||
/**
|
|
||||||
* library square jsdoc
|
|
||||||
*/
|
|
||||||
square: {
|
|
||||||
IS_READ_ONLY: true,
|
|
||||||
NUMBER_OF_KEYS: 0,
|
|
||||||
/**
|
|
||||||
* @param {number} number
|
|
||||||
*/
|
|
||||||
transformArguments(number) {
|
|
||||||
return [number.toString()];
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* @callback SquareResp2
|
|
||||||
* @returns {import('./lib/RESP/types').BlobStringReply}
|
|
||||||
*
|
|
||||||
* @callback SquareResp3
|
|
||||||
* @returns {import('./lib/RESP/types').DoubleReply}
|
|
||||||
*
|
|
||||||
* @type {{ 2: SquareResp2, 3: SquareResp3 }}
|
|
||||||
*/
|
|
||||||
transformReply: undefined
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
scripts: {
|
|
||||||
/**
|
|
||||||
* square jsdoc
|
|
||||||
*/
|
|
||||||
square: defineScript({
|
|
||||||
SCRIPT: 'return { double = ARGV[1] * ARGV[1] };',
|
|
||||||
NUMBER_OF_KEYS: 0,
|
|
||||||
/**
|
|
||||||
* @param {number} number
|
|
||||||
*/
|
|
||||||
transformArguments(number) {
|
|
||||||
return [number.toString()];
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @callback SquareResp2
|
|
||||||
* @returns {import('./lib/RESP/types').BlobStringReply}
|
|
||||||
*
|
|
||||||
* @callback SquareResp3
|
|
||||||
* @returns {import('./lib/RESP/types').DoubleReply}
|
|
||||||
*
|
|
||||||
* @type {{ 2: SquareResp2, 3: SquareResp3 }}
|
|
||||||
*/
|
|
||||||
transformReply: undefined
|
|
||||||
})
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const multi = client.multi()
|
|
||||||
.get('a')
|
|
||||||
.set('a', 'b');
|
|
||||||
|
|
||||||
for (let i = 0; i< 10; i++) {
|
|
||||||
multi.incr('a');
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = await multi.exec();
|
|
||||||
|
|
||||||
const bufferClient = client.withFlags({
|
|
||||||
[TYPES.SIMPLE_STRING]: Buffer,
|
|
||||||
[TYPES.BLOB_STRING]: Buffer
|
|
||||||
});
|
|
||||||
|
|
||||||
client.on('error', err => console.error(err));
|
|
||||||
|
|
||||||
await client.connect();
|
|
||||||
|
|
||||||
client.ping()
|
|
||||||
|
|
||||||
// console.log(
|
|
||||||
// 'SCAN',
|
|
||||||
// await client.scan(0),
|
|
||||||
// await bufferClient.scan(0)
|
|
||||||
// );
|
|
||||||
|
|
||||||
// const fn =
|
|
||||||
// `#!LUA name=math
|
|
||||||
// redis.register_function{
|
|
||||||
// function_name = "square",
|
|
||||||
// callback = function(keys, args) return { double = args[1] * args[1] } end,
|
|
||||||
// flags = { "no-writes" }
|
|
||||||
// }`;
|
|
||||||
|
|
||||||
// await client.sendCommand(['FLUSHALL']);
|
|
||||||
// await client.sendCommand(['FUNCTION', 'LOAD', 'REPLACE', fn]);
|
|
||||||
|
|
||||||
// console.log(
|
|
||||||
// 'info:\n',
|
|
||||||
// await client.info(),
|
|
||||||
// 'info with flags:\n',
|
|
||||||
// await client.withFlags({
|
|
||||||
// [TYPES.VERBATIM_STRING]: VerbatimString
|
|
||||||
// }).info(),
|
|
||||||
// );
|
|
||||||
|
|
||||||
// console.log(
|
|
||||||
// 'client.module.square (module):',
|
|
||||||
// await client.module.square(1),
|
|
||||||
// await client.withFlags({
|
|
||||||
// [TYPES.DOUBLE]: String
|
|
||||||
// }).module.square(1)
|
|
||||||
// );
|
|
||||||
|
|
||||||
// console.log(
|
|
||||||
// 'client.library.square (function):',
|
|
||||||
// await client.library.square(2),
|
|
||||||
// await client.withFlags({
|
|
||||||
// [TYPES.DOUBLE]: String
|
|
||||||
// }).library.square(2)
|
|
||||||
// );
|
|
||||||
|
|
||||||
// console.log(
|
|
||||||
// 'client.square (script):',
|
|
||||||
// await client.square(4),
|
|
||||||
// await client.withFlags({
|
|
||||||
// [TYPES.DOUBLE]: String
|
|
||||||
// }).square(4)
|
|
||||||
// );
|
|
||||||
|
|
||||||
// console.log(
|
|
||||||
// 'MULTI',
|
|
||||||
// await client.multi()
|
|
||||||
// .ping()
|
|
||||||
// .module.ping()
|
|
||||||
// .library.square(2)
|
|
||||||
// .square(4)
|
|
||||||
// .exec()
|
|
||||||
// );
|
|
||||||
|
|
||||||
// console.log(
|
|
||||||
// 'SET key value',
|
|
||||||
// await client.set('key', 'value'),
|
|
||||||
// );
|
|
||||||
|
|
||||||
// console.log(
|
|
||||||
// 'GET key',
|
|
||||||
// await client.get('key'),
|
|
||||||
// );
|
|
||||||
|
|
||||||
// console.log(
|
|
||||||
// 'GET key (bufferClient)',
|
|
||||||
// await bufferClient.get('key'),
|
|
||||||
// );
|
|
||||||
|
|
||||||
// console.log(
|
|
||||||
// 'sendCommand DEL key',
|
|
||||||
// await client.sendCommand(['DEL', 'key'])
|
|
||||||
// );
|
|
||||||
|
|
||||||
// console.log(
|
|
||||||
// 'HSET key field value',
|
|
||||||
// await client.hSet('key', 'field', 'value')
|
|
||||||
// );
|
|
||||||
|
|
||||||
// console.log(
|
|
||||||
// 'HGET key field',
|
|
||||||
// await client.hGet('key', 'field')
|
|
||||||
// );
|
|
||||||
|
|
||||||
// console.log(
|
|
||||||
// 'HGETALL key',
|
|
||||||
// await client.hGetAll('key')
|
|
||||||
// );
|
|
||||||
|
|
||||||
// console.log(
|
|
||||||
// 'HGETALL key (bufferClient)',
|
|
||||||
// await bufferClient.hGetAll('key')
|
|
||||||
// );
|
|
||||||
|
|
||||||
// console.log(
|
|
||||||
// 'CLIENT ID',
|
|
||||||
// await client.sendCommand(['CLIENT', 'ID']),
|
|
||||||
// );
|
|
||||||
|
|
||||||
// await client.subscribe('channel', message => {
|
|
||||||
// console.log('channel', message);
|
|
||||||
// });
|
|
||||||
|
|
||||||
// let publisherClient;
|
|
||||||
// if (RESP !== 3) {
|
|
||||||
// publisherClient = client.duplicate();
|
|
||||||
// publisherClient.on('error', err => console.error('PubSubClient error', err));
|
|
||||||
|
|
||||||
// await publisherClient.connect();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const TIMES = 3;
|
|
||||||
// console.log(
|
|
||||||
// `[PUBLISH channel <i>] [PING <i>] * ${TIMES}`,
|
|
||||||
// await Promise.all(
|
|
||||||
// Array.from({ length: 5 }).map((_, i) =>
|
|
||||||
// Promise.all([
|
|
||||||
// (publisherClient ?? client).sendCommand(['PUBLISH', 'channel', i.toString()]).catch(),
|
|
||||||
// client.ping(i.toString()),
|
|
||||||
// client.isolated().clientId(),
|
|
||||||
// client.executeIsolated(client => client.clientId())
|
|
||||||
// ])
|
|
||||||
// )
|
|
||||||
// )
|
|
||||||
// );
|
|
||||||
|
|
||||||
const entries = Array.from({ length: 100 }).map((_, i) => ['{a}' + i.toString(), i.toString()])
|
|
||||||
|
|
||||||
await client.mSet(entries);
|
|
||||||
for await (const key of client.scanIterator()) {
|
|
||||||
console.log('SCAN', key);
|
|
||||||
}
|
|
||||||
|
|
||||||
await client.hSet('hash', entries.flat());
|
|
||||||
for await (const entry of client.hScanIterator('hash')) {
|
|
||||||
console.log('HSCAN', entry)
|
|
||||||
}
|
|
||||||
|
|
||||||
await Promise.all([
|
|
||||||
// publisherClient?.disconnect(),
|
|
||||||
client.disconnect()
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function cluster() {
|
|
||||||
console.log(`!!! CLUSTER !!!`);
|
|
||||||
|
|
||||||
const cluster = createCluster({
|
|
||||||
rootNodes: [{}],
|
|
||||||
RESP
|
|
||||||
});
|
|
||||||
cluster.on('error', err => console.error(err));
|
|
||||||
|
|
||||||
await cluster.connect();
|
|
||||||
|
|
||||||
console.log(
|
|
||||||
'SET key value',
|
|
||||||
await cluster.set('key', 'value')
|
|
||||||
);
|
|
||||||
|
|
||||||
console.log(
|
|
||||||
'GET key',
|
|
||||||
await cluster.get('key')
|
|
||||||
);
|
|
||||||
|
|
||||||
await cluster.subscribe('channel', message => {
|
|
||||||
console.log('(cluster) channel', message);
|
|
||||||
});
|
|
||||||
|
|
||||||
const CLUSTER_TIMES = 3;
|
|
||||||
console.log(
|
|
||||||
`[PUBLISH channel <i>] [PING <i>] * ${CLUSTER_TIMES}`,
|
|
||||||
await Promise.all(
|
|
||||||
Array.from({ length: 5 }).map(async (_, i) => {
|
|
||||||
const client = await cluster.nodeClient(cluster.getRandomNode());
|
|
||||||
return client.sendCommand(['PUBLISH', 'channel', i.toString()]);
|
|
||||||
})
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
// wait for messages
|
|
||||||
await setTimeout(1000);
|
|
||||||
|
|
||||||
await cluster.disconnect();
|
|
||||||
}
|
|
||||||
|
|
||||||
const RESP = 3;
|
|
||||||
|
|
||||||
await client();
|
|
||||||
// await cluster();
|
|
Reference in New Issue
Block a user