Skip to content

Finish RPC transport encoding levels from #144#186

Open
teamchong wants to merge 16 commits into
cloudflare:mainfrom
teamchong:review/encoding-levels-fixes
Open

Finish RPC transport encoding levels from #144#186
teamchong wants to merge 16 commits into
cloudflare:mainfrom
teamchong:review/encoding-levels-fixes

Conversation

@teamchong

Copy link
Copy Markdown
Collaborator

Builds on @ashkalor’s PR #144 and finishes the remaining review feedback for transport encoding levels.

This keeps the original encoding-level design, with follow-up fixes for the rebase and review comments:

  • Type custom transport receive values as unknown at the transport boundary.
  • Estimate stream message sizes when a custom transport cannot report an encoded size, preserving stream flow-control behavior.
  • Preserve async string transport send failures by observing returned promises.
  • Preserve default error stack redaction for structured-clone transports.
  • Simplify the README custom transport docs and remove the stray character.
  • Add a changeset for the new transport encoding-level support.

Testing:

npm run test:types
npm run build
npm test -- __tests__/index.test.ts -t "applies backpressure when custom transport omits stream message size"
npm test -- __tests__/index.test.ts -t "propagates async send failures"
npm test -- __tests__/index.test.ts -t "hides the stack by default with structured clone"

Original implementation by @ashkalor in #144.

@changeset-bot

changeset-bot Bot commented Jun 1, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 4b41568

The changes in this PR will be included in the next version bump.

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new

pkg-pr-new Bot commented Jun 1, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/cloudflare/capnweb@186

commit: 4b41568

@teamchong teamchong force-pushed the review/encoding-levels-fixes branch from 11280c2 to ab11478 Compare June 1, 2026 20:57
@teamchong teamchong marked this pull request as ready for review June 8, 2026 16:07
@teamchong teamchong force-pushed the review/encoding-levels-fixes branch from 6c2dad0 to 2380156 Compare June 10, 2026 02:26
@dimitropoulos dimitropoulos requested review from Copilot and dimitropoulos and removed request for Copilot June 13, 2026 20:26
Comment thread __tests__/index.test.ts Outdated
Comment thread README.md Outdated
Comment thread README.md Outdated
Comment thread README.md Outdated
Comment thread README.md Outdated
Comment thread README.md Outdated

@dimitropoulos dimitropoulos left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

code looks good, but personally I think it could be confusing that "string" means "json string" and "json" means "objects that could be serialized to a json string - but not actually a json string". I think of json as a serialization format, and when I see "json" as an option I'd wrongly think it means what "string" actually means.

my suggestion would be:

string -> json (possibly, more clear, jsonStringified or jsonString)
json -> jsonCompatible
jsonWithBytes -> jsonCompatibleWithBytes
structuredClone -> good! but maybe there's an argument to be made for structuredClonable

@kentonv

kentonv commented Jun 13, 2026

Copy link
Copy Markdown
Member

/bonk review this

ashkalor and others added 13 commits June 13, 2026 23:56
WebSocketTransport is generic, so it can't `implements RpcTransport` directly (the
ArrayBuffer instantiation intentionally doesn't conform). Replace the inline
`const _typeCheck` assertion in websocket.ts with an explanatory comment, and assert
the string instantiation's conformance in __type-tests__/rpc-types.test.ts instead.

This matches the repo's existing type-test convention (run in CI via `test:types`),
keeps the shipped source free of a dead runtime constant, and still honors the cloudflare#144
review request to separately assert WebSocketTransport<string> implements RpcTransport.
- Restore Custom transports section to match main byte-for-byte
- Append suggested Custom encoding levels text verbatim
- Note MessagePort wire-format break in changeset
- Narrow RpcTransport.send return to void | Promise<void>
- Drop unused number return from batch transport send
- Treat non-number custom send() results as unknown size
- Abort session consistently on sync and async send failures
- Restrict structuredClone passthrough to Date and BigInt
- Export WebSocketTransport for use as a building block
- Add test proving reported send() size drives flow control
@teamchong teamchong force-pushed the review/encoding-levels-fixes branch from 2380156 to a43b006 Compare June 14, 2026 04:03
@teamchong teamchong requested a review from dimitropoulos June 15, 2026 03:34
@teamchong

