Commit Graph

1495 Commits

Author SHA1 Message Date
Steve Faulkner 170511e9e4 test: add ShadCN/Radix UI ecosystem coverage (#210)
* 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.

* test: add ShadCN/Radix UI ecosystem coverage

Add ecosystem integration tests for ShadCN components backed by Radix UI
primitives. This ensures we don't regress on a popular library combination.

The fixture tests:
- Button (server component) using @radix-ui/react-slot without "use client"
- Dialog ("use client") using @radix-ui/react-dialog with createContext
- DropdownMenu ("use client") using @radix-ui/react-dropdown-menu

Ref: #100
2026-03-02 01:57:04 +00:00
harris.dev 74f92ac910 fix(shims): copy request headers into mutable Headers in headersContextFromRequest (#128)
In Cloudflare Workers, Request.headers is immutable per the Fetch API spec.
headersContextFromRequest was storing the original request.headers reference
directly in HeadersContext, so when applyMiddlewareRequestHeaders later called
ctx.headers.set() (to propagate middleware-modified headers like x-request-url
into the server component context), Workers threw:

  TypeError: Can't modify immutable headers.

Fix by wrapping with new Headers(request.headers), which creates an independent
mutable copy while preserving all header values.
2026-03-01 19:44:37 -06:00
Jökull Sólberg Auðunsson f56b14d474 fix: matchConfigPattern incorrectly matches :param with literal suffix (#191)
Patterns like `/:slug.md` were falling through to the simple segment
matcher, which treated `slug.md` as the entire parameter name and
matched any single-segment path (including `/`). This caused rewrites
like `{ source: "/:slug.md", destination: "/api/markdown/:slug" }` to
rewrite every page request, resulting in 404s.

Add `/:[\w-]+\./.test(pattern)` to the condition that triggers the
regex-based matcher, which already tokenizes `:slug` + `.` + `md`
correctly.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 19:27:15 -06:00
Steve Faulkner 512bf0dc71 fix: make RequestContext required on matchHeaders/matchRedirect/matchRewrite (#213)
Next.js always has request context when evaluating has/missing
conditions on headers, redirects, and rewrites. There is no code path
where these are evaluated without a request.

Making ctx required eliminates ambiguity about what happens when
context is missing, and surfaces any future call sites that forget
to pass it as compile-time errors instead of silent behavioral bugs.

Also fixes deploy.ts matchHeaders call that was missing reqCtx.

Co-authored-by: Eray <erayackgoz@gmail.com>
2026-03-01 12:03:02 -06:00
Steve Faulkner 6f1902201d fix: use exact heading locator in next/dynamic E2E to fix flaky test (#209) 2026-03-01 07:53:54 -06:00
Steve Faulkner 79d33791b3 test: add Pages Router has/missing header condition coverage (#206)
Add integration tests for has/missing conditions on next.config.js
headers in the Pages Router, covering both dev and production servers.
The implementation was merged in #48 but lacked Pages Router test
coverage.

Co-authored-by: Bruno Maciel <ibruno@users.noreply.github.com>
2026-02-28 23:41:32 +00:00
Steve Faulkner 199297c4f4 fix: throw on missing middleware/proxy exports instead of failing open (#203)
* fix: throw on missing middleware/proxy exports instead of failing open

Middleware and proxy files that don't export the expected function
(e.g. misspelled export, wrong named export) were silently skipped,
letting requests through unprotected. This matches Next.js behavior
which throws a ProxyMissingExportError in the same scenario.

Changes:
- proxy.ts files now require 'proxy' or 'default' export
- middleware.ts files now require 'middleware' or 'default' export
- All 3 code paths updated (Pages dev, Pages prod, App Router)
- Added resolveMiddlewareHandler() with file-type-aware validation
- Ported test cases from Next.js proxy-missing-export test suite
- Updated AGENTS.md: added required step to search Next.js tests

Ported from Next.js: test/e2e/app-dir/proxy-missing-export/
https://github.com/vercel/next.js/blob/canary/test/e2e/app-dir/proxy-missing-export/proxy-missing-export.test.ts

* fix: update export precedence test to match Next.js behavior (named > default)

* fix: use ?? instead of || in generated middleware export resolution for consistency

Matches the shared resolveMiddlewareHandler() which uses nullish
coalescing. No behavioral difference since typeof check catches
all cases, but keeps the three code paths consistent.
2026-02-28 23:18:38 +00:00
Paul 584c2e9266 Fix Pages prod middleware named proxy export resolution (#188)
* fix pages prod middleware named proxy export

* remove security remediation report from PR
2026-02-28 22:26:37 +00:00
tomasgferreira 3d8aed9868 fix(config): respect has/missing conditions in next.config headers (#48)
* fix(config): respect has/missing conditions in next.config headers

Adds has/missing condition support to matchHeaders, wires request context
through dev/prod paths, updates NextHeader types, and adds unit + e2e tests
for header/cookie/query conditions.

* chore: add backward-compat comment for optional ctx in matchHeaders

Addresses review feedback: clarify that when no request context is
passed, has/missing conditions are silently skipped for backward
compatibility with existing callers.

* fix: wire has/missing conditions through redirects and rewrites in Pages Router dev

Pages Router dev was the only path missing has/missing support for
redirects and rewrites. The reqCtx was already being built for headers,
so hoist it and pass it to applyRedirects and applyRewrites too.

Also refactors applyRedirects and applyRewrites to delegate to the
shared matchRedirect/matchRewrite functions, eliminating duplicated
pattern-matching logic (same dedup pattern used for applyHeaders).

---------

Co-authored-by: tomoconstrutor <tomoconstrutor@gmail.com>
Co-authored-by: Steve Faulkner <sfaulkner@cloudflare.com>
2026-02-28 16:05:47 -06:00
Stephen Zhou c0bc16c549 fix: export all google font (#57) 2026-02-28 21:35:54 +00:00
Zeb Piasecki bcd4bb14cf feat: add CJS support via vite-plugin-commonjs (#198)
* feat: add CJS support via vite-plugin-commonjs

Add vite-plugin-commonjs to handle require() and module.exports in
both App Router and Pages Router. This enables CJS interop in server
components and pages, including CJS require('server-only') boundary
enforcement by @vitejs/plugin-rsc.

- Add vite-plugin-commonjs as a dependency and register it in the plugin array
- Add App Router fixtures: CJS basic, CJS server-only, CJS server-only
  violation (client component importing server-only module)
- Add Pages Router fixture: CJS basic (require/module.exports)
- Add integration tests for both routers (cjs.test.ts)
- Add App Router e2e tests for CJS rendering and server-only boundary
  enforcement (tests/e2e/app-router/cjs.spec.ts)
- Add Pages Router e2e test for CJS rendering
  (tests/e2e/pages-router/cjs.spec.ts)

* fix: use buildViteConfig() for Pages Router production builds

The Pages Router build path hardcoded plugins: [vinext()] in inline
config, duplicating plugins when a vite.config.ts also registers
vinext(). This caused double-transformation errors during production
builds. Use buildViteConfig() instead, matching the App Router path
which already handles this correctly.

* fix: move server-only-violation fixture to isolated directory

The server-only-violation fixture causes a fatal build error during
production builds of app-basic (the RSC plugin rejects the intentional
violation). This breaks unrelated tests in app-router.test.ts.

Move the fixture to tests/fixtures/app-cjs-violation/ so it doesn't
poison the shared app-basic production build. Remove the E2E test case
since it requires the app-basic dev server; the server-only boundary
enforcement for CJS require() is verified by the RSC plugin's build
validation.

* fix: address review feedback from bonk

- Remove unused components/random.tsx (dead code, Pages Router CJS test
  uses pages/cjs/random.ts instead)
- Add trailing newlines to fixture files
- Move commonjs() plugin earlier in array (after tsconfigPaths, before
  vinext:config) so CJS->ESM transform runs before directive scanning
- Tighten test assertions: use regex for 'Random:.*4' to be specific
  while handling React SSR comment nodes between text and expressions

---------

Co-authored-by: Steve Faulkner <sfaulkner@cloudflare.com>
2026-02-28 15:23:21 -06:00
Dane e9fdc7b5b0 fix: bound fetch cache key body serialization (#176)
* fix: bound fetch cache key body serialization

* Safeguard cache key serialization from large bodies

Bump cache key version to v2 and add safeguards to serializeBody to avoid OOM when processing large request bodies. Track cumulative raw byte size of streamed chunks and throw BodyTooLargeForCacheKeyError when exceeding the 1 MiB limit, call reader.cancel on errors, and add a cheap string-length pre-check. Adds tests covering oversized Uint8Array, string, multi-chunk ReadableStream, and FormData/File cases to verify such requests bypass the cache and still perform fetches.

---------

Co-authored-by: Sunil Pai <spai@cloudflare.com>
2026-02-27 22:30:02 +00:00
Dane b70f4037fb fix: preserve x-middleware-request-* headers in generated runMiddleware (#175)
* fix: preserve middleware request override headers in runMiddleware

* Strip x-middleware-* headers, detect bun, update fixtures

Preserve and unpack x-middleware-request-* headers while removing any other x-middleware-* headers in the worker entry (defense-in-depth). Add bun package manager detection in project utils (detectPackageManager and detectPackageManagerName). Update tests to assert the new header-stripping behavior. Update pages-basic fixture entry: add explicit void for promise calls, inject a middleware header override route, add dangerous-scheme blocking for Link hrefs, include credentials on prefetch, update dynamic import asset names and file metadata, and rename several getServerSideProps helper identifiers to avoid collisions.

* Normalize absolute file paths in fixture

Update fileName annotations in tests/fixtures/pages-basic/dist/server/entry.js to replace local developer paths (/Users/sunilpai/...) with CI runner paths (/home/runner/work/...). This cleans up generated JSXDEV metadata in the compiled test fixture to avoid leaking local machine paths and make outputs reproducible.

---------

Co-authored-by: Sunil Pai <spai@cloudflare.com>
2026-02-27 22:29:46 +00:00
Steve Faulkner 75703d3b72 Align Pages Router worker entry with prod-server request handling (#171)
* 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
2026-02-27 13:01:34 -06:00
Fred K. Schott 7b050d2064 chore: suppress no-implied-eval warnings in codegen tests (#156)
These tests intentionally use new Function() to evaluate generated
middleware codegen output. Add eslint-disable-next-line comments to
fix --deny-warnings CI failures.
2026-02-27 08:18:26 +00:00
ubugeeei 2c650ec089 feat: type aware linting with tsgolint (#38) 2026-02-27 07:37:12 +00:00
nianyi(likai) 35757845d1 fix: eliminate react-dom/server.edge dual-import Rollup warning in SSR entry (#41)
* fix: use static import for renderToStaticMarkup to eliminate dual-import warning

The generated SSR entry imported renderToReadableStream statically from
react-dom/server.edge at the top level, but later used a dynamic
import() for renderToStaticMarkup from the same module. Rollup detects
this mixed static+dynamic import pattern and emits a warning for every
build:

  "react-dom/server.edge" is imported as both a static and dynamic
  import in the same module

Fix: add renderToStaticMarkup to the existing static import, removing
the redundant dynamic import(). The module was already loaded — the
dynamic import added no lazy-loading benefit since it runs
unconditionally during SSR.

* Remove comment on renderToStaticMarkup import

Removed comment about static import of renderToStaticMarkup.

* Remove unneccesary test

---------

Co-authored-by: Fred K. Schott <622227+FredKSchott@users.noreply.github.com>
2026-02-27 07:13:10 +00:00
ToddLiao a82478404d fix: detect src/app App Router (#90) 2026-02-27 07:08:00 +00:00
ask-bonk[bot] aff5d24449 fix(routing): allow hyphens in dynamic segment param names (#78)
* fix(routing): allow hyphens in dynamic segment param names

Catch-all and dynamic route params with hyphens (e.g. [[...sign-in]],
[auth-method]) caused 404 because the regex used \w+ which doesn't
match hyphens. Next.js allows hyphens in param directory names — this
is commonly used by Clerk ([[...sign-in]], [[...sign-up]]).

Changed all \w+ patterns to [\w-]+ across:
- app-router.ts (fileToAppRoute, discoverSlotSubRoutes, computeInterceptTarget)
- pages-router.ts (fileToRoute, patternToNextFormat)
- app-dev-server.ts (middleware matcher regex, config pattern matching)
- middleware.ts (pattern matching)

Closes #71

* fix(routing): allow hyphens in config matching and middleware-codegen param patterns

Addresses review feedback on PR #78: the same \w+ bug that was fixed in
routing/middleware also existed in the config pattern matching functions
(redirects, rewrites, headers) and the middleware-codegen tokenizer.

Updated all remaining param-related regex patterns to [\w-]+ across:
- index.ts: matchConfigPattern guard, tokenizer, catchAllMatch, applyHeaders
- config-matchers.ts: matchConfigPattern guard, tokenizer, catchAllMatch,
  escapeHeaderSource tokenizer
- app-dev-server.ts: __matchConfigPattern template (4 .replace() chains,
  catchAllMatch, __applyConfigHeaders)
- middleware-codegen.ts: generated matchMiddlewarePattern tokenizer

Subsumes PR #127 which fixed middleware-codegen.ts independently.

Co-authored-by: SeolJaeHyeok <milkboy2564@naver.com>

---------

Co-authored-by: ask-bonk[bot] <ask-bonk[bot]@users.noreply.github.com>
Co-authored-by: Fred K. Schott <622227+FredKSchott@users.noreply.github.com>
Co-authored-by: SeolJaeHyeok <milkboy2564@naver.com>
2026-02-26 22:15:45 -08:00
luo jiyin dff6da26af fix: correct various typos (#114)
* fix: typo filesytem -> filesystem

* fix: typo filesytem -> filesystem

* fix: typo filesytem -> filesystem

* fix: typo Overwite -> Overwrite

* fix: typo hypen -> hyphen
2026-02-26 21:31:20 -08:00
Steve Faulkner 0c66d98fc8 fix: use decoded pathname for config matcher evaluation (#150)
Config matchers (redirects, rewrites, headers) now receive the decoded,
normalized pathname so that percent-encoded request paths like /%61bout
correctly match config rules defined for /about.

Both dev and prod servers rebuild the working URL from the decoded
pathname + original query string, ensuring all downstream consumers
operate on canonical paths.

Also adds unit and integration tests for percent-encoded path handling
in config matchers across App Router and Pages Router (dev + prod).
2026-02-27 02:15:50 +00:00
Steve Faulkner 65f78a4db6 fix: strip internal routing headers from dev server responses (#149)
Strip all x-middleware-* prefixed headers from responses in both the
App Router and Pages Router dev servers before sending to clients.
These headers are internal routing signals (x-middleware-next,
x-middleware-rewrite, x-middleware-override-headers, etc.) and should
not be visible to end users.

The prod server already stripped these correctly. This brings the dev
servers into parity.
2026-02-26 20:05:30 -06:00
Steve Faulkner 6fb62b6017 fix: decode request pathname once at entry point for consistent routing (#148)
Ensures the pathname is decoded exactly once (via decodeURIComponent +
normalizePath) at the request entry point, and all downstream consumers
(middleware, config matchers, route matching) receive the same canonical
path. Previously, some code paths decoded the pathname a second time,
which could cause inconsistent matching between middleware and routing.

Also removes redundant decodeURIComponent calls from matchRoute and
matchConfigPattern catch-all branches that are no longer needed since
the caller now guarantees a pre-decoded pathname.
2026-02-26 20:04:40 -06:00
Sunil Pai c4f50b616f fix: fetch cache key generation — handle all body types, include all headers minus blocklist (#111)
* fix: fetch cache key generation — handle all body types, include all headers minus blocklist

- Make buildFetchCacheKey async to support stream/blob body serialization
- Handle all BodyInit types: string, Uint8Array, ReadableStream, FormData, Blob, URLSearchParams
- Switch header strategy from allowlist (3 auth headers) to blocklist (traceparent/tracestate), matching Next.js
- Include RequestInit fields (mode, redirect, credentials, etc.) in cache key
- SHA-256 hash the cache key for compact, deterministic storage
- Fix spent stream bug: restore _ogBody in stripNextFromInit so originalFetch gets usable body
- Add 19 new tests covering body types, header inclusion, body restoration, and URLSearchParams

* Improve fetch cache key generation

Replace simple auth-header extraction with a unified header collector, add a header blocklist and cache version prefix, and introduce async cache key building. serializeBody now handles various body types (Uint8Array, streams, URLSearchParams, FormData, Blob, string), preserves the original body on init._ogBody, and body content is included in the cache key. The cache key is generated by normalizing URL, method, headers and init options and hashing them with SHA-256. buildFetchCacheKey is now async and awaited where used, and stripNextFromInit restores the preserved original body. Also add hasAuthHeaders and move auth header list into a constant.

* PR #111 approved. 52 tests pass, 3 fixes correct.

Co-authored-by: threepointone <threepointone@users.noreply.github.com>

---------

Co-authored-by: ask-bonk[bot] <ask-bonk[bot]@users.noreply.github.com>
Co-authored-by: threepointone <threepointone@users.noreply.github.com>
2026-02-26 12:33:37 +00:00
Sunil Pai 12b364705a chore: rebuild pages-basic fixture dist (#110)
Syncs the checked-in build artifact with current source. Notable
changes: middleware runner now wraps decodeURIComponent in try/catch,
jsx-dev-runtime switched to jsx-runtime for production build.
2026-02-26 11:24:05 +00:00
Sunil Pai 8a618af9f6 fix: handle malformed percent-encoded URLs gracefully (#107)
Wrap all decodeURIComponent calls on user-controlled URL paths in
try/catch to return 400 Bad Request instead of throwing an uncaught
URIError that crashes the Node process.

A single request with a malformed percent-encoded path (e.g. /%E0%A4%A)
could terminate the entire server process. This affected all server
entry points: prod server (App + Pages Router), dev server middleware,
Cloudflare Worker entry, and generated RSC/middleware handlers.

Fixes:
- prod-server.ts: App Router and Pages Router request handlers
- app-router-entry.ts: Cloudflare Worker entry
- app-dev-server.ts: Generated RSC entry handler
- index.ts: Dev server connect middleware + generated middleware runner
  + generated NEXT_LOCALE cookie parser
- middleware.ts: Pages Router dev middleware runner

Includes 11 regression tests across app-router, pages-router, and
features test suites.
2026-02-26 10:52:57 +00:00
Sunil Pai fd01d7d8a6 fix: close security gaps in init.ts shell exec and app-dev-server external proxy (#106)
* fix: close security gaps in init.ts shell exec and app-dev-server external proxy

Gap 1 (GHSA-w7vc-6jjg-498h): migrate init.ts from execSync to execFileSync
with argument array, matching the pattern already applied in deploy.ts and
cli.ts. Inputs are hardcoded package names so practical risk is near zero,
but this eliminates the last instance of the flagged pattern.

Gap 2 (GHSA-pf96-3m7r-pv3j): harden the inline __proxyExternalRequest in
the generated RSC entry (app-dev-server.ts) to strip Cookie, Authorization,
x-api-key, proxy-authorization, and all x-middleware-* headers before
forwarding to external rewrite targets. Also adds a 30-second
AbortSignal.timeout. This brings the dev-server code path into parity with
the already-fixed proxyExternalRequest in config-matchers.ts.

Also fixes a missed GSP redirect path in dev-server.ts where backslash
normalization was not applied (GHSA-79rw-r8hc-qg8v — the GSSP path was
already committed).

Includes an integration test that verifies credential stripping end-to-end
via a mock HTTP server and a conditional external rewrite in next.config.ts.

* fix: unify isExternalUrl to detect all URL schemes and protocol-relative URLs

All four copies of isExternalUrl (config-matchers.ts, router.ts,
navigation.ts, app-dev-server.ts inline) now use the same RFC 3986
scheme regex: /^[a-z][a-z0-9+.-]*:/i.test(url) || url.startsWith('//')

Previously:
- config-matchers.ts and app-dev-server.ts only checked http:// and
  https:// (missing // protocol-relative)
- router.ts and navigation.ts checked http://, https://, and // but
  missed exotic schemes (data:, javascript:, blob:, ftp:, etc.)

Now all copies are consistent and detect any URL scheme per RFC 3986
plus protocol-relative URLs.

Adds test cases for exotic schemes, protocol-relative, hash-only, and
bare string inputs.

* fix: add shell: true to execFileSync for Windows .cmd file compat

On Windows, package managers (pnpm, npm, yarn) are .cmd batch files.
execFileSync without shell: true cannot execute them. Adding shell: true
lets the OS shell resolve .cmd files while still using the argument array
form for safer argument passing.
2026-02-26 10:33:08 +00:00
Sunil Pai 0e750f7934 Security hardening: redirect sanitization, header stripping, ALS isolation tests, and safer command execution (#105)
* Sanitize redirects/headers and use execFileSync

Replace execSync calls with execFileSync (pass argv arrays) to avoid shell interpolation and adjust package-install logic to split package manager and args. Sanitize redirect destinations in SSR paths to prevent protocol-relative open-redirects and use sanitizeDestinationLocal for Next.js redirects. Ensure middleware receives a cleaned pathname by stripping internal .rsc suffix before creating NextRequest and add a test for this behavior. Strip sensitive headers (cookie, authorization and x-middleware-*) from proxied external requests and add a test. Document cache key hazards for auth headers in fetch-cache, add a security note for dangerouslySetInnerHTML in head SSR, and set the Draft Mode cookie to include Secure in production (with tests for production/dev behavior).

* Rename middleware header to x-custom-middleware

Change middleware header name from x-middleware-test to x-custom-middleware across fixtures and tests, and clarify server comment about stripping x-middleware-* headers from responses (reserved for internal routing signals). Files updated: packages/vinext/src/server/prod-server.ts (comment tweak), tests/fixtures/pages-basic/middleware.ts (header set), and tests/pages-router.test.ts (expectations and test titles/comments). This ensures tests reflect the new public header name while preserving internal x-middleware-* semantics.

* Normalize leading slashes and strip headers

Normalize redirect/rewrite destinations by collapsing any leading slashes or backslashes to a single "/" to avoid protocol-relative redirects and handle browser-backslash behavior. Strip additional sensitive headers (x-api-key, proxy-authorization) from proxied requests and ensure all x-middleware-* internal headers are removed from responses (broadened from x-middleware-request-*). Update dev/server sanitization logic accordingly. Update tests and fixture middleware to use shortened x-mw-* header names and add tests covering backslash normalization.
2026-02-26 08:50:12 +00:00
Dane 98b243493f fix: resolve CodeQL security alerts (ReDoS, incomplete sanitization, bad code gen) (#87)
Refactor chained .replace() to single-pass tokenizers in pattern matching:
- matchConfigPattern in config-matchers.ts and index.ts
- escapeHeaderSource (new) replaces inline chained escaping in matchHeaders/applyHeaders
- matchMiddlewarePattern in prod server entry template
- matchPattern in middleware.ts

Also:
- escapeHeaderSource correctly handles :param(constraint) patterns
- Fix decodeURIComponent parity gap in index.ts matchConfigPattern
- Fix redundant dot in middleware.ts tokenizer char class
- Add lgtm suppression comments for false positives in test files
- Add unit tests for escapeHeaderSource
2026-02-26 00:18:46 -06:00
Steve Faulkner 83495447ae fix: normalize request pathname before middleware and route matching (#102)
- Add normalizePath utility that collapses consecutive slashes,
  resolves . and .. segments, and ensures a leading slash
- Apply decodeURIComponent + normalizePath before middleware matching
  and route matching in all entry points (dev server, prod server,
  Cloudflare Worker entry, codegen entries)
- Extract shared middleware codegen module (middleware-codegen.ts) as
  single source of truth for pattern matching logic, replacing
  hand-copied inline implementations in index.ts and app-dev-server.ts
- Fix default middleware matcher to return true when no matcher is
  configured, matching Next.js behavior
- Fix regex pattern corruption in middleware matcher: patterns containing
  parentheses or backslashes are now passed to RegExp directly without
  dot-escaping
2026-02-26 05:39:36 +00:00
Steve Faulkner 6986e1017f fix: validate image optimization content types and width bounds (#101)
- 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
2026-02-26 05:08:44 +00:00
Steve Faulkner 16988a5403 fix: add request validation to dev server (#98) 2026-02-25 22:51:16 -06:00
Steve Faulkner aca46417d9 fix: use Host header for server action origin check (#95) 2026-02-26 04:22:31 +00:00
Steve Faulkner af485aec24 fix: validate module paths during client-side navigation (#94) 2026-02-26 04:19:43 +00:00
Steve Faulkner dda10590d7 fix: include relevant headers in fetch cache key (#93) 2026-02-26 04:17:18 +00:00
Steve Faulkner 3ff59fa6d4 fix: sanitize redirect destinations and digest parsing (#92) 2026-02-26 04:14:56 +00:00
Steve Faulkner 8dac26c0e5 fix(dotenv): load .env files with Next.js precedence (#70)
* feat(cli): load .env files with Next.js precedence

* fix(dotenv): address review feedback on .env loading

- Document Vite double-loading interaction in loadDotenv JSDoc
- Fix escaped dollar sign bug ($100 was not unescaped when $ wasn't
  followed by a valid variable name)
- Add type cast for parseEnv return value
- Expand test coverage: loadedEnv assertions, production mode
  precedence, ${VAR} expansion, escaped dollars, circular refs,
  missing files, empty result
- Add getDotenvFiles unit tests for all three modes
- Add 'Prefer Node.js Built-in APIs' guideline to AGENTS.md

Co-authored-by: liuxiaopai-ai <liuxiaopai-ai@users.noreply.github.com>

* test(dotenv): add cross-file expansion and multi-line value tests

Address review suggestions: test that .env can reference vars from
higher-priority .env.development.local, and that parseEnv handles
quoted multi-line values correctly.

---------

Co-authored-by: root <root@localhost.localdomain>
Co-authored-by: liuxiaopai-ai <liuxiaopai-ai@users.noreply.github.com>
2026-02-25 14:21:48 -06:00
Steve Faulkner 61554eaf37 fix: install react-server-dom-webpack during init and deploy for App Router (#31)
* fix: install react-server-dom-webpack during init and deploy for App Router

vinext init was missing react-server-dom-webpack from its dependency list,
causing 'Could not resolve react-server-dom-webpack/server.edge' errors on
fresh create-next-app projects. Additionally, react-server-dom-webpack@19.2.4
requires react@^19.2.4, but create-next-app ships react@19.2.3 -- the version
mismatch caused npm to nest rsdw inside vinext/node_modules where Vite can't
resolve it.

Fix: add react-server-dom-webpack to getInitDeps for App Router, detect when
the installed React version is too old, and upgrade react/react-dom as regular
dependencies before installing dev deps (avoiding ERESOLVE peer-dep conflicts
and keeping react in 'dependencies' rather than 'devDependencies').

Also applies the same fix to vinext deploy for parity, and adds a CI job that
scaffolds a real create-next-app project, runs vinext init, and verifies the
dev server returns HTTP 200.

Closes #22

* refactor: use createRequire for package resolution instead of direct node_modules reads

Address review feedback (PR #18 pattern): replace fs.existsSync(node_modules/...)
with createRequire-based resolution, which handles hoisting, pnpm symlinks,
monorepos, and Yarn PnP correctly.

- deploy.ts: extract isPackageResolvable() helper using createRequire, use it
  in getMissingDeps for react-server-dom-webpack detection
- init.ts: refactor getReactUpgradeDeps() to use createRequire + findPackageVersion
  walk-up pattern instead of reading node_modules/react/package.json directly
- tests: add isPackageResolvable unit tests, use dependency injection for
  getMissingDeps rsdw test (Vitest's createRequire intercepts resolution)

* ci: add Windows to create-next-app smoke test

Run the create-next-app compatibility test on both ubuntu-latest and
windows-latest. Replace bash-specific server health check (curl, kill,
backgrounding) with a cross-platform Node script using child_process
and http. Use runner.temp instead of /tmp for portable paths.

* fix: use pathToFileURL for dynamic import() calls on Windows

On Windows, Node's ESM loader requires file:// URLs for absolute paths.
Bare paths like D:\... are rejected with ERR_UNSUPPORTED_ESM_URL_SCHEME.

Wrap all dynamic import() calls that receive absolute paths from
require.resolve() or path.join() with pathToFileURL().href:

- index.ts: RSC plugin import, RSC transforms import, PostCSS config
  loading, PostCSS plugin resolution (5 call sites)
- cli.ts: Vite module import from project root

* fix: pathToFileURL for cache-runtime codegen and test imports

Additional Windows fixes found via PR #43 review:

- index.ts: use pathToFileURL for runtimeModulePath in 'use cache'
  transform code generation (2 locations) — these embed absolute paths
  into generated import() statements via JSON.stringify
- pages-router.test.ts: wrap 9 import(serverEntryPath) calls with
  pathToFileURL so tests work on Windows
2026-02-25 08:38:54 -06:00
Steve Faulkner 05ef04788c feat(deploy): add --env support with security and parsing improvements (#49)
* feat(deploy): add --env support for wrangler deploy

Adds vinext deploy --env <name> and threads it through to wrangler deploy --env <name>. Keeps --preview working as shorthand for preview environment, adds unit tests for wrangler arg construction, and updates README/help examples. Refs #33.

* fix(deploy): use execFileSync and util.parseArgs for --env support

Build on #44 by liuxiaopai-ai — adds --env flag for wrangler deploy.

Security: switch from execSync(shell string) to execFileSync(binary, args)
to eliminate command injection via unsanitized --env/--name values.

Replace hand-rolled flag parsing (parseStringFlag, parseNumericFlag,
rawArgs.includes) with Node.js built-in util.parseArgs. This gives us
strict mode (throws on unknown flags), automatic --flag=value handling,
and proper error messages for missing values.

Also: buildWranglerDeployArgs now returns { args, env } directly instead
of re-parsing the args array for the log message.

---------

Co-authored-by: root <root@localhost.localdomain>
2026-02-25 06:57:44 -06:00
Tiago Linares 0d5b42c79e fix: preserve binary API response bytes in prod-server (#30)
Updated the production server to handle binary responses correctly by using Buffer.from with arrayBuffer instead of text(). Added a test to ensure binary data integrity and created a new fixture for testing binary API responses.
2026-02-25 04:36:28 +00:00
Fred K. Schott 2b04681406 fix: resolve vinext init issues with missing dep, duplicate rsc plugin, and peer dep conflict (#15)
- Install vinext itself as a devDependency during init (#11)
- Remove explicit rsc() from generated configs since vinext auto-registers it (#12)
- Widen react/react-dom peer dep to >=19.2.0 for create-next-app compat (#13)

Co-authored-by: Fred K. Schott <fschott@cloudflare.com>
2026-02-24 12:29:26 -06:00
Sunil Pai 493a4c1b70 fix: add 'use client' to next/dynamic shim for App Router RSC compatibility (#7)
When a server component imports next/dynamic, the RSC serializer must
treat it as a client boundary. Without 'use client', the dynamic()
function executes inline in the RSC environment — for ssr:false, this
serializes null into the RSC payload and the client never loads the
component.

Adding 'use client' matches Next.js's App Router behavior where
next/dynamic is a client module. The RSC serializer emits a client
reference, and the client executes the full dynamic logic including
the ssr:false mount-on-client behavior.

Updated test fixtures that called dynamic() from server modules to
include 'use client' — this is required since dynamic() is now a
client export and can only be called from client modules.

Added E2E test for ssr:false from a server component page.
2026-02-24 11:13:29 -06:00
Sunil Pai 3ed38d1c4a fix: exclude vinext from optimizeDeps to prevent virtual module resolution errors (#3)
* fix: exclude vinext from optimizeDeps to prevent virtual module resolution errors

When vinext is installed from npm (not symlinked), esbuild's dependency
optimization scans vinext/dist/ and hits virtual:vinext-* imports that
only exist at Vite plugin resolution time. This causes build failures
in all three environments (client, rsc, ssr).

Adding optimizeDeps.exclude: ['vinext'] at the top level and in each
environment prevents esbuild from scanning vinext's dist files entirely.

Fixes #1

* test: verify optimizeDeps.exclude contains vinext for all environments
2026-02-24 11:13:28 -06:00
skeptrune 4349af3ecc fix: decode URL-encoded directory names and improve route precedence (#386)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 09:29:45 -06:00
Steve Faulkner 12fea722b6 Initial public release of vinext 2026-02-24 09:29:39 -06:00