Repository navigation
refactor(router): wire protocol module for partial-request parameters (S16) - #1248
Merged
Merged
Conversation
…ters The `_rsc_*` query parameter names now live in one table, `WIRE` in src/wire-params.ts, imported by both graphs. The navigation, prefetch and JS server action URLs are written by one function, browser/wire-protocol.ts `writePartialParams`; its `emptySegments` option keeps the prefetch's omitted empty `_rsc_segments`. The serveShellRequest action request uses the same writer. Every server reader and the testing primitives read `WIRE.*`. The duplicated names go: `FILL_PARAM` and `STALE_PARAM` (prefetch-markers.ts), `SHELL_MISS_PARAM` (rsc/shell-serve.ts) and its browser copy `SHELL_MISS_MARKER` (browser/history-state.ts), with the test that kept those two equal. Parameter names, values and order are unchanged; browser/__tests__/wire-urls.test.ts pins the exact URLs and prefetch keys. check-internals rsc-param-literal: 71 hits in 22 files down to 4, all in use-loader.tsx (a Hard rule 10 file, left as it is); baseline lowered.
49 of 50 tasks
ivogt
added a commit
that referenced
this pull request
Oct 23, 2026
Brings in S16 (#1248, wire-params.ts WIRE), S5 (#1245, deriveRenderPass), S15 (#1243, discovery phases), #1246 and #1247. Conflicts, each resolved by keeping S14's structure and applying S16's intent to it: - rsc/handler.ts imports: kept the request-stages.js import; dropped the classifyRequest, RequestPlan and INTERNAL_RANGO_DEBUG imports S14 had already moved out; added S16's WIRE import, still read by the code that stayed in the handler (warmup, loader refuse, redirect tracking mode, isPartialAction). - rsc/handler.ts response tail: kept the finalizeRequestResponse call; the inline partial/action interception S16 rewrote lives in request-stages.ts and now reads WIRE.partial and WIRE.action. - rsc/handler.ts terminal plans: kept the terminalPlanResponse call; the action-rewrite check, partial redirect and version-mismatch log S16 rewrote live in request-stages.ts and now read WIRE.action, WIRE.partial and WIRE.version. - testing/dispatch.ts imports: added WIRE; dropped applyStreamIdleTimeout, which the stages now call. - testing/dispatch.ts isPartial: kept S14's shape (the reverse and isAction moved into the stages) with S16's WIRE.partial. - tools/internals-baseline.json: took the stack's version; check-internals --update leaves it unchanged (rsc-param-literal 4, all in use-loader.tsx). request-stages.ts has no `_rsc_*` literal left. The handler and stages together make the same searchParams calls as S16's handler.ts: has() on action x3, partial x3, loader and warmup; get() on loader and version.
16 of 17 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
devcorner: rango-t144 · session rango-s282
Problem
The browser built partial-request URLs in three places: the navigation in
browser/navigation-client.ts, the prefetch inbrowser/prefetch/fetch.ts(buildPrefetchUrl), and the JS server action inbrowser/server-action-bridge.ts. All three wrote the same sequence of parameters, and they differed in two ways that matter. A prefetch omits an empty_rsc_segmentswhere the other two send it, and an action sends_rsc_action=<id>where the other two send_rsc_partial=true. The server read the names as string literals in about twenty files._rsc_fill,_rsc_staleand_rsc_shellalso had their own constants, and_rsc_shellhad two of them: one in the server (SHELL_MISS_PARAM) and a browser copy (SHELL_MISS_MARKER), kept equal by a test. A prefetch is adopted only while both writers produce identical prefetch cache keys, so any drift between them breaks adoption without an error.Plan step S16 (rango-d195 section 3; target architecture rango-d194 section 3.5). The change preserves behaviour: no consumer-visible change, and the URLs are byte-identical.
Before / after (internal shape)
Before, each sender wrote its own parameters:
After, every sender calls one writer:
emptySegmentsnavigation-client.ts)"send"stale,version,routerId,rewriteTarget,fillprefetch/fetch.ts)"omit"version,routerId,rewriteTargetserver-action-bridge.ts, URL block only)"send"action,version,routerId,rewriteTargetserveShellRequestaction request (testing)"send"action,rewriteTargetThe plan's sketch had
kind: "navigation" | "prefetch" | { action }next toemptySegments.emptySegmentsalready carries the whole difference between navigation and prefetch, so the writer takesaction?: stringinstead.Code changes
src/wire-params.ts(WIRE,WIRE_PREFIX) andsrc/browser/wire-protocol.ts(writePartialParams,PartialRequestInit).browser/navigation-client.ts,browser/prefetch/fetch.ts, and the URL block ofbrowser/server-action-bridge.ts.commitActionand every other Hard rule 10 region are untouched.testing/serve-shell-request.tsbuildActionRequestcalls the writer too.buildRequestthere keeps writing its params by hand, because its order (rt,fill,stale) differs from the browser's.WIRE.*:router/request-classification.ts,router/navigation-snapshot.ts,router/segment-resolution/prefetch-deferral.ts,rsc/handler.ts,rsc/ssr-setup.ts,rsc/response-route-handler.ts,rsc/rsc-rendering.ts,rsc/loader-fetch.ts,rsc/rewrite-passes.ts,rsc/shell-serve.tscache/cache-scope.ts,cache/document-cache.tstesting/dispatch.ts,testing/serve-shell-request.ts,testing/e2e/page-helpers.tsbrowser/connection-warmup.ts,browser/history-state.ts,browser/prefetch/cache.ts,browser/prefetch/queue.tsFILL_PARAMandSTALE_PARAM(prefetch-markers.tskeepsPREFETCH_HEADERandPREFETCH_FAILED_ROW),SHELL_MISS_PARAM(rsc/shell-serve.ts) andSHELL_MISS_MARKER(browser/history-state.ts). Also removed: thehistory-state.test.tscase that asserted those two were equal. Both sides now readWIRE.shellMiss.tools/internals-baseline.jsonis lowered. Rulersc-param-literaldrops from 71 hits in 22 files to 4, all insrc/use-loader.tsx. That file is on the Hard rule 10 list, so it stays as it is. The tool exemptspackages/rangojs-router/src/wire-params.ts(WIRE_PARAMS_FILE), which is where the new file lives.server-action-bridge.tsandbrowser/prefetch/import the writer statically.Test plan
src/browser/__tests__/wire-urls.test.ts(S3): 10/10. The exact navigation, prefetch and action URLs and the prefetch cache keys are unchanged.src/__tests__/prefetch-fetch.test.ts41/41;src/__tests__/server-action-bridge.test.ts45/45. The redirect and transition-when bridge suites,history-state.test.tsand the browser tests: 27 files, 501 tests, green.E2e, router test-app,
--grep "default-prefetch|prefetch-intercept-source|pending-actions"(checked with--listfirst):--project=dev --no-deps: 21 passed--project=production --no-deps: 21 passedClient bundle, production build, base vs head. Only
assets/router-*.jschanges:Both are within the 200-byte budget. Rolldown keeps the whole
WIREobject in the client chunk, so its two server-only entries (loader,loaderParams) account for about 55 of those bytes.pnpm run typecheck: 16/16 tasks.pnpm run test:unit:all:After the review amend (
serveShellRequestaction writer,loader-fetchmessage, comments):src/testing/__tests__RSC suites: 64 files, 903 passed, 3 expected failtsc: cleanoxlint --deny-warningsandoxfmt --check .: clean.CI lint scripts all OK:
check-e2e-bucketing,check-e2e-parity --strict,check-e2e-console-baselinecheck-suspense-contract,check-no-external-storenode --test tools/*.test.mjscheck-doc-links,check-docs-api,check-skillsgenerate-skills-catalog --check,generate-route-types --checkcheck-internalsNo route changes; no
*.gen.tsdiff.Practices
Lint: I ran
oxlint --deny-warningsandoxfmt --check .on the change. Both are clean; nothing was silenced.Dead code: I searched the repo (src, tests, e2e, docs, skills) for
FILL_PARAM,STALE_PARAM,SHELL_MISS_PARAMandSHELL_MISS_MARKER. Nothing references them any more, and the docs that named them now nameWIRE.*. EveryWIREkey has a reader.WIRE_PREFIXis used byprefetch/queue.ts.PartialRequestInitstays exported becausewritePartialParams's signature needs it underisolatedDeclarations.Tests:
wire-urls.test.tsis the behaviour proof. It already covers the empty-segments send/omit split, the stale and fill order, the action URL and the prefetch keys, so I added no new test.test:unit:allonce.Code review: I reviewed the diff for reuse, simplification, efficiency and altitude. Fixed:
serveShellRequest's action request now useswritePartialParams; its order was already identical.loader-fetch's 400 message readsWIRE.loader.shell-serve.ts,history-state.ts,wire-params.ts,prefetch-markers.tsandprefetch/queue.ts.Left, recorded on rango-t144:
buildRequest's partial order differs from the browser's._rsc_here and_rscinstripInternalParams,cache-key-utils.tsandtesting/dispatch.ts.__rsc,__htmland__no_cacheparams sit outsideWIRE.stale === "true"check.WIREentries ship in the client chunk.use-loader.tsxkeeps its literals (Hard rule 10).None of these is a behaviour defect.
Docs:
docs/internal/feature-map.mdhas a new "Partial-request wire protocol" entry and the shell-miss marker renamed.docs/internal/feature-file-map.mdhas a new row and an updated prefetch-markers note.docs/design/prefetch-false.mdnamesWIRE.fill.Notes
src/use-loader.tsxkeeps 4_rsc_loader/_rsc_loader_paramsliterals in its loader-fetch URL build. The literals sit outside commit timing, but the whole file is on the Hard rule 10 list. Moving them is a separate one-line change once the owner agrees.MISSING SEGMENT ... not in cacheconsole errors duringpending-actions. The same two errors appear on the base in the same test (dev, 21/21 on both), and both are listed intools/e2e-console-baseline.jsonas intermittent on main: not a regression.searchParamscalls is unchanged.