Copy link
Copy Markdown
Collaborator Author

/bonk review this

@ask-bonk ask-bonk Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: Transport encoding levels (finishing #144)

I checked this out locally and verified the build and tests:

  • npm run test:types — passes
  • npm run build — succeeds
  • npx vitest run --project node __tests__/index.test.ts140 passed, including all the new encoding-level, backpressure, async-send-failure, and structured-clone stack-redaction tests.

The overall design is clean and the implementation is careful. The split between RpcTransport (string, default) and RpcTransportWithCustomEncoding, the defensive encodingLevel validation in the RpcSession constructor, and the size-estimation fallback for flow control are all well thought out and thoroughly tested. The README docs read clearly.

A few non-blocking observations below, plus one item I'd ask the maintainers to confirm.

1. Changeset semver level vs. MessagePort wire-format break

The changeset is marked minor, and the body itself notes that MessagePort sessions now post structured-clonable objects instead of JSON strings, requiring both ends to upgrade together. That's a wire-format breaking change for existing MessagePort users (a v0.x minor bump per the changeset's own warning). It's worth an explicit maintainer decision on whether this should be major/minor and whether the break is acceptable, since it's the kind of thing easy to miss in a 0.x release. (The note in the changeset is good — just flagging the policy question.)

2. Synchronous-throw send path is not directly tested

The new send() adds a queueMicrotask(() => this.abort(err, false)) branch for transports that throw synchronously (both the string and custom-encoding paths). The added test propagates async send failures from string transports only covers the rejected-promise path. The synchronous-throw branch — and the deliberate microtask deferral — has no direct coverage. Consider a small test where send() throws synchronously to lock in that behavior.

3. Minor: estimate under-counts shared (non-cyclic) substructures

In estimateEncodedSize, seen is only ever added to, never removed, so a value reachable via two different paths (shared, not cyclic) is counted in full the first time and as ESTIMATED_ENTRY_OVERHEAD (8) afterward. For flow-control estimation this is harmless (it biases low only for shared refs, and the function otherwise biases high), but it's a subtle deviation from a true serialized size. Worth a one-line comment noting this is intentional for cycle-safety rather than precise sizing.

Comment thread src/rpc.ts
Comment thread src/rpc.ts
@@ -0,0 +1,7 @@
---
"capnweb": minor

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The note below correctly calls out that MessagePort sessions change wire format and both ends must upgrade together. Since that's a breaking change for existing MessagePort users, please confirm minor is the intended bump level for this (the changeset's own warning describes a minor 0.x bump). Flagging for an explicit maintainer call rather than asserting it's wrong.

@ask-bonk

ask-bonk Bot commented Jun 19, 2026

Copy link
Copy Markdown

fatal: could not read Username for 'https://github.com': No such device or address

github run

@ask-bonk

ask-bonk Bot commented Jun 19, 2026

Copy link
Copy Markdown

@teamchong Bonk workflow failed. Check the logs for details.

View workflow run · To retry, trigger Bonk again.

Addresses Bonk review feedback on cloudflare#186:
- Add direct tests for the synchronous-throw branches of send() for both
  string and custom-encoding transports, locking in the queueMicrotask
  deferred-abort timing (previously only the async rejected-promise path
  was covered).
- Add a comment to estimateEncodedSize() explaining that the 'seen' set is
  intentionally never pruned: this keeps the estimate cycle-safe at the cost
  of slightly under-counting shared (acyclic) substructure, which is fine for
  a flow-control estimate that otherwise biases high.
The Bonk action's 'Configure Git' step is designed to replace the credential
that actions/checkout persists, swapping in the App installation token. With
persist-credentials: false there was no credential left behind, so any git
command not covered by that rewrite failed with 'could not read Username for
https://github.com'. Let persist-credentials default to true (the official
Bonk template relies on this); contents: read keeps the review job's token
read-only.
@teamchong

Copy link
Copy Markdown
Collaborator Author

bonk's working now, the run only failed on a git credential thing after the review, I included the fixes in this PR

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.

4 participants