mirror of
https://github.com/cloudflare/vinext.git
synced 2026-05-09 08:25:34 +02:00
[PR #1035] [MERGED] refactor(app-rsc): extract response finalisation into server/app-rsc-response-finalizer #1036
Labels
No labels
enhancement
enhancement
good first issue
help wanted
nextjs-tracking
nextjs-tracking
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
starred/vinext#1036
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
📋 Pull Request Information
Original PR: https://github.com/cloudflare/vinext/pull/1035
Author: @NathanDrake2406
Created: 5/3/2026
Status: ✅ Merged
Merged: 5/3/2026
Merged by: @james-elicx
Base:
main← Head:refactor/app-rsc-response-finalizer📝 Commits (2)
7037c10refactor(app-rsc): introduce app-rsc-request-normalization moduled5e9b24refactor(app-rsc): extract response finalisation into typed helper📊 Changes
4 files changed (+279 additions, -22 deletions)
View changed files
📝
packages/vinext/src/entries/app-rsc-entry.ts(+12 -21)➕
packages/vinext/src/server/app-rsc-response-finalizer.ts(+70 -0)📝
tests/app-router.test.ts(+1 -1)➕
tests/app-rsc-response-finalizer.test.ts(+196 -0)📄 Description
Based on #1034
What this changes
Extracts the App Router response finalisation step from an inline template string in the generated
handler()function into a dedicated typed module:server/app-rsc-response-finalizer.ts.The extracted module owns:
Response.redirect()creates immutableHeadersthat throwTypeError: Cannot modify immutable headerson write. Next.js also excludes config headers from redirects.applyConfigHeadersToResponsewithout overwriting middleware-set headers.The generated entry is reduced to a single delegation call.
Why
Every response variant (page, route handler, server action, metadata, not-found) passes through
handler(). The shared finalisation logic lived in a template string with no direct tests and no reuse path. Per the architecture rule: codegen describes app shape; normal modules implement behaviour.Bug fixed in the same change: the inline basePath strip used
pathname.startsWith(basePath), which is a string-prefix match./app2/pagewithbasePath=/appwould be incorrectly stripped to/2/page, potentially causing wrong config headers to apply.stripBasePath()enforces a path-separator boundary.Refs #1020.
Approach
New module imports from existing typed helpers only:
applyConfigHeadersToResponsefromserver/request-pipeline.ts(source)stripBasePathfromutils/base-path.tsfor segment-boundary correctnessnormalizePath+normalizePathnameForRouteMatchfor pathname normalisationGenerated entry drops two imports (
applyConfigHeadersToResponse,normalizePathnameForRouteMatch) that are now internal to the helper.Validation
New test file
tests/app-rsc-response-finalizer.test.ts— 10 tests covering:configHeaders: []is a no-op (returns same response object)Response.redirect()) does not throwstartsWithbug)has-condition match applies headerhas-condition miss skips headerEntry template snapshot updated automatically by pre-commit hook.
vp checkclean.Risks / follow-ups
mergeMiddlewareResponseHeadersin the plain-text 404 path inside_handleRequestis not yet moved; that path is simpler and lower risk. Can be a follow-up if desired.server/prod-server.ts) has its own header finalisation path that is separately out of scope for this PR.🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.