# vercel/next.js — codebase unpack

> Canonical page: https://codevetter.com/unpack/nextjs

This is a deterministic Repo Unpack scan of vercel/next.js — a structural map, not a review, security audit, or maintainer-endorsed description.

- Scanned commit: 2861a364fcbe75fe96b0b9127f9b335edfcf288c (2026-09-22)
- Last upstream commit: v16.4.0-canary.38
- Generated by codevetter 1.12.0 on 2026-09-22
- 4000 files scanned · 10.7 MB

## Languages

- TypeScript: 12573 files
- JavaScript: 11974 files
- JSON: 1675 files
- Markdown: 1313 files
- Rust: 1050 files
- CSS: 761 files
- Sass: 216 files
- YAML: 111 files

## Repository structure

- crates/ — 434 files
- evals/ — 433 files
- packages/ — 433 files
- turbopack/ — 433 files
- examples/ — 433 files
- test/ — 433 files
- docs/ — 433 files
- bench/ — 317 files

Probable source entrypoint: `Cargo.toml` (inferred from the tree, not upstream docs)

## Package

- Cargo.toml: null — 0 declared dependencies

## Structural health signals

Average score 7.1/10 across 96 files; 24 hotspots. These are deterministic review leads, not proof of a bug.

## Recent history

- 2861a364fcbe (2026-09-22) v16.4.0-canary.38
- 95c5e39062e4 (2026-09-22) feat(turbopack): support `false` values for `resolveAlias` config (#93331)
- 7353da4cc9f7 (2026-09-22) Enable AI upgrade for canary (#99001)
- becee66e9b71 (2026-09-22) fix(turbo-persistence): preserve write errors on writer drop (#98840)
- cdecbe095305 (2026-09-22) Skip slow valued tombstone test under Miri (#98992)

## Overview

This is the `vercel/next.js` monorepo at canary `16.4.0-canary.38` — the Next.js React framework itself, plus the Rust Turbopack bundler (vendored as a git subtree under `turbopack/`), the SWC-based native transform crates under `crates/`, and ~20 sibling npm packages (create-next-app, eslint-plugin-next, next-codemod, font, third-parties, next-rspack). The published `next` package is a dual-runtime system: a Node.js router/render server plus an edge-runtime variant, driven by a Commander-based CLI (`next dev|build|start|typegen|upgrade|experimental-test|…`) and fed by either Turbopack (default) or webpack/rspack. What is distinctive is the sheer test-matrix machinery: every integration test runs across three modes (dev/start/deploy) × three bundlers (turbo/webpack/rspack) × an experimental `cacheComponents` axis, orchestrated by `scripts/run-jest.sh` and a custom `@gate` pragma transformer. It is a framework maintained for millions of downstream apps, so nearly every subsystem is an extension point with a stability contract.

## System map

A pnpm + Turborepo + Lerna monorepo (`pnpm-workspace.yaml` globs `apps/*`, `packages/*`, `bench/*`, `crates/*/js`, `turbopack/crates/*/js`) layered on top of a single Cargo workspace (`Cargo.toml`) that builds Turbopack, the next-swc transforms, and the napi bindings. The JS entrypoint is `packages/next/src/bin/next.ts`, a Commander program that lazily dynamic-imports one `src/cli/next-*.ts` module per subcommand. At runtime `next dev` forks a child process running `server/lib/start-server.ts`, which boots `server/lib/router-server.ts` (routing, middleware, dev bundler) which in turn lazily loads `server/lib/render-server.ts` → `NextNodeServer` (`server/next-server.ts`) extending the abstract `BaseServer` (`server/base-server.ts`). Storage is the filesystem: `.next/` manifests plus a pluggable `IncrementalCache` / `CacheHandler` layer. Turbopack is reached through napi via `packages/next/src/build/swc/index.ts` wrapping the Rust `Project` type in `crates/next-api/src/project.rs`.
- `packages/next/src/bin/next.ts` is the single published binary (`"bin": {"next": "./dist/bin/next"}`) and registers every subcommand on a `NextRootCommand extends Command`: `build`, `experimental-analyze`, `dev` (isDefault), `export` (hidden), `info`, `start`, `telemetry`, `typegen`, `upgrade`, `experimental-test`, `experimental-request-insights`, and an `internal` group with `trace`, `query-trace`, `post-build`, `upload-trace`, `static-routes-info`, `agent-feedback-instructions`. — [packages/next/src/bin/next.ts#L187-L836](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/bin/next.ts#L187-L836) [packages/next/package.json#L72-L75](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/package.json#L72-L75)
- Each subcommand is code-split behind a dynamic import inside `.action()` (e.g. `import('../cli/next-export.js').then((mod) => mod.nextExport())`), so CLI startup never loads the build or dev-server graph unless that command is invoked. — [packages/next/src/bin/next.ts#L430-L433](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/bin/next.ts#L430-L433)
- `next dev` runs as two processes: the CLI process supervises, and `fork(require.resolve('../server/lib/start-server'))` spawns the actual server with `NEXT_PRIVATE_WORKER=1`, `__NEXT_DEV_SERVER='1'`, `NEXT_PRIVATE_TRACE_ID`, and a `WATCHPACK_WATCHER_LIMIT=20` workaround on darwin; options cross the boundary over IPC messages `nextWorkerReady` → `nextWorkerOptions` → `nextServerReady`. — [packages/next/src/cli/next-dev.ts#L352-L452](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/cli/next-dev.ts#L352-L452) [packages/next/src/server/lib/start-server.ts#L648-L673](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/lib/start-server.ts#L648-L673)
- `startServer()` in `server/lib/start-server.ts` creates the raw `http`/`https` server (https only when `--experimental-https` supplies a self-signed cert) and installs a `requestListener` that awaits a `handlersPromise` before delegating to the router-server handler, so the socket is listening before the bundler is ready. — [packages/next/src/server/lib/start-server.ts#L186-L292](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/lib/start-server.ts#L186-L292)
- `server/lib/router-server.ts#initialize()` is the real runtime boundary: it builds the filesystem checker, optionally instantiates `setupDevBundler` + `DevBundlerService` in dev, wires `renderServer.instance = require('./render-server')`, and returns `{ requestHandler, upgradeHandler }` used by both `next dev` and `next start`. — [packages/next/src/server/lib/router-server.ts#L130-L306](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/lib/router-server.ts#L130-L306) [packages/next/src/server/lib/router-server.ts#L904-L1110](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/lib/router-server.ts#L904-L1110)
- `BaseServer` (3459 lines) is the abstract render core; its `protected abstract` surface — `findPageComponents`, `getPagesManifest`, `getPrerenderManifest`, `renderHTML`, `getIncrementalCache`, `handleUpgrade`, `getMiddleware` — is implemented by `NextNodeServer` for Node and by a separate web/edge server under `packages/next/src/server/web/`. — [packages/next/src/server/base-server.ts#L358-L441](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/base-server.ts#L358-L441) [packages/next/src/server/next-server.ts#L185-L360](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/next-server.ts#L185-L360)
- Build is one giant traced function: `build()` in `packages/next/src/build/index.ts` (4815 lines) wraps everything in a `trace('next-build')` span, loads config, runs `discoverRoutes`/`createPagesMapping`, then branches to either `turbopackBuild(telemetry)` or `webpackBuild(useBuildWorker, [...])`, and finally writes `ROUTES_MANIFEST`, `PRERENDER_MANIFEST`, `APP_PATH_ROUTES_MANIFEST` and friends via a shared `writeManifest<T>()` helper. — [packages/next/src/build/index.ts#L1058-L1120](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/build/index.ts#L1058-L1120) [packages/next/src/build/index.ts#L1810-L1860](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/build/index.ts#L1810-L1860) [packages/next/src/build/index.ts#L664-L810](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/build/index.ts#L664-L810)
- Turbopack is consumed through napi: `packages/next/src/build/swc/index.ts` (1905 lines) defines a `ProjectImpl implements Project` class that forwards to native calls like `binding.projectEntrypointsSubscribe`, `binding.projectWriteAllEntrypointsToDisk`, `binding.projectClientHmrEvents`, and `binding.projectGetSourceMap`, all backed by `crates/next-api/src/project.rs`'s `ProjectContainer`/`Project` turbo-tasks types. — [packages/next/src/build/swc/index.ts#L683-L835](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/build/swc/index.ts#L683-L835) [crates/next-api/src/project.rs#L448-L1045](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/crates/next-api/src/project.rs#L448-L1045)
- The Rust workspace excludes `rspack/crates/binding` and tunes profiles aggressively — `turbo-persistence` gets `opt-level = 1` even in dev, release uses `lto = "thin"` — indicating the persistence layer and cold-build time are known hot spots. — [Cargo.toml#L1-L60](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/Cargo.toml#L1-L60)
- Storage is layered: `IncrementalCache` (`server/lib/incremental-cache/index.ts`) picks a handler from `globalThis[Symbol.for('@next/cache-handlers')].FetchCache` or falls back to `FileSystemCache`, which writes under `serverDistDir/../cache/fetch-cache`, `serverDistDir/pages`, and `serverDistDir/app`. — [packages/next/src/server/lib/incremental-cache/index.ts#L143-L220](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/lib/incremental-cache/index.ts#L143-L220) [packages/next/src/server/lib/incremental-cache/file-system-cache.ts#L460-L480](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/lib/incremental-cache/file-system-cache.ts#L460-L480)
- Build/test commands are defined in the root `package.json`: `pnpm build` (`turbo run build`), `pnpm build-all` (adds `build-native-auto`), `pnpm --filter=next dev` for the taskr watch build, `pnpm test-unit`, and a 20-entry matrix of `test-{dev,start,deploy}-{webpack,turbo,rspack}[-experimental]` scripts that all shell into `scripts/run-jest.sh`. — [package.json#L8-L70](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/package.json#L8-L70) [scripts/run-jest.sh#L1-L60](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/scripts/run-jest.sh#L1-L60)
- Turborepo caches `build`/`dev` on `dist/**` outputs and declares a `pack-for-isolated-tests` task producing `packed.tgz`, which is how integration tests install a real tarball of `next` rather than a symlinked workspace copy. — [turbo.json#L6-L30](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/turbo.json#L6-L30) [packages/next/package.json#L86-L88](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/package.json#L86-L88)
- The `next` package vendors its own dependencies at build time: `packages/next/taskfile.js` (3140 lines) declares an `externals[...]` map and one `ncc_*` task per bundled dep (`node-html-parser`, `@vercel/routing-utils`, `busboy`, `@babel/runtime`, …), all remapped to `next/dist/compiled/*`. — [packages/next/taskfile.js#L100-L200](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/taskfile.js#L100-L200) [jest.config.js#L20-L26](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/jest.config.js#L20-L26)
- Deployment shape for downstream users is pluggable via `config.adapterPath` (defaulting to `process.env.NEXT_ADAPTER_PATH`): after the build, `handleBuildComplete()` dynamic-imports that module and hands it an `AdapterOutputs` description of every route, so hosting platforms can consume the build without reading `.next` internals. — [packages/next/src/server/config-shared.ts#L1848](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/config-shared.ts#L1848) [packages/next/src/server/config-shared.ts#L2356](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/config-shared.ts#L2356) [packages/next/src/build/adapter/build-complete.ts#L499-L660](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/build/adapter/build-complete.ts#L499-L660) [packages/next/src/build/index.ts#L4594](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/build/index.ts#L4594)
- CI is GitHub Actions with a reusable-workflow pattern: `build_and_test.yml` fans out to `build_reusable.yml` for `build-native`, `build-native-windows`, `build-next`, `lint`, `types-and-precompiled`, `test-cargo-unit`, `test-cargo-unit-miri`, `rust-check`, and `rustdoc-check`, each passing an `afterBuild:` shell body and a `stepName`. — [.github/workflows/build_and_test.yml#L129-L380](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/.github/workflows/build_and_test.yml#L129-L380) [.github/workflows/build_reusable.yml](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/.github/workflows/build_reusable.yml)

## Feature catalog

User-facing surface splits four ways: the `next` CLI subcommands, the runtime request-handling features (App Router, Pages Router, middleware/proxy, server actions, image optimization, RSC prefetching), the sibling npm packages (create-next-app, eslint-plugin-next, codemods, fonts, third-parties, bundle-analyzer UI), and a dev-time developer-experience layer (dev overlay, MCP server, request insights, typegen). Nearly every experimental capability is gated behind a key in `ExperimentalConfig` and mirrored into bundles by `define-env.ts`. Documentation is itself a product: 1153 markdown files including `docs/01-app` + `docs/02-pages` and 258 `errors/*.mdx` pages that the dev overlay deep-links into.
- `next dev` supports self-signed HTTPS (`--experimental-https`, `--experimental-https-key/-cert/-ca` via `createSelfSignedCertificate`), Node inspector passthrough with automatic port+1 remapping for the forked child, and CPU profiling through `--experimental-cpu-prof` writing into a `.next-profiles` directory. — [packages/next/src/cli/next-dev.ts#L45-L63](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/cli/next-dev.ts#L45-L63) [packages/next/src/cli/next-dev.ts#L378-L428](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/cli/next-dev.ts#L378-L428) [packages/next/src/bin/next.ts#L38-L56](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/bin/next.ts#L38-L56)
- A dev-only MCP server is mounted at `/_next/mcp` by both hot reloaders via `getMcpMiddleware`, using `StreamableHTTPServerTransport` with a 1MB body cap, exposing agent tools `compile-route`, `get-compilation-issues`, `get-errors`, `get-logs`, `get-page-metadata`, `get-project-metadata`, `get-request-insights`, `get-routes`, and `get-server-action-by-id`. — [packages/next/src/server/mcp/get-mcp-middleware.ts#L9-L30](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/mcp/get-mcp-middleware.ts#L9-L30) [packages/next/src/server/dev/hot-reloader-turbopack.ts#L1271](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/dev/hot-reloader-turbopack.ts#L1271) [packages/next/src/server/dev/hot-reloader-webpack.ts#L1721](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/dev/hot-reloader-webpack.ts#L1721)
- Request Insights is a dev feature reachable at a dedicated endpoint (`REQUEST_INSIGHTS_DEV_ENDPOINT`) that returns `getRequestInsightsSnapshot()` as JSON, 404s with an explanatory body when `experimental.requestInsights` is off, and is also surfaced as the `next experimental-request-insights` CLI subcommand. — [packages/next/src/server/lib/router-server.ts#L316-L355](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/lib/router-server.ts#L316-L355) [packages/next/src/bin/next.ts#L636-L664](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/bin/next.ts#L636-L664) [packages/next/src/cli/next-request-insights.ts](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/cli/next-request-insights.ts)
- Image optimization is a first-class route: `NextNodeServer.handleNextImageRequest` validates params, consults an `ImageOptimizerCache`, and calls `imageOptimizer`/`fetchExternalImage`/`fetchInternalImage`, enforcing `images.localPatterns`, `images.remotePatterns`, http/https-only protocols, and an anti-recursion check on `/_next/image`. — [packages/next/src/server/next-server.ts#L954-L1065](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/next-server.ts#L954-L1065) [packages/next/src/server/image-optimizer.ts#L173-L235](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/image-optimizer.ts#L173-L235)
- Server Actions ship with built-in CSRF defence: `action-handler.ts` compares the `Origin` header against `x-forwarded-host`/`host`, allows extra hosts via `serverActions.allowedOrigins` through `isCsrfOriginAllowed`, and aborts with `Invalid Server Actions request.` when neither host header is present. — [packages/next/src/server/app-render/action-handler.ts#L506-L545](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/app-render/action-handler.ts#L506-L545) [packages/next/src/server/app-render/action-handler.ts#L710-L745](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/app-render/action-handler.ts#L710-L745) [packages/next/src/server/app-render/csrf-protection.ts#L69-L90](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/app-render/csrf-protection.ts#L69-L90)
- Both routers are live: `server/route-modules/` carries four families — `app-page`, `app-route`, `pages`, `pages-api` — each a `RouteModule` subclass, with `AppPageRouteModule.render()` and `.prerender()` delegating to `renderToHTMLOrFlight` / `prerenderToHTMLOrFlight`. — [packages/next/src/server/route-modules/route-module.ts#L111-L150](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/route-modules/route-module.ts#L111-L150) [packages/next/src/server/route-modules/app-page/module.ts#L88-L250](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/route-modules/app-page/module.ts#L88-L250)
- Cache Components / PPR is the headline experimental feature: `cacheComponents` is now a top-level config key (the old `experimental.cacheComponents` and `experimental.ppr` are marked `@deprecated`), and enabling it implicitly turns on `experimental.ppr` and `experimental.cachedNavigations`. — [packages/next/src/server/config-shared.ts#L1231-L1234](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/config-shared.ts#L1231-L1234) [packages/next/src/server/config-shared.ts#L2126-L2135](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/config-shared.ts#L2126-L2135) [test/lib/gate/conditions.ts#L23-L28](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/test/lib/gate/conditions.ts#L23-L28)
- `"use cache"` is backed by a swappable handler registry keyed on `Symbol.for('@next/cache-handlers')` / `-map` / `-set`, with dev-only extras (`-dev-fronts`, `-dev-tiered`, `-private`) and a `DEV_MEMORY_CACHE_SIZE` of 50MB so `cacheMaxMemorySize: 0` does not make dev reloads uncached. — [packages/next/src/server/use-cache/handlers.ts#L14-L95](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/use-cache/handlers.ts#L14-L95) [packages/next/src/server/lib/cache-handlers/types.ts#L1-L60](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/lib/cache-handlers/types.ts#L1-L60)
- i18n domain routing and locale redirects are handled before render, directly in the router-server request handler, using `detectDomainLocale` + `getLocaleRedirect` and short-circuiting with a `307 TemporaryRedirect`. — [packages/next/src/server/lib/router-server.ts#L358-L405](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/lib/router-server.ts#L358-L405)
- `packages/next-codemod` ships 21 named transforms for migrations, including `next-async-request-api`, `middleware-to-proxy`, `metadata-to-viewport-export`, `cra-to-next`, `remove-experimental-ppr`, and `next-lint-to-eslint-cli`, driven by the `next upgrade` subcommand. — [packages/next-codemod/transforms/](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next-codemod/transforms/) [packages/next/src/bin/next.ts#L564-L600](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/bin/next.ts#L564-L600) [packages/next/src/cli/next-upgrade.ts](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/cli/next-upgrade.ts)
- `@next/eslint-plugin-next` exposes 23 rules through a single `plugin.rules` map with two presets — `recommendedRules` (16 warn + 6 error) and `coreWebVitalsRules` which escalates `no-html-link-for-pages` and `no-sync-scripts` to error. — [packages/eslint-plugin-next/src/index.ts#L1-L80](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/eslint-plugin-next/src/index.ts#L1-L80) [packages/eslint-plugin-next/src/rules/](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/eslint-plugin-next/src/rules/)
- Telemetry is opt-out: `Telemetry` stores an `anonymousId` and `telemetry.enabled` in a conf file, disables itself on `NEXT_TELEMETRY_DISABLED`, prints the `https://nextjs.org/telemetry` notice, and is toggled by the `next telemetry` subcommand. — [packages/next/src/telemetry/storage.ts#L16-L155](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/telemetry/storage.ts#L16-L155) [packages/next/src/bin/next.ts#L508-L527](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/bin/next.ts#L508-L527)
- `next typegen` generates route types independently of a full build, backed by `server/lib/router-utils/typegen.ts` and `route-types-utils.ts` / `root-params-type-utils.ts`. — [packages/next/src/bin/next.ts#L529-L545](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/bin/next.ts#L529-L545) [packages/next/src/server/lib/router-utils/typegen.ts](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/lib/router-utils/typegen.ts) [packages/next/src/server/lib/router-utils/route-types-utils.ts](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/lib/router-utils/route-types-utils.ts)
- The dev overlay is a separate sub-application under `packages/next/src/next-devtools/` with `dev-overlay/`, `userspace/`, `server/`, and `shared/` layers plus a browser entry (`dev-overlay.browser.tsx`) and a shim, and its UX can be killed at build time via `process.env.__NEXT_DISABLE_DEV_OVERLAY_UX`. — [packages/next/src/next-devtools/](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/next-devtools/) [packages/next/src/build/define-env.ts#L161](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/build/define-env.ts#L161)
- An `evals/` workspace exists for agent evaluation (`evals/next-upgrade`, `eval.config.json`, run via `pnpm eval` / `pnpm eval:upgrade` using `@vercel/agent-eval`), making automated upgrade quality a tracked product surface. — [package.json#L14-L16](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/package.json#L14-L16) [evals/README.md](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/evals/README.md) [evals/eval.config.json](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/evals/eval.config.json)

## Data flow

A request enters at the Node `http.Server` in `start-server.ts`, is immediately sanitised by `filterInternalHeaders()` (which deletes a fixed `INTERNAL_HEADERS` list so clients cannot forge `x-middleware-rewrite`, `x-matched-path`, etc.), then flows through the router-server's `resolveRoutes` pipeline (headers → redirects → middleware → beforeFiles → filesystem → afterFiles → dynamic routes → fallback), and only then crosses into the render server. Inside `BaseServer.handleRequest` the request is wrapped in an OTEL span and per-request metadata is attached via `addRequestMeta`, after which render state is carried in AsyncLocalStorage (`workAsyncStorage`, `workUnitAsyncStorage`) rather than threaded through arguments. Output is a `RenderResult` piped to the socket, with cacheable results passing through `ResponseCache` → `IncrementalCache` → a `CacheHandler`. Build-time data flows the other direction: source files → route discovery → bundler (Turbopack over napi, or webpack) → `.next/*-manifest.json` → read back by `setupFsCheck` at server boot.
- The first thing `requestHandlerImpl` does with a live request is `addRequestMeta(req, 'relativeProjectDir', ...)` and then `filterInternalHeaders(req.headers)` — unless `NEXT_PRIVATE_TEST_HEADERS` is set, which is the documented test-only escape hatch. — [packages/next/src/server/lib/router-server.ts#L307-L314](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/lib/router-server.ts#L307-L314) [packages/next/src/server/lib/server-ipc/utils.ts#L38-L62](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/lib/server-ipc/utils.ts#L38-L62)
- `INTERNAL_HEADERS` is an explicit allow-nothing list of 11 names (`x-middleware-rewrite`, `x-middleware-redirect`, `x-middleware-set-cookie`, `x-middleware-skip`, `x-middleware-override-headers`, `x-middleware-next`, `x-now-route-matches`, `x-matched-path`, `x-nextjs-data`, `x-next-resume-state-length`, `next-resume`); any new internal header must be added here or it is forgeable from outside. — [packages/next/src/server/lib/server-ipc/utils.ts#L38-L52](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/lib/server-ipc/utils.ts#L38-L52) [AGENTS.md](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/AGENTS.md)
- Routing is a declarative ordered list built once in `resolve-routes.ts`: `middleware_next_data`, `middleware`, `rewrites.beforeFiles`, a second middleware check, `rewrites.afterFiles`, then `rewrites.fallback` — with all rewrite phases skipped entirely when `opts.minimalMode` is set (the deployed/serverless shape). — [packages/next/src/server/lib/router-utils/resolve-routes.ts#L86-L118](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/lib/router-utils/resolve-routes.ts#L86-L118)
- Middleware runs out-of-process-shaped: `resolveRoutes` sets `middlewareInvoke: true` request meta, invokes the render server, and then translates the returned `Response` headers — hoisting `x-middleware-override-headers` into real request headers by reading each `x-middleware-request-<key>` and deleting it, and synthesising `x-middleware-refresh` when neither rewrite, next, nor location was set. — [packages/next/src/server/lib/router-utils/resolve-routes.ts#L566-L710](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/lib/router-utils/resolve-routes.ts#L566-L710)
- `setupFsCheck()` is the build→runtime data handoff: it reads `routes-manifest.json`, the app/pages routes manifests, and the `public/` + `.next/static/` directory listings into `Set`s (`publicFolderItems`, `nextStaticFolderItems`, `appFiles`, `pageFiles`) plus a sorted `dynamicRoutes` array and a `middlewareMatcher`, then exposes a single LRU-backed `getItem(pathname)`. — [packages/next/src/server/lib/router-utils/filesystem.ts#L192-L340](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/lib/router-utils/filesystem.ts#L192-L340) [packages/next/src/server/lib/router-utils/filesystem.ts#L489-L700](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/lib/router-utils/filesystem.ts#L489-L700)
- Per-request state is passed via `addRequestMeta`/`getRequestMeta` keys (`invokePath`, `invokeQuery`, `middlewareInvoke`, `locale`, `isRSCRequest`, `relativeProjectDir`) rather than function parameters, which is what lets the router server and render server share a request object across the lazy-init boundary. — [packages/next/src/server/lib/router-server.ts#L460-L480](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/lib/router-server.ts#L460-L480) [packages/next/src/server/request-meta.ts](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/request-meta.ts) [packages/next/src/server/base-server.ts#L899-L905](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/base-server.ts#L899-L905)
- Render-time state lives in AsyncLocalStorage: `app-render.tsx` repeatedly does `workUnitAsyncStorage.run(...)` to produce the RSC payload and the Flight stream, and `workAsyncStorage` carries the work store; there is no explicit context object threaded through the React tree. — [packages/next/src/server/app-render/app-render.tsx#L19-L25](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/app-render/app-render.tsx#L19-L25) [packages/next/src/server/app-render/app-render.tsx#L975-L1025](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/app-render/app-render.tsx#L975-L1025)
- Cache Components prerendering is a two-pass flow: a prospective prerender fills a `PrerenderResumeDataCache` created by `createPrerenderResumeDataCache()`, that cache is attached to `requestStore.resumeDataCache`, and a second 'final'/runtime prerender reuses the filled cache — the comments state caches must be shared between the prospective and final prerender. — [packages/next/src/server/app-render/app-render.tsx#L1111-L1145](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/app-render/app-render.tsx#L1111-L1145) [packages/next/src/server/app-render/app-render.tsx#L1597-L1730](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/app-render/app-render.tsx#L1597-L1730)
- Cacheable responses flow value-as-stream: a `CacheEntry.value` is a `ReadableStream<Uint8Array>` with a documented contract that handlers must return a new stream on every `get` and must not retain the stream after `set` resolves, because a retained stream pins the originating request's async context. — [packages/next/src/server/lib/cache-handlers/types.ts#L6-L30](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/lib/cache-handlers/types.ts#L6-L30)
- Tag-based revalidation is a side-channel: `FileSystemCache` mutates a module-scoped `tagsManifest` map with `{ expired: now }` entries and reads it back through `areTagsExpired`, so tag state is per-process in the filesystem handler. — [packages/next/src/server/lib/incremental-cache/file-system-cache.ts#L24-L100](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/lib/incremental-cache/file-system-cache.ts#L24-L100)
- Build-time config is projected into user bundles as `process.env.__NEXT_*` string literals by `getDefineEnv()` — 40+ keys including `__NEXT_BUNDLER`, `__NEXT_DEV_SERVER`, `__NEXT_CACHE_COMPONENTS`, `__NEXT_USE_CACHE`, `__NEXT_IMAGE_OPTS`, and the client router bloom filters `__NEXT_CLIENT_ROUTER_S_FILTER`/`_D_FILTER`. — [packages/next/src/build/define-env.ts#L82-L285](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/build/define-env.ts#L82-L285)
- `process.env.__NEXT_USE_NODE_STREAMS` is set to `false` for the edge server and `true` otherwise, which is the fork point for which stream implementation the render path compiles against. — [packages/next/src/build/define-env.ts#L192](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/build/define-env.ts#L192)
- Outbound network egress from the framework is narrow and bounded: the image optimizer `fetch`es upstream images with `AbortSignal.timeout(7_000)` and `redirect: 'manual'`, following at most 3 redirects manually. — [packages/next/src/server/image-optimizer.ts#L517-L575](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/image-optimizer.ts#L517-L575)
- Build output data leaves the repo through the adapter boundary: `handleBuildComplete()` resolves `adapterPath` with `require.resolve` + `pathToFileURL` and, for `output: 'export'`, enumerates `configOutDir` with `recursiveReadDir` to hand the adapter a static-file list. — [packages/next/src/build/adapter/build-complete.ts#L595-L685](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/build/adapter/build-complete.ts#L595-L685)

## Behavior traces

Three flows dominate the codebase. (1) Dev startup: CLI parse → fork → HTTP listen → router-server init → dev bundler → render server lazy init. (2) A production request: socket → header filtering → route resolution → `BaseServer.handleRequest` → route module render → response cache → pipe. (3) A production build: config load → route discovery → bundler compile → static generation → manifests → trace collection → adapter callback. Each is traceable through named functions, and each is instrumented with a `trace()`/OTEL span so the sequence is visible in `.next/trace`.
- Dev startup order: `bin/next.ts` checks the Node version against `__NEXT_REQUIRED_NODE_VERSION_RANGE` and exits 1 on mismatch, warns if `react`/`react-dom` fail `require.resolve`, stamps `NEXT_PRIVATE_START_TIME`, then runs the `dev` action. — [packages/next/src/bin/next.ts#L61-L90](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/bin/next.ts#L61-L90)
- `nextDev` then opens a `trace('next-dev')` session span, records a 'rage restart' if the previous session for this project dir stopped within `RAGE_RESTART_THRESHOLD_MS` (90s) using a shared `dev-state.json` keyed by project directory under the OS cache dir, and forks the child. — [packages/next/src/cli/next-dev.ts#L68-L100](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/cli/next-dev.ts#L68-L100) [packages/next/src/cli/next-dev.ts#L352-L400](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/cli/next-dev.ts#L352-L400)
- In the child, `startServer()` sets `process.title = 'next-server (v...)'`, creates the http(s) server with a `requestListener` that awaits `handlersPromise`, registers an `'upgrade'` handler that destroys the socket on error, and only afterwards calls `initialize()` to populate `requestHandler`/`upgradeHandler`. — [packages/next/src/server/lib/start-server.ts#L200-L292](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/lib/start-server.ts#L200-L292) [packages/next/src/server/lib/start-server.ts#L495-L505](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/lib/start-server.ts#L495-L505)
- `initialize()` runs `setupDevBundler({ ... renderServer ... })` inside a `setupDevBundlerSpan`, wraps the result in `new DevBundlerService(...)`, assigns `renderServer.instance = require('./render-server')`, builds `renderServerOpts`, sets `renderServerOpts.serverFields.routerServerHandler = requestHandlerImpl` (a deliberate back-reference), and awaits `renderServer.instance.initialize(renderServerOpts)`. — [packages/next/src/server/lib/router-server.ts#L206-L306](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/lib/router-server.ts#L206-L306) [packages/next/src/server/lib/router-server.ts#L917-L950](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/lib/router-server.ts#L917-L950)
- Only once handlers exist does the child `process.send({ nextWorkerReady: true })`; the parent replies with `nextWorkerOptions`, the child later sends `{ nextServerReady: true, port, distDir }`, and the parent stores `port` (for restart reuse) and `distDir` (for telemetry and trace upload). — [packages/next/src/server/lib/start-server.ts#L648-L673](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/lib/start-server.ts#L648-L673) [packages/next/src/cli/next-dev.ts#L434-L452](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/cli/next-dev.ts#L434-L452)
- Dev restart is signalled by exit code: `requestListener`'s `finally` block calls `getMemoryRestartStats()` and, when near the heap limit, emits a `server-restart-close-to-memory-threshold` span, flushes traces and `process.exit(RESTART_EXIT_CODE)`; the parent sees that code, uploads the existing `.next/trace` before it is overwritten, and restarts. — [packages/next/src/server/lib/start-server.ts#L253-L270](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/lib/start-server.ts#L253-L270) [packages/next/src/cli/next-dev.ts#L457-L475](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/cli/next-dev.ts#L457-L475)
- Request trace (dev): `requestHandlerImpl` → `filterInternalHeaders` → request-insights endpoint check → i18n `getLocaleRedirect` → optional `compress()` with a `res.once('close')` hook calling `releaseCompressionStream` to avoid a zlib leak on client disconnect → `handleRequest(0)`, which first calls `blockCrossSiteDEV`. — [packages/next/src/server/lib/router-server.ts#L307-L420](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/lib/router-server.ts#L307-L420) [packages/next/src/server/lib/router-server.ts#L490-L500](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/lib/router-server.ts#L490-L500)
- `handleRequest(handleIndex)` is recursive with a hard stop — it throws `Attempted to handle request too many times` once `handleIndex > 5` — and `invokeRender` re-enters it at `handleIndex + 1` when the render server throws `NoFallbackError`. — [packages/next/src/server/lib/router-server.ts#L470-L500](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/lib/router-server.ts#L470-L500)
- Render trace: `BaseServer.handleRequest` → `await this.prepare()` → `tracer.withPropagatedContext(req.headers, ...)` → `tracer.trace(BaseServerSpan.handleRequest, ...)` → `handleRequestImpl`, with the span renamed in `.finally()` to `RSC GET /route` or `GET /route` from `next.route`, and `http.route` propagated up to a platform-created parent span for APM tools. — [packages/next/src/server/base-server.ts#L866-L955](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/base-server.ts#L866-L955)
- When request insights are enabled the whole handler is additionally wrapped in `runWithRequestInsightsIdentity({ requestId, htmlRequestId, url }, handleRequest)`, taking the id from the `NEXT_REQUEST_ID_HEADER`/`NEXT_HTML_REQUEST_ID_HEADER` headers or minting one with `nanoid()`. — [packages/next/src/server/base-server.ts#L955-L980](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/base-server.ts#L955-L980)
- `handleRequestImpl` normalises before routing: it calls `patchSetHeaderWithCookieSupport` so middleware-set cookies survive API routes/`getServerSideProps`, then 308-redirects any URL matching `/(\\|\/\/)/` through `normalizeRepeatedSlashes`. — [packages/next/src/server/base-server.ts#L982-L1000](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/base-server.ts#L982-L1000)
- Page resolution: `NextNodeServer.findPageComponents` → `loadComponents({...})` reads the pages manifest and returns a `ComponentMod`; the corresponding `RouteModule` subclass then runs, e.g. `AppPageRouteModule.render()` → `renderToHTMLOrFlight()`. — [packages/next/src/server/next-server.ts#L837-L910](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/next-server.ts#L837-L910) [packages/next/src/server/route-modules/app-page/module.ts#L158-L185](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/route-modules/app-page/module.ts#L158-L185)
- Build trace: `build()` → `trace('next-build')` → load env files → resolve `NextConfigComplete` → `setAttribute('bundler', ...)` → `loadCustomRoutes` → create distDir → `discoverRoutes` → `createPagesMapping` → write `ROUTES_MANIFEST` → bundle → static generation → `writePrerenderManifest` → `printTreeView` → `telemetry.flush()`. — [packages/next/src/build/index.ts#L1098-L1310](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/build/index.ts#L1098-L1310) [packages/next/src/build/index.ts#L1477-L1710](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/build/index.ts#L1477-L1710) [packages/next/src/build/index.ts#L4454-L4765](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/build/index.ts#L4454-L4765)
- Webpack builds can be offloaded: when `experimental.webpackBuildWorker` is enabled the build sets a `use-build-worker` span attribute and runs `webpackBuild(useBuildWorker, [...])` in a worker exposing `collectBuildTraces`; Turbopack instead goes through a single `turbopackBuild(telemetry)` call. — [packages/next/src/build/index.ts#L1775-L1885](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/build/index.ts#L1775-L1885)
- Static export is a nested build stage: `build()` `require`s `../export` and calls `exportApp(...)` twice — once for `output: 'export'` full static output (`writeFullyStaticExport`) and once for per-page static generation — before manifests are finalised. — [packages/next/src/build/index.ts#L1014-L1022](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/build/index.ts#L1014-L1022) [packages/next/src/build/index.ts#L3114-L3270](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/build/index.ts#L3114-L3270) [packages/next/src/export/index.ts](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/export/index.ts)

## Testing signals

Jest 29 is the single test runner for JS, configured through `next/jest` itself (dogfooding) in the root `jest.config.js`, with `rootDir: 'test'` plus extra roots into `packages/next/src`, `packages/next-codemod`, `packages/font/src`, `packages/next-routing`, and `packages/eslint-plugin-internal`. Integration coverage is very large — roughly 1183 test files in `test/e2e`, 375 in `test/production`, 314 in `test/development`, 116 in `test/unit` — and every one of them runs under a mode × bundler × experimental-axis matrix selected by env vars in `scripts/run-jest.sh`. Rust has its own lane (`test-cargo-unit`, `test-cargo-unit-miri`, `rust-check`, `rustdoc-check`) in CI. The notable gap is that the harness itself, the CI orchestration in `run-tests.js`, and the deploy mode are lightly covered by anything other than running them.
- `jest.config.js` is built from `nextJest()` — the framework tests itself through its own `next/jest` transformer — and then wrapped by `withGateTransformer(await createConfig())` so the `@gate` pragma rewrite chains in front of next/jest's SWC transform rather than replacing it. — [jest.config.js#L1-L10](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/jest.config.js#L1-L10) [jest.config.js#L70-L90](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/jest.config.js#L70-L90) [test/lib/gate/jest-transformer.js](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/test/lib/gate/jest-transformer.js)
- The test matrix is expressed purely as environment variables: `scripts/run-jest.sh` maps `--mode` to `NEXT_TEST_MODE`, `--bundler` to `IS_WEBPACK_TEST` / `IS_TURBOPACK_TEST` / `NEXT_RSPACK`+`NEXT_TEST_USE_RSPACK`, `--experimental` to `__NEXT_CACHE_COMPONENTS=true`, and `--headless` to `HEADLESS=true`, then `exec jest --runInBand`. — [scripts/run-jest.sh#L17-L56](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/scripts/run-jest.sh#L17-L56) [scripts/run-jest.sh#L96-L100](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/scripts/run-jest.sh#L96-L100)
- `__NEXT_TEST_AXIS` implements a lettered flag-matrix dimension: setting the axis implies `__NEXT_CACHE_COMPONENTS=true` and vice-versa, and a fixture opts in by keying a flag on it (`concurrentRouterQueue: process.env.__NEXT_TEST_AXIS !== 'A'`) paired with a `// @gate concurrentRouterQueue` on affected tests. — [scripts/run-jest.sh#L58-L94](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/scripts/run-jest.sh#L58-L94) [test/lib/gate/README.md](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/test/lib/gate/README.md)
- The `@gate` registry has two tiers: `staticCondition` (run mode, bundler, React version — known at collection time and the only kind `@force-gate` accepts) and `lazyCondition` (a predicate over the fixture's *resolved* `next.config` read inside the test body); an undeclared condition name fails the whole suite at collection time, so typos cannot silently disable a gate. — [test/lib/gate/conditions.ts#L1-L70](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/test/lib/gate/conditions.ts#L1-L70) [test/lib/gate/resolved-config.ts](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/test/lib/gate/resolved-config.ts)
- `nextTestSetup()` in `test/lib/e2e-utils/index.ts` is the mandated entry point; it internally calls `createNext()` (explicitly documented as not for direct test use), builds a `NextInstance` from one of `NextDevInstance` / `NextStartInstance` / `NextDeployInstance`, and returns a `Proxy<NextInstance>` so the instance can be referenced before it exists. — [test/lib/e2e-utils/index.ts#L267-L347](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/test/lib/e2e-utils/index.ts#L267-L347) [test/lib/e2e-utils/index.ts#L384-L470](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/test/lib/e2e-utils/index.ts#L384-L470)
- Test timeouts are enforced by proxying jest's `it`/`test`: `wrapJestTestFn` injects a 60s `individualTestTimeout` when a test supplies no explicit timeout, while setup hooks get a longer 120s (240s on win32) budget overridable by `NEXT_E2E_TEST_TIMEOUT`. — [test/lib/e2e-utils/index.ts#L26-L76](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/test/lib/e2e-utils/index.ts#L26-L76)
- `retry()` in `test/lib/next-test-utils.ts` is the sanctioned wait primitive and `check()` still exists at line 844 but is deprecated by project convention — new tests are required to use `retry()` + `expect()`. — [test/lib/next-test-utils.ts#L844](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/test/lib/next-test-utils.ts#L844) [test/lib/next-test-utils.ts#L926](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/test/lib/next-test-utils.ts#L926) [AGENTS.md](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/AGENTS.md)
- Integration tests install a real tarball rather than symlinking: `NextInstance` calls `createNextInstall(...)` from `test/lib/create-next-install.js`, and `test/lib/pnpm-security-settings.js` copies `blockExoticSubdeps` / `minimumReleaseAge` from `pnpm-workspace.yaml` into the isolated fixture dir. — [test/lib/next-modes/base.ts#L10](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/test/lib/next-modes/base.ts#L10) [test/lib/next-modes/base.ts#L402](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/test/lib/next-modes/base.ts#L402) [pnpm-workspace.yaml#L20-L30](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/pnpm-workspace.yaml#L20-L30) [test/lib/pnpm-security-settings.js](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/test/lib/pnpm-security-settings.js)
- Deploy mode shells out: `NextDeployInstance` runs `execa(process.env.NEXT_TEST_DEPLOY_SCRIPT_PATH)` and expects the deployment URL on stdout, with separate logs and cleanup scripts — so `pnpm test-deploy` needs external credentials and cannot run hermetically. — [test/lib/next-modes/next-deploy.ts#L66-L190](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/test/lib/next-modes/next-deploy.ts#L66-L190)
- `run-tests.js` (1164 lines) is the CI orchestrator, not jest: it reads/writes test timings to Vercel KV (`@vercel/kv`, `KV_TIMINGS_KEY = 'test-timings'`), applies `getTestFilter()`, defaults to `DEFAULT_CONCURRENCY = 2` and `DEFAULT_NUM_RETRIES = 2`, and emits `##[group]` markers when `CI` is set. — [run-tests.js#L1-L220](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/run-tests.js#L1-L220) [turbo.json#L28-L33](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/turbo.json#L28-L33)
- Bundler-specific expected-failure lists are checked-in manifests — `test/rspack-build-tests-manifest.json`, `test/rspack-dev-tests-manifest.json`, `test/cache-components-tests-manifest.json`, `test/deploy-tests-manifest.json` — maintained by `test/update-bundler-manifest.js`, so 'known failing under rspack' is data, not a skip. — [test/cache-components-tests-manifest.json](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/test/cache-components-tests-manifest.json) [test/rspack-build-tests-manifest.json](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/test/rspack-build-tests-manifest.json) [test/update-bundler-manifest.js](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/test/update-bundler-manifest.js)
- JUnit reporting is opt-in via `NEXT_JUNIT_TEST_REPORT` and routes output to a bundler-specific directory (`turbopack-test-junit-report`, `rspack-test-junit-report`, or `test-junit-report`), deliberately not counting retries so flaky-but-passing tests still report as failed. — [jest.config.js#L45-L70](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/jest.config.js#L45-L70)
- Unit tests are colocated with source and run separately: `pnpm test-unit` targets `test/unit/`, `packages/next/`, and `packages/font`, picking up files like `packages/next/src/server/is-private-ip.test.ts`, `config.test.ts`, `load-manifest.test.ts`, and `patch-fetch.test.ts`. — [package.json#L22](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/package.json#L22) [packages/next/src/server/is-private-ip.test.ts](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/is-private-ip.test.ts) [packages/next/src/server/lib/patch-fetch.test.ts](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/lib/patch-fetch.test.ts)
- CI runs a distinct lint/type lane separate from tests: `pnpm lint` parallelises `test-types`, `lint-typescript`, `prettier-check`, `lint-eslint`, `lint-ast-grep`, `lint-language` (alex), and `check-unused-turbo-tasks`, plus a `types-and-precompiled` job that also type-checks every `crates/next-custom-transforms/tests/fixture/**/tsconfig.json`. — [package.json#L86-L100](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/package.json#L86-L100) [.github/workflows/build_and_test.yml#L236-L290](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/.github/workflows/build_and_test.yml#L236-L290)
- `createRouterAct` / `LinkAccordion` exist as a dedicated harness for deterministically controlling prefetch timing in router tests (`test/lib/router-act.ts`), which is the documented way to avoid flakiness from BFCache-triggered re-prefetches. — [test/lib/router-act.ts](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/test/lib/router-act.ts) [.agents/skills/router-act/SKILL.md](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/.agents/skills/router-act/SKILL.md) [AGENTS.md](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/AGENTS.md)

## Risk map

The biggest blast-radius surfaces are the header-filtering allowlist, the dev-server origin guard, the Server Actions CSRF check, the image optimizer's SSRF path, and the PR-adoption script that grants contributor code access to CI secrets. Structurally, `app-render.tsx` (10,768 lines), `build/index.ts` (4,815) and `base-server.ts` (3,459) are single files that concentrate enormous behaviour and are the natural home of regressions. There is also a large amount of implicit coupling via global symbols, `process.env.__NEXT_*` compile-time constants, and an env-var-driven test matrix, all of which make 'works locally' a weak signal unless the exact CI env is mirrored.
- `INTERNAL_HEADERS` is a hardcoded allowlist, not a prefix rule — any new internal header (e.g. a future `x-middleware-*`) that is read by server code but not added to this array is forgeable by an external client, and the repo's own AGENTS.md flags this as a mandatory PR review check. — [packages/next/src/server/lib/server-ipc/utils.ts#L38-L62](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/lib/server-ipc/utils.ts#L38-L62) [AGENTS.md](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/AGENTS.md)
- `filterInternalHeaders` is skipped entirely when `NEXT_PRIVATE_TEST_HEADERS` is set; anything that leaks that env var into a non-test process disables internal-header sanitisation for the whole server. — [packages/next/src/server/lib/router-server.ts#L310-L314](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/lib/router-server.ts#L310-L314)
- `blockCrossSiteDEV` only guards URLs matching `/_next` or `/__nextjs` and explicitly excludes `/_next/image`, `/_next/static/media`, and `/_next/static/immutable/media` from the check, so those three paths are reachable cross-site in dev by design — a deliberate CORS tradeoff worth re-checking whenever media handling changes. — [packages/next/src/server/lib/router-utils/block-cross-site-dev.ts#L93-L115](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/lib/router-utils/block-cross-site-dev.ts#L93-L115)
- The dev MCP server at `/_next/mcp` executes agent tools (`get-logs`, `get-server-action-by-id`, `compile-route`) and is protected only by virtue of sitting under the `/_next` prefix that `blockCrossSiteDEV` covers; it parses request bodies with a 1MB cap and swallows every error into a generic JSON-RPC 500. — [packages/next/src/server/mcp/get-mcp-middleware.ts#L16-L43](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/mcp/get-mcp-middleware.ts#L16-L43) [packages/next/src/server/mcp/tools/](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/mcp/tools/)
- Server Actions CSRF hinges on `x-forwarded-host` being trustworthy: `getIsPossibleServerAction`-adjacent logic prefers `x-forwarded-host` over `host` when comparing to `Origin`, so a misconfigured reverse proxy that lets clients set that header weakens the check. — [packages/next/src/server/app-render/action-handler.ts#L529-L545](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/app-render/action-handler.ts#L529-L545) [packages/next/src/server/app-render/action-handler.ts#L710-L745](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/app-render/action-handler.ts#L710-L745)
- SSRF protection in the image optimizer is DNS-resolution-based and opt-out: `fetchExternalImage` resolves the hostname and rejects private IPs via `isPrivateIp`, but `images.dangerouslyAllowLocalIP = true` disables it, and the resolve-then-fetch pattern is inherently TOCTOU against DNS rebinding. — [packages/next/src/server/image-optimizer.ts#L521-L555](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/image-optimizer.ts#L521-L555) [packages/next/src/server/is-private-ip.ts](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/is-private-ip.ts)
- `matchWildcardDomain` in `csrf-protection.ts` is a hand-rolled glob matcher written specifically because micromatch cannot run at the edge; it carries explicit special-cases (rejecting bare `*`/`**`, rejecting `**` in non-terminal position) and is the shared basis for both `serverActions.allowedOrigins` and `allowedDevOrigins`, so a bug there affects two security checks at once. — [packages/next/src/server/app-render/csrf-protection.ts#L1-L90](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/app-render/csrf-protection.ts#L1-L90) [packages/next/src/server/lib/router-utils/block-cross-site-dev.ts#L5](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/lib/router-utils/block-cross-site-dev.ts#L5)
- `pnpm pr-adopt` (`scripts/adopt-pr.js`) re-pushes contributor commits into `vercel/next.js`, which grants that code access to repository secrets during install/build/test; it defends with an interactive confirmation naming the author and head SHA, a throwaway worktree, and `core.hooksPath` redirected to an empty directory via `GIT_CONFIG_COUNT` to neutralise `.husky/*` hooks. — [scripts/adopt-pr.js](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/scripts/adopt-pr.js) [package.json#L6](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/package.json#L6) [AGENTS.md](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/AGENTS.md)
- `app-render.tsx` is 10,768 lines and holds the entire RSC render, prospective/final prerender, and resume-data-cache logic in one module — the single highest blast-radius file in the repo. — [packages/next/src/server/app-render/app-render.tsx](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/app-render/app-render.tsx) [packages/next/src/server/app-render/app-render.tsx#L1111-L1260](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/app-render/app-render.tsx#L1111-L1260)
- Cache handlers are wired through nine `Symbol.for('@next/cache-handlers*')` globals rather than imports; because they live on `globalThis`, two copies of the module share state, and a stale symbol contract between the framework and an external handler package fails silently rather than at type-check time. — [packages/next/src/server/use-cache/handlers.ts#L14-L52](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/use-cache/handlers.ts#L14-L52)
- Fragile coupling: `renderServerOpts.serverFields.routerServerHandler = requestHandlerImpl` creates a cycle where the render server calls back into the router server's handler, which is what makes the `handleIndex > 5` recursion guard necessary. — [packages/next/src/server/lib/router-server.ts#L942](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/lib/router-server.ts#L942) [packages/next/src/server/lib/router-server.ts#L470-L475](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/lib/router-server.ts#L470-L475)
- Deprecated-but-live config surface is a trap: `experimental.ppr`, `experimental.cacheComponents`, and `images.domains` are all marked deprecated yet still read at runtime, so code must handle both the old and new location of the same setting. — [packages/next/src/server/config-shared.ts#L1231-L1234](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/config-shared.ts#L1231-L1234) [packages/next/src/server/config-shared.ts#L1373-L1375](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/config-shared.ts#L1373-L1375) [packages/next/src/server/image-optimizer.ts#L173-L182](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/image-optimizer.ts#L173-L182)
- `NextServer` retains five deprecated methods that all emit the same 'Please use `app.getRequestHandler()` with an adjusted parsed URL instead.' warning — legacy API surface that still has to keep working for the programmatic-server use case. — [packages/next/src/server/next.ts#L97-L182](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/next.ts#L97-L182)
- Compile-time env constants are the main source of 'passes locally, fails in CI': `process.env.NODE_ENV !== 'production'` and `process.env.__NEXT_DEV_SERVER` mean different things (`next build --debug-prerender` produces a development bundle without a dev server), and snapshot output differs between Turbopack (`react-dom/server.edge`) and webpack (`react-dom/server.node`). — [packages/next/src/build/define-env.ts#L160](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/build/define-env.ts#L160) [AGENTS.md](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/AGENTS.md) [.agents/skills/dce-edge/SKILL.md](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/.agents/skills/dce-edge/SKILL.md)
- Untested-by-construction flow: the dev memory-threshold restart path exits the process from inside a request's `finally` block, so any in-flight streaming responses are dropped; it is triggered by real heap pressure and therefore hard to cover in the integration suite. (inferred) — [packages/next/src/server/lib/start-server.ts#L253-L270](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/lib/start-server.ts#L253-L270)
- The `patches/` directory carries 11 pnpm patches against third-party packages (`@rspack/core@1.6.7`, `playwright-core@1.61.0`, `@types/node@20.17.7`, `@modelcontextprotocol/sdk`, `webpack-sources`, `taskr`, …), each of which must be re-verified on every upstream bump. — [package.json#L1](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/package.json#L1) [patches/](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/patches/)

## Extension points

Next.js is built to be extended at five distinct seams: the CLI command table, the config schema + `define-env` pipeline for feature flags, the `RouteModule` class hierarchy for new route kinds, the `CacheHandler` / adapter interfaces for hosting and storage backends, and the test-harness registries (`@gate` conditions, bundler manifests). The repo also formalises extension of the *agent* workflow itself through `.agents/skills/`, with a meta-skill for authoring new ones.
- New CLI subcommands plug into the Commander table in `bin/next.ts` — add a `.command(name)` + `.action(() => import('../cli/next-<name>.js'))` pair and a matching `Next<Name>Options` type; the `internal` sub-group shows the pattern for hidden/maintenance commands. — [packages/next/src/bin/next.ts#L187-L200](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/bin/next.ts#L187-L200) [packages/next/src/bin/next.ts#L665-L700](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/bin/next.ts#L665-L700) [packages/next/src/cli/](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/cli/)
- A new feature flag is a four-file change by convention: the type in `server/config-shared.ts` (`ExperimentalConfig`), the zod entry in `server/config-schema.ts`, a default in `defaultConfig`, and a `process.env.__NEXT_*` entry in `build/define-env.ts` when the flag must reach user-bundled code — with the `$flags` skill documenting the runtime-env plumbing needed for pre-compiled internals. — [packages/next/src/server/config-shared.ts#L501](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/config-shared.ts#L501) [packages/next/src/server/config-schema.ts](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/config-schema.ts) [packages/next/src/build/define-env.ts#L129-L270](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/build/define-env.ts#L129-L270) [.agents/skills/flags/SKILL.md](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/.agents/skills/flags/SKILL.md)
- `RouteModule<D, U, X>` is the abstract base every route kind extends; adding a new kind means a new directory under `server/route-modules/` alongside `app-page`, `app-route`, `pages`, `pages-api`, plus a `RouteKind` entry in `server/route-kind.ts` and a definition in `server/route-definitions/`. — [packages/next/src/server/route-modules/route-module.ts#L77-L150](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/route-modules/route-module.ts#L77-L150) [packages/next/src/server/route-kind.ts](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/route-kind.ts) [packages/next/src/server/route-definitions/](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/route-definitions/)
- Hosting platforms extend the build via `NextAdapter`: set `config.adapterPath` (or `NEXT_ADAPTER_PATH`) and export the adapter interface declared in `build/adapter/build-complete.ts`, which receives `AdapterOutput`/`AdapterOutputs` describing every emitted route including edge entries and their `handlerExport`. — [packages/next/src/build/adapter/build-complete.ts#L124-L499](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/build/adapter/build-complete.ts#L124-L499) [packages/next/src/server/config-shared.ts#L1848](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/config-shared.ts#L1848)
- Cache backends plug in two ways: the legacy `CacheHandler` class contract (`get`/`set`/`revalidateTag`) consumed by `IncrementalCache`, and the newer `"use cache"` handler registry populated by `setCacheHandler(kind, handler)` / `initializeCacheHandlers(cacheMaxMemorySize)` with a documented stream-ownership contract. — [packages/next/src/server/lib/incremental-cache/index.ts#L103-L125](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/lib/incremental-cache/index.ts#L103-L125) [packages/next/src/server/use-cache/handlers.ts#L58-L95](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/use-cache/handlers.ts#L58-L95) [packages/next/src/server/lib/cache-handlers/types.ts#L6-L60](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/lib/cache-handlers/types.ts#L6-L60)
- A tiered cache composition point already exists (`createTieredCacheHandler` returning a `CacheReadWriteHandler`), with dev-only maps at `Symbol.for('@next/cache-handlers-dev-tiered')` and `-dev-fronts` for layering an in-memory front over a remote handler. — [packages/next/src/server/use-cache/handlers.ts#L1-L52](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/use-cache/handlers.ts#L1-L52) [packages/next/src/server/use-cache/tiered-cache-handler.ts](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/use-cache/tiered-cache-handler.ts)
- ESLint rules register in exactly one place: add the rule module under `packages/eslint-plugin-next/src/rules/`, import it in `src/index.ts`, and add entries to `plugin.rules` plus `recommendedRules` (and `coreWebVitalsRules` if it should escalate). — [packages/eslint-plugin-next/src/index.ts#L1-L80](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/eslint-plugin-next/src/index.ts#L1-L80)
- The `@gate` condition registry in `test/lib/gate/conditions.ts` is a deliberate hand-written extension point — declare a `staticCondition` (available to `@force-gate`) or a `lazyCondition` reading the fixture's resolved config; referencing an undeclared name is a hard collection-time error. — [test/lib/gate/conditions.ts#L1-L70](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/test/lib/gate/conditions.ts#L1-L70)
- New test suites are scaffolded, not hand-written: `pnpm new-test --args <appDir> <name> <type>` runs a `turbo gen test` plop generator (types `e2e | production | development | unit`), and `pnpm new-error` generates an `errors/*.mdx` page the same way. — [package.json#L4-L6](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/package.json#L4-L6) [AGENTS.md](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/AGENTS.md)
- Codemods extend via `packages/next-codemod/transforms/` with colocated `__testfixtures__` and `__tests__` directories, and are surfaced to users through `next upgrade`. — [packages/next-codemod/transforms/](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next-codemod/transforms/) [packages/next/src/cli/next-upgrade.ts](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/cli/next-upgrade.ts)
- Turbopack's Rust side extends through `#[turbo_tasks::function]` methods on `Project` / `ProjectContainer` in `crates/next-api/src/project.rs`, which are then exposed to JS by adding a napi binding and a corresponding method on `ProjectImpl` in `packages/next/src/build/swc/index.ts`. — [crates/next-api/src/project.rs#L919-L1045](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/crates/next-api/src/project.rs#L919-L1045) [packages/next/src/build/swc/index.ts#L683-L835](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/build/swc/index.ts#L683-L835) [crates/next-napi-bindings/src/lib.rs](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/crates/next-napi-bindings/src/lib.rs)
- Bundled third-party deps extend through `packages/next/taskfile.js`: register `externals['pkg'] = 'next/dist/compiled/pkg'` and add an `ncc_pkg` task, which is what keeps `next` dependency-light at install time. — [packages/next/taskfile.js#L100-L200](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/taskfile.js#L100-L200) [packages/next/package.json#L100-L110](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/package.json#L100-L110)
- Agent workflows are themselves an extension point: `.agents/skills/` holds 20 skill directories (`flags`, `dce-edge`, `react-vendoring`, `router-act`, `gate-tests`, `pr-status-triage`, `create-pr`, `backport-pr`, …) and `authoring-skills` documents the frontmatter spec and the AGENTS.md-vs-skill split. — [.agents/skills/README.md](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/.agents/skills/README.md) [.agents/skills/authoring-skills/SKILL.md](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/.agents/skills/authoring-skills/SKILL.md) [AGENTS.md](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/AGENTS.md)
- `create-next-app` extends through `packages/create-next-app/templates/`, driven by `create-app.ts`, so adding a starter variant is a template-directory change rather than CLI surgery. — [packages/create-next-app/create-app.ts](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/create-next-app/create-app.ts) [packages/create-next-app/templates/](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/create-next-app/templates/) [packages/create-next-app/index.ts](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/create-next-app/index.ts)

## Contributor handoff

Read `AGENTS.md` first — `CLAUDE.md` is a symlink to it, and it encodes the non-obvious rules an agent will otherwise get wrong (watch-mode-before-editing, mode-specific test scripts, the PR-description policy for fork contributions, the internal-header security review rule). Work in a throwaway git worktree rather than the user's checkout. Expect slow feedback loops: a full `pnpm build-all` is minutes, `pnpm --filter=next build` ~60s, `pnpm --filter=next types` ~10s, and a single e2e file can take minutes because it installs a real tarball. The dominant trap is the env-var matrix: the same test can pass under webpack and fail under Turbopack, or pass plainly and fail under `__NEXT_CACHE_COMPONENTS=true`.
- `CLAUDE.md` is a symlink to `AGENTS.md` (verified via `ls -la`), so editing either edits the same 524-line file — do not treat them as two documents to keep in sync. — [AGENTS.md](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/AGENTS.md) [CLAUDE.md](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/CLAUDE.md)
- Before editing Next.js source or integration tests, start `pnpm --filter=next dev` (the taskr watch build, ~1-2s per change) in a separate session; skipping it means tests run against a stale `packages/next/dist`, and the project convention is to state explicitly why if you skip it. — [AGENTS.md](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/AGENTS.md) [packages/next/package.json#L78-L80](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/package.json#L78-L80)
- Rebuild rules by change scope: edits under `packages/next/**` after bootstrap → `pnpm --filter=next build`; any Rust/Turbopack change or a branch switch → `pnpm build-all`; type errors only → `pnpm --filter=next types`. — [AGENTS.md](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/AGENTS.md) [package.json#L11-L12](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/package.json#L11-L12)
- Always run a test with the script matching the mode AND bundler under verification (`test-dev-turbo`, `test-dev-webpack`, `test-start-turbo`, `test-start-webpack`); never re-run the same suite with different `-t` filters — capture output once with `> /tmp/test-output.log 2>&1` and grep the file. — [AGENTS.md](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/AGENTS.md) [package.json#L24-L40](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/package.json#L24-L40) [scripts/run-jest.sh](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/scripts/run-jest.sh)
- Files to read first for any server-side change: `packages/next/src/server/lib/router-server.ts` (request entry), `packages/next/src/server/base-server.ts` (render core contract), `packages/next/src/server/route-modules/route-module.ts` (route kind contract), and `packages/next/src/build/define-env.ts` (what actually reaches the bundle). — [packages/next/src/server/lib/router-server.ts](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/lib/router-server.ts) [packages/next/src/server/base-server.ts](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/base-server.ts) [packages/next/src/server/route-modules/route-module.ts](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/route-modules/route-module.ts) [packages/next/src/build/define-env.ts](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/build/define-env.ts)
- Prefer a throwaway worktree (`git worktree add /tmp/scratch-work <branch>`) and symlink the root `node_modules` into it; expect `TS2307: Cannot find module` for `fast-glob`, `dotenv`, `@playwright/test` as worktree artifacts — those are not regressions and must not be 'fixed'. — [AGENTS.md](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/AGENTS.md)
- Type-check with the repo's own commands only: `pnpm typescript` runs `tsc --noEmit` against the root `tsconfig.json`, which pulls in `packages/next/types/global.d.ts` augmentations (e.g. `NodeJS.ProcessEnv` requiring `NODE_ENV`); a hand-rolled single-file tsconfig reports clean while CI fails. — [AGENTS.md](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/AGENTS.md) [package.json#L85](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/package.json#L85) [tsconfig.json](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/tsconfig.json)
- Pre-validate before committing to avoid ~2min lint-staged failures by running exactly what the hook runs: `pnpm prettier --with-node-modules --ignore-path .prettierignore --write <files>` then `npx eslint --config eslint.config.mjs --fix <files>`. — [AGENTS.md](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/AGENTS.md) [package.json#L98-L104](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/package.json#L98-L104)
- Hard policy boundary: you may not write full PR descriptions for fork PRs targeting `vercel/next.js`, and you may not create issues/discussions/comments unless the user is verified (via `gh api /user/memberships/orgs`) as a `vercel`/`vercel-labs` org member — offer drafting help and links instead. — [AGENTS.md](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/AGENTS.md) [.github/pull_request_template.md](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/.github/pull_request_template.md) [.github/ISSUE_TEMPLATE/1.bug_report.yml](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/.github/ISSUE_TEMPLATE/1.bug_report.yml)
- Security review boundary: if a change reads a non-standard request header, check it against `INTERNAL_HEADERS` in `packages/next/src/server/lib/server-ipc/utils.ts` — this is called out in AGENTS.md as a mandatory PR review step, not a suggestion. — [AGENTS.md](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/AGENTS.md) [packages/next/src/server/lib/server-ipc/utils.ts#L38-L52](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/lib/server-ipc/utils.ts#L38-L52)
- Known trap — `app-page.ts` is a build template compiled by the *user's* bundler, so relative `require()` of internal modules will not resolve in a user project; new helpers must be exported from `entry-base.ts` and reached as `entryBase.*`, and all `react-server-dom-webpack/*` imports must stay in `entry-base.ts`. — [AGENTS.md](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/AGENTS.md) [.agents/skills/react-vendoring/SKILL.md](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/.agents/skills/react-vendoring/SKILL.md) [packages/next/src/server/route-modules/app-page/module.ts#L248](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/route-modules/app-page/module.ts#L248)
- Known trap — keep `require()` behind compile-time `if/else` branches (not early-return/throw) so webpack DCE can eliminate Node-only imports from edge builds, and force Node-gating flags to `false` for edge in `define-env.ts`. — [AGENTS.md](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/AGENTS.md) [.agents/skills/dce-edge/SKILL.md](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/.agents/skills/dce-edge/SKILL.md) [packages/next/src/build/define-env.ts](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/build/define-env.ts)
- Known trap — a stale `packages/next-swc/native/*.node` after a branch switch produces bogus Turbopack errors; delete it and re-run `pnpm install` to fall back to the npm-published binary. — [AGENTS.md](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/AGENTS.md) [scripts/install-native.mjs](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/scripts/install-native.mjs)
- For debugging, set `__NEXT_SHOW_IGNORE_LISTED=true` to disable the ignore-list frame collapsing implemented in `packages/next/src/server/patch-error-inspect.ts`, and use `DEBUG=next:*` plus `NEXT_TELEMETRY_DISABLED=1` for local runs. — [AGENTS.md](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/AGENTS.md) [packages/next/src/server/patch-error-inspect.ts](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/server/patch-error-inspect.ts) [packages/next/src/telemetry/storage.ts#L64](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/packages/next/src/telemetry/storage.ts#L64)
- For CI triage use `$pr-status-triage` and `scripts/pr-status.js`, and mirror the failing job's env vars exactly (the workflow's `afterBuild:` block in `.github/workflows/build_and_test.yml` is the source of truth for things like `IS_WEBPACK_TEST=1`) before attempting local reproduction. — [.agents/skills/pr-status-triage/SKILL.md](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/.agents/skills/pr-status-triage/SKILL.md) [scripts/pr-status.js](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/scripts/pr-status.js) [.github/workflows/build_and_test.yml#L15-L18](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/.github/workflows/build_and_test.yml#L15-L18)
- Commit convention: no 'Generated with Claude Code' or co-author footers, and never run `gh pr ready` — PRs stay in draft until the user decides. — [AGENTS.md](https://github.com/vercel/next.js/blob/2861a364fcbe75fe96b0b9127f9b335edfcf288c/AGENTS.md)

Source repository: https://github.com/vercel/next.js
Full evidence corpus: https://github.com/Codevetter/codevetter/tree/main/benchmarks/repo-unpacks/nextjs.json

## Public product links

- [CodeVetter](https://codevetter.com/)
- [Download](https://codevetter.com/download)
- [Documentation](https://codevetter.com/docs/)
- [Source](https://github.com/Codevetter/codevetter)
