From c6249c7109a234c457f0345cdd21b1e769f26d6f Mon Sep 17 00:00:00 2001 From: Jean-Samuel Girard Date: Wed, 25 Mar 2026 14:17:33 -0400 Subject: [PATCH 01/14] feat(json): add reviver support to GET, MGET, and ARRPOP Allows passing an optional JSON.parse reviver function to transformReply. This enables automatic rehydration of types (like Dates) that are not natively supported by JSON. Modified: - JSON.GET: Added reviver to GetOptions - JSON.MGET: Added reviver to arguments - JSON.ARRPOP: Added reviver to ArrPopOptions - generic-transformers: Added reviver to transformRedisJsonReply and transformRedisJsonNullReply arguments Utilized .preserve to pass reviver through commands. --- packages/client/lib/commands/generic-transformers.ts | 10 ++++++---- packages/json/lib/commands/ARRPOP.ts | 12 ++++++++---- packages/json/lib/commands/GET.ts | 5 ++++- packages/json/lib/commands/MGET.ts | 10 ++++++---- 4 files changed, 24 insertions(+), 13 deletions(-) diff --git a/packages/client/lib/commands/generic-transformers.ts b/packages/client/lib/commands/generic-transformers.ts index 33286ea7392..3a615175c50 100644 --- a/packages/client/lib/commands/generic-transformers.ts +++ b/packages/client/lib/commands/generic-transformers.ts @@ -678,11 +678,13 @@ 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()); +export type JsonReviver = Parameters[1]; + +export function transformRedisJsonReply(json: BlobStringReply, reviver?: JsonReviver): RedisJSON { + const res = JSON.parse((json as unknown as UnwrapReply).toString(), reviver); return res; } -export function transformRedisJsonNullReply(json: NullReply | BlobStringReply): NullReply | RedisJSON { - return isNullReply(json) ? json : transformRedisJsonReply(json); +export function transformRedisJsonNullReply(json: NullReply | BlobStringReply, reviver?: JsonReviver ): NullReply | RedisJSON { + return isNullReply(json) ? json : transformRedisJsonReply(json, reviver); } diff --git a/packages/json/lib/commands/ARRPOP.ts b/packages/json/lib/commands/ARRPOP.ts index 88e4da96980..421647fdc54 100644 --- a/packages/json/lib/commands/ARRPOP.ts +++ b/packages/json/lib/commands/ARRPOP.ts @@ -1,9 +1,10 @@ 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, transformRedisJsonNullReply } from '@redis/client/dist/lib/commands/generic-transformers'; +import { isArrayReply, transformRedisJsonNullReply, JsonReviver } from '@redis/client/dist/lib/commands/generic-transformers'; export interface RedisArrPopOptions { path: RedisArgument; + reviver?: JsonReviver; index?: number; } @@ -17,6 +18,7 @@ export default { * @param key - The key containing the array * @param options - Optional parameters * @param options.path - Path to the array in the JSON document + * @param options.reviver - An optional reviver function to call when parsing the reply from Redis * @param options.index - Optional index to pop from. Default is -1 (last element) */ parseCommand(parser: CommandParser, key: RedisArgument, options?: RedisArrPopOptions) { @@ -29,12 +31,14 @@ export default { if (options.index !== undefined) { parser.push(options.index.toString()); } + + parser.preserve = options.reviver; } }, - transformReply(reply: NullReply | BlobStringReply | ArrayReply) { + transformReply(reply: NullReply | BlobStringReply | ArrayReply, reviver?: JsonReviver) { return isArrayReply(reply) ? - (reply as unknown as UnwrapReply).map(item => transformRedisJsonNullReply(item)) : - transformRedisJsonNullReply(reply); + (reply as unknown as UnwrapReply).map(item => transformRedisJsonNullReply(item, reviver)) : + transformRedisJsonNullReply(reply, reviver); } } as const satisfies Command; diff --git a/packages/json/lib/commands/GET.ts b/packages/json/lib/commands/GET.ts index 14ec46a53af..304d9b871ca 100644 --- a/packages/json/lib/commands/GET.ts +++ b/packages/json/lib/commands/GET.ts @@ -1,9 +1,10 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser'; import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; -import { RedisVariadicArgument, transformRedisJsonNullReply } from '@redis/client/dist/lib/commands/generic-transformers'; +import { RedisVariadicArgument, transformRedisJsonNullReply, JsonReviver } from '@redis/client/dist/lib/commands/generic-transformers'; export interface JsonGetOptions { path?: RedisVariadicArgument; + reviver?: JsonReviver } export default { @@ -16,6 +17,7 @@ export default { * @param key - The key containing the JSON document * @param options - Optional parameters * @param options.path - Path(s) to the value(s) to retrieve + * @param options.reviver - An optional reviver function to call when parsing the reply from Redis */ parseCommand( parser: CommandParser, @@ -27,6 +29,7 @@ export default { if (options?.path !== undefined) { parser.pushVariadic(options.path); } + parser.preserve = options?.reviver; }, transformReply: transformRedisJsonNullReply } as const satisfies Command; \ No newline at end of file diff --git a/packages/json/lib/commands/MGET.ts b/packages/json/lib/commands/MGET.ts index 01a7783b922..5d79d944815 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 '@redis/client/dist/lib/commands/generic-transformers'; +import { transformRedisJsonNullReply, JsonReviver } from '@redis/client/dist/lib/commands/generic-transformers'; export default { IS_READ_ONLY: true, @@ -11,13 +11,15 @@ export default { * @param parser - The Redis command parser * @param keys - Array of keys containing JSON documents * @param path - Path to retrieve from each document + * @param reviver - An optional reviver function to call when parsing the reply from Redis */ - parseCommand(parser: CommandParser, keys: Array, path: RedisArgument) { + parseCommand(parser: CommandParser, keys: Array, path: RedisArgument, reviver?: JsonReviver) { parser.push('JSON.MGET'); parser.pushKeys(keys); parser.push(path); + parser.preserve = reviver; }, - transformReply(reply: UnwrapReply>) { - return reply.map(json => transformRedisJsonNullReply(json)) + transformReply(reply: UnwrapReply>, reviver?: JsonReviver) { + return reply.map(json => transformRedisJsonNullReply(json, reviver)) } } as const satisfies Command; From 5e1d3d79b2e91077f312f1c3cd2e00f8eb8aa5bf Mon Sep 17 00:00:00 2001 From: Jean-Samuel Girard Date: Thu, 26 Mar 2026 13:34:22 -0400 Subject: [PATCH 02/14] fix(json): ARRPOP path should be optional even if reviver is set --- packages/json/lib/commands/ARRPOP.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/json/lib/commands/ARRPOP.ts b/packages/json/lib/commands/ARRPOP.ts index 421647fdc54..9205f478f7d 100644 --- a/packages/json/lib/commands/ARRPOP.ts +++ b/packages/json/lib/commands/ARRPOP.ts @@ -26,7 +26,9 @@ export default { parser.pushKey(key); if (options) { - parser.push(options.path); + if (options.path !== undefined) { + parser.push(options.path); + } if (options.index !== undefined) { parser.push(options.index.toString()); From a1f862bf6012a833d2469a23d5ca9c68b6728635 Mon Sep 17 00:00:00 2001 From: Jean-Samuel Girard Date: Thu, 26 Mar 2026 13:35:01 -0400 Subject: [PATCH 03/14] tests(json): add reviver tests --- packages/json/lib/commands/ARRPOP.spec.ts | 12 ++++++++++++ packages/json/lib/commands/GET.spec.ts | 13 +++++++++++++ packages/json/lib/commands/MGET.spec.ts | 9 +++++++++ 3 files changed, 34 insertions(+) diff --git a/packages/json/lib/commands/ARRPOP.spec.ts b/packages/json/lib/commands/ARRPOP.spec.ts index f823e7fc08a..965fc73b8a3 100644 --- a/packages/json/lib/commands/ARRPOP.spec.ts +++ b/packages/json/lib/commands/ARRPOP.spec.ts @@ -63,5 +63,17 @@ describe('JSON.ARRPOP', () => { assert.deepEqual(reply, ['value']); }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('$ path with reviver', async client => { + const [, res] = await Promise.all([ + client.json.set('key', '$', [{ name: 'Alice', birthday: new Date('1998-02-12') }]), + client.json.arrPop('key', { + path: '$', + reviver: (key, value) => { if (key === 'birthdate') return new Date(value); else return value; } + }) + ]); + + assert(typeof res === 'object' && res !== null && 'birthday' in res && res.birthday instanceof Date && res.birthday.getTime() === new Date('1998-02-12').getTime()); + }, GLOBAL.SERVERS.OPEN); }); }); diff --git a/packages/json/lib/commands/GET.spec.ts b/packages/json/lib/commands/GET.spec.ts index 6b4f44871cb..b4b92158843 100644 --- a/packages/json/lib/commands/GET.spec.ts +++ b/packages/json/lib/commands/GET.spec.ts @@ -41,4 +41,17 @@ describe('JSON.GET', () => { assert.deepEqual(res, { name: 'Alice', age: 32, }) }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('client.json.get with reviver', async client => { + assert.equal( + await client.json.get('key',{ reviver: ()=>{ assert.fail() } }), + null + ); + + await client.json.set('noderedis:users:1', '$', { name: 'Alice', birthday: new Date('1998-02-12') }); + const res = await client.json.get('noderedis:users:1', { reviver: (key, value) => { if (key === 'birthdate') return new Date(value); else return value; } }); + assert(typeof res === 'object' && res !== null && 'birthday' in res && res.birthday instanceof Date && res.birthday.getTime() === new Date('1998-02-12').getTime()); + + }, GLOBAL.SERVERS.OPEN); + }); diff --git a/packages/json/lib/commands/MGET.spec.ts b/packages/json/lib/commands/MGET.spec.ts index 2d8efafde71..fd848644e9c 100644 --- a/packages/json/lib/commands/MGET.spec.ts +++ b/packages/json/lib/commands/MGET.spec.ts @@ -17,4 +17,13 @@ describe('JSON.MGET', () => { [null, null] ); }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('client.json.mGet with reviver', async client => { + await client.json.set('noderedis:users:1', '$', { name: 'Alice', birthday: new Date('1998-02-12') }); + await client.json.set('noderedis:users:2', '$', { name: 'Bob', birthday: new Date('1996-07-23') }); + const res = await client.json.mGet(['noderedis:users:1', 'noderedis:users:2'], '$', (key, value) => { if (key === 'birthdate') return new Date(value); else return value; }); + assert(typeof res[0] === 'object' && res[0] !== null && 'birthday' in res[0] && (res[0].birthday instanceof Date) && res[0].birthday.getTime() === new Date('1998-02-12').getTime()); + assert(typeof res[1] === 'object' && res[1] !== null && 'birthday' in res[1] && res[1].birthday instanceof Date && res[1].birthday.getTime() === new Date('1996-07-23').getTime()); + + }, GLOBAL.SERVERS.OPEN); }); From 9254d8a5a87a15d05378795bb31bfc5c2f7378dc Mon Sep 17 00:00:00 2001 From: Jean-Samuel Girard Date: Thu, 26 Mar 2026 13:48:50 -0400 Subject: [PATCH 04/14] tests(json): typo in tests --- packages/json/lib/commands/ARRPOP.spec.ts | 2 +- packages/json/lib/commands/GET.spec.ts | 2 +- packages/json/lib/commands/MGET.spec.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/json/lib/commands/ARRPOP.spec.ts b/packages/json/lib/commands/ARRPOP.spec.ts index 965fc73b8a3..d4e7f122ecd 100644 --- a/packages/json/lib/commands/ARRPOP.spec.ts +++ b/packages/json/lib/commands/ARRPOP.spec.ts @@ -69,7 +69,7 @@ describe('JSON.ARRPOP', () => { client.json.set('key', '$', [{ name: 'Alice', birthday: new Date('1998-02-12') }]), client.json.arrPop('key', { path: '$', - reviver: (key, value) => { if (key === 'birthdate') return new Date(value); else return value; } + reviver: (key, value) => { if (key === 'birthday') return new Date(value); else return value; } }) ]); diff --git a/packages/json/lib/commands/GET.spec.ts b/packages/json/lib/commands/GET.spec.ts index b4b92158843..d4a640b3993 100644 --- a/packages/json/lib/commands/GET.spec.ts +++ b/packages/json/lib/commands/GET.spec.ts @@ -49,7 +49,7 @@ describe('JSON.GET', () => { ); await client.json.set('noderedis:users:1', '$', { name: 'Alice', birthday: new Date('1998-02-12') }); - const res = await client.json.get('noderedis:users:1', { reviver: (key, value) => { if (key === 'birthdate') return new Date(value); else return value; } }); + const res = await client.json.get('noderedis:users:1', { reviver: (key, value) => { if (key === 'birthday') return new Date(value); else return value; } }); assert(typeof res === 'object' && res !== null && 'birthday' in res && res.birthday instanceof Date && res.birthday.getTime() === new Date('1998-02-12').getTime()); }, GLOBAL.SERVERS.OPEN); diff --git a/packages/json/lib/commands/MGET.spec.ts b/packages/json/lib/commands/MGET.spec.ts index fd848644e9c..952cb899719 100644 --- a/packages/json/lib/commands/MGET.spec.ts +++ b/packages/json/lib/commands/MGET.spec.ts @@ -21,7 +21,7 @@ describe('JSON.MGET', () => { testUtils.testWithClient('client.json.mGet with reviver', async client => { await client.json.set('noderedis:users:1', '$', { name: 'Alice', birthday: new Date('1998-02-12') }); await client.json.set('noderedis:users:2', '$', { name: 'Bob', birthday: new Date('1996-07-23') }); - const res = await client.json.mGet(['noderedis:users:1', 'noderedis:users:2'], '$', (key, value) => { if (key === 'birthdate') return new Date(value); else return value; }); + const res = await client.json.mGet(['noderedis:users:1', 'noderedis:users:2'], '$', (key, value) => { if (key === 'birthday') return new Date(value); else return value; }); assert(typeof res[0] === 'object' && res[0] !== null && 'birthday' in res[0] && (res[0].birthday instanceof Date) && res[0].birthday.getTime() === new Date('1998-02-12').getTime()); assert(typeof res[1] === 'object' && res[1] !== null && 'birthday' in res[1] && res[1].birthday instanceof Date && res[1].birthday.getTime() === new Date('1996-07-23').getTime()); From ed7aa40aebad8e326b67b5e9039930f7c0ed94c3 Mon Sep 17 00:00:00 2001 From: Jean-Samuel Girard Date: Thu, 26 Mar 2026 13:56:44 -0400 Subject: [PATCH 05/14] tests(json): fix MGET test with reviver using $ but not expecting an array (using . instead) --- packages/json/lib/commands/MGET.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/json/lib/commands/MGET.spec.ts b/packages/json/lib/commands/MGET.spec.ts index 952cb899719..eaa3584972c 100644 --- a/packages/json/lib/commands/MGET.spec.ts +++ b/packages/json/lib/commands/MGET.spec.ts @@ -21,7 +21,7 @@ describe('JSON.MGET', () => { testUtils.testWithClient('client.json.mGet with reviver', async client => { await client.json.set('noderedis:users:1', '$', { name: 'Alice', birthday: new Date('1998-02-12') }); await client.json.set('noderedis:users:2', '$', { name: 'Bob', birthday: new Date('1996-07-23') }); - const res = await client.json.mGet(['noderedis:users:1', 'noderedis:users:2'], '$', (key, value) => { if (key === 'birthday') return new Date(value); else return value; }); + const res = await client.json.mGet(['noderedis:users:1', 'noderedis:users:2'], '.', (key, value) => { if (key === 'birthday') return new Date(value); else return value; }); assert(typeof res[0] === 'object' && res[0] !== null && 'birthday' in res[0] && (res[0].birthday instanceof Date) && res[0].birthday.getTime() === new Date('1998-02-12').getTime()); assert(typeof res[1] === 'object' && res[1] !== null && 'birthday' in res[1] && res[1].birthday instanceof Date && res[1].birthday.getTime() === new Date('1996-07-23').getTime()); From d26496f3f80c410b895fa3b1a76c365fb6fc70ea Mon Sep 17 00:00:00 2001 From: Jean-Samuel Girard Date: Thu, 26 Mar 2026 14:00:57 -0400 Subject: [PATCH 06/14] fix(json): ARRPOP should ignore index if path is not defined --- packages/json/lib/commands/ARRPOP.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/json/lib/commands/ARRPOP.ts b/packages/json/lib/commands/ARRPOP.ts index 9205f478f7d..f4c0f664b9d 100644 --- a/packages/json/lib/commands/ARRPOP.ts +++ b/packages/json/lib/commands/ARRPOP.ts @@ -28,10 +28,10 @@ export default { if (options) { if (options.path !== undefined) { parser.push(options.path); - } - if (options.index !== undefined) { - parser.push(options.index.toString()); + if (options.index !== undefined) { + parser.push(options.index.toString()); + } } parser.preserve = options.reviver; From 7d09619e31091bb30a261526ac215277f014cb42 Mon Sep 17 00:00:00 2001 From: Jean-Samuel Girard Date: Thu, 26 Mar 2026 14:08:27 -0400 Subject: [PATCH 07/14] fix(json): ARRPOP path option is optional --- packages/json/lib/commands/ARRPOP.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/json/lib/commands/ARRPOP.ts b/packages/json/lib/commands/ARRPOP.ts index f4c0f664b9d..5efd05cf186 100644 --- a/packages/json/lib/commands/ARRPOP.ts +++ b/packages/json/lib/commands/ARRPOP.ts @@ -3,7 +3,7 @@ import { RedisArgument, ArrayReply, NullReply, BlobStringReply, Command, UnwrapR import { isArrayReply, transformRedisJsonNullReply, JsonReviver } from '@redis/client/dist/lib/commands/generic-transformers'; export interface RedisArrPopOptions { - path: RedisArgument; + path?: RedisArgument; reviver?: JsonReviver; index?: number; } From ebffff020c33d722dfdbc9394c5edcb3f888febc Mon Sep 17 00:00:00 2001 From: Jean-Samuel Girard Date: Tue, 31 Mar 2026 12:14:19 -0400 Subject: [PATCH 08/14] tests(json): added test with multi() to json.GET --- packages/json/lib/commands/GET.spec.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages/json/lib/commands/GET.spec.ts b/packages/json/lib/commands/GET.spec.ts index d4a640b3993..9fae8bb9b00 100644 --- a/packages/json/lib/commands/GET.spec.ts +++ b/packages/json/lib/commands/GET.spec.ts @@ -53,5 +53,17 @@ describe('JSON.GET', () => { assert(typeof res === 'object' && res !== null && 'birthday' in res && res.birthday instanceof Date && res.birthday.getTime() === new Date('1998-02-12').getTime()); }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('client.multi().json.get with reviver', async client => { + assert.equal( + (await client.multi().json.get('key',{ reviver: ()=>{ assert.fail() } }).exec()), + null + ); + + await client.json.set('noderedis:users:1', '$', { name: 'Alice', birthday: new Date('1998-02-12') }); + const res = (await client.multi().json.get('noderedis:users:1', { reviver: (key, value) => { if (key === 'birthday') return new Date(value); else return value; } }).exec())[0]; + assert(typeof res === 'object' && res !== null && 'birthday' in res && res.birthday instanceof Date && res.birthday.getTime() === new Date('1998-02-12').getTime()); + + }, GLOBAL.SERVERS.OPEN); }); From 9cc42f83cd268d5d6deb2501db010cd1877e4198 Mon Sep 17 00:00:00 2001 From: Jean-Samuel Girard Date: Tue, 31 Mar 2026 12:14:19 -0400 Subject: [PATCH 09/14] tests(json): added test with multi() to json.GET --- packages/json/lib/commands/GET.spec.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages/json/lib/commands/GET.spec.ts b/packages/json/lib/commands/GET.spec.ts index d4a640b3993..5fcb1705b98 100644 --- a/packages/json/lib/commands/GET.spec.ts +++ b/packages/json/lib/commands/GET.spec.ts @@ -53,5 +53,17 @@ describe('JSON.GET', () => { assert(typeof res === 'object' && res !== null && 'birthday' in res && res.birthday instanceof Date && res.birthday.getTime() === new Date('1998-02-12').getTime()); }, GLOBAL.SERVERS.OPEN); + + testUtils.testWithClient('client.multi().json.get with reviver', async client => { + assert.equal( + (await client.multi().json.get('key',{ reviver: ()=>{ assert.fail() } }).exec())[0], + null + ); + + await client.json.set('noderedis:users:1', '$', { name: 'Alice', birthday: new Date('1998-02-12') }); + const res = (await client.multi().json.get('noderedis:users:1', { reviver: (key, value) => { if (key === 'birthday') return new Date(value); else return value; } }).exec())[0]; + assert(typeof res === 'object' && res !== null && 'birthday' in res && res.birthday instanceof Date && res.birthday.getTime() === new Date('1998-02-12').getTime()); + + }, GLOBAL.SERVERS.OPEN); }); From 6885b6c4e95740b7d8fac15eee16ea5c47318c74 Mon Sep 17 00:00:00 2001 From: Jean-Samuel Girard Date: Mon, 25 May 2026 13:58:10 -0400 Subject: [PATCH 10/14] fix(json): revert unintended change due to merging from older source --- packages/json/lib/commands/ARRPOP.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/json/lib/commands/ARRPOP.ts b/packages/json/lib/commands/ARRPOP.ts index 4c3e10b49b1..4582bf328bb 100644 --- a/packages/json/lib/commands/ARRPOP.ts +++ b/packages/json/lib/commands/ARRPOP.ts @@ -3,7 +3,7 @@ import { RedisArgument, ArrayReply, NullReply, BlobStringReply, Command, UnwrapR import { isArrayReply, transformRedisJsonNullReply, JsonReviver } from '@redis/client/dist/lib/commands/generic-transformers'; export interface RedisArrPopOptions { - path?: RedisArgument; + path: RedisArgument; reviver?: JsonReviver; index?: number; } @@ -15,13 +15,12 @@ export default { parser.pushKey(key); if (options) { - if (options.path !== undefined) { parser.push(options.path); if (options.index !== undefined) { parser.push(options.index.toString()); } - } + parser.preserve = options.reviver; } From cf2c5684e88aa4df638e61a3d6dac8b1182124ba Mon Sep 17 00:00:00 2001 From: Jean-Samuel Girard Date: Mon, 25 May 2026 14:11:38 -0400 Subject: [PATCH 11/14] docs(json): js-doc for reviver option --- packages/json/lib/commands/index.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/json/lib/commands/index.ts b/packages/json/lib/commands/index.ts index 774706dc5c9..4337b804348 100644 --- a/packages/json/lib/commands/index.ts +++ b/packages/json/lib/commands/index.ts @@ -120,6 +120,7 @@ export default { * @param options - Optional parameters * @param options.path - Path to the array in the JSON document * @param options.index - Optional index to pop from. Default is -1 (last element) + * @param options.reviver - An optional reviver function to call when parsing the reply from Redis */ ARRPOP, /** @@ -130,6 +131,7 @@ export default { * @param options - Optional parameters * @param options.path - Path to the array in the JSON document * @param options.index - Optional index to pop from. Default is -1 (last element) + * @param options.reviver - An optional reviver function to call when parsing the reply from Redis */ arrPop: ARRPOP, /** @@ -231,6 +233,7 @@ export default { * @param key - The key containing the JSON document * @param options - Optional parameters * @param options.path - Path(s) to the value(s) to retrieve + * @param options.reviver - An optional reviver function to call when parsing the reply from Redis */ GET, /** @@ -240,6 +243,7 @@ export default { * @param key - The key containing the JSON document * @param options - Optional parameters * @param options.path - Path(s) to the value(s) to retrieve + * @param options.reviver - An optional reviver function to call when parsing the reply from Redis */ get: GET, /** @@ -266,6 +270,7 @@ export default { * * @param keys - Array of keys containing JSON documents * @param path - Path to retrieve from each document + * @param reviver - An optional reviver function to call when parsing the reply from Redis */ MGET, /** @@ -274,6 +279,7 @@ export default { * * @param keys - Array of keys containing JSON documents * @param path - Path to retrieve from each document + * @param reviver - An optional reviver function to call when parsing the reply from Redis */ mGet: MGET, /** From 9b35e7fa26044a871788e4cbf51740744dc56554 Mon Sep 17 00:00:00 2001 From: Jean-Samuel Girard Date: Mon, 25 May 2026 14:22:07 -0400 Subject: [PATCH 12/14] fix(json): ARRPOP Optional path option --- packages/json/lib/commands/ARRPOP.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/json/lib/commands/ARRPOP.ts b/packages/json/lib/commands/ARRPOP.ts index 4582bf328bb..2d35d50504e 100644 --- a/packages/json/lib/commands/ARRPOP.ts +++ b/packages/json/lib/commands/ARRPOP.ts @@ -2,11 +2,12 @@ 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, transformRedisJsonNullReply, JsonReviver } from '@redis/client/dist/lib/commands/generic-transformers'; -export interface RedisArrPopOptions { - path: RedisArgument; +export type RedisArrPopOptions = { reviver?: JsonReviver; - index?: number; -} +} & ( + | { path?: never; index?: never } + | { path: RedisArgument; index?: number } +); export default { IS_READ_ONLY: false, @@ -15,12 +16,13 @@ export default { parser.pushKey(key); if (options) { + if (options.path !== undefined) { parser.push(options.path); if (options.index !== undefined) { parser.push(options.index.toString()); } - + } parser.preserve = options.reviver; } From 4012f0701a4e8911bbc9b2cba4064ade575a221f Mon Sep 17 00:00:00 2001 From: Jean-Samuel Girard Date: Tue, 7 Jul 2026 10:28:39 -0400 Subject: [PATCH 13/14] test(json): fix ARRPOP reviver test accessing the array directly instead of the item, causing the test to fail --- packages/json/lib/commands/ARRPOP.spec.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/json/lib/commands/ARRPOP.spec.ts b/packages/json/lib/commands/ARRPOP.spec.ts index d4e7f122ecd..25782d8dcd1 100644 --- a/packages/json/lib/commands/ARRPOP.spec.ts +++ b/packages/json/lib/commands/ARRPOP.spec.ts @@ -73,7 +73,8 @@ describe('JSON.ARRPOP', () => { }) ]); - assert(typeof res === 'object' && res !== null && 'birthday' in res && res.birthday instanceof Date && res.birthday.getTime() === new Date('1998-02-12').getTime()); + const [item] = (res as any[]); + assert(typeof item === 'object' && item !== null && 'birthday' in item && item.birthday instanceof Date && item.birthday.getTime() === new Date('1998-02-12').getTime()); }, GLOBAL.SERVERS.OPEN); }); }); From af50332a63221885c8e783957b97503fc64bf6ef Mon Sep 17 00:00:00 2001 From: Nikolay Karadzhov Date: Wed, 8 Jul 2026 15:14:52 +0300 Subject: [PATCH 14/14] fix(json): type ARRPOP reviver test reply instead of any Replace `res as any[]` cast with `res as Array` to satisfy @typescript-eslint/no-explicit-any lint rule. Co-Authored-By: Claude Opus 4.8 (1M context) --- packages/json/lib/commands/ARRPOP.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/json/lib/commands/ARRPOP.spec.ts b/packages/json/lib/commands/ARRPOP.spec.ts index 25782d8dcd1..e108d74f1e6 100644 --- a/packages/json/lib/commands/ARRPOP.spec.ts +++ b/packages/json/lib/commands/ARRPOP.spec.ts @@ -1,7 +1,7 @@ import { strict as assert } from 'node:assert'; import testUtils, { GLOBAL } from '../test-utils'; import ARRPOP from './ARRPOP'; -import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; +import { parseArgs, RedisJSON } from '@redis/client/lib/commands/generic-transformers'; describe('JSON.ARRPOP', () => { describe('transformArguments', () => { @@ -73,7 +73,7 @@ describe('JSON.ARRPOP', () => { }) ]); - const [item] = (res as any[]); + const [item] = (res as Array); assert(typeof item === 'object' && item !== null && 'birthday' in item && item.birthday instanceof Date && item.birthday.getTime() === new Date('1998-02-12').getTime()); }, GLOBAL.SERVERS.OPEN); });