mirror of
https://github.com/cloudflare/vinext.git
synced 2026-09-14 19:04:59 +08:00
vinext@0.1.1
10 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
5748be3ba5 |
feat(cache): gate planner commits on explicit reuse proof (#1327)
* feat(cache): gate planner commits on explicit reuse proof Runtime cache entries could enter browser commit planning as ordinary payloads even when the entry had no planner-visible reuse proof. That violated the #726 invariant that a cache hit is not semantic authority by itself. The planner now receives a compact cache-entry proof decision through AppElements metadata and rejects missing or incompatible cache entries before route-topology commit approval. Cache-restored browser payloads synthesize the missing-proof rejection when metadata is absent, while fresh payloads keep the existing path. Targeted tests cover proof projection, metadata transport, planner decisions, and the cached-payload lifecycle path. * fix(cache): skip unproofed visited RSC cache restores Visited App Router RSC snapshots were still eligible for replay even though production payloads do not yet carry cache-entry reuse proof. That let the new planner gate reject the cache path and turn soft restores into hard navigations. Require decoded proof metadata before a payload is stored as a visited-response restore candidate, and update the real app-router navigation test to prove unproofed responses refetch without reloading. Also make cache-proof rejection-code parsing exhaustive at the type boundary. * fix(cache): require explicit App Router payload origins App Router navigation commits still carried cache-restore semantics as an optional boolean. A fresh default could hide a future restore path that forgot to request reuse proof, reopening the cache authority bug for visited responses. The violated invariant is that cache origin is part of the payload contract, not a caller memory check. Replace the boolean with a discriminated AppNavigationPayloadOrigin and thread it through browser entry, navigation controller, visible commit classification, and tests. Fresh payloads remain proof-optional; visited-cache payloads synthesize a missing-proof rejection and fail closed. * docs(cache): document static layout proof parsing boundary Runtime cache proof parsing currently accepts one proven reuse class: static layout. Future proof classes must update the parser before visited-cache responses can safely restore as commit-capable payloads. The review also noted that the cache-restorable metadata predicate behaves as a narrowing boundary. Make that contract explicit as a TypeScript type guard without changing runtime behavior. * fix(cache): keep accepted proof in rejection traces Cache-proofed payloads that failed a later interception or root-boundary gate produced correct hard-navigation decisions, but the rejection trace lost the accepted proof fields. That made cache-proof diagnostics incomplete for payloads that passed proof validation and failed a later topology gate. The root cause was that later rejection branches still used the pre-cache-proof trace fields after commitTraceFields had been computed. Thread the enriched trace fields into those rejection decisions, document cache proof parser three-way semantics, narrow the cache-restorable metadata type guard precisely, and read the pending commit proof from the canonical top-level field. |
||
|
|
0d8d3e3c60 |
refactor(router): quarantine navigation runtime topology (#1322)
* refactor(router): quarantine navigation runtime topology * fix(router): restore manifest-backed interception runtime App Router browser navigations lost first-hop intercepted route context after snapshot topology stopped acting as fallback proof. That made /feed to /photos/:id request the direct page payload instead of the declared modal payload. The violated invariant was that route topology must come from the manifest, while snapshots only carry runtime proof. Resolve first-hop interception from the manifest, move RSC bootstrap streams onto the symbol-backed runtime, teach prerender extraction that script shape, and harden the runtime contract around route manifest and RSC data. Coverage includes runtime seam tests, prerender extraction tests, planner/browser navigation tests, package rebuild, and the app-router intercepted routes E2E slice. * fix(router): narrow prerender RSC script parsing The prerender extractor accepted any script starting with the symbol-runtime bootstrap expression, then parsed it as though it had the exact RSC chunk-push prefix. That was harmless at the current head, but it made future runtime scripts a foot-gun. Only the exact runtime chunk prefix and exact done marker are now accepted. A focused extractor test covers non-chunk runtime scripts that share the bootstrap expression prefix. * fix(router): address navigation runtime review * fix(router): resolve navigation runtime review threads |
||
|
|
e0eabc122b |
feat(router): add manifest-backed interception topology (#1314)
* feat(router): add manifest-backed interception topology * test(router): cover concrete dynamic interception proof ids * feat(router): index interception facts by slot |
||
|
|
1dbcbfbcc8 |
feat(app-router): plan navigation from RouteManifest topology (#1312)
* feat(app-router): plan navigation from RouteManifest topology Navigation commit approval still trusted flight payload snapshots for root-boundary and target-slot topology even when the static RouteManifest existed. That made stale or missing snapshot metadata authoritative during App Router commits, which could preserve layouts across the wrong root or fall back to soft commits when the graph already proved a same-root transition. The planner now resolves current and target topology through RouteManifest when available, uses snapshot slot bindings only as visible content proof, and keeps explicit interception proof as the authority for intercepted slot commits. The generated browser entry embeds the manifest and approval paths pass it through to the planner. Targeted coverage exercises stale snapshot roots, missing snapshot roots, dynamic URL pattern resolution, manifest target slot bindings, visible-slot proof, intercepted source topology, and browser-entry manifest serialization. * fix(app-router): read route manifest lazily during navigation approval The browser entry assigned the generated RouteManifest global before a static import, but ESM evaluates imported modules before the importer body. That let the browser navigation controller capture null at module evaluation, leaving manifest-aware planning disabled in real browser bootstraps. Make the controller accept a lazy RouteManifest getter and read it at commit-planning time. Also require direct manifest route-id hits to match the snapshot matchedUrl before trusting them, so stale-but-valid route ids fall back to concrete URL pattern resolution. Covered by app browser controller and navigation planner regressions, plus targeted entry template checks. * fix(app-router): clarify manifest route topology resolution Manifest route lookup now shares one matched-url normalization helper across direct route checks and pattern fallback. That keeps concrete dynamic URL fallback from drifting across call sites. Document that manifest pattern iteration follows buildAppRouteGraph route precedence, and record why intercepted targets cannot use direct-target manifest slot bindings. |
||
|
|
16f9ea393f |
fix(app-router): model RSC redirect and traversal lifecycle (#1248)
RSC redirects were followed by mutating browser history before the redirected payload had passed the visible commit lifecycle. That could collapse the initiating history entry and leave back/forward traversal without an explicit intent model. Keep redirect hops inside the initiating navigation until an approved commit publishes history, add explicit terminal redirect decisions, and record per-entry traversal metadata so unknown browser history state stays unknown instead of being guessed. Add focused unit coverage for redirect decisions and traversal metadata plus a browser regression for router.push redirect back navigation. |
||
|
|
656d954abf |
feat(router): promote intercepted preservation through planner (#1249)
* feat(router): promote intercepted preservation through planner Intercepted navigation could still preserve source UI through context-bearing payload shape and same-URL history state. That made modal preservation depend on transport metadata instead of the visible route world that had actually been committed. The planner now requires explicit interception proof before preserving source layouts or unrelated slots, and the browser lifecycle clears stale previous-next history state when normal target payloads commit. Wire metadata carries proof, while missing, stale, or incompatible proof falls back to hard navigation. Tests cover approved source-slot preservation, missing, stale, and malformed proof rejection, current-context refresh and action handling, traverse restoration, and direct-target refresh clearing stale interception state. * fix(router): normalize intercepted proof paths Interception proof could be built from browser-derived percent-encoded paths while committed route snapshots used the server-normalized route-state form. Non-ASCII intercepted source or target paths could then fail proof validation and fall back to a hard navigation. Normalize proof matched URLs before encoding source and target route IDs, and normalize client planner snapshot matched URLs to the same route-state representation without changing the user-facing navigation snapshot. Adds regressions for encoded non-ASCII proof generation and planner validation. * review: address ask-bonk PR review comments - Add interceptedRejectedTargetMismatch reason code for target URL mismatch in planner traces (was misusing UnknownSource) - Document double-attempt replaceState fallback for browser quirks - Clarify navigate branches: proven interception vs legacy fallback - Document cache migration safety for legacy interceptionContext - Tighten isInterceptionMetadataValue type guard with string checks - Extract interception attachment into shared step in wire metadata entries to remove asymmetric branching * fix(router): ignore context-only interception metadata Context-only AppElements payloads could enter intercepted preservation planning or leave context-suffixed route identity as later proof authority. That made transport metadata influence visible-world preservation when a normal response was rendered from an intercepted request context. Only explicit interception proof now enters the preservation branch. Planner snapshots also canonicalize context-suffixed route IDs when no proof is present, keeping render keys partitioned while preventing stale context from poisoning future explicit interception proof. Tests cover the direct planner decision and the two-step browser lifecycle regression. * test(router): cover intercepted proof rejection matrix Planner traces now expose distinct rejection reasons for mismatched intercepted targets and incompatible roots. Without direct tests, those branches could regress while the broader intercepted route scenarios still passed. Add focused planner tests for target mismatch and incompatible root rejection, use the local target snapshot consistently, document two lifecycle ordering contracts, and avoid the no-op interception metadata spread on non-intercepted payloads. |
||
|
|
7d299088b8 |
fix(app-router): promote default slot persistence through route state (#1229)
* fix(app-router): promote default slot persistence through route state Default and unmatched parallel slots were still inferred from AppElements transport markers. That made soft navigation persistence depend on wire shape instead of planner-owned route state, which breaks the #726 invariant that AppElementsWire is transport only. Thread slot binding metadata through AppElements, browser state, and the navigation planner. The commit boundary now preserves previous slot content and binding proof only when target route-state marks the slot default or unmatched and the visible state proves a mounted/default value exists. Tests cover the new planner contract, metadata validation, route wiring, mergeElements behavior, and browser commit lifecycle for default and unmatched slot targets. * fix(app-router): align intercepted slot binding metadata Intercepted slot payloads could render active modal content while their route-state metadata still described the slot as default. The browser planner could then preserve the previous default slot for an ID that the fresh payload should replace, breaking intercepted navigations in app-router E2E. Derive slot IDs through one helper for both metadata and rendering, assert graph/wire ID divergence, and compute binding state from the same resolved override default export used by the render path. Move the app hydration marker to the committed root layout effect so E2E link clicks wait for a hydrated tree. * fix(app-router): harden slot binding preservation Review follow-up for #726-CORE-14. Centralize AppElements slot binding normalization so duplicate slot ids and stale owner layout ids fail at the wire boundary instead of creating quiet last-write-wins behavior. Alias planner slot binding snapshots to the AppElements binding shape, share slot id ordering helpers, document the no-proof compatibility law, and add negative planner/wire tests plus a browser-visible soft navigation E2E. Refs #726. * fix(app-router): align slot binding proof with rendering * docs(app-router): clarify slot preservation invariants |
||
|
|
e534e9bdc3 |
fix(app-router): promote mounted parallel slot preservation (#1191)
* fix(app-router): promote mounted parallel slot preservation Mounted slots were still preserved by treating missing AppElements slot keys as proof during promoted same-root commits. That kept legacy wire absence as semantic authority after layout persistence moved into the planner. The planner snapshot now carries mounted parallel slot ownership and approves preservation only when the slot owner layout is in the retained same-root layout prefix. Approved commits pass that decision to mergeElements, while unknown-root fallbacks keep the legacy absent-slot path fenced. * test(app-router): lock traverse slot cleanup precedence Traverse commits already clear absent slots after copying planner-approved preserved ids. That ordering means a mounted parallel slot can be approved by the planner and still be removed when the traverse payload omits it. Cover that precedence explicitly so the mounted-slot preservation slice keeps navigation slot preservation and traversal cleanup as separate contracts. * fix(app-router): align traverse slot preservation metadata Traverse commits clear absent slots, so a planner proposal should not name mounted slot ids that the visible reducer will remove. That made preserveElementIds semantically wider than the commit contract. Split current-root commit preservation by lane so traverse commits preserve the retained layout prefix only, while ordinary current-root commits still preserve mounted slots owned by retained layouts. Cover both the pure planner decision and browser-state approval boundary. |
||
|
|
04140e47a3 |
feat(app-router): promote same-layout ancestor persistence (#1167)
* feat(app-router): promote same-layout ancestor persistence App Router soft commits previously preserved absent non-slot entries through the merge layer and kept root-layout hard-navigation policy split between browser state and planner. That made same-layout ancestor persistence implicit and allowed unknown root identity to behave like proof of reuse. The planner now returns explicit preserveElementIds for proven same-root transitions, browser state stores layoutIds from wire metadata, and visible commits preserve only planner-approved ancestors while leaving parallel-slot persistence semantics intact. Adds coverage for layout id metadata, unknown-root fallback, stale element removal, and visible layout persistence. * fix(app-router): validate layout persistence proofs App Router payload metadata treated every string in __layoutIds as planner preservation authority. That let a bad producer, stale payload, or future refactor pass a page, slot, or malformed key as proof for layout ancestor reuse. The violated invariant is that layout persistence proof must be validated before it reaches the commit planner. Parse __layoutIds through the AppElements wire-key codec and accept only layout keys. Also add dynamic segment browser coverage for /blog/[slug] navigation, proving param-sensitive layout and page content update while the layout client counter persists. * fix(app-router): scope boundary layout proofs HTTP access fallback payloads derived layout metadata from the full route definition. Layout-level access fallbacks can render only ancestor layouts, so the old proof could let the planner preserve child layouts that were absent from the response. Derive boundary layout metadata from the rendered layout module subset and mark the root boundary unknown when no route layout is rendered. * docs(app-router): document layout id ordering Document the ancestor-first layoutIds invariant at the planner snapshot boundary so future payload producers do not break same-layout prefix persistence. |
||
|
|
77c4d9ce84 |
feat(app-router): plan root-boundary navigation decisions (#1114)
* feat(app-router): plan root-boundary navigation decisions * test(app-router): cover unknown visible root planning * test(app-router): cover stale root-boundary precedence * fix(app-router): thread target href into navigation approval Root-boundary hard-navigation planning could derive its decision URL from the pending route snapshot even though the browser executor navigated to the controller target href. That left planner traces able to disagree with the actual hard navigation target. Require targetHref at visible commit approval boundaries, pass the controller target through navigation and same-url server-action approvals, and record it in lifecycle traces. The focused app-browser tests now assert the planner trace carries the executor URL for hard navigation decisions. * docs(app-router): note route manifest planner follow-up The planner input already carries routeManifest for the issue 726 route-graph slices, but CORE-07/08 always passes null and does not read it. Without a comment, the reserved field looks accidentally unused. Document that routeManifest becomes non-null in CORE-09 and loosen the disposition test helper so render identity can diverge from navigation identity in future coverage. |