fix(client): stop client proxies from resolving primitive coercion over the network - #1815
Conversation
…er the network Backport RECURSIVE_CLIENT_UNWRAP_KEYS from v2 so createORPCClient, createSafeClient, and every createRouterUtils integration resolve bind/valueOf/toString/toJSON to the underlying value instead of treating them as procedure paths. Unlike v2, 'then' stays out of the set because v1 supports procedures named 'then' via preventNativeAwait. Resolves #1809
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Please review this PR if you have time @jeshuaborges |
More templates
@orpc/ai-sdk
@orpc/arktype
@orpc/client
@orpc/contract
@orpc/experimental-durable-iterator
@orpc/hey-api
@orpc/interop
@orpc/json-schema
@orpc/nest
@orpc/openapi
@orpc/openapi-client
@orpc/otel
@orpc/experimental-pino
@orpc/experimental-publisher
@orpc/experimental-publisher-durable-object
@orpc/experimental-ratelimit
@orpc/react
@orpc/react-query
@orpc/experimental-react-swr
@orpc/server
@orpc/shared
@orpc/solid-query
@orpc/standard-server
@orpc/standard-server-aws-lambda
@orpc/standard-server-fastify
@orpc/standard-server-fetch
@orpc/standard-server-node
@orpc/standard-server-peer
@orpc/svelte-query
@orpc/tanstack-query
@orpc/trpc
@orpc/valibot
@orpc/vue-colada
@orpc/vue-query
@orpc/zod
commit: |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes — backport of v2's RECURSIVE_CLIENT_UNWRAP_KEYS (bind, valueOf, toString, toJSON) to the 1.x client proxies:
packages/client/src/consts.ts— newRECURSIVE_CLIENT_UNWRAP_KEYSset.packages/client/src/client.ts+packages/client/src/client-safe.ts— recursive proxygetnow returnsReflect.get(target, key)for unwrap keys instead of recursing into a new sub-client, so primitive coercion /JSON.stringifyno longer fire RPCs or throw.- Seven query integrations'
router-utils.ts(react-query, react-swr, solid-query, svelte-query, tanstack-query, vue-query, vue-colada) — same unwrap guard in outer and inner proxy, plus anisTypescriptObjectguard so utils built over a non-object (e.g.undefined) server-side client no longer crash, andutils.callreturns the underlying client as documented. - Coercion/serialization + non-object-client tests added in every touched package (claimed 100% statement/branch/function/line coverage).
I traced the guard logic against the v2 reference on origin/main and it is faithful, with the one documented divergence that then is intentionally omitted to keep then-named procedures working via preventNativeAwait. The new tests are meaningful — without the fix String(client) demonstrably fires link.call, and they assert not.toBeCalled(). Non-string props, symbol access, and leaf-procedure recursion (existing utils.key.pong tests) all remain intact because isTypescriptObject returns true for functions.
DeepSeek Flash (free via Pullfrog for OSS) | 𝕏
There was a problem hiding this comment.
ℹ️ Minor suggestions only — the docs warning is accurate and well-placed; one small clarification inline.
Reviewed changes — since the prior pullfrog review (75e6c74), one new commit 5802a53e adds a single docs change to apps/content/docs/router.md: a ::: warning note after the basic router example advising against the reserved router keys then, bind, valueOf, toString, toJSON.
The rest of the PR (the RECURSIVE_CLIENT_UNWRAP_KEYS backport across @orpc/client and all query integrations, plus tests) is unchanged from my earlier review, which found no issues.
DeepSeek Flash (free via Pullfrog for OSS) | 𝕏
|
Run failed. View the logs →
|

Backports
RECURSIVE_CLIENT_UNWRAP_KEYSfrom v2 to the 1.x line. Client proxies no longer treatbind,valueOf,toString, andtoJSONas procedure paths, soString(client), template literals, andJSON.stringify(client)resolve normally instead of throwing or issuing spurious RPCs — including the React 19.2 dev-build freeze when a client is passed through props.Resolves #1809
Fixes
createORPCClientandcreateSafeClientno longer fire network requests or throw on primitive coercion and serialization.createRouterUtilsin all query integrations (tanstack-query, react-query, vue-query, solid-query, svelte-query, vue-colada, react-swr) gained the same unwrap keys plus v2'sisTypescriptObjectguard:JSON.stringifyof utils built over a server-side client no longer crashes, andutils.callon server-side clients now returns the underlying client as documented.thenis intentionally not in the unwrap set: v1 keeps supporting procedures namedthenviapreventNativeAwait, so no existing behavior breaks in a patch release.Testing