feat(build): publish static app route handlers as assets#2508
Draft
NathanDrake2406 wants to merge 7 commits into
Draft
feat(build): publish static app route handlers as assets#2508NathanDrake2406 wants to merge 7 commits into
NathanDrake2406 wants to merge 7 commits into
Conversation
Fully static App Router prerender outputs currently stay server-owned, so Workers still execute the App Router request lifecycle for cache-hit document and RSC responses. That is unnecessary when Cloudflare Assets can represent the response and no middleware, config transforms, slash redirect, basePath, or i18n routing can observe the request. Add a conservative build-time publisher that copies eligible static App Router HTML/RSC artifacts into the Cloudflare assets directory and emits matching _headers. Skip ISR, Pages routes, /404 and /500 status routes, existing asset collisions, middleware/proxy projects, and request-transforming config. Tests cover publication, generated headers, middleware/config opt-outs, ISR/pages/status-route skips, and asset-collision skips.
Cloudflare asset publication for prerendered App Router routes could publish static HTML and RSC files from route metadata alone. That bypassed the runtime cache guard that only serves query-bearing requests when render observation proves searchParams were not observed. The publisher now requires prerender query-invariance metadata before exposing static App assets. Prerender records the proof using the same render observation check as the runtime cache path, and RSC assets require their own proof bit.
The request-pipeline test hard-codes the Vinext-only internal header set. The new prerender query-invariance side-channel is intentionally internal, so CI failed until the test covered that header as stripped and separate from Next.js INTERNAL_HEADERS.
Cloudflare asset publication could still publish generated HTML when a matching RSC asset already existed and generated RSC publication was disabled by query proof. That left Cloudflare serving mixed route semantics: generated HTML for the document request and an unrelated existing asset for the RSC request. The route collision guard now treats any existing RSC target as a route-level collision, independent of whether generated RSC will be copied. The regression test pins the html-proof true, rsc-proof false case with an existing about.rsc asset.
commit: |
Contributor
Performance benchmarksCompared 0 improved · 0 regressed · 6 within ±1.5%
View detailed results and traces 🟢 improvement · 🔴 regression · ⚫ change below 1.5% · paired base/head |
Static App Router HTML assets can bypass the Worker on Cloudflare, but browser RSC requests cannot share the public document URL because Assets matches before the Worker sees RSC headers. Publishing document HTML at the visible route path without a separate RSC transport lets /about?_rsc collide with /about. Route RSC requests through a Cloudflare-only split transport: plain proven-static RSC uses a reserved static asset namespace, variant RSC uses a Worker-owned namespace that maps back to the visible route before dispatch, and publication stays gated by query proof plus request-transform safety checks. Tests cover Cloudflare transport URL selection, stale transport redirect canonicalisation, reserved RSC publication, and collision safety.
Explicitly static App Router GET route handlers currently prerender like API skips, so Cloudflare Workers still dispatch through the generated RSC entry and request-state plumbing before running user code. The missing invariant is that permanent static route-handler responses can use the same Cloudflare asset bypass boundary as static App page artifacts when request transforms and middleware cannot observe the request. Render conservative non-dynamic GET route handlers during prerender, mark their artifacts in the manifest, publish them through the shared Cloudflare prerender asset publisher, and exclude them from App page cache seeding and pregenerated page path tables.
8d889f6 to
9d963df
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Overview
dynamic = "force-static",dynamic = "error", orrevalidate = falseare considered.packages/vinext/src/build/prerender.ts,packages/vinext/src/build/cloudflare-prerender-assets.ts,packages/vinext/src/server/prerender-manifest.tsWhy
Explicit permanent static route handlers should not pay the generated RSC entry, request context, tracked request, route dispatch, and response finalisation cost on every Workers request when their response can be safely emitted at build time. The invariant is the same as #2506: ASSETS-first serving is only correct when request transforms and middleware cannot observe the request.
revalidate = 0,force-dynamic, dynamic segments,Set-Cookie,no-store, and non-200 statuses.What changed
dist/clientbecause #2506 middleware gate skips publicationMaintainer review path
packages/vinext/src/build/static-route-handler-assets.ts: conservative source-level eligibility and safe asset path mapping.packages/vinext/src/build/prerender.ts: queueing and rendering route-handler artifacts through the built RSC handler with GET.packages/vinext/src/build/cloudflare-prerender-assets.ts: shared perf(cloudflare): serve static App Router HTML and RSC from Cloudflare Assets, bypassing the Worker #2506 publisher extended for route-handler artifacts.packages/vinext/src/server/prerender-manifest.ts: route-handler exclusion from App page consumers.tests/cloudflare-prerender-assets.test.ts,tests/prerender.test.ts,tests/seed-cache.test.ts: publication, render, safety skip, and cache exclusion coverage.Validation
vp check packages/vinext/src/build/cloudflare-prerender-assets.ts packages/vinext/src/build/static-route-handler-assets.ts packages/vinext/src/build/prerender.ts packages/vinext/src/server/prerender-manifest.ts tests/cloudflare-prerender-assets.test.ts tests/prerender.test.ts tests/seed-cache.test.tsvp test run tests/cloudflare-prerender-assets.test.ts tests/seed-cache.test.tsvp test run tests/prerender.test.ts -t "Cloudflare Workers hybrid build"vp run vinext#buildRisks and compatibility
Non-goals
References