Commit Graph

6 Commits

Author SHA1 Message Date
James Anderson fd086c4475 fix(app-router): apply x-edge-runtime header on all App Router response paths (#1603)
Extends #1565: the merged change gated the marker on the resolved segment
config, but only covered the two `buildAppPage*Response` builders. Five
other response construction paths bypassed those helpers and skipped the
header for edge-runtime routes:

- `app-page-cache.ts` — ISR HIT/STALE responses
- `app-server-action-execution.ts` — server action RSC responses
  (redirect, no-revalidate, re-render)
- `app-page-boundary.ts` — boundary RSC responses
- `app-page-execution.ts` — 200 + flight-payload redirect responses
- `app-page-stream.ts` — HTML stream responses used by the boundary path

Centralizes the gating in a single `applyEdgeRuntimeHeader` helper so
every site shares one source of truth for header name + condition, and
threads `isEdgeRuntime` through the fallback renderer + dispatch options
so each construction site receives it.

Adds positive + negative coverage for each newly-covered path.
2026-05-26 11:28:33 +01:00
James Anderson b4898289e7 fix(app-router): match Next.js RSC Content-Type and 404 plain-text body (#1233)
* fix(app-router): match Next.js RSC Content-Type (no charset suffix)

Next.js sets the RSC (React Server Components / 'flight') response
Content-Type to exactly 'text/x-component' (RSC_CONTENT_TYPE_HEADER in
.nextjs-ref/packages/next/src/client/components/app-router-headers.ts:17,
used by FlightRenderResult in
.nextjs-ref/packages/next/src/server/app-render/flight-render-result.ts:15).
vinext was sending 'text/x-component; charset=utf-8'. The suffix breaks
Next.js's own segment-cache deduplication checks (action-handler.ts uses
.startsWith(RSC_CONTENT_TYPE_HEADER) on the unsuffixed string) and fails
Next.js e2e tests that assert strict equality, e.g.:
  - test/e2e/app-dir/app/index.test.ts L362, L371
  - test/e2e/app-dir/segment-cache/deployment-skew/deployment-skew.test.ts L80

Replace the duplicated literal at seven sites with the existing
VINEXT_RSC_CONTENT_TYPE constant in app-rsc-cache-busting.ts.

* fix(app-router): match Next.js 404 plain-text body 'This page could not be found'

Next.js's plain-text 404 response body is exactly
'This page could not be found' (no trailing period in the response body;
the React-rendered not-found component uses the same text with a period).
vinext was returning 'Not Found' / '404 Not Found' / '404 - Not found',
which fails Next.js e2e tests that assert .toContain('This page could
not be found'), e.g.:
  - test/e2e/app-dir/app/index.test.ts (several cases)
  - test/e2e/app-dir/parallel-route-not-found/parallel-route-not-found.test.ts L52
  - test/e2e/app-dir/prefetching-not-found/prefetching-not-found.test.ts L16

Next.js source citations (in .nextjs-ref):
  - packages/next/src/server/route-modules/pages/pages-handler.ts L121, L535
  - packages/next/src/build/templates/app-route.ts L170, L349
  - packages/next/src/build/templates/app-page.ts L701, L1043
  - packages/next/src/pages/_error.tsx L7
  - packages/next/src/client/components/builtin/not-found.tsx L7 (HTML variant)

Updates the body in notFoundResponse() and the open-redirect / pages-router
SSR-fallback / worker-entry inline 404 sites. The HTTP status reason phrase
('Not Found') is unchanged. Route-specific bodies like '404 - API route not
found' are intentionally retained.
2026-05-15 12:39:05 +01:00
Nathan Nguyen dd1dfae3c6 fix(app-router): scope layout params and layout error boundaries (#938)
* fix(app-router): scope layout params and propagate layout head errors

Layouts received full route params, layout generateMetadata failures were swallowed, and layout-thrown forbidden()/unauthorized() fell back through the not-found boundary path. Those diverge from Next.js when nested layouts depend on segment params, when head generation fails before page render, or when HTTP access APIs are thrown from a layout.

The generated RSC entry now delegates param slicing, head resolution, and parent access-boundary selection to typed runtime helpers. The helpers keep route-specific imports in the entry while moving the behavioral core into unit-tested modules.

Tests port Next.js layout params, global-error, forbidden, and unauthorized coverage into focused helper and integration regressions.

* test(app-router): cover layout viewport error boundaries

Layout generateViewport() failures now intentionally share the same boundary path as layout generateMetadata() failures. Copilot flagged that this behavior change needed direct coverage.

Add dev and production-preview compat assertions for co-located error.tsx handling and global-error escalation, with fixtures that throw from layout generateViewport().

* chore(app-router): address head review nits
2026-04-29 10:40:49 +01:00
Nathan Nguyen 7d9f4a09b7 feat: implement parallelRoutesKey support in useSelectedLayoutSegment(s) (#737)
* feat: migrate LayoutSegmentContext to segmentMap for parallelRoutesKey

Context type changes from string[] to SegmentMap — a record with a
required "children" key plus optional parallel route keys. useChildSegments()
now accepts a parallelRoutesKey parameter to index into the map.

Consumers not yet updated — entry template and boundary rendering still
use the old childSegments prop. Next commit updates them.

* refactor: update all LayoutSegmentProvider consumers to segmentMap

Mechanical change: every createElement(LayoutSegmentProvider, { childSegments })
becomes createElement(LayoutSegmentProvider, { segmentMap: { children: ... } }).
Behavioral output is identical — useSelectedLayoutSegments() returns the
same values. The segmentMap structure is ready for parallel slot entries.

* test: add parallelRoutesKey integration tests

Verifies useSelectedLayoutSegments(parallelRoutesKey) returns correct
per-slot segments through the full SSR pipeline. Flat slots correctly
return [] until PR 2 populates per-slot entries in the segmentMap.

* refactor: remove as cast from useSelectedLayoutSegments

SegmentMap arrays changed from readonly string[] to string[] to match
Next.js public API return type without requiring a forbidden as cast.
The map itself stays Readonly — arrays are created fresh at each layout
boundary so mutable typing is safe.

* Update packages/vinext/src/shims/layout-segment-context.tsx

Co-authored-by: ask-bonk[bot] <249159057+ask-bonk[bot]@users.noreply.github.com>

* Update packages/vinext/src/shims/navigation.ts

Co-authored-by: ask-bonk[bot] <249159057+ask-bonk[bot]@users.noreply.github.com>

* Update packages/vinext/src/shims/layout-segment-context.tsx

Co-authored-by: ask-bonk[bot] <249159057+ask-bonk[bot]@users.noreply.github.com>

---------

Co-authored-by: ask-bonk[bot] <249159057+ask-bonk[bot]@users.noreply.github.com>
Co-authored-by: James Anderson <james@eli.cx>
2026-04-01 21:17:47 +00:00
James Anderson 1ff166eb92 chore: turn on more lint rules (#714)
* chore: turn on more lint rules

* fmt

* add todos
2026-03-29 20:40:57 +01:00
Steve Faulkner 81003c9604 refactor: extract app page boundary helpers (#634) 2026-03-21 15:34:27 -05:00