* fix: preserve multiple Set-Cookie headers in prod-server and worker entry
The response header merging in prod-server.ts and the generated Cloudflare
worker entry used Record<string, string> which flattened multiple Set-Cookie
headers — the last value won, or cookies with Expires dates got corrupted
by comma-joining.
Extract mergeResponseHeaders() helper that uses getSetCookie() to preserve
array-valued Set-Cookie headers. Apply the same fix to the worker entry
template in deploy.ts using the Headers API.
Fixes#295
* fix: address review feedback on Set-Cookie header preservation
- Add `as string` cast in deploy.ts worker entry for x-middleware-request-*
header unpacking (matches prod-server.ts)
- Normalize Vary header with Array.isArray check in sendCompressed to handle
the widened type correctly
- Add edge-case test for middleware cookie as plain string (not array)
* fix: apply Set-Cookie fix to example workers, remove redundant toLowerCase
- Fix the same Set-Cookie flattening bug in hand-written example worker
entries (pages-router-cloudflare, realworld-api-rest)
- Remove 4 redundant .toLowerCase() calls — Headers.forEach() always
yields lowercase keys
* fix: add missing as string cast in deploy.ts middleware header collection
* refactor: extract mergeHeaders to shared vinext/server/worker-utils
The mergeHeaders function was duplicated in both example worker entries
(pages-router-cloudflare, realworld-api-rest) and the deploy.ts template.
Extract it to a shared module that example workers import. The deploy.ts
template still inlines it (code generation constraint).
Reduces 3 copies to 2 (shared module + generated template).
* fix: array-accumulate Set-Cookie in config headers, fix JSDoc, add behavioral test
- deploy.ts template: config headers section was comma-joining Set-Cookie values
using += which corrupts cookies with Expires dates and loses all but the last
cookie when multiple config rules match. Matches the array-accumulation pattern
already used in prod-server.ts (lines 899-907) and the middleware section above.
- worker-utils.ts + deploy.ts template JSDoc: 'Response headers take precedence'
was misleading — Set-Cookie is additive, not overriding. Clarify both docs.
- tests/deploy.test.ts: add behavioral test for mergeHeaders via worker-utils import
(same function inlined in the generated template), replacing reliance on
string-contains assertions alone.
* fix: indentation in deploy.ts template, array-accumulate Set-Cookie in example workers
- deploy.ts: fix extra leading space in config headers block (lines 636-659)
and JSDoc lines 740-743 introduced in previous commit
- pages-router-cloudflare, realworld-api-rest: config headers section was
doing a plain assignment (middlewareHeaders[lk] = h.value) which overwrites
array-valued Set-Cookie built up by the middleware section above; use the
same array-accumulation pattern as prod-server.ts and the deploy.ts template
* fix: remove as any casts in prod-server.ts, add Vary handling to example workers
- prod-server.ts lines 904/906: middlewareHeaders is now Record<string, string | string[]>
so the as any casts are unnecessary; replace with as string (consistent with the
middleware collection section above at line 848)
- pages-router-cloudflare, realworld-api-rest: add Vary comma-joining to config
headers section to match prod-server.ts (line 908) and deploy.ts template (line 653)
---------
Co-authored-by: James <james@eli.cx>
* feat: support nitro plugin to deploy everywhere
* try vite 7
* cover more features in playground
---------
Co-authored-by: Steve Faulkner <sfaulkner@cloudflare.com>
* Skip deploy previews for fork PRs that lack Cloudflare secrets
Fork PRs don't have access to repository secrets (CLOUDFLARE_API_TOKEN,
CLOUDFLARE_ACCOUNT_ID), so deploy/smoke-test/comment steps always fail.
Add a fork detection condition to skip these steps gracefully. The build
steps still run, so example builds are still validated for fork PRs.
* fix: align Pages Router worker entry with prod-server request handling
The generated Cloudflare Worker entry for Pages Router apps was missing
several request handling steps that the Node.js production server
(prod-server.ts) already handled. This brings the two in sync:
- Run middleware (runMiddleware) before routing
- Apply next.config.js redirects, rewrites (before/after/fallback), and headers
- Handle basePath stripping and trailing slash normalization
- Merge middleware response headers with correct precedence
- Guard renderPage with typeof check
Also updates the two Pages Router examples (pages-router-cloudflare,
realworld-api-rest) and exports vinext/config/config-matchers so the
worker entry can import the shared matching utilities.
* chore: sync pnpm-lock.yaml with upstream package.json changes
App Router examples were missing react-server-dom-webpack as an explicit
dependency. With pnpm's strict module resolution, this caused the Vite
dev server to fail resolving react-server-dom-webpack/client.browser,
breaking client-side hydration — "use client" components rendered as
static HTML with no interactivity.
The @vitejs/plugin-rsc correctly adds react-server-dom-webpack to
optimizeDeps.include, but the package must be resolvable from the
project root. Without it in package.json, pnpm cannot link it.
- Add Content-Security-Policy and X-Content-Type-Options headers to image responses
- Validate Content-Type allowlist (reject SVG, HTML, non-image types)
- Bound width parameter to configured deviceSizes/imageSizes with 3840px max
- Add deviceSizes and imageSizes to NextConfig type
- Apply to all image paths: Workers, Node.js prod server (App + Pages Router)
- Fix benchmarks limit param NaN handling