Skip to content

refactor: extract app page boundary helpers#634

Merged
southpolesteve merged 1 commit intomainfrom
codex/app-page-boundary-runtime
Mar 21, 2026
Merged

refactor: extract app page boundary helpers#634
southpolesteve merged 1 commit intomainfrom
codex/app-page-boundary-runtime

Conversation

@southpolesteve
Copy link
Copy Markdown
Collaborator

Summary

  • extract App Router page boundary selection and shared wrap/render branching into app-page-boundary.ts
  • keep app-rsc-entry.ts focused on route-specific metadata and tree wiring while delegating fallback/error boundary response orchestration to typed helpers
  • add focused boundary helper tests and relax the Suspense not-found assertion to check for preserved digests across equivalent React dev payload shapes

Testing

  • vp check packages/vinext/src/server/app-page-boundary.ts packages/vinext/src/entries/app-rsc-entry.ts tests/app-page-boundary.test.ts tests/app-router.test.ts
  • vp test run tests/app-page-boundary.test.ts tests/app-router.test.ts -t 'app page boundary helpers|generated code delegates page boundary rendering'
  • vp test run tests/app-page-boundary.test.ts tests/app-page-request.test.ts tests/app-page-stream.test.ts tests/app-page-execution.test.ts tests/app-page-cache.test.ts tests/app-page-response.test.ts tests/error-boundary.test.ts
  • vp test run tests/entry-templates.test.ts -u
  • vp test run tests/app-router.test.ts -t 'notFound\(\) inside Suspense boundary preserves digest for not-found UI|renders intercepted photo modal on RSC navigation from feed|renders custom not-found.tsx for unmatched routes|notFound\(\) escalates to nearest ancestor not-found.tsx|forbidden\(\) from Server Component returns 403 with forbidden.tsx|unauthorized\(\) from Server Component returns 401 with unauthorized.tsx|renders error boundary wrapper for routes with error.tsx|generated code delegates page boundary rendering to typed helpers|generated code|dynamicParams = false|page ISR \+ searchParams'
  • vp run vinext#build

Written by Codex.

Base automatically changed from codex/app-page-request-runtime to main March 21, 2026 20:03
@southpolesteve southpolesteve force-pushed the codex/app-page-boundary-runtime branch from a6dd296 to d9556ad Compare March 21, 2026 20:04
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Mar 21, 2026

Open in StackBlitz

npm i https://pkg.pr.new/vinext@634

commit: d9556ad

@github-actions
Copy link
Copy Markdown

Example Preview Production Original
app-router-cloudflare preview production
pages-router-cloudflare preview production
app-router-playground preview production original
realworld-api-rest preview production
nextra-docs-template preview production
benchmarks preview production
hackernews preview production original

@southpolesteve
Copy link
Copy Markdown
Collaborator Author

/bigbonk review please

Copy link
Copy Markdown
Contributor

@ask-bonk ask-bonk Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: refactor: extract app page boundary helpers

Clean, well-structured refactoring that eliminates significant code duplication between renderHTTPAccessFallbackPage and renderErrorBoundaryPage. The four extracted helpers (resolveAppPageHttpAccessBoundaryComponent, resolveAppPageErrorBoundary, wrapAppPageBoundaryElement, renderAppPageBoundaryResponse) each have a clear single responsibility and are well-typed.

Behavioral equivalence verified

I compared the original inline code on main against the new helper-delegating code line by line:

resolveAppPageHttpAccessBoundaryComponent — The status code branching (403/401/fallback-to-not-found) and route-then-root fallback chain are identical to the original inline if/else if/else block.

resolveAppPageErrorBoundary — The page > layout (innermost-first) > global-error resolution order matches the original. The isGlobalError flag correctly replaces the old _isGlobalError variable and is used identically to gate skipLayoutWrapping.

wrapAppPageBoundaryElement — The layout wrapping loop, RSC-only LayoutSegmentProvider wrapping, and RSC-only global error boundary wrapping all match the original branching structure. The old code had separate RSC and HTML paths that both computed _fallbackParams / _fallbackParamsHtml with the same expression (opts?.matchedParams ?? route?.params ?? {}); the new code correctly unifies this into a single matchedParams field.

renderAppPageBoundaryResponse — The RSC path returns a Response with text/x-component content type and the correct status code. The HTML path delegates to createHtmlResponse. The important "do NOT clear context here" invariant is preserved via a comment in the helper, and the actual clearing still happens only inside createHtmlResponse (i.e., the HTML/SSR path).

One subtle improvement

The createHtmlResponse callback pattern defers font data creation and SSR entry loading until actually needed. In the original code, the HTML path computed these eagerly even before checking isRscRequest. The new pattern avoids unnecessary work for RSC requests.

Type design

The heavy generics (5 type params on WrapAppPageBoundaryElementOptions) are the right call here. The helpers are framework-agnostic — they don't import React or any vinext-specific types — which means they can be unit tested with plain strings (as the tests demonstrate). The getDefaultExport, renderLayout, renderErrorBoundary, and renderLayoutSegmentProvider callbacks cleanly separate the "what to do" logic (in the helper) from the "how to create elements" logic (in the generated code).

Test coverage

Good coverage of the pure helpers with string-based assertions that are easy to read. The renderAppPageBoundaryResponse tests verify both the RSC and HTML paths, including that createHtmlResponse is only called for non-RSC requests.

Minor observations (non-blocking)

  • The Vary header key changed from "Vary" to Vary (unquoted object key) — cosmetic only, both are equivalent in JS.
  • Architectural comments explaining why LayoutSegmentProvider and GlobalErrorBoundary are needed for RSC tree reconciliation were removed from the generated code. The brief comment in renderAppPageBoundaryResponse covers the context-clearing invariant. The tree reconciliation rationale might be worth preserving as a comment in wrapAppPageBoundaryElement for future readers, but this is minor.

LGTM — all CI green, behavior preserved, good test coverage.

@ask-bonk
Copy link
Copy Markdown
Contributor

ask-bonk Bot commented Mar 21, 2026

The review has been posted and the PR has been approved. Here's a summary of the review:

PR #634 approved — This is a clean refactoring that extracts duplicated boundary resolution, element wrapping, and response rendering logic from app-rsc-entry.ts into four well-typed helper functions in a new app-page-boundary.ts module.

Key findings:

  • Behavioral equivalence verified — All four helpers (resolveAppPageHttpAccessBoundaryComponent, resolveAppPageErrorBoundary, wrapAppPageBoundaryElement, renderAppPageBoundaryResponse) produce identical behavior to the original inline code
  • Slight efficiency improvement — The createHtmlResponse callback pattern defers font data/SSR entry loading for RSC requests where they aren't needed
  • Good type design — Framework-agnostic generics allow unit testing with plain strings
  • All CI checks pass — Check, Vitest, all E2E projects, deployments, and smoke tests are green

github run

@southpolesteve southpolesteve merged commit 81003c9 into main Mar 21, 2026
30 checks passed
@southpolesteve southpolesteve deleted the codex/app-page-boundary-runtime branch March 21, 2026 20:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant