diff --git a/packages/bloom/lib/commands/bloom/INFO.ts b/packages/bloom/lib/commands/bloom/INFO.ts index b715bf57738..7074885a41e 100644 --- a/packages/bloom/lib/commands/bloom/INFO.ts +++ b/packages/bloom/lib/commands/bloom/INFO.ts @@ -1,6 +1,6 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { RedisArgument, Command, UnwrapReply, NullReply, NumberReply, TuplesToMapReply, Resp2Reply, SimpleStringReply, TypeMapping } from '@redis/client/dist/lib/RESP/types'; -import { transformInfoV2Reply } from '.'; +import { transformInfoV2Reply } from './helpers'; export type BfInfoReplyMap = TuplesToMapReply<[ [SimpleStringReply<'Capacity'>, NumberReply], diff --git a/packages/bloom/lib/commands/bloom/helpers.ts b/packages/bloom/lib/commands/bloom/helpers.ts new file mode 100644 index 00000000000..f5b39c71aa8 --- /dev/null +++ b/packages/bloom/lib/commands/bloom/helpers.ts @@ -0,0 +1,29 @@ +import { RESP_TYPES, TypeMapping } from "@redis/client"; + +export function transformInfoV2Reply(reply: Array, typeMapping?: TypeMapping): T { + const mapType = typeMapping ? typeMapping[RESP_TYPES.MAP] : undefined; + + switch (mapType) { + case Array: { + return reply as unknown as T; + } + case Map: { + const ret = new Map(); + + for (let i = 0; i < reply.length; i += 2) { + ret.set(reply[i].toString(), reply[i + 1]); + } + + return ret as unknown as T; + } + default: { + const ret = Object.create(null); + + for (let i = 0; i < reply.length; i += 2) { + ret[reply[i].toString()] = reply[i + 1]; + } + + return ret as unknown as T; + } + } +} \ No newline at end of file diff --git a/packages/bloom/lib/commands/bloom/index.ts b/packages/bloom/lib/commands/bloom/index.ts index a93f79c9c56..d49ac63b2ea 100644 --- a/packages/bloom/lib/commands/bloom/index.ts +++ b/packages/bloom/lib/commands/bloom/index.ts @@ -1,4 +1,4 @@ -import type { RedisCommands, TypeMapping } from '@redis/client/dist/lib/RESP/types'; +import type { RedisCommands } from '@redis/client/dist/lib/RESP/types'; import ADD from './ADD'; import CARD from './CARD'; @@ -10,7 +10,8 @@ import MADD from './MADD'; import MEXISTS from './MEXISTS'; import RESERVE from './RESERVE'; import SCANDUMP from './SCANDUMP'; -import { RESP_TYPES } from '@redis/client'; + +export * from './helpers'; export default { ADD, @@ -34,31 +35,3 @@ export default { SCANDUMP, scanDump: SCANDUMP } as const satisfies RedisCommands; - -export function transformInfoV2Reply(reply: Array, typeMapping?: TypeMapping): T { - const mapType = typeMapping ? typeMapping[RESP_TYPES.MAP] : undefined; - - switch (mapType) { - case Array: { - return reply as unknown as T; - } - case Map: { - const ret = new Map(); - - for (let i = 0; i < reply.length; i += 2) { - ret.set(reply[i].toString(), reply[i + 1]); - } - - return ret as unknown as T; - } - default: { - const ret = Object.create(null); - - for (let i = 0; i < reply.length; i += 2) { - ret[reply[i].toString()] = reply[i + 1]; - } - - return ret as unknown as T; - } - } -} \ No newline at end of file diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index f48e03d0c19..f9e95025c6a 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -704,7 +704,8 @@ export default class RedisClient< const reply = await this.sendCommand(parser.redisArgs, commandOptions); if (transformReply) { - return transformReply(reply, parser.preserve, commandOptions?.typeMapping); + const res = transformReply(reply, parser.preserve, commandOptions?.typeMapping); + return res } return reply; diff --git a/packages/json/lib/commands/ARRAPPEND.ts b/packages/json/lib/commands/ARRAPPEND.ts index 539eb91a297..d2283b128e3 100644 --- a/packages/json/lib/commands/ARRAPPEND.ts +++ b/packages/json/lib/commands/ARRAPPEND.ts @@ -1,5 +1,5 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; -import { RedisJSON, transformRedisJsonArgument } from '.'; +import { RedisJSON, transformRedisJsonArgument } from './helpers'; import { RedisArgument, NumberReply, ArrayReply, NullReply, Command } from '@redis/client/dist/lib/RESP/types'; export default { diff --git a/packages/json/lib/commands/ARRINDEX.ts b/packages/json/lib/commands/ARRINDEX.ts index 23f010b1f0b..6ffcf9f5f0e 100644 --- a/packages/json/lib/commands/ARRINDEX.ts +++ b/packages/json/lib/commands/ARRINDEX.ts @@ -1,6 +1,6 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { RedisArgument, NumberReply, ArrayReply, NullReply, Command } from '@redis/client/dist/lib/RESP/types'; -import { RedisJSON, transformRedisJsonArgument } from '.'; +import { RedisJSON, transformRedisJsonArgument } from './helpers'; export interface JsonArrIndexOptions { range?: { diff --git a/packages/json/lib/commands/ARRINSERT.ts b/packages/json/lib/commands/ARRINSERT.ts index eb1d7c882f2..e64e0b18559 100644 --- a/packages/json/lib/commands/ARRINSERT.ts +++ b/packages/json/lib/commands/ARRINSERT.ts @@ -1,6 +1,6 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { RedisArgument, NumberReply, ArrayReply, NullReply, Command } from '@redis/client/dist/lib/RESP/types'; -import { RedisJSON, transformRedisJsonArgument } from '.'; +import { RedisJSON, transformRedisJsonArgument } from './helpers'; export default { IS_READ_ONLY: false, diff --git a/packages/json/lib/commands/ARRPOP.ts b/packages/json/lib/commands/ARRPOP.ts index 5f1489c3bd4..30ed34c37be 100644 --- a/packages/json/lib/commands/ARRPOP.ts +++ b/packages/json/lib/commands/ARRPOP.ts @@ -1,7 +1,7 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { RedisArgument, ArrayReply, NullReply, BlobStringReply, Command, UnwrapReply } from '@redis/client/dist/lib/RESP/types'; import { isArrayReply } from '@redis/client/dist/lib/commands/generic-transformers'; -import { transformRedisJsonNullReply } from '.'; +import { transformRedisJsonNullReply } from './helpers'; export interface RedisArrPopOptions { path: RedisArgument; diff --git a/packages/json/lib/commands/GET.spec.ts b/packages/json/lib/commands/GET.spec.ts index 0741de316e1..6b4f44871cb 100644 --- a/packages/json/lib/commands/GET.spec.ts +++ b/packages/json/lib/commands/GET.spec.ts @@ -34,5 +34,11 @@ describe('JSON.GET', () => { await client.json.get('key'), null ); + + await client.json.set('noderedis:users:1', '$', { name: 'Alice', age: 32, }) + const res = await client.json.get('noderedis:users:1'); + assert.equal(typeof res, 'object') + assert.deepEqual(res, { name: 'Alice', age: 32, }) + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/json/lib/commands/GET.ts b/packages/json/lib/commands/GET.ts index d43d7464c50..6705ac534bc 100644 --- a/packages/json/lib/commands/GET.ts +++ b/packages/json/lib/commands/GET.ts @@ -1,7 +1,7 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; -import { transformRedisJsonNullReply } from '.'; +import { transformRedisJsonNullReply } from './helpers'; export interface JsonGetOptions { path?: RedisVariadicArgument; @@ -9,12 +9,16 @@ export interface JsonGetOptions { export default { IS_READ_ONLY: false, - parseCommand(parser: CommandParser, key: RedisArgument, options?: JsonGetOptions) { + parseCommand( + parser: CommandParser, + key: RedisArgument, + options?: JsonGetOptions + ) { parser.push('JSON.GET'); parser.pushKey(key); if (options?.path !== undefined) { - parser.pushVariadic(options.path) + parser.pushVariadic(options.path); } }, transformReply: transformRedisJsonNullReply -} as const satisfies Command; +} as const satisfies Command; \ No newline at end of file diff --git a/packages/json/lib/commands/MERGE.ts b/packages/json/lib/commands/MERGE.ts index 0cb8131a68c..3c93913f91a 100644 --- a/packages/json/lib/commands/MERGE.ts +++ b/packages/json/lib/commands/MERGE.ts @@ -1,6 +1,6 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { SimpleStringReply, Command, RedisArgument } from '@redis/client/dist/lib/RESP/types'; -import { RedisJSON, transformRedisJsonArgument } from '.'; +import { RedisJSON, transformRedisJsonArgument } from './helpers'; export default { IS_READ_ONLY: false, diff --git a/packages/json/lib/commands/MGET.ts b/packages/json/lib/commands/MGET.ts index 447de064d2b..d0fc0a99089 100644 --- a/packages/json/lib/commands/MGET.ts +++ b/packages/json/lib/commands/MGET.ts @@ -1,6 +1,6 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { RedisArgument, UnwrapReply, ArrayReply, NullReply, BlobStringReply, Command } from '@redis/client/dist/lib/RESP/types'; -import { transformRedisJsonNullReply } from '.'; +import { transformRedisJsonNullReply } from './helpers'; export default { IS_READ_ONLY: true, diff --git a/packages/json/lib/commands/MSET.ts b/packages/json/lib/commands/MSET.ts index cb0bea26ddd..2dfab142493 100644 --- a/packages/json/lib/commands/MSET.ts +++ b/packages/json/lib/commands/MSET.ts @@ -1,6 +1,6 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; -import { RedisJSON, transformRedisJsonArgument } from '.'; +import { RedisJSON, transformRedisJsonArgument } from './helpers'; export interface JsonMSetItem { key: RedisArgument; diff --git a/packages/json/lib/commands/SET.ts b/packages/json/lib/commands/SET.ts index 75d7099acfb..27da2ec64ee 100644 --- a/packages/json/lib/commands/SET.ts +++ b/packages/json/lib/commands/SET.ts @@ -1,6 +1,6 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { RedisArgument, SimpleStringReply, NullReply, Command } from '@redis/client/dist/lib/RESP/types'; -import { RedisJSON, transformRedisJsonArgument } from '.'; +import { RedisJSON, transformRedisJsonArgument } from './helpers'; export interface JsonSetOptions { condition?: 'NX' | 'XX'; diff --git a/packages/json/lib/commands/STRAPPEND.ts b/packages/json/lib/commands/STRAPPEND.ts index 45d503856ac..3c0e5767549 100644 --- a/packages/json/lib/commands/STRAPPEND.ts +++ b/packages/json/lib/commands/STRAPPEND.ts @@ -1,6 +1,6 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { RedisArgument, Command, NullReply, NumberReply, ArrayReply } from '@redis/client/dist/lib/RESP/types'; -import { transformRedisJsonArgument } from '.'; +import { transformRedisJsonArgument } from './helpers'; export interface JsonStrAppendOptions { path?: RedisArgument; diff --git a/packages/json/lib/commands/helpers.ts b/packages/json/lib/commands/helpers.ts new file mode 100644 index 00000000000..26ff12f6834 --- /dev/null +++ b/packages/json/lib/commands/helpers.ts @@ -0,0 +1,22 @@ +import { isNullReply } from "@redis/client/dist/lib/commands/generic-transformers"; +import { BlobStringReply, NullReply, UnwrapReply } from "@redis/client/dist/lib/RESP/types"; + +export function transformRedisJsonNullReply(json: NullReply | BlobStringReply): NullReply | RedisJSON { + console.log('transformRedisJsonNullReply', json) + return isNullReply(json) ? json : transformRedisJsonReply(json); +} + +export type RedisJSON = null | boolean | number | string | Date | Array | { + [key: string]: RedisJSON; + [key: number]: RedisJSON; +}; + +export function transformRedisJsonArgument(json: RedisJSON): string { + return JSON.stringify(json); +} + +export function transformRedisJsonReply(json: BlobStringReply): RedisJSON { + const res = JSON.parse((json as unknown as UnwrapReply).toString()); + console.log('transformRedisJsonReply', json, res) + return res; +} diff --git a/packages/json/lib/commands/index.ts b/packages/json/lib/commands/index.ts index 2724ff2565c..a9e16bde757 100644 --- a/packages/json/lib/commands/index.ts +++ b/packages/json/lib/commands/index.ts @@ -1,4 +1,3 @@ -import { BlobStringReply, NullReply, UnwrapReply } from '@redis/client/dist/lib/RESP/types'; import ARRAPPEND from './ARRAPPEND'; import ARRINDEX from './ARRINDEX'; import ARRINSERT from './ARRINSERT'; @@ -23,7 +22,8 @@ import STRAPPEND from './STRAPPEND'; import STRLEN from './STRLEN'; import TOGGLE from './TOGGLE'; import TYPE from './TYPE'; -import { isNullReply } from '@redis/client/dist/lib/commands/generic-transformers'; + +export * from './helpers'; export default { ARRAPPEND, @@ -82,19 +82,3 @@ export default { type: TYPE }; -export type RedisJSON = null | boolean | number | string | Date | Array | { - [key: string]: RedisJSON; - [key: number]: RedisJSON; -}; - -export function transformRedisJsonArgument(json: RedisJSON): string { - return JSON.stringify(json); -} - -export function transformRedisJsonReply(json: BlobStringReply): RedisJSON { - return JSON.parse((json as unknown as UnwrapReply).toString()); -} - -export function transformRedisJsonNullReply(json: NullReply | BlobStringReply): NullReply | RedisJSON { - return isNullReply(json) ? json : transformRedisJsonReply(json); -} diff --git a/packages/time-series/lib/commands/ADD.spec.ts b/packages/time-series/lib/commands/ADD.spec.ts index 055d2246d8b..d66c85441a1 100644 --- a/packages/time-series/lib/commands/ADD.spec.ts +++ b/packages/time-series/lib/commands/ADD.spec.ts @@ -1,7 +1,7 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ADD from './ADD'; -import { TIME_SERIES_ENCODING, TIME_SERIES_DUPLICATE_POLICIES } from '.'; +import { TIME_SERIES_ENCODING, TIME_SERIES_DUPLICATE_POLICIES } from './helpers'; import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('TS.ADD', () => { diff --git a/packages/time-series/lib/commands/ADD.ts b/packages/time-series/lib/commands/ADD.ts index e7626d227da..0f254339ff9 100644 --- a/packages/time-series/lib/commands/ADD.ts +++ b/packages/time-series/lib/commands/ADD.ts @@ -11,7 +11,7 @@ import { parseLabelsArgument, Timestamp, parseIgnoreArgument -} from '.'; +} from './helpers'; export interface TsIgnoreOptions { maxTimeDiff: number; diff --git a/packages/time-series/lib/commands/ALTER.spec.ts b/packages/time-series/lib/commands/ALTER.spec.ts index 560d9ffde2c..46b94c5863a 100644 --- a/packages/time-series/lib/commands/ALTER.spec.ts +++ b/packages/time-series/lib/commands/ALTER.spec.ts @@ -1,7 +1,7 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ALTER from './ALTER'; -import { TIME_SERIES_DUPLICATE_POLICIES } from '.'; +import { TIME_SERIES_DUPLICATE_POLICIES } from './helpers'; import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('TS.ALTER', () => { diff --git a/packages/time-series/lib/commands/ALTER.ts b/packages/time-series/lib/commands/ALTER.ts index f7f6948da71..29f99290a52 100644 --- a/packages/time-series/lib/commands/ALTER.ts +++ b/packages/time-series/lib/commands/ALTER.ts @@ -1,7 +1,8 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { RedisArgument, SimpleStringReply, Command } from '@redis/client/dist/lib/RESP/types'; import { TsCreateOptions } from './CREATE'; -import { parseRetentionArgument, parseChunkSizeArgument, parseDuplicatePolicy, parseLabelsArgument, parseIgnoreArgument } from '.'; +import { parseRetentionArgument, parseChunkSizeArgument, parseDuplicatePolicy, parseLabelsArgument, parseIgnoreArgument } from './helpers'; + export type TsAlterOptions = Pick; diff --git a/packages/time-series/lib/commands/CREATE.spec.ts b/packages/time-series/lib/commands/CREATE.spec.ts index 795b59b880d..4fbfabb6858 100644 --- a/packages/time-series/lib/commands/CREATE.spec.ts +++ b/packages/time-series/lib/commands/CREATE.spec.ts @@ -1,7 +1,7 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import CREATE from './CREATE'; -import { TIME_SERIES_ENCODING, TIME_SERIES_DUPLICATE_POLICIES } from '.'; +import { TIME_SERIES_ENCODING, TIME_SERIES_DUPLICATE_POLICIES } from './helpers'; import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; describe('TS.CREATE', () => { diff --git a/packages/time-series/lib/commands/CREATE.ts b/packages/time-series/lib/commands/CREATE.ts index 39f35c06ed7..c499a752f23 100644 --- a/packages/time-series/lib/commands/CREATE.ts +++ b/packages/time-series/lib/commands/CREATE.ts @@ -10,7 +10,7 @@ import { Labels, parseLabelsArgument, parseIgnoreArgument -} from '.'; +} from './helpers'; import { TsIgnoreOptions } from './ADD'; export interface TsCreateOptions { diff --git a/packages/time-series/lib/commands/DEL.ts b/packages/time-series/lib/commands/DEL.ts index fc96c989b18..de9cadf88c9 100644 --- a/packages/time-series/lib/commands/DEL.ts +++ b/packages/time-series/lib/commands/DEL.ts @@ -1,5 +1,5 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; -import { Timestamp, transformTimestampArgument } from '.'; +import { Timestamp, transformTimestampArgument } from './helpers'; import { RedisArgument, NumberReply, Command, } from '@redis/client/dist/lib/RESP/types'; export default { diff --git a/packages/time-series/lib/commands/INCRBY.ts b/packages/time-series/lib/commands/INCRBY.ts index e62ec42690a..2365f716a83 100644 --- a/packages/time-series/lib/commands/INCRBY.ts +++ b/packages/time-series/lib/commands/INCRBY.ts @@ -1,6 +1,6 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { RedisArgument, NumberReply, Command } from '@redis/client/dist/lib/RESP/types'; -import { Timestamp, transformTimestampArgument, parseRetentionArgument, parseChunkSizeArgument, Labels, parseLabelsArgument, parseIgnoreArgument } from '.'; +import { Timestamp, transformTimestampArgument, parseRetentionArgument, parseChunkSizeArgument, Labels, parseLabelsArgument, parseIgnoreArgument } from './helpers'; import { TsIgnoreOptions } from './ADD'; export interface TsIncrByOptions { diff --git a/packages/time-series/lib/commands/INFO.spec.ts b/packages/time-series/lib/commands/INFO.spec.ts index 73b9d8dc930..994cb281915 100644 --- a/packages/time-series/lib/commands/INFO.spec.ts +++ b/packages/time-series/lib/commands/INFO.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'node:assert'; -import { TIME_SERIES_DUPLICATE_POLICIES } from '.'; +import { TIME_SERIES_DUPLICATE_POLICIES } from './helpers'; import testUtils, { GLOBAL } from '../test-utils'; import INFO, { InfoReply } from './INFO'; import { TIME_SERIES_AGGREGATION_TYPE } from './CREATERULE'; diff --git a/packages/time-series/lib/commands/INFO.ts b/packages/time-series/lib/commands/INFO.ts index fe0e49e095a..62cc1108a80 100644 --- a/packages/time-series/lib/commands/INFO.ts +++ b/packages/time-series/lib/commands/INFO.ts @@ -1,6 +1,6 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { ArrayReply, BlobStringReply, Command, DoubleReply, NumberReply, ReplyUnion, SimpleStringReply, TypeMapping } from "@redis/client/dist/lib/RESP/types"; -import { TimeSeriesDuplicatePolicies } from "."; +import { TimeSeriesDuplicatePolicies } from "./helpers"; import { TimeSeriesAggregationType } from "./CREATERULE"; import { transformDoubleReply } from '@redis/client/dist/lib/commands/generic-transformers'; diff --git a/packages/time-series/lib/commands/INFO_DEBUG.spec.ts b/packages/time-series/lib/commands/INFO_DEBUG.spec.ts index 063b9126550..ff9d6aa3c72 100644 --- a/packages/time-series/lib/commands/INFO_DEBUG.spec.ts +++ b/packages/time-series/lib/commands/INFO_DEBUG.spec.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'node:assert'; -import { TIME_SERIES_DUPLICATE_POLICIES } from '.'; +import { TIME_SERIES_DUPLICATE_POLICIES } from './helpers'; import testUtils, { GLOBAL } from '../test-utils'; import { assertInfo } from './INFO.spec'; import INFO_DEBUG from './INFO_DEBUG'; diff --git a/packages/time-series/lib/commands/MADD.ts b/packages/time-series/lib/commands/MADD.ts index 5af94d6d497..b4c91a98384 100644 --- a/packages/time-series/lib/commands/MADD.ts +++ b/packages/time-series/lib/commands/MADD.ts @@ -1,5 +1,5 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; -import { Timestamp, transformTimestampArgument } from '.'; +import { Timestamp, transformTimestampArgument } from './helpers'; import { ArrayReply, NumberReply, SimpleErrorReply, Command } from '@redis/client/dist/lib/RESP/types'; export interface TsMAddSample { diff --git a/packages/time-series/lib/commands/MGET.ts b/packages/time-series/lib/commands/MGET.ts index fa4e3fc63d6..fd5e8c71b93 100644 --- a/packages/time-series/lib/commands/MGET.ts +++ b/packages/time-series/lib/commands/MGET.ts @@ -1,6 +1,6 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { Command, BlobStringReply, ArrayReply, Resp2Reply, MapReply, TuplesReply, TypeMapping } from '@redis/client/dist/lib/RESP/types'; -import { resp2MapToValue, resp3MapToValue, SampleRawReply, transformSampleReply } from '.'; +import { resp2MapToValue, resp3MapToValue, SampleRawReply, transformSampleReply } from './helpers'; import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; export interface TsMGetOptions { diff --git a/packages/time-series/lib/commands/MGET_SELECTED_LABELS.ts b/packages/time-series/lib/commands/MGET_SELECTED_LABELS.ts index e93b517f80a..d74d073c174 100644 --- a/packages/time-series/lib/commands/MGET_SELECTED_LABELS.ts +++ b/packages/time-series/lib/commands/MGET_SELECTED_LABELS.ts @@ -2,7 +2,7 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { Command, BlobStringReply, NullReply } from '@redis/client/dist/lib/RESP/types'; import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; import { TsMGetOptions, parseLatestArgument, parseFilterArgument } from './MGET'; -import { parseSelectedLabelsArguments } from '.'; +import { parseSelectedLabelsArguments } from './helpers'; import { createTransformMGetLabelsReply } from './MGET_WITHLABELS'; export default { diff --git a/packages/time-series/lib/commands/MGET_WITHLABELS.ts b/packages/time-series/lib/commands/MGET_WITHLABELS.ts index 38b8442db31..737e7236130 100644 --- a/packages/time-series/lib/commands/MGET_WITHLABELS.ts +++ b/packages/time-series/lib/commands/MGET_WITHLABELS.ts @@ -2,7 +2,7 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { Command, BlobStringReply, ArrayReply, Resp2Reply, MapReply, TuplesReply, TypeMapping } from '@redis/client/dist/lib/RESP/types'; import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; import { TsMGetOptions, parseLatestArgument, parseFilterArgument } from './MGET'; -import { RawLabelValue, resp2MapToValue, resp3MapToValue, SampleRawReply, transformRESP2Labels, transformSampleReply } from '.'; +import { RawLabelValue, resp2MapToValue, resp3MapToValue, SampleRawReply, transformRESP2Labels, transformSampleReply } from './helpers'; export interface TsMGetWithLabelsOptions extends TsMGetOptions { SELECTED_LABELS?: RedisVariadicArgument; diff --git a/packages/time-series/lib/commands/MRANGE.ts b/packages/time-series/lib/commands/MRANGE.ts index 95fa5297bdd..3351b755499 100644 --- a/packages/time-series/lib/commands/MRANGE.ts +++ b/packages/time-series/lib/commands/MRANGE.ts @@ -1,7 +1,7 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { Command, ArrayReply, BlobStringReply, Resp2Reply, MapReply, TuplesReply, TypeMapping, RedisArgument } from '@redis/client/dist/lib/RESP/types'; import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; -import { resp2MapToValue, resp3MapToValue, SampleRawReply, Timestamp, transformSamplesReply } from '.'; +import { resp2MapToValue, resp3MapToValue, SampleRawReply, Timestamp, transformSamplesReply } from './helpers'; import { TsRangeOptions, parseRangeArguments } from './RANGE'; import { parseFilterArgument } from './MGET'; diff --git a/packages/time-series/lib/commands/MRANGE_GROUPBY.ts b/packages/time-series/lib/commands/MRANGE_GROUPBY.ts index 5ccd61b2a26..74279d00b6d 100644 --- a/packages/time-series/lib/commands/MRANGE_GROUPBY.ts +++ b/packages/time-series/lib/commands/MRANGE_GROUPBY.ts @@ -1,7 +1,7 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { Command, ArrayReply, BlobStringReply, Resp2Reply, MapReply, TuplesReply, TypeMapping, RedisArgument, TuplesToMapReply, UnwrapReply } from '@redis/client/dist/lib/RESP/types'; import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; -import { resp2MapToValue, resp3MapToValue, SampleRawReply, Timestamp, transformSamplesReply } from '.'; +import { resp2MapToValue, resp3MapToValue, SampleRawReply, Timestamp, transformSamplesReply } from './helpers'; import { TsRangeOptions, parseRangeArguments } from './RANGE'; import { parseFilterArgument } from './MGET'; diff --git a/packages/time-series/lib/commands/MRANGE_SELECTED_LABELS.ts b/packages/time-series/lib/commands/MRANGE_SELECTED_LABELS.ts index 643b57a67e7..75affc54aeb 100644 --- a/packages/time-series/lib/commands/MRANGE_SELECTED_LABELS.ts +++ b/packages/time-series/lib/commands/MRANGE_SELECTED_LABELS.ts @@ -1,7 +1,7 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { Command, ArrayReply, BlobStringReply, Resp2Reply, MapReply, TuplesReply, TypeMapping, NullReply, RedisArgument } from '@redis/client/dist/lib/RESP/types'; import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; -import { parseSelectedLabelsArguments, resp2MapToValue, resp3MapToValue, SampleRawReply, Timestamp, transformRESP2Labels, transformSamplesReply } from '.'; +import { parseSelectedLabelsArguments, resp2MapToValue, resp3MapToValue, SampleRawReply, Timestamp, transformRESP2Labels, transformSamplesReply } from './helpers'; import { TsRangeOptions, parseRangeArguments } from './RANGE'; import { parseFilterArgument } from './MGET'; diff --git a/packages/time-series/lib/commands/MRANGE_SELECTED_LABELS_GROUPBY.ts b/packages/time-series/lib/commands/MRANGE_SELECTED_LABELS_GROUPBY.ts index c5cf1ef56c5..99429a9bb76 100644 --- a/packages/time-series/lib/commands/MRANGE_SELECTED_LABELS_GROUPBY.ts +++ b/packages/time-series/lib/commands/MRANGE_SELECTED_LABELS_GROUPBY.ts @@ -1,7 +1,7 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { Command, ArrayReply, BlobStringReply, MapReply, TuplesReply, RedisArgument, NullReply } from '@redis/client/dist/lib/RESP/types'; import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; -import { parseSelectedLabelsArguments, resp3MapToValue, SampleRawReply, Timestamp, transformSamplesReply } from '.'; +import { parseSelectedLabelsArguments, resp3MapToValue, SampleRawReply, Timestamp, transformSamplesReply } from './helpers'; import { TsRangeOptions, parseRangeArguments } from './RANGE'; import { extractResp3MRangeSources, parseGroupByArguments, TsMRangeGroupBy, TsMRangeGroupByRawMetadataReply3 } from './MRANGE_GROUPBY'; import { parseFilterArgument } from './MGET'; diff --git a/packages/time-series/lib/commands/MRANGE_WITHLABELS.ts b/packages/time-series/lib/commands/MRANGE_WITHLABELS.ts index 19641596a67..ef4864a0307 100644 --- a/packages/time-series/lib/commands/MRANGE_WITHLABELS.ts +++ b/packages/time-series/lib/commands/MRANGE_WITHLABELS.ts @@ -1,7 +1,7 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { Command, UnwrapReply, ArrayReply, BlobStringReply, Resp2Reply, MapReply, TuplesReply, TypeMapping, RedisArgument } from '@redis/client/dist/lib/RESP/types'; import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; -import { resp2MapToValue, resp3MapToValue, SampleRawReply, Timestamp, transformSamplesReply } from '.'; +import { resp2MapToValue, resp3MapToValue, SampleRawReply, Timestamp, transformSamplesReply } from './helpers'; import { TsRangeOptions, parseRangeArguments } from './RANGE'; import { parseFilterArgument } from './MGET'; diff --git a/packages/time-series/lib/commands/MRANGE_WITHLABELS_GROUPBY.ts b/packages/time-series/lib/commands/MRANGE_WITHLABELS_GROUPBY.ts index ff0065e22b7..6552f6328e8 100644 --- a/packages/time-series/lib/commands/MRANGE_WITHLABELS_GROUPBY.ts +++ b/packages/time-series/lib/commands/MRANGE_WITHLABELS_GROUPBY.ts @@ -1,7 +1,7 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { Command, ArrayReply, BlobStringReply, Resp2Reply, MapReply, TuplesReply, TypeMapping, RedisArgument } from '@redis/client/dist/lib/RESP/types'; import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; -import { resp2MapToValue, resp3MapToValue, SampleRawReply, Timestamp, transformRESP2LabelsWithSources, transformSamplesReply } from '.'; +import { resp2MapToValue, resp3MapToValue, SampleRawReply, Timestamp, transformRESP2LabelsWithSources, transformSamplesReply } from './helpers'; import { TsRangeOptions, parseRangeArguments } from './RANGE'; import { extractResp3MRangeSources, parseGroupByArguments, TsMRangeGroupBy, TsMRangeGroupByRawMetadataReply3 } from './MRANGE_GROUPBY'; import { parseFilterArgument } from './MGET'; diff --git a/packages/time-series/lib/commands/RANGE.ts b/packages/time-series/lib/commands/RANGE.ts index f7f808cecdb..44da30d81de 100644 --- a/packages/time-series/lib/commands/RANGE.ts +++ b/packages/time-series/lib/commands/RANGE.ts @@ -1,6 +1,6 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; -import { Timestamp, transformTimestampArgument, SamplesRawReply, transformSamplesReply } from '.'; +import { Timestamp, transformTimestampArgument, SamplesRawReply, transformSamplesReply } from './helpers'; import { TimeSeriesAggregationType } from './CREATERULE'; import { Resp2Reply } from '@redis/client/dist/lib/RESP/types'; diff --git a/packages/time-series/lib/commands/helpers.ts b/packages/time-series/lib/commands/helpers.ts new file mode 100644 index 00000000000..3e277d0747d --- /dev/null +++ b/packages/time-series/lib/commands/helpers.ts @@ -0,0 +1,306 @@ +import { CommandParser } from "@redis/client/dist/lib/client/parser"; +import { TsIgnoreOptions } from "./ADD"; +import { ArrayReply, BlobStringReply, DoubleReply, MapReply, NullReply, NumberReply, ReplyUnion, Resp2Reply, RespType, TuplesReply, TypeMapping, UnwrapReply } from "@redis/client/dist/lib/RESP/types"; +import { RESP_TYPES } from "@redis/client"; +import { RedisVariadicArgument } from "@redis/client/dist/lib/commands/generic-transformers"; + +export function parseIgnoreArgument(parser: CommandParser, ignore?: TsIgnoreOptions) { + if (ignore !== undefined) { + parser.push('IGNORE', ignore.maxTimeDiff.toString(), ignore.maxValDiff.toString()); + } +} + +export function parseRetentionArgument(parser: CommandParser, retention?: number) { + if (retention !== undefined) { + parser.push('RETENTION', retention.toString()); + } +} + +export const TIME_SERIES_ENCODING = { + COMPRESSED: 'COMPRESSED', + UNCOMPRESSED: 'UNCOMPRESSED' +} as const; + +export type TimeSeriesEncoding = typeof TIME_SERIES_ENCODING[keyof typeof TIME_SERIES_ENCODING]; + +export function parseEncodingArgument(parser: CommandParser, encoding?: TimeSeriesEncoding) { + if (encoding !== undefined) { + parser.push('ENCODING', encoding); + } +} + +export function parseChunkSizeArgument(parser: CommandParser, chunkSize?: number) { + if (chunkSize !== undefined) { + parser.push('CHUNK_SIZE', chunkSize.toString()); + } +} + +export const TIME_SERIES_DUPLICATE_POLICIES = { + BLOCK: 'BLOCK', + FIRST: 'FIRST', + LAST: 'LAST', + MIN: 'MIN', + MAX: 'MAX', + SUM: 'SUM' +} as const; + +export type TimeSeriesDuplicatePolicies = typeof TIME_SERIES_DUPLICATE_POLICIES[keyof typeof TIME_SERIES_DUPLICATE_POLICIES]; + +export function parseDuplicatePolicy(parser: CommandParser, duplicatePolicy?: TimeSeriesDuplicatePolicies) { + if (duplicatePolicy !== undefined) { + parser.push('DUPLICATE_POLICY', duplicatePolicy); + } +} + +export type Timestamp = number | Date | string; + +export function transformTimestampArgument(timestamp: Timestamp): string { + if (typeof timestamp === 'string') return timestamp; + + return ( + typeof timestamp === 'number' ? + timestamp : + timestamp.getTime() + ).toString(); +} + +export type Labels = { + [label: string]: string; +}; + +export function parseLabelsArgument(parser: CommandParser, labels?: Labels) { + if (labels) { + parser.push('LABELS'); + + for (const [label, value] of Object.entries(labels)) { + parser.push(label, value); + } + } +} + +export type SampleRawReply = TuplesReply<[timestamp: NumberReply, value: DoubleReply]>; + +export const transformSampleReply = { + 2(reply: Resp2Reply) { + const [ timestamp, value ] = reply as unknown as UnwrapReply; + return { + timestamp, + value: Number(value) // TODO: use double type mapping instead + }; + }, + 3(reply: SampleRawReply) { + const [ timestamp, value ] = reply as unknown as UnwrapReply; + return { + timestamp, + value + }; + } +}; + +export type SamplesRawReply = ArrayReply; + +export const transformSamplesReply = { + 2(reply: Resp2Reply) { + return (reply as unknown as UnwrapReply) + .map(sample => transformSampleReply[2](sample)); + }, + 3(reply: SamplesRawReply) { + return (reply as unknown as UnwrapReply) + .map(sample => transformSampleReply[3](sample)); + } +}; + +// TODO: move to @redis/client? +export function resp2MapToValue< + RAW_VALUE extends TuplesReply<[key: BlobStringReply, ...rest: Array]>, + TRANSFORMED +>( + wrappedReply: ArrayReply, + parseFunc: (rawValue: UnwrapReply) => TRANSFORMED, + typeMapping?: TypeMapping +): MapReply { + const reply = wrappedReply as unknown as UnwrapReply; + switch (typeMapping?.[RESP_TYPES.MAP]) { + case Map: { + const ret = new Map(); + for (const wrappedTuple of reply) { + const tuple = wrappedTuple as unknown as UnwrapReply; + const key = tuple[0] as unknown as UnwrapReply; + ret.set(key.toString(), parseFunc(tuple)); + } + return ret as never; + } + case Array: { + for (const wrappedTuple of reply) { + const tuple = wrappedTuple as unknown as UnwrapReply; + (tuple[1] as unknown as TRANSFORMED) = parseFunc(tuple); + } + return reply as never; + } + default: { + const ret: Record = Object.create(null); + for (const wrappedTuple of reply) { + const tuple = wrappedTuple as unknown as UnwrapReply; + const key = tuple[0] as unknown as UnwrapReply; + ret[key.toString()] = parseFunc(tuple); + } + return ret as never; + } + } +} + +export function resp3MapToValue< + RAW_VALUE extends RespType, // TODO: simplify types + TRANSFORMED +>( + wrappedReply: MapReply, + parseFunc: (rawValue: UnwrapReply) => TRANSFORMED +): MapReply { + const reply = wrappedReply as unknown as UnwrapReply; + if (reply instanceof Array) { + for (let i = 1; i < reply.length; i += 2) { + (reply[i] as unknown as TRANSFORMED) = parseFunc(reply[i] as unknown as UnwrapReply); + } + } else if (reply instanceof Map) { + for (const [key, value] of reply.entries()) { + (reply as unknown as Map).set( + key, + parseFunc(value as unknown as UnwrapReply) + ); + } + } else { + for (const [key, value] of Object.entries(reply)) { + (reply[key] as unknown as TRANSFORMED) = parseFunc(value as unknown as UnwrapReply); + } + } + return reply as never; +} + +export function parseSelectedLabelsArguments( + parser: CommandParser, + selectedLabels: RedisVariadicArgument +) { + parser.push('SELECTED_LABELS'); + parser.pushVariadic(selectedLabels); +} + +export type RawLabelValue = BlobStringReply | NullReply; + +export type RawLabels = ArrayReply>; + +export function transformRESP2Labels( + labels: RawLabels, + typeMapping?: TypeMapping +): MapReply { + const unwrappedLabels = labels as unknown as UnwrapReply; + switch (typeMapping?.[RESP_TYPES.MAP]) { + case Map: + const map = new Map(); + for (const tuple of unwrappedLabels) { + const [key, value] = tuple as unknown as UnwrapReply; + const unwrappedKey = key as unknown as UnwrapReply; + map.set(unwrappedKey.toString(), value); + } + return map as never; + + case Array: + return unwrappedLabels.flat() as never; + + case Object: + default: + const labelsObject: Record = Object.create(null); + for (const tuple of unwrappedLabels) { + const [key, value] = tuple as unknown as UnwrapReply; + const unwrappedKey = key as unknown as UnwrapReply; + labelsObject[unwrappedKey.toString()] = value; + } + return labelsObject as never; + } +} + +export function transformRESP2LabelsWithSources( + labels: RawLabels, + typeMapping?: TypeMapping +) { + const unwrappedLabels = labels as unknown as UnwrapReply; + const to = unwrappedLabels.length - 2; // ignore __reducer__ and __source__ + let transformedLabels: MapReply; + switch (typeMapping?.[RESP_TYPES.MAP]) { + case Map: + const map = new Map(); + for (let i = 0; i < to; i++) { + const [key, value] = unwrappedLabels[i] as unknown as UnwrapReply; + const unwrappedKey = key as unknown as UnwrapReply; + map.set(unwrappedKey.toString(), value); + } + transformedLabels = map as never; + break; + + case Array: + transformedLabels = unwrappedLabels.slice(0, to).flat() as never; + break; + + case Object: + default: + const labelsObject: Record = Object.create(null); + for (let i = 0; i < to; i++) { + const [key, value] = unwrappedLabels[i] as unknown as UnwrapReply; + const unwrappedKey = key as unknown as UnwrapReply; + labelsObject[unwrappedKey.toString()] = value; + } + transformedLabels = labelsObject as never; + break; + } + + const sourcesTuple = unwrappedLabels[unwrappedLabels.length - 1]; + const unwrappedSourcesTuple = sourcesTuple as unknown as UnwrapReply; + // the __source__ label will never be null + const transformedSources = transformRESP2Sources(unwrappedSourcesTuple[1] as BlobStringReply); + + return { + labels: transformedLabels, + sources: transformedSources + }; +} + +function transformRESP2Sources(sourcesRaw: BlobStringReply) { + // if a label contains "," this function will produce incorrcet results.. + // there is not much we can do about it, and we assume most users won't be using "," in their labels.. + + const unwrappedSources = sourcesRaw as unknown as UnwrapReply; + if (typeof unwrappedSources === 'string') { + return unwrappedSources.split(','); + } + + const indexOfComma = unwrappedSources.indexOf(','); + if (indexOfComma === -1) { + return [unwrappedSources]; + } + + const sourcesArray = [ + unwrappedSources.subarray(0, indexOfComma) + ]; + + let previousComma = indexOfComma + 1; + while (true) { + const indexOf = unwrappedSources.indexOf(',', previousComma); + if (indexOf === -1) { + sourcesArray.push( + unwrappedSources.subarray(previousComma) + ); + break; + } + + const source = unwrappedSources.subarray( + previousComma, + indexOf + ); + sourcesArray.push(source); + previousComma = indexOf + 1; + } + + return sourcesArray; +} \ No newline at end of file diff --git a/packages/time-series/lib/commands/index.spec.ts b/packages/time-series/lib/commands/index.spec.ts index 5b28708152f..b565abea476 100644 --- a/packages/time-series/lib/commands/index.spec.ts +++ b/packages/time-series/lib/commands/index.spec.ts @@ -24,7 +24,7 @@ // TimeSeriesDuplicatePolicies, // pushLatestArgument, // TimeSeriesBucketTimestamp -// } from '.'; +// } from './helpers'; // describe('transformTimestampArgument', () => { // it('number', () => { diff --git a/packages/time-series/lib/commands/index.ts b/packages/time-series/lib/commands/index.ts index f340861cb96..43bde4767bf 100644 --- a/packages/time-series/lib/commands/index.ts +++ b/packages/time-series/lib/commands/index.ts @@ -1,5 +1,4 @@ -import type { DoubleReply, NumberReply, RedisCommands, TuplesReply, UnwrapReply, Resp2Reply, ArrayReply, BlobStringReply, MapReply, NullReply, TypeMapping, ReplyUnion, RespType } from '@redis/client/dist/lib/RESP/types'; -import ADD, { TsIgnoreOptions } from './ADD'; +import ADD from './ADD'; import ALTER from './ALTER'; import CREATE from './CREATE'; import CREATERULE from './CREATERULE'; @@ -29,9 +28,9 @@ import MREVRANGE from './MREVRANGE'; import QUERYINDEX from './QUERYINDEX'; import RANGE from './RANGE'; import REVRANGE from './REVRANGE'; -import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; -import { CommandParser } from '@redis/client/dist/lib/client/parser'; -import { RESP_TYPES } from '@redis/client/dist/lib/RESP/decoder'; +import { RedisCommands } from '@redis/client/dist/lib/RESP/types'; + +export * from './helpers'; export default { ADD, @@ -96,303 +95,3 @@ export default { revRange: REVRANGE } as const satisfies RedisCommands; -export function parseIgnoreArgument(parser: CommandParser, ignore?: TsIgnoreOptions) { - if (ignore !== undefined) { - parser.push('IGNORE', ignore.maxTimeDiff.toString(), ignore.maxValDiff.toString()); - } -} - -export function parseRetentionArgument(parser: CommandParser, retention?: number) { - if (retention !== undefined) { - parser.push('RETENTION', retention.toString()); - } -} - -export const TIME_SERIES_ENCODING = { - COMPRESSED: 'COMPRESSED', - UNCOMPRESSED: 'UNCOMPRESSED' -} as const; - -export type TimeSeriesEncoding = typeof TIME_SERIES_ENCODING[keyof typeof TIME_SERIES_ENCODING]; - -export function parseEncodingArgument(parser: CommandParser, encoding?: TimeSeriesEncoding) { - if (encoding !== undefined) { - parser.push('ENCODING', encoding); - } -} - -export function parseChunkSizeArgument(parser: CommandParser, chunkSize?: number) { - if (chunkSize !== undefined) { - parser.push('CHUNK_SIZE', chunkSize.toString()); - } -} - -export const TIME_SERIES_DUPLICATE_POLICIES = { - BLOCK: 'BLOCK', - FIRST: 'FIRST', - LAST: 'LAST', - MIN: 'MIN', - MAX: 'MAX', - SUM: 'SUM' -} as const; - -export type TimeSeriesDuplicatePolicies = typeof TIME_SERIES_DUPLICATE_POLICIES[keyof typeof TIME_SERIES_DUPLICATE_POLICIES]; - -export function parseDuplicatePolicy(parser: CommandParser, duplicatePolicy?: TimeSeriesDuplicatePolicies) { - if (duplicatePolicy !== undefined) { - parser.push('DUPLICATE_POLICY', duplicatePolicy); - } -} - -export type Timestamp = number | Date | string; - -export function transformTimestampArgument(timestamp: Timestamp): string { - if (typeof timestamp === 'string') return timestamp; - - return ( - typeof timestamp === 'number' ? - timestamp : - timestamp.getTime() - ).toString(); -} - -export type Labels = { - [label: string]: string; -}; - -export function parseLabelsArgument(parser: CommandParser, labels?: Labels) { - if (labels) { - parser.push('LABELS'); - - for (const [label, value] of Object.entries(labels)) { - parser.push(label, value); - } - } -} - -export type SampleRawReply = TuplesReply<[timestamp: NumberReply, value: DoubleReply]>; - -export const transformSampleReply = { - 2(reply: Resp2Reply) { - const [ timestamp, value ] = reply as unknown as UnwrapReply; - return { - timestamp, - value: Number(value) // TODO: use double type mapping instead - }; - }, - 3(reply: SampleRawReply) { - const [ timestamp, value ] = reply as unknown as UnwrapReply; - return { - timestamp, - value - }; - } -}; - -export type SamplesRawReply = ArrayReply; - -export const transformSamplesReply = { - 2(reply: Resp2Reply) { - return (reply as unknown as UnwrapReply) - .map(sample => transformSampleReply[2](sample)); - }, - 3(reply: SamplesRawReply) { - return (reply as unknown as UnwrapReply) - .map(sample => transformSampleReply[3](sample)); - } -}; - -// TODO: move to @redis/client? -export function resp2MapToValue< - RAW_VALUE extends TuplesReply<[key: BlobStringReply, ...rest: Array]>, - TRANSFORMED ->( - wrappedReply: ArrayReply, - parseFunc: (rawValue: UnwrapReply) => TRANSFORMED, - typeMapping?: TypeMapping -): MapReply { - const reply = wrappedReply as unknown as UnwrapReply; - switch (typeMapping?.[RESP_TYPES.MAP]) { - case Map: { - const ret = new Map(); - for (const wrappedTuple of reply) { - const tuple = wrappedTuple as unknown as UnwrapReply; - const key = tuple[0] as unknown as UnwrapReply; - ret.set(key.toString(), parseFunc(tuple)); - } - return ret as never; - } - case Array: { - for (const wrappedTuple of reply) { - const tuple = wrappedTuple as unknown as UnwrapReply; - (tuple[1] as unknown as TRANSFORMED) = parseFunc(tuple); - } - return reply as never; - } - default: { - const ret: Record = Object.create(null); - for (const wrappedTuple of reply) { - const tuple = wrappedTuple as unknown as UnwrapReply; - const key = tuple[0] as unknown as UnwrapReply; - ret[key.toString()] = parseFunc(tuple); - } - return ret as never; - } - } -} - -export function resp3MapToValue< - RAW_VALUE extends RespType, // TODO: simplify types - TRANSFORMED ->( - wrappedReply: MapReply, - parseFunc: (rawValue: UnwrapReply) => TRANSFORMED -): MapReply { - const reply = wrappedReply as unknown as UnwrapReply; - if (reply instanceof Array) { - for (let i = 1; i < reply.length; i += 2) { - (reply[i] as unknown as TRANSFORMED) = parseFunc(reply[i] as unknown as UnwrapReply); - } - } else if (reply instanceof Map) { - for (const [key, value] of reply.entries()) { - (reply as unknown as Map).set( - key, - parseFunc(value as unknown as UnwrapReply) - ); - } - } else { - for (const [key, value] of Object.entries(reply)) { - (reply[key] as unknown as TRANSFORMED) = parseFunc(value as unknown as UnwrapReply); - } - } - return reply as never; -} - -export function parseSelectedLabelsArguments( - parser: CommandParser, - selectedLabels: RedisVariadicArgument -) { - parser.push('SELECTED_LABELS'); - parser.pushVariadic(selectedLabels); -} - -export type RawLabelValue = BlobStringReply | NullReply; - -export type RawLabels = ArrayReply>; - -export function transformRESP2Labels( - labels: RawLabels, - typeMapping?: TypeMapping -): MapReply { - const unwrappedLabels = labels as unknown as UnwrapReply; - switch (typeMapping?.[RESP_TYPES.MAP]) { - case Map: - const map = new Map(); - for (const tuple of unwrappedLabels) { - const [key, value] = tuple as unknown as UnwrapReply; - const unwrappedKey = key as unknown as UnwrapReply; - map.set(unwrappedKey.toString(), value); - } - return map as never; - - case Array: - return unwrappedLabels.flat() as never; - - case Object: - default: - const labelsObject: Record = Object.create(null); - for (const tuple of unwrappedLabels) { - const [key, value] = tuple as unknown as UnwrapReply; - const unwrappedKey = key as unknown as UnwrapReply; - labelsObject[unwrappedKey.toString()] = value; - } - return labelsObject as never; - } -} - -export function transformRESP2LabelsWithSources( - labels: RawLabels, - typeMapping?: TypeMapping -) { - const unwrappedLabels = labels as unknown as UnwrapReply; - const to = unwrappedLabels.length - 2; // ignore __reducer__ and __source__ - let transformedLabels: MapReply; - switch (typeMapping?.[RESP_TYPES.MAP]) { - case Map: - const map = new Map(); - for (let i = 0; i < to; i++) { - const [key, value] = unwrappedLabels[i] as unknown as UnwrapReply; - const unwrappedKey = key as unknown as UnwrapReply; - map.set(unwrappedKey.toString(), value); - } - transformedLabels = map as never; - break; - - case Array: - transformedLabels = unwrappedLabels.slice(0, to).flat() as never; - break; - - case Object: - default: - const labelsObject: Record = Object.create(null); - for (let i = 0; i < to; i++) { - const [key, value] = unwrappedLabels[i] as unknown as UnwrapReply; - const unwrappedKey = key as unknown as UnwrapReply; - labelsObject[unwrappedKey.toString()] = value; - } - transformedLabels = labelsObject as never; - break; - } - - const sourcesTuple = unwrappedLabels[unwrappedLabels.length - 1]; - const unwrappedSourcesTuple = sourcesTuple as unknown as UnwrapReply; - // the __source__ label will never be null - const transformedSources = transformRESP2Sources(unwrappedSourcesTuple[1] as BlobStringReply); - - return { - labels: transformedLabels, - sources: transformedSources - }; -} - -function transformRESP2Sources(sourcesRaw: BlobStringReply) { - // if a label contains "," this function will produce incorrcet results.. - // there is not much we can do about it, and we assume most users won't be using "," in their labels.. - - const unwrappedSources = sourcesRaw as unknown as UnwrapReply; - if (typeof unwrappedSources === 'string') { - return unwrappedSources.split(','); - } - - const indexOfComma = unwrappedSources.indexOf(','); - if (indexOfComma === -1) { - return [unwrappedSources]; - } - - const sourcesArray = [ - unwrappedSources.subarray(0, indexOfComma) - ]; - - let previousComma = indexOfComma + 1; - while (true) { - const indexOf = unwrappedSources.indexOf(',', previousComma); - if (indexOf === -1) { - sourcesArray.push( - unwrappedSources.subarray(previousComma) - ); - break; - } - - const source = unwrappedSources.subarray( - previousComma, - indexOf - ); - sourcesArray.push(source); - previousComma = indexOf + 1; - } - - return sourcesArray; -}