diff --git a/packages/client/lib/RESP/types.ts b/packages/client/lib/RESP/types.ts index 53f4d8a742..004da46756 100644 --- a/packages/client/lib/RESP/types.ts +++ b/packages/client/lib/RESP/types.ts @@ -1,3 +1,4 @@ +import { BlobError, SimpleError } from '../errors'; import { RedisScriptConfig, SHA1 } from '../lua-script'; import { RESP_TYPES } from './decoder'; import { VerbatimString } from './verbatim-string'; @@ -6,120 +7,138 @@ export type RESP_TYPES = typeof RESP_TYPES; export type RespTypes = RESP_TYPES[keyof RESP_TYPES]; -export type RespType< +// using interface(s) to allow circular references +// type X = BlobStringReply | ArrayReply; + +export interface RespType< RESP_TYPE extends RespTypes, DEFAULT, TYPES = never, - FLAG_TYPES = DEFAULT | TYPES -> = (DEFAULT | TYPES) & { + TYPE_MAPPING = DEFAULT | TYPES +> { RESP_TYPE: RESP_TYPE; DEFAULT: DEFAULT; TYPES: TYPES; - FLAG: Flag; -}; + TYPE_MAPPING: MappedType; +} -export type NullReply = RespType< +export interface NullReply extends RespType< RESP_TYPES['NULL'], null ->; -export type BooleanReply< +> {} + +export interface BooleanReply< T extends boolean = boolean -> = RespType< +> extends RespType< RESP_TYPES['BOOLEAN'], T ->; -export type NumberReply< +> {} + +export interface NumberReply< T extends number = number -> = RespType< +> extends RespType< RESP_TYPES['NUMBER'], T, `${T}`, number | string ->; -export type BigNumberReply< +> {} + +export interface BigNumberReply< T extends bigint = bigint -> = RespType< +> extends RespType< RESP_TYPES['BIG_NUMBER'], T, number | `${T}`, bigint | number | string ->; -export type DoubleReply< +> {} + +export interface DoubleReply< T extends number = number -> = RespType< +> extends RespType< RESP_TYPES['DOUBLE'], T, `${T}`, number | string ->; -export type SimpleStringReply< +> {} + +export interface SimpleStringReply< T extends string = string -> = RespType< +> extends RespType< RESP_TYPES['SIMPLE_STRING'], T, Buffer, string | Buffer ->; -export type BlobStringReply< +> {} + +export interface BlobStringReply< T extends string = string -> = RespType< +> extends RespType< RESP_TYPES['BLOB_STRING'], T, Buffer, string | Buffer ->; -export type VerbatimStringReply< +> {} + +export interface VerbatimStringReply< T extends string = string -> = RespType< +> extends RespType< RESP_TYPES['VERBATIM_STRING'], T, Buffer | VerbatimString, string | Buffer | VerbatimString ->; -export type SimpleErrorReply = RespType< +> {} + +export interface SimpleErrorReply extends RespType< RESP_TYPES['SIMPLE_ERROR'], + SimpleError, Buffer ->; -export type BlobErrorReply = RespType< +> {} + +export interface BlobErrorReply extends RespType< RESP_TYPES['BLOB_ERROR'], + BlobError, Buffer ->; -export type ArrayReply = RespType< +> {} + +export interface ArrayReply extends RespType< RESP_TYPES['ARRAY'], Array, never, Array ->; -export type TuplesReply]> = RespType< +> {} + +export interface TuplesReply]> extends RespType< RESP_TYPES['ARRAY'], T, never, Array ->; -export type SetReply = RespType< +> {} + +export interface SetReply extends RespType< RESP_TYPES['SET'], Array, Set, Array | Set ->; -export type MapReply = RespType< +> {} + +export interface MapReply extends RespType< RESP_TYPES['MAP'], { [key: string]: V }, Map | Array, Map | Array ->; +> {} type MapKeyValue = [key: BlobStringReply, value: unknown]; type MapTuples = Array; -export type TuplesToMapReply = RespType< +export interface TuplesToMapReply extends RespType< RESP_TYPES['MAP'], { [P in T[number] as P[0] extends BlobStringReply ? S : never]: P[1]; }, Map | FlattenTuples ->; +> {} type FlattenTuples = ( T extends [] ? [] : @@ -131,31 +150,28 @@ type FlattenTuples = ( never ); -export type ReplyUnion = NullReply | BooleanReply | NumberReply | BigNumberReply | DoubleReply | SimpleStringReply | BlobStringReply | VerbatimStringReply | SimpleErrorReply | BlobErrorReply | - // cannot reuse ArrayReply, SetReply and MapReply because of circular reference - RespType< - RESP_TYPES['ARRAY'], - Array - > | - RespType< - RESP_TYPES['SET'], - Array, - Set - > | - RespType< - RESP_TYPES['MAP'], - { [key: string]: ReplyUnion }, - Map | Array - >; +export type ReplyUnion = ( + NullReply | + BooleanReply | + NumberReply | + BigNumberReply | + DoubleReply | + SimpleStringReply | + BlobStringReply | + VerbatimStringReply | + SimpleErrorReply | + BlobErrorReply | + ArrayReply | + SetReply | + MapReply +); -export type Reply = ReplyWithTypeMapping; +export type MappedType = ((...args: any) => T) | (new (...args: any) => T); -export type Flag = ((...args: any) => T) | (new (...args: any) => T); - -type RespTypeUnion = T extends RespType ? FLAG_TYPES : never; +type InferTypeMapping = T extends RespType ? FLAG_TYPES : never; export type TypeMapping = { - [P in RespTypes]?: Flag>>>; + [P in RespTypes]?: MappedType>>>; }; type MapKey< @@ -167,13 +183,15 @@ type MapKey< [RESP_TYPES.BLOB_STRING]: StringConstructor; }>; +export type UnwrapReply> = REPLY['DEFAULT' | 'TYPES']; + export type ReplyWithTypeMapping< REPLY, TYPE_MAPPING extends TypeMapping > = ( // if REPLY is a type, extract the coresponding type from TYPE_MAPPING or use the default type REPLY extends RespType ? - TYPE_MAPPING[RESP_TYPE] extends Flag ? + TYPE_MAPPING[RESP_TYPE] extends MappedType ? ReplyWithTypeMapping, TYPE_MAPPING> : ReplyWithTypeMapping : ( @@ -193,6 +211,11 @@ export type ReplyWithTypeMapping< ) ); +type a = ReplyWithTypeMapping< + ArrayReply>, + {} +>; + export type TransformReply = (this: void, reply: any, preserve?: any) => any; // TODO; export type RedisArgument = string | Buffer; @@ -323,7 +346,7 @@ export type CommandReply< // if transformReply[RESP] is a function, use its return type COMMAND['transformReply'] extends Record infer T> ? T : // otherwise use the generic reply type - Reply + ReplyUnion ); export type CommandSignature< diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index 5a96388b4d..dbf8b85b03 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -425,7 +425,6 @@ export default class RedisClient< } catch (err) { this._queue.decoder.reset(); this.emit('error', err); - } }) .on('error', err => { diff --git a/packages/client/lib/commands/ACL_GETUSER.ts b/packages/client/lib/commands/ACL_GETUSER.ts index c9489abaf5..cbbf48a4c6 100644 --- a/packages/client/lib/commands/ACL_GETUSER.ts +++ b/packages/client/lib/commands/ACL_GETUSER.ts @@ -1,4 +1,4 @@ -import { RedisArgument, TuplesToMapReply, BlobStringReply, ArrayReply, Resp2Reply, Command } from '../RESP/types'; +import { RedisArgument, TuplesToMapReply, BlobStringReply, ArrayReply, UnwrapReply, Resp2Reply, Command } from '../RESP/types'; type AclUser = TuplesToMapReply<[ [BlobStringReply<'flags'>, ArrayReply], @@ -23,17 +23,20 @@ export default { return ['ACL', 'GETUSER', username]; }, transformReply: { - 2: (reply: Resp2Reply) => ({ + 2: (reply: UnwrapReply>) => ({ flags: reply[1], passwords: reply[3], commands: reply[5], keys: reply[7], channels: reply[9], - selectors: reply[11]?.map(selector => ({ - commands: selector[1], - keys: selector[3], - channels: selector[5] - })) + selectors: (reply[11] as unknown as UnwrapReply)?.map(selector => { + const inferred = selector as unknown as UnwrapReply; + return { + commands: inferred[1], + keys: inferred[3], + channels: inferred[5] + }; + }) }), 3: undefined as unknown as () => AclUser } diff --git a/packages/client/lib/commands/ACL_LOG.ts b/packages/client/lib/commands/ACL_LOG.ts index 55172095d1..fab870f27c 100644 --- a/packages/client/lib/commands/ACL_LOG.ts +++ b/packages/client/lib/commands/ACL_LOG.ts @@ -1,5 +1,4 @@ -import { DoubleReply, Resp2Reply } from '../RESP/types'; -import { ArrayReply, BlobStringReply, Command, NumberReply, TuplesToMapReply } from '../RESP/types'; +import { ArrayReply, TuplesToMapReply, BlobStringReply, NumberReply, DoubleReply, UnwrapReply, Resp2Reply, Command } from '../RESP/types'; export type AclLogReply = ArrayReply, NumberReply], @@ -30,18 +29,23 @@ export default { return args; }, transformReply: { - 2: (reply: Resp2Reply) => reply.map(item => ({ - count: item[1], - reason: item[3], - context: item[5], - object: item[7], - username: item[9], - 'age-seconds': Number(item[11]), - 'client-info': item[13], - 'entry-id': item[15], - 'timestamp-created': item[17], - 'timestamp-last-updated': item[19] - })), + 2: (reply: UnwrapReply>) => { + return reply.map(item => { + const inferred = item as unknown as UnwrapReply; + return { + count: inferred[1], + reason: inferred[3], + context: inferred[5], + object: inferred[7], + username: inferred[9], + 'age-seconds': Number(inferred[11]), + 'client-info': inferred[13], + 'entry-id': inferred[15], + 'timestamp-created': inferred[17], + 'timestamp-last-updated': inferred[19] + }; + }) + }, 3: undefined as unknown as () => AclLogReply } } as const satisfies Command; diff --git a/packages/client/lib/commands/BITFIELD.spec.ts b/packages/client/lib/commands/BITFIELD.spec.ts index 9b94b19d24..93ca665deb 100644 --- a/packages/client/lib/commands/BITFIELD.spec.ts +++ b/packages/client/lib/commands/BITFIELD.spec.ts @@ -34,6 +34,12 @@ describe('BITFIELD', () => { }); testUtils.testAll('bitField', async client => { + const a = client.bitField('key', [{ + operation: 'GET', + encoding: 'i8', + offset: 0 + }]); + assert.deepEqual( await client.bitField('key', [{ operation: 'GET', diff --git a/packages/client/lib/commands/BLPOP.ts b/packages/client/lib/commands/BLPOP.ts index c40a18b3c8..c9f8b4775e 100644 --- a/packages/client/lib/commands/BLPOP.ts +++ b/packages/client/lib/commands/BLPOP.ts @@ -1,4 +1,4 @@ -import { BlobStringReply, NullReply, Command } from '../RESP/types'; +import { UnwrapReply, NullReply, TuplesReply, BlobStringReply, Command } from '../RESP/types'; import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; export default { @@ -12,7 +12,7 @@ export default { args.push(timeout.toString()); return args; }, - transformReply(reply: NullReply | [BlobStringReply, BlobStringReply]) { + transformReply(reply: UnwrapReply>) { if (reply === null) return null; return { diff --git a/packages/client/lib/commands/BZPOPMAX.ts b/packages/client/lib/commands/BZPOPMAX.ts index 64059f1f78..c498a3b804 100644 --- a/packages/client/lib/commands/BZPOPMAX.ts +++ b/packages/client/lib/commands/BZPOPMAX.ts @@ -1,4 +1,4 @@ -import { RedisArgument, Command, NullReply, TuplesReply, BlobStringReply, DoubleReply } from '../RESP/types'; +import { RedisArgument, NullReply, TuplesReply, BlobStringReply, DoubleReply, UnwrapReply, Command } from '../RESP/types'; import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; export function transformBZPopArguments( @@ -20,14 +20,14 @@ export default { return transformBZPopArguments('BZPOPMAX', ...args); }, transformReply: { - 2: (reply: NullReply | TuplesReply<[BlobStringReply, BlobStringReply, BlobStringReply]>) => { + 2(reply: UnwrapReply>) { return reply === null ? null : { key: reply[0], value: reply[1], score: Number(reply[2]) }; }, - 3: (reply: NullReply | TuplesReply<[BlobStringReply, BlobStringReply, DoubleReply]>) => { + 3(reply: UnwrapReply>) { return reply === null ? null : { key: reply[0], value: reply[1], diff --git a/packages/client/lib/commands/CLIENT_INFO.spec.ts b/packages/client/lib/commands/CLIENT_INFO.spec.ts index ccb99017cf..ecc290d2f2 100644 --- a/packages/client/lib/commands/CLIENT_INFO.spec.ts +++ b/packages/client/lib/commands/CLIENT_INFO.spec.ts @@ -1,50 +1,50 @@ import { strict as assert } from 'assert'; -import { transformArguments, transformReply } from './CLIENT_INFO'; +import CLIENT_INFO from './CLIENT_INFO'; import testUtils, { GLOBAL } from '../test-utils'; describe('CLIENT INFO', () => { - testUtils.isVersionGreaterThanHook([6, 2]); + testUtils.isVersionGreaterThanHook([6, 2]); - it('transformArguments', () => { - assert.deepEqual( - transformArguments(), - ['CLIENT', 'INFO'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + CLIENT_INFO.transformArguments(), + ['CLIENT', 'INFO'] + ); + }); - testUtils.testWithClient('client.clientInfo', async client => { - const reply = await client.clientInfo(); - assert.equal(typeof reply.id, 'number'); - assert.equal(typeof reply.addr, 'string'); - assert.equal(typeof reply.laddr, 'string'); - assert.equal(typeof reply.fd, 'number'); - assert.equal(typeof reply.name, 'string'); - assert.equal(typeof reply.age, 'number'); - assert.equal(typeof reply.idle, 'number'); - assert.equal(typeof reply.flags, 'string'); - assert.equal(typeof reply.db, 'number'); - assert.equal(typeof reply.sub, 'number'); - assert.equal(typeof reply.psub, 'number'); - assert.equal(typeof reply.multi, 'number'); - assert.equal(typeof reply.qbuf, 'number'); - assert.equal(typeof reply.qbufFree, 'number'); - assert.equal(typeof reply.argvMem, 'number'); - assert.equal(typeof reply.obl, 'number'); - assert.equal(typeof reply.oll, 'number'); - assert.equal(typeof reply.omem, 'number'); - assert.equal(typeof reply.totMem, 'number'); - assert.equal(typeof reply.events, 'string'); - assert.equal(typeof reply.cmd, 'string'); - assert.equal(typeof reply.user, 'string'); - assert.equal(typeof reply.redir, 'number'); + testUtils.testWithClient('client.clientInfo', async client => { + const reply = await client.clientInfo(); + assert.equal(typeof reply.id, 'number'); + assert.equal(typeof reply.addr, 'string'); + assert.equal(typeof reply.laddr, 'string'); + assert.equal(typeof reply.fd, 'number'); + assert.equal(typeof reply.name, 'string'); + assert.equal(typeof reply.age, 'number'); + assert.equal(typeof reply.idle, 'number'); + assert.equal(typeof reply.flags, 'string'); + assert.equal(typeof reply.db, 'number'); + assert.equal(typeof reply.sub, 'number'); + assert.equal(typeof reply.psub, 'number'); + assert.equal(typeof reply.multi, 'number'); + assert.equal(typeof reply.qbuf, 'number'); + assert.equal(typeof reply.qbufFree, 'number'); + assert.equal(typeof reply.argvMem, 'number'); + assert.equal(typeof reply.obl, 'number'); + assert.equal(typeof reply.oll, 'number'); + assert.equal(typeof reply.omem, 'number'); + assert.equal(typeof reply.totMem, 'number'); + assert.equal(typeof reply.events, 'string'); + assert.equal(typeof reply.cmd, 'string'); + assert.equal(typeof reply.user, 'string'); + assert.equal(typeof reply.redir, 'number'); - if (testUtils.isVersionGreaterThan([7, 0])) { - assert.equal(typeof reply.multiMem, 'number'); - assert.equal(typeof reply.resp, 'number'); - } + if (testUtils.isVersionGreaterThan([7, 0])) { + assert.equal(typeof reply.multiMem, 'number'); + assert.equal(typeof reply.resp, 'number'); - if (testUtils.isVersionGreaterThan([7, 0, 3])) { - assert.equal(typeof reply.ssub, 'number'); - } - }, GLOBAL.SERVERS.OPEN); + if (testUtils.isVersionGreaterThan([7, 0, 3])) { + assert.equal(typeof reply.ssub, 'number'); + } + } + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/CLIENT_LIST.spec.ts b/packages/client/lib/commands/CLIENT_LIST.spec.ts index c9c720e12e..5da86aa297 100644 --- a/packages/client/lib/commands/CLIENT_LIST.spec.ts +++ b/packages/client/lib/commands/CLIENT_LIST.spec.ts @@ -1,78 +1,77 @@ import { strict as assert } from 'assert'; -import { transformArguments, transformReply } from './CLIENT_LIST'; +import CLIENT_LIST from './CLIENT_LIST'; import testUtils, { GLOBAL } from '../test-utils'; describe('CLIENT LIST', () => { - describe('transformArguments', () => { - it('simple', () => { - assert.deepEqual( - transformArguments(), - ['CLIENT', 'LIST'] - ); - }); - - it('with TYPE', () => { - assert.deepEqual( - transformArguments({ - TYPE: 'NORMAL' - }), - ['CLIENT', 'LIST', 'TYPE', 'NORMAL'] - ); - }); - - it('with ID', () => { - assert.deepEqual( - transformArguments({ - ID: ['1', '2'] - }), - ['CLIENT', 'LIST', 'ID', '1', '2'] - ); - }); + describe('transformArguments', () => { + it('simple', () => { + assert.deepEqual( + CLIENT_LIST.transformArguments(), + ['CLIENT', 'LIST'] + ); }); - testUtils.testWithClient('client.clientList', async client => { - const reply = await client.clientList(); - assert.ok(Array.isArray(reply)); + it('with TYPE', () => { + assert.deepEqual( + CLIENT_LIST.transformArguments({ + TYPE: 'NORMAL' + }), + ['CLIENT', 'LIST', 'TYPE', 'NORMAL'] + ); + }); - for (const item of reply) { - assert.equal(typeof item.id, 'number'); - assert.equal(typeof item.addr, 'string'); - assert.equal(typeof item.fd, 'number'); - assert.equal(typeof item.name, 'string'); - assert.equal(typeof item.age, 'number'); - assert.equal(typeof item.idle, 'number'); - assert.equal(typeof item.flags, 'string'); - assert.equal(typeof item.db, 'number'); - assert.equal(typeof item.sub, 'number'); - assert.equal(typeof item.psub, 'number'); - assert.equal(typeof item.multi, 'number'); - assert.equal(typeof item.qbuf, 'number'); - assert.equal(typeof item.qbufFree, 'number'); - assert.equal(typeof item.obl, 'number'); - assert.equal(typeof item.oll, 'number'); - assert.equal(typeof item.omem, 'number'); - assert.equal(typeof item.events, 'string'); - assert.equal(typeof item.cmd, 'string'); + it('with ID', () => { + assert.deepEqual( + CLIENT_LIST.transformArguments({ + ID: ['1', '2'] + }), + ['CLIENT', 'LIST', 'ID', '1', '2'] + ); + }); + }); - if (testUtils.isVersionGreaterThan([6, 0])) { - assert.equal(typeof item.argvMem, 'number'); - assert.equal(typeof item.totMem, 'number'); - assert.equal(typeof item.user, 'string'); - } + testUtils.testWithClient('client.clientList', async client => { + const reply = await client.clientList(); + assert.ok(Array.isArray(reply)); + for (const item of reply) { + assert.equal(typeof item.id, 'number'); + assert.equal(typeof item.addr, 'string'); + assert.equal(typeof item.fd, 'number'); + assert.equal(typeof item.name, 'string'); + assert.equal(typeof item.age, 'number'); + assert.equal(typeof item.idle, 'number'); + assert.equal(typeof item.flags, 'string'); + assert.equal(typeof item.db, 'number'); + assert.equal(typeof item.sub, 'number'); + assert.equal(typeof item.psub, 'number'); + assert.equal(typeof item.multi, 'number'); + assert.equal(typeof item.qbuf, 'number'); + assert.equal(typeof item.qbufFree, 'number'); + assert.equal(typeof item.obl, 'number'); + assert.equal(typeof item.oll, 'number'); + assert.equal(typeof item.omem, 'number'); + assert.equal(typeof item.events, 'string'); + assert.equal(typeof item.cmd, 'string'); - if (testUtils.isVersionGreaterThan([6, 2])) { - assert.equal(typeof item.redir, 'number'); - assert.equal(typeof item.laddr, 'string'); - } - - if (testUtils.isVersionGreaterThan([7, 0])) { - assert.equal(typeof item.multiMem, 'number'); - assert.equal(typeof item.resp, 'number'); - } + if (testUtils.isVersionGreaterThan([6, 0])) { + assert.equal(typeof item.argvMem, 'number'); + assert.equal(typeof item.totMem, 'number'); + assert.equal(typeof item.user, 'string'); + + if (testUtils.isVersionGreaterThan([6, 2])) { + assert.equal(typeof item.redir, 'number'); + assert.equal(typeof item.laddr, 'string'); + + if (testUtils.isVersionGreaterThan([7, 0])) { + assert.equal(typeof item.multiMem, 'number'); + assert.equal(typeof item.resp, 'number'); if (testUtils.isVersionGreaterThan([7, 0, 3])) { - assert.equal(typeof item.ssub, 'number'); + assert.equal(typeof item.ssub, 'number'); } + } } - }, GLOBAL.SERVERS.OPEN); + } + } + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/CLIENT_LIST.ts b/packages/client/lib/commands/CLIENT_LIST.ts index f50634d56d..dc43fb8855 100644 --- a/packages/client/lib/commands/CLIENT_LIST.ts +++ b/packages/client/lib/commands/CLIENT_LIST.ts @@ -36,7 +36,7 @@ export default { length = split.length - 1, reply: Array = []; for (let i = 0; i < length; i++) { - reply.push(CLIENT_INFO.transformReply(split[i] as VerbatimStringReply)); + reply.push(CLIENT_INFO.transformReply(split[i] as unknown as VerbatimStringReply)); } return reply; diff --git a/packages/client/lib/commands/CLIENT_TRACKINGINFO.ts b/packages/client/lib/commands/CLIENT_TRACKINGINFO.ts index 4c443532f5..d969ba0219 100644 --- a/packages/client/lib/commands/CLIENT_TRACKINGINFO.ts +++ b/packages/client/lib/commands/CLIENT_TRACKINGINFO.ts @@ -1,4 +1,4 @@ -import { TuplesToMapReply, BlobStringReply, SetReply, NumberReply, ArrayReply, Resp2Reply, Command } from '../RESP/types'; +import { TuplesToMapReply, BlobStringReply, SetReply, NumberReply, ArrayReply, UnwrapReply, Resp2Reply, Command } from '../RESP/types'; type TrackingInfo = TuplesToMapReply<[ [BlobStringReply<'flags'>, SetReply], @@ -13,7 +13,7 @@ export default { return ['CLIENT', 'TRACKINGINFO']; }, transformReply: { - 2: (reply: Resp2Reply) => ({ + 2: (reply: UnwrapReply>) => ({ flags: reply[1], redirect: reply[3], prefixes: reply[5] diff --git a/packages/client/lib/commands/CLUSTER_LINKS.ts b/packages/client/lib/commands/CLUSTER_LINKS.ts index 486553f9d0..df83f3f7a1 100644 --- a/packages/client/lib/commands/CLUSTER_LINKS.ts +++ b/packages/client/lib/commands/CLUSTER_LINKS.ts @@ -1,4 +1,4 @@ -import { ArrayReply, TuplesToMapReply, BlobStringReply, NumberReply, Resp2Reply, Command } from '../RESP/types'; +import { ArrayReply, TuplesToMapReply, BlobStringReply, NumberReply, UnwrapReply, Resp2Reply, Command } from '../RESP/types'; type ClusterLinksReply = ArrayReply, BlobStringReply], @@ -16,14 +16,17 @@ export default { return ['CLUSTER', 'LINKS']; }, transformReply: { - 2: (reply: Resp2Reply) => reply.map(link => ({ - direction: link[1], - node: link[3], - 'create-time': link[5], - events: link[7], - 'send-buffer-allocated': link[9], - 'send-buffer-used': link[11] - })), + 2: (reply: UnwrapReply>) => reply.map(link => { + const unwrapped = link as unknown as UnwrapReply; + return { + direction: unwrapped[1], + node: unwrapped[3], + 'create-time': unwrapped[5], + events: unwrapped[7], + 'send-buffer-allocated': unwrapped[9], + 'send-buffer-used': unwrapped[11] + }; + }), 3: undefined as unknown as () => ClusterLinksReply } } as const satisfies Command; diff --git a/packages/client/lib/commands/CLUSTER_SLOTS.ts b/packages/client/lib/commands/CLUSTER_SLOTS.ts index 13a925e9a4..1b523328bb 100644 --- a/packages/client/lib/commands/CLUSTER_SLOTS.ts +++ b/packages/client/lib/commands/CLUSTER_SLOTS.ts @@ -1,10 +1,10 @@ -import { NumberReply, ArrayReply, BlobStringReply, Command } from '../RESP/types'; +import { TuplesReply, BlobStringReply, NumberReply, ArrayReply, UnwrapReply, Command } from '../RESP/types'; -type RawNode = [ +type RawNode = TuplesReply<[ host: BlobStringReply, port: NumberReply, id: BlobStringReply -]; +]>; type ClusterSlotsRawReply = ArrayReply<[ from: NumberReply, @@ -21,7 +21,7 @@ export default { transformArguments() { return ['CLUSTER', 'SLOTS']; }, - transformReply(reply: ClusterSlotsRawReply) { + transformReply(reply: UnwrapReply) { return reply.map(([from, to, master, ...replicas]) => ({ from, to, @@ -31,7 +31,8 @@ export default { } } as const satisfies Command; -function transformNode([host, port, id ]: RawNode) { +function transformNode(node: RawNode) { + const [host, port, id] = node as unknown as UnwrapReply; return { host, port, diff --git a/packages/client/lib/commands/COMMAND_GETKEYSANDFLAGS.ts b/packages/client/lib/commands/COMMAND_GETKEYSANDFLAGS.ts index a3581237f4..a032190c16 100644 --- a/packages/client/lib/commands/COMMAND_GETKEYSANDFLAGS.ts +++ b/packages/client/lib/commands/COMMAND_GETKEYSANDFLAGS.ts @@ -1,4 +1,4 @@ -import { RedisArgument, ArrayReply, TuplesReply, BlobStringReply, SetReply, Command } from '../RESP/types'; +import { RedisArgument, ArrayReply, TuplesReply, BlobStringReply, SetReply, UnwrapReply, Command } from '../RESP/types'; export type CommandGetKeysAndFlagsRawReply = ArrayReply) { return ['COMMAND', 'GETKEYSANDFLAGS', ...args]; }, - transformReply(reply: CommandGetKeysAndFlagsRawReply) { - return reply.map(([key, flags]) => ({ - key, - flags - })); + transformReply(reply: UnwrapReply) { + return reply.map(entry => { + const [key, flags] = entry as unknown as UnwrapReply; + return { + key, + flags + }; + }); } } as const satisfies Command; diff --git a/packages/client/lib/commands/FUNCTION_LIST.spec.ts b/packages/client/lib/commands/FUNCTION_LIST.spec.ts index c630b3aa52..52c0ea9b63 100644 --- a/packages/client/lib/commands/FUNCTION_LIST.spec.ts +++ b/packages/client/lib/commands/FUNCTION_LIST.spec.ts @@ -25,19 +25,21 @@ describe('FUNCTION LIST', () => { }); testUtils.testWithClient('client.functionList', async client => { - await loadMathFunction(client); + const [, reply] = await Promise.all([ + loadMathFunction(client), + client.functionList() + ]); - assert.deepEqual( - await client.functionList(), - [{ - library_name: MATH_FUNCTION.name, - engine: MATH_FUNCTION.engine, - functions: [{ - name: MATH_FUNCTION.library.square.NAME, - description: null, - flags: ['no-writes'] - }] + reply[0].library_name; + + assert.deepEqual(reply, [{ + library_name: MATH_FUNCTION.name, + engine: MATH_FUNCTION.engine, + functions: [{ + name: MATH_FUNCTION.library.square.NAME, + description: null, + flags: ['no-writes'] }] - ); + }]); }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/FUNCTION_LIST.ts b/packages/client/lib/commands/FUNCTION_LIST.ts index 7150499e6d..8993496ca2 100644 --- a/packages/client/lib/commands/FUNCTION_LIST.ts +++ b/packages/client/lib/commands/FUNCTION_LIST.ts @@ -1,18 +1,18 @@ -import { RedisArgument, TuplesToMapReply, BlobStringReply, ArrayReply, NullReply, SetReply, Resp2Reply, CommandArguments, Command } from '../RESP/types'; +import { RedisArgument, TuplesToMapReply, BlobStringReply, ArrayReply, NullReply, SetReply, UnwrapReply, Resp2Reply, CommandArguments, Command, ReplyWithTypeMapping } from '../RESP/types'; export interface FunctionListOptions { LIBRARYNAME?: RedisArgument; } export type FunctionListReplyItem = [ - [BlobStringReply<'library_name'>, BlobStringReply], + [BlobStringReply<'library_name'>, BlobStringReply | NullReply], [BlobStringReply<'engine'>, BlobStringReply], [BlobStringReply<'functions'>, ArrayReply, BlobStringReply], [BlobStringReply<'description'>, BlobStringReply | NullReply], [BlobStringReply<'flags'>, SetReply], ]>>] -] +]; export type FunctionListReply = ArrayReply>; @@ -29,16 +29,22 @@ export default { return args; }, transformReply: { - 2: (reply: Resp2Reply) => { - return reply.map(library => ({ - library_name: library[1], - engine: library[3], - functions: library[5].map(fn => ({ - name: fn[1], - description: fn[3], - flags: fn[5] - })) - })); + 2: (reply: UnwrapReply>) => { + return reply.map(library => { + const unwrapped = library as unknown as UnwrapReply; + return { + library_name: unwrapped[1], + engine: unwrapped[3], + functions: (unwrapped[5] as unknown as UnwrapReply).map(fn => { + const unwrapped = fn as unknown as UnwrapReply; + return { + name: unwrapped[1], + description: unwrapped[3], + flags: unwrapped[5] + }; + }) + }; + }); }, 3: undefined as unknown as () => FunctionListReply } diff --git a/packages/client/lib/commands/FUNCTION_LIST_WITHCODE.spec.ts b/packages/client/lib/commands/FUNCTION_LIST_WITHCODE.spec.ts index 3c8342ab50..528062eba6 100644 --- a/packages/client/lib/commands/FUNCTION_LIST_WITHCODE.spec.ts +++ b/packages/client/lib/commands/FUNCTION_LIST_WITHCODE.spec.ts @@ -25,20 +25,24 @@ describe('FUNCTION LIST WITHCODE', () => { }); testUtils.testWithClient('client.functionListWithCode', async client => { - await loadMathFunction(client); + const [, reply] = await Promise.all([ + loadMathFunction(client), + client.functionListWithCode() + ]); + + const a = reply[0]; + + const b = a.functions[0].description; - assert.deepEqual( - await client.functionListWithCode(), - [{ - library_name: MATH_FUNCTION.name, - engine: MATH_FUNCTION.engine, - functions: [{ - name: MATH_FUNCTION.library.square.NAME, - description: null, - flags: ['no-writes'] - }], - library_code: MATH_FUNCTION.code - }] - ); + assert.deepEqual(reply, [{ + library_name: MATH_FUNCTION.name, + engine: MATH_FUNCTION.engine, + functions: [{ + name: MATH_FUNCTION.library.square.NAME, + description: null, + flags: ['no-writes'] + }], + library_code: MATH_FUNCTION.code + }]); }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/FUNCTION_LIST_WITHCODE.ts b/packages/client/lib/commands/FUNCTION_LIST_WITHCODE.ts index 6aa83d1d6a..47a02a3da8 100644 --- a/packages/client/lib/commands/FUNCTION_LIST_WITHCODE.ts +++ b/packages/client/lib/commands/FUNCTION_LIST_WITHCODE.ts @@ -1,4 +1,4 @@ -import { TuplesToMapReply, BlobStringReply, ArrayReply, Command, Resp2Reply } from '../RESP/types'; +import { TuplesToMapReply, BlobStringReply, ArrayReply, UnwrapReply, Resp2Reply, Command } from '../RESP/types'; import FUNCTION_LIST, { FunctionListReplyItem } from './FUNCTION_LIST'; export type FunctionListWithCodeReply = ArrayReply) => { - return reply.map((library: any) => ({ - library_name: library[1], - engine: library[3], - functions: library[5].map((fn: any) => ({ - name: fn[1], - description: fn[3], - flags: fn[5] - })), - library_code: library[7] - })) as unknown as number; + 2: (reply: UnwrapReply>) => { + return reply.map(library => { + const unwrapped = library as unknown as UnwrapReply; + return { + library_name: unwrapped[1], + engine: unwrapped[3], + functions: (unwrapped[5] as unknown as UnwrapReply).map(fn => { + const unwrapped = fn as unknown as UnwrapReply; + return { + name: unwrapped[1], + description: unwrapped[3], + flags: unwrapped[5] + }; + }), + library_code: unwrapped[7] + }; + }); }, 3: undefined as unknown as () => FunctionListWithCodeReply } diff --git a/packages/client/lib/commands/GEOPOS.ts b/packages/client/lib/commands/GEOPOS.ts index d9fb5d3a25..30273c64c1 100644 --- a/packages/client/lib/commands/GEOPOS.ts +++ b/packages/client/lib/commands/GEOPOS.ts @@ -1,4 +1,4 @@ -import { ArrayReply, BlobStringReply, NullReply, Command, RedisArgument } from '../RESP/types'; +import { RedisArgument, ArrayReply, TuplesReply, BlobStringReply, NullReply, UnwrapReply, Command } from '../RESP/types'; import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; export default { @@ -10,10 +10,13 @@ export default { ) { return pushVariadicArguments(['GEOPOS', key], member); }, - transformReply(reply: ArrayReply<[BlobStringReply, BlobStringReply] | NullReply>) { - return reply.map(item => item === null ? null : { - longitude: item[0], - latitude: item[1] + transformReply(reply: UnwrapReply | NullReply>>) { + return reply.map(item => { + const unwrapped = item as unknown as UnwrapReply; + return unwrapped === null ? null : { + longitude: unwrapped[0], + latitude: unwrapped[1] + }; }); } } as const satisfies Command; diff --git a/packages/client/lib/commands/GEOSEARCH_WITH.ts b/packages/client/lib/commands/GEOSEARCH_WITH.ts index 7dfb65c34c..19088230f0 100644 --- a/packages/client/lib/commands/GEOSEARCH_WITH.ts +++ b/packages/client/lib/commands/GEOSEARCH_WITH.ts @@ -1,4 +1,4 @@ -import { ArrayReply, BlobStringReply, NumberReply, DoubleReply, Command, RedisArgument } from '../RESP/types'; +import { RedisArgument, ArrayReply, TuplesReply, BlobStringReply, NumberReply, DoubleReply, UnwrapReply, Command } from '../RESP/types'; import GEOSEARCH, { GeoSearchBy, GeoSearchFrom, GeoSearchOptions } from './GEOSEARCH'; export const GEO_REPLY_WITH = { @@ -35,7 +35,7 @@ export default { return args; }, transformReply( - reply: ArrayReply<[BlobStringReply, ...Array]>, + reply: UnwrapReply]>>>, replyWith: Array ) { const replyWithSet = new Set(replyWith); @@ -45,20 +45,22 @@ export default { coordinatesIndex = replyWithSet.has(GEO_REPLY_WITH.COORDINATES) && ++index; return reply.map(raw => { + const unwrapped = raw as unknown as UnwrapReply; + const item: GeoReplyWithMember = { - member: raw[0] + member: unwrapped[0] }; if (distanceIndex) { - item.distance = raw[distanceIndex]; + item.distance = unwrapped[distanceIndex]; } if (hashIndex) { - item.hash = raw[hashIndex]; + item.hash = unwrapped[hashIndex]; } if (coordinatesIndex) { - const [longitude, latitude] = raw[coordinatesIndex]; + const [longitude, latitude] = unwrapped[coordinatesIndex]; item.coordinates = { longitude, latitude diff --git a/packages/client/lib/commands/HELLO.ts b/packages/client/lib/commands/HELLO.ts index 717ee05130..0fb2960d02 100644 --- a/packages/client/lib/commands/HELLO.ts +++ b/packages/client/lib/commands/HELLO.ts @@ -1,4 +1,4 @@ -import { RedisArgument, ArrayReply, BlobStringReply, Command, NumberReply, Resp2Reply, RespVersions, TuplesToMapReply } from '../RESP/types'; +import { RedisArgument, RespVersions, TuplesToMapReply, BlobStringReply, NumberReply, ArrayReply, UnwrapReply, Resp2Reply, Command } from '../RESP/types'; export interface HelloOptions { protover?: RespVersions; @@ -45,7 +45,7 @@ export default { return args; }, transformReply: { - 2: (reply: Resp2Reply) => ({ + 2: (reply: UnwrapReply>) => ({ server: reply[1], version: reply[3], proto: reply[5], diff --git a/packages/client/lib/commands/HRANDFIELD_COUNT_WITHVALUES.ts b/packages/client/lib/commands/HRANDFIELD_COUNT_WITHVALUES.ts index 2c7b17d576..ab36183c4a 100644 --- a/packages/client/lib/commands/HRANDFIELD_COUNT_WITHVALUES.ts +++ b/packages/client/lib/commands/HRANDFIELD_COUNT_WITHVALUES.ts @@ -1,4 +1,4 @@ -import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types'; +import { RedisArgument, ArrayReply, TuplesReply, BlobStringReply, UnwrapReply, Command } from '../RESP/types'; export type HRandFieldCountWithValuesReply = Array<{ field: BlobStringReply; @@ -12,7 +12,7 @@ export default { return ['HRANDFIELD', key, count.toString(), 'WITHVALUES']; }, transformReply: { - 2: (rawReply: ArrayReply) => { + 2: (rawReply: UnwrapReply>) => { const reply: HRandFieldCountWithValuesReply = []; let i = 0; @@ -25,11 +25,14 @@ export default { return reply; }, - 3: (reply: ArrayReply<[BlobStringReply, BlobStringReply]>) => { - return reply.map(([field, value]) => ({ - field, - value - })) satisfies HRandFieldCountWithValuesReply; + 3: (reply: UnwrapReply>>) => { + return reply.map(entry => { + const [field, value] = entry as unknown as UnwrapReply; + return { + field, + value + }; + }) satisfies HRandFieldCountWithValuesReply; } } } as const satisfies Command; diff --git a/packages/client/lib/commands/LCS_IDX.ts b/packages/client/lib/commands/LCS_IDX.ts index 41a0b61e8a..0c266fffe1 100644 --- a/packages/client/lib/commands/LCS_IDX.ts +++ b/packages/client/lib/commands/LCS_IDX.ts @@ -1,4 +1,4 @@ -import { RedisArgument, TuplesToMapReply, BlobStringReply, ArrayReply, NumberReply, Resp2Reply, Command, TuplesReply } from '../RESP/types'; +import { RedisArgument, TuplesToMapReply, BlobStringReply, ArrayReply, NumberReply, UnwrapReply, Resp2Reply, Command, TuplesReply } from '../RESP/types'; import LCS from './LCS'; export interface LcsIdxOptions { @@ -41,7 +41,7 @@ export default { return args; }, transformReply: { - 2: (reply: Resp2Reply) => ({ + 2: (reply: UnwrapReply>) => ({ matches: reply[1], len: reply[3] }), diff --git a/packages/client/lib/commands/LCS_IDX_WITHMATCHLEN.ts b/packages/client/lib/commands/LCS_IDX_WITHMATCHLEN.ts index abd3bb361f..4e64585203 100644 --- a/packages/client/lib/commands/LCS_IDX_WITHMATCHLEN.ts +++ b/packages/client/lib/commands/LCS_IDX_WITHMATCHLEN.ts @@ -1,4 +1,4 @@ -import { RedisArgument, TuplesToMapReply, BlobStringReply, ArrayReply, NumberReply, Resp2Reply, Command, TuplesReply } from '../RESP/types'; +import { RedisArgument, TuplesToMapReply, BlobStringReply, ArrayReply, TuplesReply, NumberReply, UnwrapReply, Resp2Reply, Command } from '../RESP/types'; import LCS_IDX, { LcsIdxOptions, LcsIdxRange } from './LCS_IDX'; export type LcsIdxWithMatchLenMatches = ArrayReply< @@ -27,7 +27,7 @@ export default { return args; }, transformReply: { - 2: (reply: Resp2Reply) => ({ + 2: (reply: UnwrapReply>) => ({ matches: reply[1], len: reply[3] }), diff --git a/packages/client/lib/commands/MEMORY_STATS.ts b/packages/client/lib/commands/MEMORY_STATS.ts index 4e6997ff99..38deae9013 100644 --- a/packages/client/lib/commands/MEMORY_STATS.ts +++ b/packages/client/lib/commands/MEMORY_STATS.ts @@ -1,4 +1,4 @@ -import { TuplesToMapReply, BlobStringReply, NumberReply, DoubleReply, Command, Resp2Reply } from '../RESP/types'; +import { TuplesToMapReply, BlobStringReply, NumberReply, DoubleReply, ArrayReply, UnwrapReply, Resp2Reply, Command } from '../RESP/types'; export type MemoryStatsReply = TuplesToMapReply<[ [BlobStringReply<'peak.allocated'>, NumberReply], @@ -39,13 +39,12 @@ export default { return ['MEMORY', 'STATS']; }, transformReply: { - 2: (rawReply: Array) => { - const reply: Partial> = {}; + 2: (rawReply: UnwrapReply>) => { + const reply: any = {}; let i = 0; while (i < rawReply.length) { - const key = rawReply[i++] as keyof MemoryStatsReply['DEFAULT']; - reply[key] = rawReply[i++] as any; + reply[rawReply[i++] as any] = rawReply[i++]; } return reply as MemoryStatsReply['DEFAULT']; diff --git a/packages/client/lib/commands/MODULE_LIST.ts b/packages/client/lib/commands/MODULE_LIST.ts index 9c61dcf07a..5ddd4e91ff 100644 --- a/packages/client/lib/commands/MODULE_LIST.ts +++ b/packages/client/lib/commands/MODULE_LIST.ts @@ -1,4 +1,4 @@ -import { ArrayReply, BlobStringReply, NumberReply, Command, Resp2Reply, TuplesToMapReply } from '../RESP/types'; +import { ArrayReply, TuplesToMapReply, BlobStringReply, NumberReply, UnwrapReply, Resp2Reply, Command } from '../RESP/types'; export type ModuleListReply = ArrayReply, BlobStringReply], @@ -12,11 +12,14 @@ export default { return ['MODULE', 'LIST']; }, transformReply: { - 2: (reply: Resp2Reply) => { - return reply.map(module => ({ - name: module[1], - ver: module[3] - })); + 2: (reply: UnwrapReply>) => { + return reply.map(module => { + const unwrapped = module as unknown as UnwrapReply; + return { + name: unwrapped[1], + ver: unwrapped[3] + }; + }); }, 3: undefined as unknown as () => ModuleListReply } diff --git a/packages/client/lib/commands/PUBSUB_NUMSUB.ts b/packages/client/lib/commands/PUBSUB_NUMSUB.ts index aaa6cf749c..1f7c41f5bd 100644 --- a/packages/client/lib/commands/PUBSUB_NUMSUB.ts +++ b/packages/client/lib/commands/PUBSUB_NUMSUB.ts @@ -1,4 +1,4 @@ -import { ArrayReply, BlobStringReply, NumberReply, Command } from '../RESP/types'; +import { ArrayReply, BlobStringReply, NumberReply, UnwrapReply, Command } from '../RESP/types'; import { RedisVariadicArgument, pushVariadicArguments } from './generic-transformers'; export default { @@ -11,7 +11,7 @@ export default { return args; }, - transformReply(rawReply: ArrayReply) { + transformReply(rawReply: UnwrapReply>) { const reply = Object.create(null); let i = 0; while (i < rawReply.length) { diff --git a/packages/client/lib/commands/ROLE.ts b/packages/client/lib/commands/ROLE.ts index 40b9d67047..7828e53fb6 100644 --- a/packages/client/lib/commands/ROLE.ts +++ b/packages/client/lib/commands/ROLE.ts @@ -1,9 +1,9 @@ -import { ArrayReply, BlobStringReply, Command, NumberReply } from '../RESP/types'; +import { BlobStringReply, NumberReply, ArrayReply, TuplesReply, UnwrapReply, Command } from '../RESP/types'; type MasterRole = [ role: BlobStringReply<'master'>, replicationOffest: NumberReply, - replicas: ArrayReply<[host: BlobStringReply, port: BlobStringReply, replicationOffest: BlobStringReply]> + replicas: ArrayReply> ]; type SlaveRole = [ @@ -19,7 +19,7 @@ type SentinelRole = [ masterNames: ArrayReply ]; -type Role = MasterRole | SlaveRole | SentinelRole; +type Role = TuplesReply; export default { FIRST_KEY_INDEX: undefined, @@ -27,18 +27,21 @@ export default { transformArguments() { return ['ROLE']; }, - transformReply(reply: Role) { - switch (reply[0] as Role[0]['DEFAULT']) { + transformReply(reply: UnwrapReply) { + switch (reply[0] as unknown as UnwrapReply) { case 'master': { const [role, replicationOffest, replicas] = reply as MasterRole; return { role, replicationOffest, - replicas: replicas.map(([host, port, replicationOffest]) => ({ - host, - port: Number(port), - replicationOffest: Number(replicationOffest) - })), + replicas: (replicas as unknown as UnwrapReply).map(replica => { + const [host, port, replicationOffest] = replica as unknown as UnwrapReply; + return { + host, + port: Number(port), + replicationOffest: Number(replicationOffest) + }; + }) }; } diff --git a/packages/client/lib/commands/XAUTOCLAIM.ts b/packages/client/lib/commands/XAUTOCLAIM.ts index b7a04734a3..4c78aa1231 100644 --- a/packages/client/lib/commands/XAUTOCLAIM.ts +++ b/packages/client/lib/commands/XAUTOCLAIM.ts @@ -1,4 +1,4 @@ -import { RedisArgument, TuplesReply, BlobStringReply, ArrayReply, Command } from '../RESP/types'; +import { RedisArgument, TuplesReply, BlobStringReply, ArrayReply, UnwrapReply, Command } from '../RESP/types'; import { StreamMessagesRawReply, transformStreamMessagesReply } from './generic-transformers'; export interface XAutoClaimOptions { @@ -37,7 +37,7 @@ export default { return args; }, - transformReply(reply: XAutoClaimRawReply) { + transformReply(reply: UnwrapReply) { return { nextId: reply[0], messages: transformStreamMessagesReply(reply[1]), diff --git a/packages/client/lib/commands/XAUTOCLAIM_JUSTID.ts b/packages/client/lib/commands/XAUTOCLAIM_JUSTID.ts index 2dc0961bc2..e2832f2353 100644 --- a/packages/client/lib/commands/XAUTOCLAIM_JUSTID.ts +++ b/packages/client/lib/commands/XAUTOCLAIM_JUSTID.ts @@ -1,4 +1,4 @@ -import { TuplesReply, BlobStringReply, ArrayReply, Command } from '../RESP/types'; +import { TuplesReply, BlobStringReply, ArrayReply, UnwrapReply, Command } from '../RESP/types'; import XAUTOCLAIM from './XAUTOCLAIM'; type XAutoClaimJustIdRawReply = TuplesReply<[ @@ -15,7 +15,7 @@ export default { redisArgs.push('JUSTID'); return redisArgs; }, - transformReply(reply: XAutoClaimJustIdRawReply) { + transformReply(reply: UnwrapReply) { return { nextId: reply[0], messages: reply[1], diff --git a/packages/client/lib/commands/XINFO_CONSUMERS.ts b/packages/client/lib/commands/XINFO_CONSUMERS.ts index 81da67d161..ca0076d633 100644 --- a/packages/client/lib/commands/XINFO_CONSUMERS.ts +++ b/packages/client/lib/commands/XINFO_CONSUMERS.ts @@ -1,4 +1,4 @@ -import { RedisArgument, ArrayReply, TuplesToMapReply, BlobStringReply, NumberReply, Resp2Reply, Command } from '../RESP/types'; +import { RedisArgument, ArrayReply, TuplesToMapReply, BlobStringReply, NumberReply, UnwrapReply, Resp2Reply, Command } from '../RESP/types'; export type XInfoConsumersReply = ArrayReply, BlobStringReply], @@ -18,13 +18,16 @@ export default { return ['XINFO', 'CONSUMERS', key, group]; }, transformReply: { - 2: (reply: Resp2Reply) => { - return reply.map(consumer => ({ - name: consumer[1], - pending: consumer[3], - idle: consumer[5], - inactive: consumer[7] - })); + 2: (reply: UnwrapReply>) => { + return reply.map(consumer => { + const unwrapped = consumer as unknown as UnwrapReply; + return { + name: unwrapped[1], + pending: unwrapped[3], + idle: unwrapped[5], + inactive: unwrapped[7] + }; + }); }, 3: undefined as unknown as () => XInfoConsumersReply } diff --git a/packages/client/lib/commands/XINFO_GROUPS.ts b/packages/client/lib/commands/XINFO_GROUPS.ts index e2e566d0e2..24661ecde8 100644 --- a/packages/client/lib/commands/XINFO_GROUPS.ts +++ b/packages/client/lib/commands/XINFO_GROUPS.ts @@ -1,4 +1,4 @@ -import { RedisArgument, ArrayReply, TuplesToMapReply, BlobStringReply, NumberReply, Resp2Reply, Command, NullReply } from '../RESP/types'; +import { RedisArgument, ArrayReply, TuplesToMapReply, BlobStringReply, NumberReply, NullReply, UnwrapReply, Resp2Reply, Command } from '../RESP/types'; export type XInfoGroupsReply = ArrayReply, BlobStringReply], @@ -18,15 +18,18 @@ export default { return ['XINFO', 'GROUPS', key]; }, transformReply: { - 2: (reply: Resp2Reply) => { - return reply.map(group => ({ - name: group[1], - consumers: group[3], - pending: group[5], - 'last-delivered-id': group[7], - 'entries-read': group[9], - lag: group[11] - })); + 2: (reply: UnwrapReply>) => { + return reply.map(group => { + const unwrapped = group as unknown as UnwrapReply; + return { + name: unwrapped[1], + consumers: unwrapped[3], + pending: unwrapped[5], + 'last-delivered-id': unwrapped[7], + 'entries-read': unwrapped[9], + lag: unwrapped[11] + }; + }); }, 3: undefined as unknown as () => XInfoGroupsReply } diff --git a/packages/client/lib/commands/XINFO_STREAM.ts b/packages/client/lib/commands/XINFO_STREAM.ts index 93416ae40a..9d809d4ab5 100644 --- a/packages/client/lib/commands/XINFO_STREAM.ts +++ b/packages/client/lib/commands/XINFO_STREAM.ts @@ -67,5 +67,5 @@ export default { } as const satisfies Command; function transformEntry(entry: StreamMessageRawReply | NullReply) { - return entry === null ? null : transformStreamMessageReply(entry); + return entry === null ? null : transformStreamMessageReply(entry as StreamMessageRawReply); } diff --git a/packages/client/lib/commands/XPENDING.ts b/packages/client/lib/commands/XPENDING.ts index efe07ceefb..a6ca4f5a77 100644 --- a/packages/client/lib/commands/XPENDING.ts +++ b/packages/client/lib/commands/XPENDING.ts @@ -1,4 +1,4 @@ -import { RedisArgument, BlobStringReply, NullReply, TuplesReply, NumberReply, Command, ArrayReply } from '../RESP/types'; +import { RedisArgument, BlobStringReply, NullReply, ArrayReply, TuplesReply, NumberReply, UnwrapReply, Command } from '../RESP/types'; type XPendingRawReply = TuplesReply<[ pending: NumberReply, @@ -16,15 +16,19 @@ export default { transformArguments(key: RedisArgument, group: RedisArgument) { return ['XPENDING', key, group]; }, - transformReply(reply: XPendingRawReply) { + transformReply(reply: UnwrapReply) { + const consumers = reply[3] as unknown as UnwrapReply; return { pending: reply[0], firstId: reply[1], lastId: reply[2], - consumers: reply[3] === null ? null : reply[3].map(([name, deliveriesCounter]) => ({ - name, - deliveriesCounter: Number(deliveriesCounter) - })) + consumers: consumers === null ? null : consumers.map(consumer => { + const [name, deliveriesCounter] = consumer as unknown as UnwrapReply; + return { + name, + deliveriesCounter: Number(deliveriesCounter) + }; + }) } } } as const satisfies Command; diff --git a/packages/client/lib/commands/XPENDING_RANGE.ts b/packages/client/lib/commands/XPENDING_RANGE.ts index 4fdf9b2b11..60a28e5172 100644 --- a/packages/client/lib/commands/XPENDING_RANGE.ts +++ b/packages/client/lib/commands/XPENDING_RANGE.ts @@ -1,4 +1,4 @@ -import { RedisArgument, ArrayReply, TuplesReply, BlobStringReply, NumberReply, Command } from '../RESP/types'; +import { RedisArgument, ArrayReply, TuplesReply, BlobStringReply, NumberReply, UnwrapReply, Command } from '../RESP/types'; export interface XPendingRangeOptions { IDLE?: number; @@ -41,12 +41,15 @@ export default { return args; }, - transformReply(reply: XPendingRangeRawReply) { - return reply.map(pending => ({ - id: pending[0], - consumer: pending[1], - millisecondsSinceLastDelivery: pending[2], - deliveriesCounter: pending[3] - })); + transformReply(reply: UnwrapReply) { + return reply.map(pending => { + const unwrapped = pending as unknown as UnwrapReply; + return { + id: unwrapped[0], + consumer: unwrapped[1], + millisecondsSinceLastDelivery: unwrapped[2], + deliveriesCounter: unwrapped[3] + }; + }); } } as const satisfies Command; diff --git a/packages/client/lib/commands/ZMPOP.ts b/packages/client/lib/commands/ZMPOP.ts index 6960006173..4cd8fc8027 100644 --- a/packages/client/lib/commands/ZMPOP.ts +++ b/packages/client/lib/commands/ZMPOP.ts @@ -1,5 +1,5 @@ -import { NullReply, TuplesReply, BlobStringReply, DoubleReply, ArrayReply, Resp2Reply, Command, RedisArgument } from '../RESP/types'; -import { pushVariadicArgument, RedisVariadicArgument, SortedSetSide, transformSortedSetReply } from './generic-transformers'; +import { RedisArgument, NullReply, TuplesReply, BlobStringReply, DoubleReply, ArrayReply, UnwrapReply, Resp2Reply, Command } from '../RESP/types'; +import { pushVariadicArgument, RedisVariadicArgument, SortedSetSide, transformSortedSetReply, transformDoubleReply } from './generic-transformers'; export interface ZMPopOptions { COUNT?: number; @@ -39,20 +39,23 @@ export default { return transformZMPopArguments(['ZMPOP'], ...args); }, transformReply: { - 2: (reply: Resp2Reply) => { + 2(reply: UnwrapReply>) { return reply === null ? null : { key: reply[0], - members: reply[1].map(([value, score]) => ({ - value, - score: Number(score) - })) + members: (reply[1] as unknown as UnwrapReply).map(member => { + const [value, score] = member as unknown as UnwrapReply; + return { + value, + score: transformDoubleReply[2](score) + }; + }) }; }, - 3: (reply: ZMPopRawReply) => { + 3(reply: UnwrapReply) { return reply === null ? null : { key: reply[0], members: transformSortedSetReply[3](reply[1]) }; - }, + } } } as const satisfies Command; diff --git a/packages/client/lib/commands/ZMSCORE.ts b/packages/client/lib/commands/ZMSCORE.ts index 4c3903b011..983503983d 100644 --- a/packages/client/lib/commands/ZMSCORE.ts +++ b/packages/client/lib/commands/ZMSCORE.ts @@ -1,4 +1,4 @@ -import { RedisArgument, ArrayReply, NullReply, BlobStringReply, DoubleReply, Command } from '../RESP/types'; +import { RedisArgument, ArrayReply, NullReply, BlobStringReply, DoubleReply, UnwrapReply, Command } from '../RESP/types'; import { pushVariadicArguments, RedisVariadicArgument, transformNullableDoubleReply } from './generic-transformers'; export default { @@ -11,7 +11,7 @@ export default { return pushVariadicArguments(['ZMSCORE', key], member); }, transformReply: { - 2: (reply: ArrayReply) => { + 2: (reply: UnwrapReply>) => { return reply.map(transformNullableDoubleReply[2]); }, 3: undefined as unknown as () => ArrayReply diff --git a/packages/client/lib/commands/ZPOPMAX.ts b/packages/client/lib/commands/ZPOPMAX.ts index fb4d2d958b..012ba1fbb5 100644 --- a/packages/client/lib/commands/ZPOPMAX.ts +++ b/packages/client/lib/commands/ZPOPMAX.ts @@ -1,4 +1,4 @@ -import { RedisArgument, TuplesReply, BlobStringReply, DoubleReply, Command } from '../RESP/types'; +import { RedisArgument, TuplesReply, BlobStringReply, DoubleReply, UnwrapReply, Command } from '../RESP/types'; export default { FIRST_KEY_INDEX: 1, @@ -7,7 +7,7 @@ export default { return ['ZPOPMAX', key]; }, transformReply: { - 2: (reply: TuplesReply<[]> | TuplesReply<[BlobStringReply, BlobStringReply]>) => { + 2: (reply: UnwrapReply>) => { if (reply.length === 0) return null; return { @@ -15,7 +15,7 @@ export default { score: Number(reply[1]) }; }, - 3: (reply: TuplesReply<[]> | TuplesReply<[BlobStringReply, DoubleReply]>) => { + 3: (reply: UnwrapReply>) => { if (reply.length === 0) return null; return { diff --git a/packages/client/lib/commands/ZRANK_WITHSCORE.ts b/packages/client/lib/commands/ZRANK_WITHSCORE.ts index e68c31c20c..39c788535e 100644 --- a/packages/client/lib/commands/ZRANK_WITHSCORE.ts +++ b/packages/client/lib/commands/ZRANK_WITHSCORE.ts @@ -1,4 +1,4 @@ -import { NullReply, TuplesReply, NumberReply, BlobStringReply, DoubleReply, Command } from '../RESP/types'; +import { NullReply, TuplesReply, NumberReply, BlobStringReply, DoubleReply, UnwrapReply, Command } from '../RESP/types'; import ZRANK from './ZRANK'; export default { @@ -10,7 +10,7 @@ export default { return redisArgs; }, transformReply: { - 2: (reply: NullReply | TuplesReply<[NumberReply, BlobStringReply]>) => { + 2: (reply: UnwrapReply>) => { if (reply === null) return null; return { @@ -18,7 +18,7 @@ export default { score: Number(reply[1]) }; }, - 3: (reply: NullReply | TuplesReply<[BlobStringReply, DoubleReply]>) => { + 3: (reply: UnwrapReply>) => { if (reply === null) return null; return { diff --git a/packages/client/lib/commands/generic-transformers.ts b/packages/client/lib/commands/generic-transformers.ts index 50fa903ea1..017b1922ad 100644 --- a/packages/client/lib/commands/generic-transformers.ts +++ b/packages/client/lib/commands/generic-transformers.ts @@ -1,12 +1,14 @@ -import { ArrayReply, BlobStringReply, BooleanReply, CommandArguments, DoubleReply, NullReply, NumberReply, RedisArgument, TuplesReply } from '../RESP/types'; +import { UnwrapReply, ArrayReply, BlobStringReply, BooleanReply, CommandArguments, DoubleReply, MapReply, NullReply, NumberReply, RedisArgument, TuplesReply } from '../RESP/types'; export const transformBooleanReply = { - 2: (reply: NumberReply<0 | 1>) => reply === 1, + 2: (reply: NumberReply<0 | 1>) => reply as unknown as UnwrapReply === 1, 3: undefined as unknown as () => BooleanReply }; export const transformBooleanArrayReply = { - 2: (reply: ArrayReply>) => reply.map(transformBooleanReply[2]), + 2: (reply: ArrayReply>) => { + return (reply as unknown as UnwrapReply).map(transformBooleanReply[2]); + }, 3: undefined as unknown as () => ArrayReply }; @@ -60,7 +62,7 @@ export const transformNullableDoubleReply = { 2: (reply: BlobStringReply | NullReply) => { if (reply === null) return null; - return transformDoubleReply[2](reply); + return transformDoubleReply[2](reply as BlobStringReply); }, 3: undefined as unknown as () => DoubleReply | NullReply }; @@ -68,10 +70,11 @@ export const transformNullableDoubleReply = { export function transformTuplesReply( reply: ArrayReply ): Record { - const message = Object.create(null); + const inferred = reply as unknown as UnwrapReply, + message = Object.create(null); - for (let i = 0; i < reply.length; i += 2) { - message[reply[i].toString()] = reply[i + 1]; + for (let i = 0; i < inferred.length; i += 2) { + message[inferred[i].toString()] = inferred[i + 1]; } return message; @@ -82,7 +85,8 @@ export type StreamMessageRawReply = TuplesReply<[ message: ArrayReply ]>; -export function transformStreamMessageReply([id, message]: StreamMessageRawReply) { +export function transformStreamMessageReply(reply: StreamMessageRawReply) { + const [id, message] = reply as unknown as UnwrapReply; return { id, message: transformTuplesReply(message) @@ -92,13 +96,11 @@ export function transformStreamMessageReply([id, message]: StreamMessageRawReply export type StreamMessagesRawReply = ArrayReply; export function transformStreamMessagesReply(reply: StreamMessagesRawReply) { - return reply.map(transformStreamMessageReply); + return (reply as unknown as UnwrapReply) + .map(message => transformStreamMessageReply(message)); } -// export type StreamsMessagesReply = Array<{ -// name: RedisArgument; -// messages: StreamMessagesReply; -// }> | null; +// export type StreamsMessagesReply = MapReply; // export function transformStreamsMessagesReply(reply: Array | null): StreamsMessagesReply | null { // if (reply === null) return null; @@ -118,21 +120,25 @@ export type SortedSetSide = 'MIN' | 'MAX'; export const transformSortedSetReply = { 2: (reply: ArrayReply) => { - const members = []; - for (let i = 0; i < reply.length; i += 2) { + const inferred = reply as unknown as UnwrapReply, + members = []; + for (let i = 0; i < inferred.length; i += 2) { members.push({ - value: reply[i], - score: transformDoubleReply[2](reply[i + 1]) + value: inferred[i], + score: transformDoubleReply[2](inferred[i + 1]) }); } return members; }, 3: (reply: ArrayReply>) => { - return reply.map(([value, score]) => ({ - value, - score - })); + return (reply as unknown as UnwrapReply).map(member => { + const [value, score] = member as unknown as UnwrapReply; + return { + value, + score + }; + }); } } diff --git a/packages/client/lib/errors.ts b/packages/client/lib/errors.ts index 898d3833a5..c0815ebde8 100644 --- a/packages/client/lib/errors.ts +++ b/packages/client/lib/errors.ts @@ -1,67 +1,67 @@ export class AbortError extends Error { - constructor() { - super('The command was aborted'); - } + constructor() { + super('The command was aborted'); + } } export class WatchError extends Error { - constructor() { - super('One (or more) of the watched keys has been changed'); - } + constructor() { + super('One (or more) of the watched keys has been changed'); + } } export class ConnectionTimeoutError extends Error { - constructor() { - super('Connection timeout'); - } + constructor() { + super('Connection timeout'); + } } export class ClientClosedError extends Error { - constructor() { - super('The client is closed'); - } + constructor() { + super('The client is closed'); + } } export class ClientOfflineError extends Error { - constructor() { - super('The client is offline'); - } + constructor() { + super('The client is offline'); + } } export class DisconnectsClientError extends Error { - constructor() { - super('Disconnects client'); - } + constructor() { + super('Disconnects client'); + } } export class SocketClosedUnexpectedlyError extends Error { - constructor() { - super('Socket closed unexpectedly'); - } + constructor() { + super('Socket closed unexpectedly'); + } } export class RootNodesUnavailableError extends Error { - constructor() { - super('All the root nodes are unavailable'); - } + constructor() { + super('All the root nodes are unavailable'); + } } export class ReconnectStrategyError extends Error { - originalError: Error; - socketError: unknown; + originalError: Error; + socketError: unknown; - constructor(originalError: Error, socketError: unknown) { - super(originalError.message); - this.originalError = originalError; - this.socketError = socketError; - } + constructor(originalError: Error, socketError: unknown) { + super(originalError.message); + this.originalError = originalError; + this.socketError = socketError; + } } export class ErrorReply extends Error { - constructor(message: string) { - super(message); - this.stack = undefined; - } + constructor(message: string) { + super(message); + this.stack = undefined; + } } export class SimpleError extends ErrorReply {}