cloudflare / cloudflare/vinext
Track remaining navigation architecture work
- Dominant language
- TypeScript
- Stars
- 8.8k
- Forks
- 406
- Avg merge
- 2d 6h
- Merged PRs (30d)
- 120
Description
This meta issue tracks the remaining navigation architecture work. Implement it through stacked PRs, not one broad branch.
## Summary
Vinext's navigation control plane is cleaner than Next.js's; Next.js has the stronger React execution layer. Vinext already models route identity, slot bindings, operation tokens, cache proofs, and approved visible commits. The browser runtime still owns semantic decisions that belong in the control plane: request classification, redirect lifecycle, prefetch cache selection, BFCache identity, scroll restoration, and segment cache authority.
Next.js earns its execution-layer edge because `FlightRouterState` and `CacheNode` map to recursive parallel routes, streamed React nodes, `rsc`, `prefetchRsc`, `head`, `prefetchHead`, scroll refs, and BFCache IDs. Vinext needs a React-native segment/cache executor under its own semantic graph, without porting that overloaded router state. That executor needs:
- typed navigation kernel
- `OperationToken` authority
- graph-backed segment identity
- segment cache
- App Shell reuse
- proof-based cache reuse
## Current shape
`navigation-planner.ts` is the small pure planner for Flight-result commit interpretation. It interprets a returned Flight payload against route graph facts, cache proofs, interception proof, root-boundary state, and visible route state. It does not yet own early navigation intent classification, request classification, redirect classification before execution, cache/prefetch candidate interpretation, or token-backed cache authority.
`app-browser-entry.ts` still owns semantic navigation work that should move into a testable control plane:
- request-state derivation
- mounted-slot header selection
- visited-response cache lookup
- prefetch consumption
- optimistic route shell learning
- invalid RSC fallback
- compatibility hard navigation
- redirect following
- streamed redirect handling
- traversal BFCache restoration
- scroll intent handling
- visited-response cache persistence
The rest of the authority is split across adjacent files. `app-browser-state.ts` and `app-browser-visible-commit.ts` gate commits with active navigation IDs, visible commit versions, and `ApprovedVisibleCommit`. `shims/navigation.ts` owns client prefetch state and router API behavior. BFCache derives identity from AppElements wire keys. `OperationToken` exists but does not yet feed every commit and cache authority path.
So traversal, refresh, intercepted routes, redirects, and segment cache reuse do not yet share one authority model.
## Control plane and execution plane
The control plane covers the small planner, lifecycle authority, approved visible commits, cache proof evaluation, and graph identity. Keep these decisions there:
- route identity
- layout persistence
- slot state
- root-boundary changes
- operation authority
- cache reuse proofs
- visible commit approval
The planner stays small. It interprets supplied facts and returns typed decisions. It does not fetch, read or write caches, mutate URL/history, touch React state, settle transition promises, execute server actions, access runtime bindings, or perform final stale-commit approval. The React execution plane runs under that model.
## Rules for every migrated branch
These apply to PRs 1 through 3; the per-PR sections below only state scope.
Planner input is already-collected facts, never live runtime handles. The planner returns a typed decision with no side effects. The executor owns fetches, history mutation, scroll effects, React commits, cache reads and writes, runtime binding access, and MPA navigation.
Each migrated branch:
- passes already-collected facts into the planner
- returns a typed decision with cause and target where relevant
- keeps side effects in the browser executor
- deletes the old inline decision path in the same PR
- covers the behavior with focused tests
Hard navigation gets typed causes and stable targets, so tests assert the cause and target rather than reload behavior alone. Do not keep a shadow path. If a branch is too broad to cut over directly, split it first.
## Architecture PRs
### ~~PR 1. First planner slice: fetch-result classification~~
Finished in #1889. Landed invalid RSC fallback, RSC compatibility hard navigation, and redirect classification/follow-up. For redirects, the planner returns the typed decision, cause, target, history mode, and redirect-depth facts; the browser entry still executes fetches, URL mutation, history updates, scroll effects, and redirect bookkeeping.
Typed hard-navigation causes covered:
- invalid or missing RSC payload
- RSC compatibility mismatch
- cached redirect terminal state
- fresh redirect terminal state
- redirect-depth exhaustion
- streamed redirect that requires MPA navigation
- external or cross-origin redirect target
### ~~PR 2. Same-page search and early navigation intent~~
Finished in #1900. Extended `navigation-planner.ts` with `classifyEarlyNavigationIntent` so early navigation intent flows through the same decision surface as Flight-result commit interpretation. Covers same-page search (cache bypass), hash-only navigation (same-document scroll), and cross-document flight.
### ~~PR 3. Remaining hard-navigation causes~~
Finished in #1901. Landed server-action result classification and RSC navigation error classification.
Typed hard-navigation causes covered:
- server-action redirect with RSC compatibility mismatch
- server-action non-redirect RSC with compatibility mismatch
- generic RSC navigation error recovery
Prefetch reuse moves to PR 4.
### PR 4. Prefetch reuse
Move prefetch reuse into the planner after the PR 1–3 hard-navigation causes stabilize.
Planner inputs:
- cache proof facts
- prefetch candidate facts
- redirect and fetch result facts
Scope:
- visited-response cache lookup
- prefetch consumption
- optimistic route shell learning
### PR 5. Promote `OperationToken`
`OperationToken` should prove that a navigation result may enter commit approval or cache reuse. The token verifies; `ApprovedVisibleCommit` mutates. It feeds the active-navigation, visible-commit-version, graph-version, and cache-variant checks. It never replaces the approved-commit brand or bypasses the visible-state mutation gates.
One token answers all of these:
- Does this result belong to the active navigation?
- Does it match the current route graph version?
- Does it match the visible commit version?
- Does it match the cache variant that produced it?
- Can it update BFCache state?
- Can it update segment cache state?
Commits and cache reuse then share that authority.
### PR 6. Replace BFCache wire-key identity
The current BFCache bridge derives identity from AppElements wire keys. Derive it from route graph state instead. Static topology alone is insufficient; identity depends on:
- route graph version
- semantic route, layout, slot, default, interception, and root-boundary IDs
- visible slot bindings
- interception source and target state
- traversal history entries
- mounted slot preservation
- document-scoped BFCache versioning
- stale history IDs after reload
- scroll snapshot restore
PR #1739 is the likely base for this work, with PR #1743 layering scroll restoration on top.
### PR 7. Build segment cache last
The segment cache needs the planner, token authority, and graph identity first. Build it before those exist and navigation decisions leak into the cache layer, duplicating planner logic.
PR #1420 can land now as a narrow server-side segment-prefetch protocol slice. The full cache still needs:
- per-segment payload storage
- client-side segment reuse
- cache variant keys
- App Shell prefetch integration
- `Next-Did-Postpone` handling
- shell byte-offset extraction
- shared authority with normal navigation
## Open PRs to resolve first
These all touch the same navigation surface. Merge, close, or rebase them before opening a broad architecture branch:
- #1739: BFCache segment state retention with Activity
- #1744: same-page search navigation and `_rsc` handling
- #1742: streamed redirect hard navigation
- #1741: hybrid Pages/App navigation params
- #1743: scroll snapshot restore after traversal
- #1420: segment prefetch request protocol
## Start order
1. **Reconcile the active tactical PRs.** Prioritize #1739; it sets the BFCache base. Rebase #1743 onto it. #1744, #1742, and #1741 land as independent behavior fixes. #1420 can land as a protocol-only segment-prefetch slice if review passes.
2. ~~**PR 1: first planner slice, fetch-result classification.**~~ Finished in #1889.
3. ~~**PR 2: same-page search and early navigation intent.**~~ Finished in #1900.
4. ~~**PR 3: remaining hard-navigation causes.**~~ Finished in #1901.
5. **PR 4: prefetch reuse.**
6. **PR 5: `OperationToken` authority**, once planner decisions reach the commit approval path and before any new cache behavior.
7. **PR 6: BFCache identity on the route graph**, after #1739 lands or rebases. Traversal restore, intercepted-route preservation, parallel-slot preservation, and #1743 scroll restoration sit on top.
8. **PR 7: segment cache and App Shell reuse**, last. #1420 is transport groundwork; hold client segment reuse until cache entries share the authority model of normal navigation.
## Test matrix
Cover the planner and executor split across:
- `push`
- `replace`
- `refresh`
- `traverse`
- `prefetch`
- same-page search
- hash-only navigation
- redirects
- invalid RSC responses
- Pages/App hybrid navigation
- root-boundary changes
- intercepted routes
- default and unmatched parallel slots
- cache reuse
- server-action redirects
- traversal scroll restoration
## Related issues
#654, #1335, #1347, #1348, #1366, #1367, #1369, #1427, #1522, #1523, #1527, #1529, #1534, #1536, #1540, #1555, #1614, #1747, #1769, #1770
## Done criteria
- Browser entry code executes navigation decisions instead of deriving them inline.
- Request classification is testable without `window`, `fetch`, React, history mutation, or cache access.
- Tests can assert hard-navigation causes.
- Migrated branches delete the old inline decision path in the same PR.
- `OperationToken` feeds commit and cache authority without replacing `ApprovedVisibleCommit`.
- BFCache identity comes from route graph state and preserves reload-stale history ID rejection.
- Traversal, refresh, intercepted routes, parallel slots, and scroll restoration use one state model.
- Segment prefetch and App Shell reuse use the same authority model as normal navigation.
Contributor guide
Assessment
This issue has not been assessed yet.