Skip to content

client: defaultCallOptions on createClient (0.216.1)#372

Merged
jackyzha0 merged 2 commits intomainfrom
jacky/withCallOptions
May 5, 2026
Merged

client: defaultCallOptions on createClient (0.216.1)#372
jackyzha0 merged 2 commits intomainfrom
jacky/withCallOptions

Conversation

@jackyzha0
Copy link
Copy Markdown
Member

@jackyzha0 jackyzha0 commented May 5, 2026

Why

Consumers of river that need to inject a default signal (or other CallOptions field) into every leaf RPC currently have to wrap the client with a recursive Proxy. That's fragile against river's callable intermediate segments — client.<service> is itself callable, so naive wrappers silently drop the signal at the leaf if the recursion doesn't special-case function-valued targets.

What changed

  • ClientOptions gains optional defaultCallOptions?: CallOptions | (() => CallOptions).
  • The createClient dispatch path resolves the default (calling the function form per leaf invocation) and spreads it under the caller's options. Caller wins field-by-field.
  • Function form lets consumers parameterize a single pooled client without re-creating it (e.g. an ambient signal that changes between invocations).
  • Exports CallOptions and ClientOptions from the router index so consumers can name them.
  • Patch bump 0.216.0 → 0.216.1 (additive, no breaking changes).

Tests

Three new E2E cases under the existing transport×codec matrix (12 runs total):

  1. defaultCallOptions provides signal when caller omits it — eager form drives cancellation.
  2. caller-supplied signal overrides defaultCallOptions — verifies caller wins.
  3. function-form defaultCallOptions is resolved per call — two subscribes capture different signals from a closure variable; aborting one signal cancels only its subscribe.

Full suite: 661 passed | 1 pre-existing skip.

Test plan

  • npx tsc --noEmit
  • npx eslint .
  • npx prettier . --check
  • npx vitest run (full suite, 661/662)
  • New tests: 12/12 across the matrix

Revertibility

One commit. The new field is optional; existing call sites are unaffected.

🤖 Generated with Claude Code

Add an optional `defaultCallOptions: CallOptions | (() => CallOptions)`
on `ClientOptions` that's merged into every leaf call (`rpc`, `stream`,
`upload`, `subscribe`). Caller-supplied `options` still win field-by-
field — so a caller can override `signal` while keeping other defaults.

Function-form is resolved per call, so consumers can parameterize a
single client without re-creating it (e.g. an ambient signal that
changes between invocations).

Motivation: consumers like agent-harness's pid2 wrapper had to roll
their own recursive `Proxy` to inject a default `{ signal }` into every
leaf RPC, which was fragile against river's callable intermediate
segments — wrapping at the wrong level silently dropped the signal.
Pushing this into river removes the ad-hoc client wrapping.

Also exports `CallOptions` and `ClientOptions` from the router index so
downstream consumers can reference both types directly.

Bumped to 0.216.1 (patch — additive, no breaking changes).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@jackyzha0 jackyzha0 requested a review from a team as a code owner May 5, 2026 00:08
@jackyzha0 jackyzha0 requested review from Monkatraz and removed request for a team May 5, 2026 00:08
@jackyzha0 jackyzha0 merged commit 9f564e9 into main May 5, 2026
8 checks passed
@jackyzha0 jackyzha0 deleted the jacky/withCallOptions branch May 5, 2026 00:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants