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.
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>
* feat: support nitro plugin to deploy everywhere
* try vite 7
* cover more features in playground
---------
Co-authored-by: Steve Faulkner <sfaulkner@cloudflare.com>
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>
The string array form of the exclude option on node:fs/promises glob
was only added in Node 22.14.0. Users on Node 22.0.0-22.13.x get
ERR_INVALID_ARG_TYPE at runtime. Switch to the function form which
works on all Node >= 22.
Co-authored-by: Eray <erayackgoz@gmail.com>
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>
* 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.
* 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>
* 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.
* Run safe CI for external contributors, add /deploy-preview slash command
Switch ci.yml from pull_request to pull_request_target so lint, typecheck,
vitest, and e2e run automatically for fork PRs without needing approval.
No secrets are used, so this is safe with untrusted code.
Add a /deploy-preview slash command workflow that lets maintainers trigger
deploy previews on fork PRs. Gated by author_association (org members,
collaborators, repo owners only).
Cloudflare employees who push branches to the main repo continue to get
automatic deploy previews via the existing deploy-examples.yml workflow.
* Revert ci.yml to pull_request, skip deploy-examples for fork PRs
Simpler approach: keep ci.yml on pull_request (first-time contributor
approval is fine, avoids cache poisoning concern with pull_request_target).
The actual fix: add a job-level if condition to deploy-examples.yml so the
entire workflow is skipped for fork PRs. This prevents the noisy
failed/skipped deploy checks on external contributor PRs.
Update docs in AGENTS.md and README.md to reflect the approach.
vinext deploy already checks if React needs upgrading for
react-server-dom-webpack compatibility, but vinext build did not.
Users who run `vinext build` + `wrangler deploy` manually (bypassing
vinext deploy) would get a cryptic runtime crash:
TypeError: Cannot read properties of undefined (reading 'moduleMap')
This adds the same getReactUpgradeDeps() check to buildApp(), so
React <19.2.4 is automatically upgraded before App Router builds.
Closes: moduleMap runtime error on deployed Workers (react@19.2.0)
* 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>
Add react/jsx-runtime and react/jsx-dev-runtime to the client
optimizeDeps.include list. These are CJS modules that Vite's
dependency scanner can miss when entries are virtual modules,
causing the raw CJS to be served to the browser and producing:
SyntaxError: does not provide an export named 'jsx'
Explicitly pre-bundling them ensures Vite always converts them
to ESM for the browser.
Fixes#25
* 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>
* 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>
Bun 1.2+ uses a text-based bun.lock file instead of the binary
bun.lockb. Add detection for the new format in both
detectPackageManager and detectPackageManagerName so the CLI
correctly identifies Bun in newer projects.
* fix: preserve multiple Set-Cookie headers in middleware forwarding
Middleware header forwarding used Headers.set() which overwrites
previous values with the same key. When middleware sets multiple
Set-Cookie headers (session, CSRF, consent), only the last one
survived.
Changes:
- middleware.ts: .set() -> .append() in next and rewrite paths
- app-dev-server.ts: .set() -> .append() in collection and emission
- prod-server.ts: array accumulation for Set-Cookie, getSetCookie()
for custom middleware responses
- index.ts codegen: .set() -> .append() in production middleware runner
- index.ts Pages dev: setHeader -> appendHeader for middleware headers
* fix: add type annotations for Headers.forEach callback in prod-server
---------
Co-authored-by: ask-bonk[bot] <ask-bonk[bot]@users.noreply.github.com>
Block javascript:, data:, and vbscript: URI schemes in the Link href
and Form action props. Handles evasion techniques including mixed case,
leading whitespace, and zero-width characters.
Link renders an inert <a> without href. Form renders without action
(submits to current page). Both warn in dev mode.
Shared isDangerousScheme() utility in url-safety.ts for consistency.
Based on the work by @kochrac in #142.
* 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
* fix: add scope constraint to Bonk agent prompts (#161)
* Apply suggestion from @elithrar
* Apply suggestion from @elithrar
* fix: reference $ISSUE_NUMBER/$PR_NUMBER env vars as ground truth in scope constraint
These tests intentionally use new Function() to evaluate generated
middleware codegen output. Add eslint-disable-next-line comments to
fix --deny-warnings CI failures.
* refactor: wrap Vite configuration with `defineConfig`
* refactor: remove type any
* refactor: remove as any
* refactor: remove as any
* refactor: use UserConfig type instead of defineConfig for internal config object
defineConfig is an identity function meant for user-land vite.config files.
Frameworks should use the UserConfig type annotation directly, which provides
the same type inference without the semantic mismatch.
---------
Co-authored-by: Fred K. Schott <622227+FredKSchott@users.noreply.github.com>
* 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>
* fix: log errors from failed error reporting calls
Previously, error reporting calls (.catch(() => {})) silently swallowed
errors, making debugging impossible when error reporting itself failed.
This fix adds proper error logging:
- Server action error reporting failures are now logged
- Route handler error reporting failures are now logged
- Async component errors in loading boundaries are logged in dev mode
The third case is intentional (loading boundaries handle errors), but
now logs in development for visibility.
* revert: keep silent .catch for loading boundary pre-render test
The testResult.catch() suppresses expected rejections from async
components with loading boundaries — redirect/notFound errors are
handled by rscOnError during streaming. Logging here would be noisy
since this is a normal code path, not an error condition. This matches
Next.js behavior where prospective render rejections are silent by
default.
---------
Co-authored-by: Fred K. Schott <622227+FredKSchott@users.noreply.github.com>
RSC navigation and prefetch fetches were missing ,
causing browsers to omit cookies from these requests. This silently broke
cookie-based authentication (Better Auth, NextAuth, etc.) during client-side
navigation: a full-page load would succeed (cookies sent normally) but any
subsequent <Link> navigation to a protected route would fail authentication
because the RSC fetch arrived without the session cookie.
Three fetch call sites are fixed:
- server/app-dev-server.ts — __VINEXT_RSC_NAVIGATE__ navigation fetch
- shims/link.tsx — <Link> prefetch on viewport intersection
- shims/navigation.ts — router.prefetch() call
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.
* fix(navigation): cache getServerSnapshot fallback in useSearchParams
useSyncExternalStore compares snapshots with Object.is. The previous
getServerSnapshot returned `new URLSearchParams()` on every call when no
server context was available, producing a new object reference each time
and triggering an infinite re-render loop in React.
Fix: introduce a module-level `_cachedServerSearchParams` that is
allocated once and reused, matching the same referential-stability
pattern already used by `getSearchParamsSnapshot` for the client
snapshot.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Refactor cached search params and pathname logic
Refactor cached variables for search params and pathname for better referential stability. Consolidate logic to avoid infinite re-renders.
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Fred K. Schott <622227+FredKSchott@users.noreply.github.com>
URL.pathname returns percent-encoded strings (e.g. %20 for spaces),
which Vite cannot resolve as filesystem paths. This causes all App
Router tests to fail when the project is in a directory with spaces
(common on macOS with iCloud, Google Drive, OneDrive).
Replace .pathname with fileURLToPath() from node:url, which properly
decodes the URL to a filesystem path. This pattern is already used
elsewhere in the codebase (cloudflare/tpr.ts).
Fixes#129
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).
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.
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.
* 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>
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.
The dev server's __sanitizeDestination only collapsed leading '//' but
ignored leading backslashes (e.g. '\evil.com'). The prod version in
config-matchers.ts already used /^[\/]+/ to handle both. Align the
dev codegen to match.
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.
* 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.
* 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.
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
- 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