[PR #242] [CLOSED] fix: RSC serialization with null-prototype params from matchPattern() #414

Closed
opened 2026-05-06 12:39:41 +02:00 by BreizhHardware · 0 comments

📋 Pull Request Information

Original PR: https://github.com/cloudflare/vinext/pull/242
Author: @gagipro
Created: 3/3/2026
Status: Closed

Base: mainHead: test/cherry-pick-rsc-fixes


📝 Commits (5)

  • ab2f9b1 fix: auto-exclude react-server packages from RSC optimizeDeps
  • 70cd2fc fix: use credentials 'same-origin' (not 'include') to match Next.js behavior
  • 32787d7 fix: use plain-object thenable for params/searchParams to fix RSC serialization
  • a25fcfa fix: apply makeThenableParams to metadata.tsx (resolveModuleViewport + resolveModuleMetadata)
  • eb2539b fix: use Promise.resolve({...obj}) for RSC-safe params

📊 Changes

8 files changed (+1129 additions, -263 deletions)

View changed files

📝 .gitignore (+1 -0)
📝 packages/vinext/README.md (+680 -1)
📝 packages/vinext/src/index.ts (+103 -1)
📝 packages/vinext/src/server/app-dev-server.ts (+29 -9)
📝 packages/vinext/src/shims/link.tsx (+1 -1)
📝 packages/vinext/src/shims/metadata.tsx (+12 -3)
📝 packages/vinext/src/shims/navigation.ts (+1 -1)
📝 tests/fixtures/pages-basic/dist/server/entry.js (+302 -247)

📄 Description

Fixes #241

Summary

matchPattern() returns objects created with Object.create(null) (null prototype). The current code wraps these in Object.assign(Promise.resolve(params), params), creating a thenable that the RSC serializer resolves back to the original null-prototype object — which it cannot serialize, causing 500 errors on all dynamic routes.

Changes

packages/vinext/src/server/app-dev-server.ts

  • Added makeThenableParams(obj) — returns { ...obj } to normalize the prototype
  • Replaced 6 occurrences of Object.assign(Promise.resolve(...), ...) with makeThenableParams(...)
  • Changed credentials: "include"credentials: "same-origin" on 4 fetch calls (RSC payload, server actions, navigation, HMR) to match Next.js behavior

packages/vinext/src/shims/metadata.tsx

  • Added same makeThenableParams() helper
  • Replaced 3 occurrences in resolveModuleViewport() and resolveModuleMetadata()

packages/vinext/src/index.ts

  • Added findReactServerPackages() to auto-exclude packages with react-server export condition from RSC optimizeDeps (prevents pre-bundling issues with packages like swr)

Why { ...obj } works

Since await plainObject returns the object itself in JavaScript, a .then() method is not needed. React 19 / Next.js 16 components can await plain objects just fine. The spread simply normalizes the null prototype to Object.prototype, which the RSC serializer accepts.

Testing

Tested with a production Next.js 16 / React 19 app with:

  • Dynamic routes (/dashboards/[id])
  • generateMetadata() with async params
  • Server actions
  • NODE_ENV=production

All pages render correctly on both dev and production builds.


🔄 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/242 **Author:** [@gagipro](https://github.com/gagipro) **Created:** 3/3/2026 **Status:** ❌ Closed **Base:** `main` ← **Head:** `test/cherry-pick-rsc-fixes` --- ### 📝 Commits (5) - [`ab2f9b1`](https://github.com/cloudflare/vinext/commit/ab2f9b1f6443f12bd2cd9c9f4fc83b7de2131628) fix: auto-exclude react-server packages from RSC optimizeDeps - [`70cd2fc`](https://github.com/cloudflare/vinext/commit/70cd2fcf274f5d4828bb894046f2850701cb2d5a) fix: use credentials 'same-origin' (not 'include') to match Next.js behavior - [`32787d7`](https://github.com/cloudflare/vinext/commit/32787d7de92d7a00e22a144f2cb5f6e2a7059dc8) fix: use plain-object thenable for params/searchParams to fix RSC serialization - [`a25fcfa`](https://github.com/cloudflare/vinext/commit/a25fcfa8705876a0776b3040a831cc2e86ce2ce1) fix: apply makeThenableParams to metadata.tsx (resolveModuleViewport + resolveModuleMetadata) - [`eb2539b`](https://github.com/cloudflare/vinext/commit/eb2539b0b18ec1e2ac02fdd70a73af17a29d1560) fix: use Promise.resolve({...obj}) for RSC-safe params ### 📊 Changes **8 files changed** (+1129 additions, -263 deletions) <details> <summary>View changed files</summary> 📝 `.gitignore` (+1 -0) 📝 `packages/vinext/README.md` (+680 -1) 📝 `packages/vinext/src/index.ts` (+103 -1) 📝 `packages/vinext/src/server/app-dev-server.ts` (+29 -9) 📝 `packages/vinext/src/shims/link.tsx` (+1 -1) 📝 `packages/vinext/src/shims/metadata.tsx` (+12 -3) 📝 `packages/vinext/src/shims/navigation.ts` (+1 -1) 📝 `tests/fixtures/pages-basic/dist/server/entry.js` (+302 -247) </details> ### 📄 Description Fixes #241 ## Summary `matchPattern()` returns objects created with `Object.create(null)` (null prototype). The current code wraps these in `Object.assign(Promise.resolve(params), params)`, creating a thenable that the RSC serializer resolves back to the original null-prototype object — which it cannot serialize, causing 500 errors on all dynamic routes. ## Changes ### `packages/vinext/src/server/app-dev-server.ts` - Added `makeThenableParams(obj)` — returns `{ ...obj }` to normalize the prototype - Replaced 6 occurrences of `Object.assign(Promise.resolve(...), ...)` with `makeThenableParams(...)` - Changed `credentials: "include"` → `credentials: "same-origin"` on 4 fetch calls (RSC payload, server actions, navigation, HMR) to match Next.js behavior ### `packages/vinext/src/shims/metadata.tsx` - Added same `makeThenableParams()` helper - Replaced 3 occurrences in `resolveModuleViewport()` and `resolveModuleMetadata()` ### `packages/vinext/src/index.ts` - Added `findReactServerPackages()` to auto-exclude packages with `react-server` export condition from RSC `optimizeDeps` (prevents pre-bundling issues with packages like `swr`) ## Why `{ ...obj }` works Since `await plainObject` returns the object itself in JavaScript, a `.then()` method is not needed. React 19 / Next.js 16 components can `await` plain objects just fine. The spread simply normalizes the null prototype to `Object.prototype`, which the RSC serializer accepts. ## Testing Tested with a production Next.js 16 / React 19 app with: - Dynamic routes (`/dashboards/[id]`) - `generateMetadata()` with async params - Server actions - `NODE_ENV=production` All pages render correctly on both dev and production builds. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
BreizhHardware 2026-05-06 12:39:41 +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#414
No description provided.