mirror of
https://github.com/cloudflare/vinext.git
synced 2026-09-14 19:04:59 +08:00
codex/cacheability-platform-io
6 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
5dd7a34eb6 |
fix(app-router): isolate page CSS chunks in production (#1738)
* fix(app-router): isolate production page CSS chunks Production App Router builds statically imported every page module into the generated RSC manifest. That let Vite/Rolldown concatenate page-level global CSS from sibling routes into shared CSS assets, so a sibling hash page with scroll-padding-top changed native hash scroll offsets on unrelated pages. The manifest now emits cached dynamic loaders for page modules and loads the matched page module at the route dispatch boundary. Page generateStaticParams sources use the same loader with an explicit missing-export sentinel so prerender still distinguishes absent exports from malformed results. * test(app-router): add focused unit test for route module loader Address the non-blocking review note from PR #1738: per AGENTS.md, behavior moved out of a template string should get a focused helper test. Cover createRouteModuleLoader (promise dedup, rejection propagation), loadRouteModules (page assignment, concurrent dedup, no-op cases, missing `page` key) and loadRouteMatch (null short-circuit, match load). Also cover createLazyGenerateStaticParamsSource (delegation, missing sentinel for null/empty/non-function exports). Document the rejected-load cache intent and the per-request intercept copy in the WeakMap so future readers don't try to "fix" them as bugs. * test: stabilize dev overlay HMR recovery * chore: retrigger ci * chore: fix knip check config * test(app-router): guard intercepted modal CSS + load lazy page before probe Adds the positive-direction guard the bonk review asked for: navigating /feed -> intercepted /photos/[id] must load the lazy modal page and emit its scroll-padding-top CSS chunk. The existing test only proved the negative (CSS absent on a direct /feed visit), so a regression where the lazy modal page failed to load its CSS would have passed silently. Also fixes the probePage() intercept branch, which read intercept.page without resolving the now-lazy __pageLoader. The render path awaits the loader (resolveAppPageInterceptState), but the probe path did not, so the dynamic-bailout probe silently inspected an undefined component and never observed the intercept page's searchParams/headers access. Resolve the lazy page before probing to match the render path. --------- Co-authored-by: James <james@eli.cx> |
||
|
|
82b2720701 |
feat(app-router): support useRouter bfcacheId semantics (#1588)
* feat(app-router): implement useRouter bfcacheId semantics * fix(app-router): refine bfcacheId navigation behavior * test(app-router): align bfcache history state expectations * fix(app-router): handle catch-all bfcache identities * fix(app-router): align bfcache context semantics * fix(app-router): avoid hidden bfcache DOM retention * fix(app-router): hydrate bfcache ids from history state * fix(app-router): handle bfcacheId redirects and slots * test(app-router): expect active slot route ids * Fix bfcache id history restoration * Clarify bfcache id sentinel formatting * Address bfcacheId review feedback * Tie bfcacheId sentinels to shared constants * Address bfcacheId review clarity * Tighten bfcache slot identity check * Deduplicate bfcache history sync check * Handle empty bfcache segment ids * Fix intercepted active slot route ids * Clarify bfcacheId form state coverage * Address bfcacheId review clarity * fix(app-router): skip invisible tree-path segments in bfcache identity getVisibleTreePathSegments only stripped route groups, not parallel @slots or "." default segments. Tree paths contain raw filesystem segments, so a slot preceding visible URL segments over-counted the consumed pathname segments and produced a wrong identity prefix. That re-minted the bfcacheId for layouts/slots that actually persisted across parallel-route navigations (e.g. @modal), diverging from Next.js bfcacheId semantics. Delegate to the canonical isInvisibleSegment helper so the filter stays in sync with the route graph, and add a parallel-slot layout regression test (fails without the fix). Also tighten the hydration-map unit test to actually exercise the "no seeding from minted ids" property instead of asserting a local literal, and correct the SSR bfcache context comment. * fix(app-router): move isInvisibleSegment to browser-safe utils The previous commit imported isInvisibleSegment from app-route-graph.ts into app-browser-state.ts, but app-route-graph.ts imports node:path, node:fs and node:crypto. Pulling it into the browser bundle broke client hydration (app-with-src E2E: instrumentation-client and dev-overlay-recovery timed out waiting for hydration / the navigation runtime). Move the pure isInvisibleSegment helper into the browser-safe routing/utils.ts module (already in the client bundle), re-export it from app-route-graph.ts so Node-side import sites keep working, and have app-browser-state.ts import it from utils.js. The app-route-graph type import is now type-only. * refactor(app-router): address bfcacheId review feedback - Reject history entries with a missing bfcache version in the document-scoped version gate (stop coercing null -> 0) so stale ids from older builds / external pushState can't be restored into a fresh document on the version-0 path. - Parse AppElements metadata once per element map per commit and resolve slot bindings via a precomputed Map (was repeated readMetadata parses plus an O(slots^2) find on every navigation commit). - Read single history-state keys without cloning the whole state object; the write path still clones before mutating. - Dedupe the two identical non-negative-safe-integer validators and share path-segment / catch-all helpers via browser-safe routing/utils. - Move tree-path consumed-segment counting into routing/utils and fix optional-catch-all over-counting. - Drop the unused bfcacheIds param from createHistoryStateWithPreviousNextUrl. * refactor(routing): make catch-all segment predicates module-private isCatchAllSegment/isOptionalCatchAllSegment are only used by countConsumedPathnameSegments in the same module; drop the exports to clear knip's unused-export warning. * test(app-router): lock in bfcache version-gate rejection of missing versions Extract the version-gate decision into a pure, exported isHistoryStateBfcacheVersionCurrent(state, currentVersion) in app-history-state (re-exported via app-browser-state); the browser entry's isCurrentBfcacheVersion now delegates to it. Add a direct regression test asserting a history entry with bfcache ids but no version key is NOT treated as current even when the current version is 0 — the exact cross-document stale-id hole that the earlier `?? 0` coercion left open. --------- Co-authored-by: James <james@eli.cx> |
||
|
|
8f224051b7 |
Preserve intercepted app-router state across server actions (#854)
* Preserve interception context on app-router actions * Keep intercept slot overrides on action rerenders * Pin React versions in static-export fixture * refactor(app-page-request): unify intercept resolution into a single state machine Collapse the parallel GET/POST intercept lookup paths into one shared internal helper that returns a tagged union: none, current-route, or source-route. Both resolveAppPageInterceptMatch and resolveAppPageActionRerenderTarget now read from the same lookup result, eliminating the double findIntercept() scan on current-route fallbacks. Also add the two missing resolveAppPageActionRerenderTarget fallthrough test cases (no intercept, current-route intercept with no matching action route) and a regression assertion that the current-route branch only performs one intercept lookup. |
||
|
|
5f6bccbd03 |
feat: track previousNextUrl for intercepted App Router entries (#755)
* docs: refresh stale app browser entry comments * feat: track previousNextUrl for intercepted App Router entries * refactor: bind getBrowserRouterState() once in refresh case Avoid calling getBrowserRouterState() twice in the refresh branch of getRequestState by binding previousNextUrl to a local. Behavior-preserving nit raised in PR review. * refactor: drop dead history-state interception-context read path After previousNextUrl became the source of truth for interception context in history state, nothing in production writes the legacy __vinext_interceptionContext key anymore. The fall-through read in getCurrentInterceptionContext was vestigial and getCurrentInterceptionContext now derives strictly from the current pathname. - Removes the unused VINEXT_INTERCEPTION_CONTEXT_HISTORY_STATE_KEY export - Removes readHistoryStateInterceptionContext - Updates the prefetch-cache unit test to assert the new pathname-derived contract instead of the legacy history-state override * test(e2e): cover back/forward restoration of intercepted modal view Adds the back-then-forward case for App Router intercepting routes: after intercepted navigation from /feed to /photos/42, going back to /feed and then forward should restore the modal (rather than rendering the full /photos/42 page). Directly exercises the previousNextUrl read in the traverse branch of getRequestState. * docs: reword stale 'belongs to PR 5' comments Two inline comments in app-browser-entry.ts referenced "PR 5" as a future PR, but this is PR 5. Reworded to describe the deferred scope as-is: interception context on server-action POSTs and HMR re-renders is intentionally not propagated. * fix: distinguish explicit null from undefined in previousNextUrl fallback --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: James <james@eli.cx> |
||
|
|
b8c44fdd52 |
feat: encode interception context in App Router payload IDs and caches (#753)
* test: add E2E verification for layout persistence flat payload pipeline Prove the flat keyed map architecture works end-to-end: - Layout state persists across sibling navigation (counter survives) - Template remounts on segment boundary change, persists within segment - Error boundary clears on navigate-away-and-back - Back/forward preserves layout state through history - Parallel slots persist on soft nav, show default.tsx on hard nav Zero production code changes — test fixtures and Playwright specs only. * test: harden layout persistence hydration assertions * test: address layout-persistence spec gaps * test: tighten not.toBeAttached() assertions for absent elements * chore: trigger CI * feat: encode interception context in App Router payload IDs and caches * test: update App Router entry snapshots for interception encoding * fix: reuse committed interception context for soft navigations * chore: fix formatting in app-browser-entry after rebase * fix: update mounted-slot test helper calls for 4-arg createResolvedElements signature PR 3 added mounted-slot tests using createResolvedElements(routeId, root, extraEntries). PR 4 inserted interceptionContext as the 3rd param, shifting extraEntries to 4th. The object was being swallowed as interceptionContext, causing getMountedSlotIds to return []. Also add missing renderId to the interception context pending-commit test. * chore: document intentional interception context omission in HMR handler Add a comment to the HMR rsc:update handler noting that interception context is intentionally omitted, matching the existing documentation pattern in the server action handler. https://claude.ai/code/session_01EnvJuSwsZS9n862q7Veamo * fix: prefetch cache test now exercises interception-context partitioning The test was passing pre-computed cache keys as the rscUrl parameter to storePrefetchResponse(), which internally calls createAppPayloadCacheKey again. With the default interceptionContext=null the key passed through unchanged, so the test only verified Map key uniqueness rather than the actual partitioning logic. Pass raw RSC URLs with separate interception context parameters instead. Also add a comment clarifying that PendingNavigationCommit's top-level interceptionContext/rootLayoutTreePath/routeId are convenience aliases for the corresponding action fields. https://claude.ai/code/session_01EnvJuSwsZS9n862q7Veamo * fix: strip NUL bytes from interception context request header Defensively remove \0 from the incoming X-Vinext-Interception-Context header to prevent a malicious client from crafting cache keys that contain the internal NUL separator. https://claude.ai/code/session_01EnvJuSwsZS9n862q7Veamo * fix: resolve rebase conflicts in slot preservation tests After rebase onto upstream main, two fixes needed: - Pass extraEntries as 4th arg to createResolvedElements (3rd is now interceptionContext) - Add missing interceptionContext field to test action objects https://claude.ai/code/session_01EnvJuSwsZS9n862q7Veamo * test: update entry template snapshots for NUL-byte header sanitization https://claude.ai/code/session_01EnvJuSwsZS9n862q7Veamo --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
12fea722b6 | Initial public release of vinext |