mirror of
https://github.com/cloudflare/vinext.git
synced 2026-05-09 08:25:34 +02:00
[GH-ISSUE #254] Refactor: Extract shared request handling logic from the four server entry points #64
Labels
No labels
enhancement
enhancement
good first issue
help wanted
nextjs-tracking
nextjs-tracking
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
starred/vinext#64
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Originally created by @southpolesteve on GitHub (Mar 5, 2026).
Original GitHub issue: https://github.com/cloudflare/vinext/issues/254
Problem
Request handling logic is duplicated across four server entry points. AGENTS.md warns about this ("When fixing a bug in any of these files, check whether the same bug exists in the others"), but warnings don't prevent drift. The duplication has already caused behavioral divergence in several areas.
The four entry points:
server/app-dev-server.ts(App Router dev, generates virtual RSC entry)server/dev-server.ts(Pages Router dev SSR handler)server/prod-server.ts(Pages + App Router production)index.tsconfigureServer middleware (Pages Router dev) +generateServerEntry()(Pages Router prod entry)There is also
config/config-matchers.ts(CM), which was a good start at extraction, but onlyprod-server.tsuses it. The same logic is independently re-implemented as generated string code in ADS and IX.Inventory of duplicated logic
16 pieces of shared logic, with copy counts:
//rejection).rsccheckHighest-risk divergences
Middleware execution (item 13) is the most fragmented. Four implementations with:
x-middleware-request-*header unpacking (ADS usesapplyMiddlewareRequestHeaders(), PS iterates manually)Config headers application order (item 6) has actual behavioral divergence: ADS applies headers after the full request (skipping 3xx redirects), PS applies them early, IX applies them before route matching. This means the same
next.config.jsheaders config produces different behavior depending on which server is running.Config redirects/rewrites (items 4-5) have three independent implementations of the same pattern matching algorithm.
config-matchers.tsis the canonical TypeScript version, but ADS and IX each generate their own as escaped string code.Proposed approach
Phase 1: Make
config-matchers.tsthe single source of truthconfig-matchers.tsalready has clean TypeScript implementations of: ReDoS-safe regex, cookie parsing, config pattern matching, has/missing conditions, redirect matching, rewrite matching, header matching, external URL detection, external proxy, and request context building.The generated code in ADS and IX should
importfrom this module instead of inlining copies. Since the generated entries are virtual modules resolved by Vite, they can import from absolute paths. The generated code already does this for other shared modules (e.g.,import { setNavigationContext } from "${absPath}").This eliminates items 4-12 (config redirects, rewrites, headers, external proxy, ReDoS regex, cookie parsing, request context, has/missing conditions, config pattern matching) as duplicated code.
Phase 2: Extract shared request pipeline utilities
Create a new module (e.g.,
server/request-pipeline.ts) for the common request lifecycle steps:These are small functions, but extracting them ensures the trivial logic (items 1-3) stays consistent.
Phase 3: Unify middleware execution
This is the hardest piece. Create a shared middleware runner:
All four entry points would call this and interpret the normalized result. The current divergence in matcher regex construction, result interpretation, and header unpacking would be eliminated.
Phase 4: Express the request pipeline ordering as a shared sequence
All servers follow the same sequence: protocol guard, basePath strip, trailing slash redirect, config redirects, beforeFiles rewrites, middleware, afterFiles rewrites, route match, fallback rewrites, config headers. Rather than each server reimplementing this procedurally, the ordering could be expressed as a composable pipeline. This is optional but would prevent future ordering divergences.
Generated code considerations
The ADS and IX-generated entries run as virtual modules in Vite's module graph. They can import from real modules using absolute paths. The pattern already exists:
This is how the generated entries already import shims like
navigation.tsandheaders.ts. The same pattern works for shared request handling logic.Verification
pnpm test(all Vitest tests pass)pnpm run test:e2e(all Playwright E2E tests pass)pnpm run typecheckpnpm run lintRelationship to other issues
This pairs with #253 (extract template strings into separate modules). That issue is about file organization (moving templates to their own files). This issue is about deduplication (making the templates import shared logic instead of inlining it). They can be done in either order, but doing #253 first makes this one easier because the templates will be in smaller, focused files.
@southpolesteve commented on GitHub (Mar 20, 2026):
Reopening. The dynamic detection bug in PR #610 (pipeline stages contaminating handler dynamic usage flags) is a direct consequence of the duplicated request handling logic described in this issue. A shared request handler module would have one place for dynamic scoping instead of repeating it in every entry point.
@southpolesteve commented on GitHub (Mar 20, 2026):
Didn't mean to reopen the PR. Meant to reopen the issue https://github.com/cloudflare/vinext/issues/253