fix: ensure module commands respect proxy typeMapping #3261
Conversation
|
@watersRand thanks, I will have a look! |
nkaradzhov
left a comment
There was a problem hiding this comment.
There are more places where this._self._commandOptions or this._self.commandOptions is used:
node-redis/packages/client/lib/client/index.ts
Line 1186 in e4cc77e
node-redis/packages/client/lib/client/pool.ts
Line 145 in e4cc77e
node-redis/packages/client/lib/client/pool.ts
Line 158 in e4cc77e
It would be great if we can fix and test those as well.
| }); | ||
|
|
||
|
|
||
| testUtils.testWithClient('Module TypeMapping Fix', async client => { |
There was a problem hiding this comment.
maybe change the title to: "proxies respect command options"
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
Reviewed by Cursor Bugbot for commit 3b671da. Configure here.
| .then(resolve, reject) | ||
| .finally(() => { | ||
| this.#returnClient(node); | ||
| }) |
There was a problem hiding this comment.
Pool sendCommand ignores proxy command options
High Severity
The pool's sendCommand passes the call-site options directly to the underlying client without merging this._commandOptions. Unlike the client's and cluster's sendCommand (both updated in this PR to merge proxy options), the pool's version just does client.sendCommand(args, options). When bufferProxy.sendCommand(['ECHO', 'hello']) is called, options is undefined, and the raw pool client has no _commandOptions, so the proxy's typeMapping is never applied. The new test asserting bufferReply instanceof Buffer would fail.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 3b671da. Configure here.
| OPTIONS extends ClusterCommandOptions<TYPE_MAPPING/*, CommandPolicies*/>, | ||
| TYPE_MAPPING extends TypeMapping, | ||
| // POLICIES extends CommandPolicies | ||
| // POLICIES extends CommandPolicies |
There was a problem hiding this comment.
Cluster withCommandOptions lacks prototype-chain inheritance for options
Medium Severity
The cluster's withCommandOptions sets proxy._commandOptions = options directly, unlike the client and pool which were updated in this PR to use Object.assign(Object.create(this._commandOptions ?? null), options). Since sendCommand and module commands were changed to read this._commandOptions (instead of this._self._commandOptions), chaining withCommandOptions calls on the cluster now silently drops parent options like timeout, whereas the same pattern on client/pool correctly inherits them via the prototype chain.
Reviewed by Cursor Bugbot for commit 3b671da. Configure here.


Description
Closes #3055 ,a bug where commands generated via static factories (Modules and Functions) were incorrectly referencing the root client's options (
this._self._commandOptions) instead of the immediate instance's options (this._commandOptions).The Problem:
When using
.withCommandOptions()or.withTypeMapping(), the library creates a proxy object. However, because Modules and Functions were hard-coded to look at the internal_selfreference for their configuration, they completely ignored any user-defined type mappings or timeouts set on the proxy. This effectively broke RESP3 type mapping for all Redis Module commands.The Solution:
#createModuleCommandand#createFunctionCommandto referencethis._commandOptions.sendCommandto prioritize instance-level options.NamespaceProxyClienttype definition to include_commandOptionsto ensure type safety.Allows developers to seamlessly switch between different configurations—such as string vs. binary (Buffer) outputs—across all available commands, including built-in modules, third-party modules, and user-defined libraries (Redis Functions). It enables this flexibility on the fly without the overhead of re-initializing the entire connection or maintaining multiple client instances for different return types.
Checklist
npm testpass with this change (including linting)?Note
Medium Risk
Touches core command dispatch and option merging for client, pool, cluster, and Sentinel; behavior change is intentional but affects all proxied commands.
Overview
Fixes command option inheritance for proxied clients so module, function, and related dispatch paths use the current instance’s
_commandOptionsinstead of always reading the root client’s options via_self.withCommandOptions/sendCommandon standalone clients and pools now layer options withObject.assign(Object.create(...)), so overrides liketypeMappingapply while timeouts and other base settings stay inherited through the prototype chain rather than being dropped or fully replaced.Namespace proxies (e.g.
client.module) are cached per client instance and expose_commandOptionsthrough a getter tied to_self, so updating base options or creating a typed proxy is reflected on module commands without stale snapshots.The same instance-level
_commandOptionswiring is applied across pool, cluster, and Sentinel command factories, with integration tests for buffertypeMappingand inherited timeouts.Reviewed by Cursor Bugbot for commit 3b671da. Bugbot is set up for automated code reviews on this repo. Configure here.