Skip to content

fix: App Router RSC dev invalidation parity across requests#449

Draft
JaredStowell wants to merge 10 commits intocloudflare:mainfrom
JaredStowell:jstowell/fix-app-router-rsc-request-invalidation
Draft

fix: App Router RSC dev invalidation parity across requests#449
JaredStowell wants to merge 10 commits intocloudflare:mainfrom
JaredStowell:jstowell/fix-app-router-rsc-request-invalidation

Conversation

@JaredStowell
Copy link
Contributor

Fix App Router dev-mode RSC invalidation so server modules re-execute with request freshness.

This change removes the old root-scoped invalidation heuristic and replaces it with route-closure invalidation:

  • match the App Router route for the request
  • collect that route's page/layout/template/boundary modules
  • invalidate the imported RSC dependency closure from those route roots
  • keep the virtual RSC entry invalidated per request

It also fixes mixed app/pages behavior by invalidating on the actual Pages -> App handoff path after middleware/config rewrites determine the final App Router URL.

Why

The previous approach was inadequate:

  • it could miss shared/workspace modules outside the app root
  • it could invalidate vinext internals unnecessarily
  • it classified some mixed app/pages requests too early, before rewrites finalized the App Router target

Tests

Added regressions for:

  • imported server modules outside the app root re-executing across requests
  • middleware rewriting a Pages route into an App Router route still getting fresh RSC execution

Verification

Passed:

  • pnpm test tests/nextjs-compat/app-rendering.test.ts
  • pnpm test tests/app-router.test.ts -t "re-executes App Router modules when middleware rewrites a Pages path into app/"
  • pnpm test tests/app-router.test.ts -t "App Router Static export"
  • pnpm run fmt
  • pnpm run typecheck

@pkg-pr-new
Copy link

pkg-pr-new bot commented Mar 11, 2026

Open in StackBlitz

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

commit: f2a8e6a

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3da870e4c6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@JaredStowell JaredStowell marked this pull request as draft March 11, 2026 05:26
@JaredStowell JaredStowell marked this pull request as ready for review March 11, 2026 07:14
@JaredStowell JaredStowell marked this pull request as draft March 11, 2026 07:19
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f3340fb355

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +1390 to +1393
const __hostPrepared = request.headers.get(__hostPreparedHeader) === "1";
const __hostRewriteStatus = request.headers.get(__hostRewriteStatusHeader);
const __hostPreparedTarget = request.headers.get(__hostTargetHeader);
if (__hostPrepared || __hostRewriteStatus || __hostPreparedTarget) {

Choose a reason for hiding this comment

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

P1 Badge Ignore user-set app-router preparation headers

This trusts x-vinext-app-router-prepared/x-vinext-app-router-target directly from the incoming request, and later _handleRequest(..., __hostPrepared) skips middleware, redirects, and rewrites when that flag is true. Because these are ordinary client-controlled HTTP headers and there is no provenance check, a caller can send x-vinext-app-router-prepared: 1 (optionally with a target) to bypass middleware-protected App Router behavior, including production requests handled by this generated entry.

Useful? React with 👍 / 👎.

Comment on lines +2234 to +2235
req.url = url;
setAppRouterPreparedRequestState(req, { rewriteStatus, requestUrl: url });

Choose a reason for hiding this comment

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

P2 Badge Preserve source URL in host-prepared rewrite handoff

This always stores the post-rewrite URL as requestUrl for host-prepared requests, and the RSC entry later reconstructs request.url from that target before running matchHeaders(...). In app-only projects (where prepareDirectAppRouterRequest runs for every request), headers rules are therefore evaluated against the rewrite destination instead of the original source path, so rewrite-source header rules can stop matching while destination headers are applied instead.

Useful? React with 👍 / 👎.

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