[PR #964] [MERGED] refactor(app-router): extract RSC runtime primitives #984

Closed
opened 2026-05-06 13:11:23 +02:00 by BreizhHardware · 0 comments

📋 Pull Request Information

Original PR: https://github.com/cloudflare/vinext/pull/964
Author: @NathanDrake2406
Created: 4/29/2026
Status: Merged
Merged: 4/30/2026
Merged by: @james-elicx

Base: mainHead: nathan/extract-rsc-runtime-primitives


📝 Commits (2)

  • ed0763a refactor(app-router): extract RSC runtime primitives
  • c490a9c fix(app-router): address RSC primitive review feedback

📊 Changes

9 files changed (+699 additions, -1962 deletions)

View changed files

📝 packages/vinext/src/entries/app-rsc-entry.ts (+28 -261)
packages/vinext/src/server/app-rsc-errors.ts (+121 -0)
📝 packages/vinext/src/server/app-server-action-execution.ts (+61 -3)
📝 packages/vinext/src/server/isr-cache.ts (+57 -4)
📝 tests/__snapshots__/entry-templates.test.ts.snap (+156 -1566)
📝 tests/app-router.test.ts (+25 -127)
tests/app-rsc-errors.test.ts (+133 -0)
📝 tests/app-server-action-execution.test.ts (+55 -0)
📝 tests/isr-cache.test.ts (+63 -1)

📄 Description

What this changes

Moves the small App Router RSC runtime primitives out of entries/app-rsc-entry.ts and into typed server modules:

  • App ISR key construction, mounted-slot normalization, cache get/set, and background regeneration now come from server/isr-cache.ts.
  • Server-action text and multipart body readers now live beside the server-action execution helpers.
  • RSC error digest generation, client sanitization, and renderToReadableStream onError handling now live in server/app-rsc-errors.ts.

The generated RSC entry now describes app shape and request-specific wiring: route imports, route tables, manifest data, and callbacks that bind the current request context.

Why

app-rsc-entry.ts is generated code, so every inline runtime helper makes the template harder to inspect and harder to test directly. The pressure here is not a behavior gap, it is ownership: codegen should describe the app shape, while normal modules should own behavior.

This also gives us direct unit coverage for the runtime contracts that were previously covered mostly by generated-code snapshots.

Approach

The branch keeps the generated entry's observable wiring intact but replaces inline helper bodies with absolute imports resolved by the generator. The focused helpers preserve the existing runtime contracts:

  • App page ISR still uses separate HTML/RSC/route cache keys and keeps mounted-slot RSC variants distinct.
  • Action body readers still enforce the configured byte limit on the stream, not only Content-Length.
  • Production RSC errors still produce string-hash style digests, while navigation digest errors pass through unchanged.

Relevant Next.js source references:

Validation

  • vp test run tests/isr-cache.test.ts tests/app-server-action-execution.test.ts tests/app-rsc-errors.test.ts tests/entry-templates.test.ts
  • vp test run tests/app-router.test.ts
  • vp test run tests/app-router.test.ts -t "RSC error runtime delegation|reports server component render errors via instrumentation in production|redirect\(\) inside Suspense|notFound\(\) inside Suspense"
  • vp check
  • git diff --check

Risks / follow-ups

This intentionally leaves the larger server-action POST orchestration in the generated entry. This PR only extracts the small primitives needed to prove the pattern with a low-risk diff.


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/cloudflare/vinext/pull/964 **Author:** [@NathanDrake2406](https://github.com/NathanDrake2406) **Created:** 4/29/2026 **Status:** ✅ Merged **Merged:** 4/30/2026 **Merged by:** [@james-elicx](https://github.com/james-elicx) **Base:** `main` ← **Head:** `nathan/extract-rsc-runtime-primitives` --- ### 📝 Commits (2) - [`ed0763a`](https://github.com/cloudflare/vinext/commit/ed0763afce18838e085c71deac539c7a97e427ad) refactor(app-router): extract RSC runtime primitives - [`c490a9c`](https://github.com/cloudflare/vinext/commit/c490a9cb491c041ad291f37b340e34f6de231c0a) fix(app-router): address RSC primitive review feedback ### 📊 Changes **9 files changed** (+699 additions, -1962 deletions) <details> <summary>View changed files</summary> 📝 `packages/vinext/src/entries/app-rsc-entry.ts` (+28 -261) ➕ `packages/vinext/src/server/app-rsc-errors.ts` (+121 -0) 📝 `packages/vinext/src/server/app-server-action-execution.ts` (+61 -3) 📝 `packages/vinext/src/server/isr-cache.ts` (+57 -4) 📝 `tests/__snapshots__/entry-templates.test.ts.snap` (+156 -1566) 📝 `tests/app-router.test.ts` (+25 -127) ➕ `tests/app-rsc-errors.test.ts` (+133 -0) 📝 `tests/app-server-action-execution.test.ts` (+55 -0) 📝 `tests/isr-cache.test.ts` (+63 -1) </details> ### 📄 Description ## What this changes Moves the small App Router RSC runtime primitives out of `entries/app-rsc-entry.ts` and into typed server modules: - App ISR key construction, mounted-slot normalization, cache get/set, and background regeneration now come from `server/isr-cache.ts`. - Server-action text and multipart body readers now live beside the server-action execution helpers. - RSC error digest generation, client sanitization, and `renderToReadableStream` `onError` handling now live in `server/app-rsc-errors.ts`. The generated RSC entry now describes app shape and request-specific wiring: route imports, route tables, manifest data, and callbacks that bind the current request context. ## Why `app-rsc-entry.ts` is generated code, so every inline runtime helper makes the template harder to inspect and harder to test directly. The pressure here is not a behavior gap, it is ownership: codegen should describe the app shape, while normal modules should own behavior. This also gives us direct unit coverage for the runtime contracts that were previously covered mostly by generated-code snapshots. ## Approach The branch keeps the generated entry's observable wiring intact but replaces inline helper bodies with absolute imports resolved by the generator. The focused helpers preserve the existing runtime contracts: - App page ISR still uses separate HTML/RSC/route cache keys and keeps mounted-slot RSC variants distinct. - Action body readers still enforce the configured byte limit on the stream, not only `Content-Length`. - Production RSC errors still produce string-hash style digests, while navigation digest errors pass through unchanged. Relevant Next.js source references: - Next.js generates RSC error digests with `stringHash(err.message + stack)` in [`create-error-handler.tsx`](https://github.com/vercel/next.js/blob/e1bb911c14e4ea3c6bb8cc98871ffdee317d513f/packages/next/src/server/app-render/create-error-handler.tsx#L66-L108). - Next.js enforces the default 1 MB server action body limit through a stream transform in [`action-handler.ts`](https://github.com/vercel/next.js/blob/e1bb911c14e4ea3c6bb8cc98871ffdee317d513f/packages/next/src/server/app-render/action-handler.ts#L888-L918). - Next.js stores App Page HTML and RSC artifacts separately in the filesystem cache read path in [`file-system-cache.ts`](https://github.com/vercel/next.js/blob/e1bb911c14e4ea3c6bb8cc98871ffdee317d513f/packages/next/src/server/lib/incremental-cache/file-system-cache.ts#L232-L250) and write path in [`file-system-cache.ts`](https://github.com/vercel/next.js/blob/e1bb911c14e4ea3c6bb8cc98871ffdee317d513f/packages/next/src/server/lib/incremental-cache/file-system-cache.ts#L393-L410). ## Validation - `vp test run tests/isr-cache.test.ts tests/app-server-action-execution.test.ts tests/app-rsc-errors.test.ts tests/entry-templates.test.ts` - `vp test run tests/app-router.test.ts` - `vp test run tests/app-router.test.ts -t "RSC error runtime delegation|reports server component render errors via instrumentation in production|redirect\(\) inside Suspense|notFound\(\) inside Suspense"` - `vp check` - `git diff --check` ## Risks / follow-ups This intentionally leaves the larger server-action POST orchestration in the generated entry. This PR only extracts the small primitives needed to prove the pattern with a low-risk diff. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
BreizhHardware 2026-05-06 13:11:23 +02:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/vinext#984
No description provided.