Repository navigation
Conversation
The `x-void-page-data` response header serializes the entire page object (JSON.stringify(pageObj)) into an HTTP header on every non-deferred SSR response. For pages with large props (e.g. CMS record maps + pre-rendered HTML totaling 100KB+), this exceeds HTTP header size limits in Miniflare/workerd, causing "fetch failed" during SSG prerendering. Real-world case: a Notion-powered blog where each page returns a full ExtendedRecordMap (~30-70KB) + pre-rendered HTML (~50-200KB) as props. All blog post pages fail with "fetch failed" during `vite build` with `output: "static"`. This commit adds: - A playground page (large-props) that generates ~300KB of props simulating CMS data with Unicode content - A failing integration test that builds the vue-pages playground in SSG mode and verifies prerendering succeeds Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds a Vue Pages playground route and an integration test to reproduce the x-void-page-data header size/encoding failure when prerendering pages with very large props (common in CMS-backed apps), as described in #19.
Changes:
- Added a
/large-propsplayground page + companion server loader that generates ~300KB of CMS-like props (record-map-ish object + pre-rendered HTML with Unicode). - Added an integration test that builds
playground/vue-pagesinoutput: "static"mode and asserts prerendering succeeds for the large-props page.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
| playground/vue-pages/pages/large-props.vue | New page rendering generated “large props” + pre-rendered HTML. |
| playground/vue-pages/pages/large-props.server.ts | New loader generating large CMS-like payload and declaring prerender paths. |
| packages/vue/test/integration/vue-pages-large-props.test.ts | New integration test that enables SSG and runs a build to catch the header-size prerender failure. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
| beforeAll(() => { | ||
| // Check if void.json already exists (shouldn't, but be safe) | ||
| voidJsonExisted = existsSync(VOID_JSON); | ||
| }); | ||
|
|
||
| afterAll(() => { | ||
| // Clean up: remove void.json if we created it | ||
| if (!voidJsonExisted && existsSync(VOID_JSON)) { | ||
| unlinkSync(VOID_JSON); | ||
| } | ||
| }); |
| writeFileSync(VOID_JSON, JSON.stringify({ output: "static" }, null, 2)); | ||
|
|
||
| let buildOutput: string; | ||
| try { | ||
| buildOutput = execSync("pnpm --dir playground/vue-pages exec vite build", { | ||
| cwd: ROOT, | ||
| stdio: "pipe", | ||
| encoding: "utf-8", | ||
| timeout: 60_000, | ||
| }); |
| it("SSG prerender should not fail for pages with large props (~300KB)", () => { | ||
| // Enable SSG mode so the build prerenders pages via Miniflare. | ||
| // The x-void-page-data header serializes the full page object (~300KB+) | ||
| // into an HTTP response header, which exceeds Miniflare's header size | ||
| // limits and causes "fetch failed" during prerendering. | ||
| // | ||
| // Real-world case: Notion-powered blog (musubi) where each page returns | ||
| // a full ExtendedRecordMap (~30-70KB) + pre-rendered HTML (~50-200KB). | ||
| // All 16 blog post pages fail with "fetch failed" during `vite build`. | ||
| writeFileSync(VOID_JSON, JSON.stringify({ output: "static" }, null, 2)); | ||
|
|
||
| let buildOutput: string; | ||
| try { | ||
| buildOutput = execSync("pnpm --dir playground/vue-pages exec vite build", { | ||
| cwd: ROOT, | ||
| stdio: "pipe", | ||
| encoding: "utf-8", | ||
| timeout: 60_000, | ||
| }); | ||
| } catch (e: any) { | ||
| // Build may "succeed" with warnings for failed prerenders, or may throw | ||
| buildOutput = (e.stdout ?? "") + (e.stderr ?? ""); | ||
| } | ||
|
|
||
| // The large-props page should prerender successfully without "fetch failed" | ||
| expect(buildOutput).not.toContain("large-props failed"); | ||
| expect(buildOutput).toContain("large-props.html"); | ||
| }, 60_000); |
| // The large-props page should prerender successfully without "fetch failed" | ||
| expect(buildOutput).not.toContain("large-props failed"); | ||
| expect(buildOutput).toContain("large-props.html"); | ||
| }, 60_000); |
| buildOutput = execSync("pnpm --dir playground/vue-pages exec vite build", { | ||
| cwd: ROOT, | ||
| stdio: "pipe", | ||
| encoding: "utf-8", | ||
| timeout: 60_000, | ||
| }); |
|
What's the status of this PR? |
|
So far we have a (failing) test but need to align on a better solution for page props/content. |
|
Tests passed so I guess the issue is solved. |
|
The underlying issue (#19) was fixed in 00bae0f — page data moved from the The large-props test added here now passes in CI, confirming the fix. Closing since the fix landed separately on main. The regression test coverage is valuable — if we want to keep it, it can be cherry-picked or re-submitted as a standalone PR. |
The framework's router.post() serializes data with JSON.stringify(),
which produces "{}" for FormData instances (no own enumerable properties).
This caused all 4 project mutations (add/remove domain, add/remove secret)
to silently fail — the server action received an empty object, no intent
matched, and no API call was made.
Switch client to pass plain objects and server to use c.req.json() instead
of c.req.parseBody(), matching the framework's JSON protocol.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Closes #19
Summary
large-propsplayground page that generates ~300KB of props simulating CMS data (record map + pre-rendered HTML with Unicode content)Test output
Context
The
x-void-page-dataresponse header (protocol.ts:221) serializes the entire page object into an HTTP header. For pages with large props (common in CMS-powered sites), this exceeds Miniflare/workerd header size limits, causingfetch failedduring SSG prerendering.See #19 for full details and suggested fixes.
Test plan
pnpm test:pages:react-vue -- -t "SSG prerender should not fail" --hookTimeout=60000— currently fails as expected🤖 Generated with Claude Code