[PR #243] [MERGED] fix: normalize null-prototype params from matchPattern() for RSC serialization #412

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/243
Author: @gagipro
Created: 3/3/2026
Status: Merged
Merged: 3/4/2026
Merged by: @southpolesteve

Base: mainHead: fix/rsc-null-prototype-params


📝 Commits (1)

  • 04a4a62 fix: normalize null-prototype params from matchPattern() for RSC serialization

📊 Changes

2 files changed (+34 additions, -11 deletions)

View changed files

📝 packages/vinext/src/server/app-dev-server.ts (+21 -7)
📝 packages/vinext/src/shims/metadata.tsx (+13 -4)

📄 Description

Summary

matchPattern() uses Object.create(null), producing objects without Object.prototype. The RSC serializer rejects these during dynamic route rendering, causing 500 errors on all dynamic routes (e.g. /dashboards/[id]).

Fixes #241

Fix

A makeThenableParams() helper that satisfies both constraints:

function makeThenableParams(obj) {
  const plain = { ...obj };
  return Object.assign(Promise.resolve(plain), plain);
}
  1. Spread ({...obj}) normalizes the null prototype → RSC serializer accepts it
  2. Object.assign onto the Promise preserves synchronous property access (params.id, params.slug) for pre-Next.js 15 code

This replaces the previous Object.assign(Promise.resolve(params), params) pattern at all 6 call sites in app-dev-server.ts and 3 in metadata.tsx. The old pattern passed the original null-prototype object to both Promise.resolve() and Object.assign() — while the thenable worked, the resolved value still had a null prototype causing RSC serialization failure.

Files Changed (2 files, 1 commit)

File Changes
packages/vinext/src/server/app-dev-server.ts Added makeThenableParams(), replaced 6 Object.assign(Promise.resolve(x), x) sites, updated stale comment
packages/vinext/src/shims/metadata.tsx Added typed makeThenableParams<T>(), replaced 3 call sites, updated comment

Testing

  • 2110 Vitest tests pass (51/51 files, 0 failures)
  • 387 Playwright E2E tests pass (0 failures, 12 skipped)
  • Specifically verified: dynamic routes with params.id, params.slug, catch-all routes (params.slug.join()), generateMetadata() with async params — all pass
  • Tested with a production Next.js 16 / React 19 app on Vinext (dynamic routes, metadata, server actions)

vs. PR #242

This is a replacement for #242, which was correctly identified by review as breaking synchronous params access. Changes from #242:

  • Fixed: Promise.resolve({...obj})Object.assign(Promise.resolve({...obj}), {...obj}) — preserves backward compat
  • Removed: Unrelated credentials: "same-origin" changes (4 fetch calls)
  • Removed: Unrelated findReactServerPackages() feature
  • Removed: README.md overwrite
  • Removed: .gitignore change
  • Removed: link.tsx / navigation.ts changes
  • Removed: Accidentally tracked build artifact update

🔄 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/243 **Author:** [@gagipro](https://github.com/gagipro) **Created:** 3/3/2026 **Status:** ✅ Merged **Merged:** 3/4/2026 **Merged by:** [@southpolesteve](https://github.com/southpolesteve) **Base:** `main` ← **Head:** `fix/rsc-null-prototype-params` --- ### 📝 Commits (1) - [`04a4a62`](https://github.com/cloudflare/vinext/commit/04a4a62dabdcba9c05c52e761aeb16959239eff0) fix: normalize null-prototype params from matchPattern() for RSC serialization ### 📊 Changes **2 files changed** (+34 additions, -11 deletions) <details> <summary>View changed files</summary> 📝 `packages/vinext/src/server/app-dev-server.ts` (+21 -7) 📝 `packages/vinext/src/shims/metadata.tsx` (+13 -4) </details> ### 📄 Description ## Summary `matchPattern()` uses `Object.create(null)`, producing objects without `Object.prototype`. The RSC serializer rejects these during dynamic route rendering, causing 500 errors on all dynamic routes (e.g. `/dashboards/[id]`). Fixes #241 ## Fix A `makeThenableParams()` helper that satisfies both constraints: ```js function makeThenableParams(obj) { const plain = { ...obj }; return Object.assign(Promise.resolve(plain), plain); } ``` 1. **Spread** (`{...obj}`) normalizes the null prototype → RSC serializer accepts it 2. **`Object.assign`** onto the Promise preserves synchronous property access (`params.id`, `params.slug`) for pre-Next.js 15 code This replaces the previous `Object.assign(Promise.resolve(params), params)` pattern at all 6 call sites in `app-dev-server.ts` and 3 in `metadata.tsx`. The old pattern passed the original null-prototype object to both `Promise.resolve()` and `Object.assign()` — while the thenable worked, the resolved value still had a null prototype causing RSC serialization failure. ## Files Changed (2 files, 1 commit) | File | Changes | |------|---------| | `packages/vinext/src/server/app-dev-server.ts` | Added `makeThenableParams()`, replaced 6 `Object.assign(Promise.resolve(x), x)` sites, updated stale comment | | `packages/vinext/src/shims/metadata.tsx` | Added typed `makeThenableParams<T>()`, replaced 3 call sites, updated comment | ## Testing - **2110 Vitest tests pass** (51/51 files, 0 failures) - **387 Playwright E2E tests pass** (0 failures, 12 skipped) - Specifically verified: dynamic routes with `params.id`, `params.slug`, catch-all routes (`params.slug.join()`), `generateMetadata()` with async params — all pass - Tested with a production Next.js 16 / React 19 app on Vinext (dynamic routes, metadata, server actions) ## vs. PR #242 This is a replacement for #242, which was correctly identified by review as breaking synchronous params access. Changes from #242: - **Fixed**: `Promise.resolve({...obj})` → `Object.assign(Promise.resolve({...obj}), {...obj})` — preserves backward compat - **Removed**: Unrelated `credentials: "same-origin"` changes (4 fetch calls) - **Removed**: Unrelated `findReactServerPackages()` feature - **Removed**: README.md overwrite - **Removed**: `.gitignore` change - **Removed**: `link.tsx` / `navigation.ts` changes - **Removed**: Accidentally tracked build artifact update --- <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#412
No description provided.