mirror of
https://github.com/vercel/next.js.git
synced 2026-09-20 02:25:18 +08:00
codex/catchall-fallback-values
35737 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
549476b05b |
turbo-tasks-backend: fix strongly consistent read hanging on a canceled task (#98312)
### What Fixes a shutdown hang in `next build`: a strongly consistent read could wait forever on a task that shutdown had canceled, which in turn blocked `stop_and_wait` and hung the build until the test timeout. Also makes the `hanging_detection` feature usable — it panics due to a `data` access check today. ### Why the read hangs 1. Shutdown sets `stopped`. A task dispatched after that point is canceled before it runs, so it has no output. 2. Its parent completes and connects its children. `process_new_children` uses `!child.has_output()` as the test for "not computed yet" and marks such a child `InitialDirty` so it gets scheduled. 3. `make_task_dirty` promotes the canceled task's `SessionDependent` to plain `Dirty` and clears `current_session_clean`. That is correct for a live task that must re-run, and wrong for one that never will. 4. `AggregatedDataUpdate::from_task` then snapshots `dirty_state() == (true, false)` and contributes a dirty container that is not session-clean. 5. The parent is itself clean, so its own `update_dirty_state(None -> None)` early-returns and never evaluates `all_clean_event`. Its dirty-container count never drains. 6. The strongly consistent reader waits on that event forever, holding a foreground job that `stop_and_wait` blocks on. `has_output()` is a proxy for "needs computing", and a canceled task breaks that assumption. ### The fix Record a terminal error output when a task execution is canceled. Make hanging_detection more reliable |
||
|
|
4fed8eaf19 | v16.4.0-canary.19 v16.4.0-canary.19 | ||
|
|
a8d189769f |
fix: route info segment overrides not updating in dev overlay (#96968)
## Summary Previously, when using the built-in route segment override in the Next.js dev tools (Route Info panel), the UI was not updating: after toggling a boundary (e.g. `loading.js` or `not-found.js`) you couldn't switch back unless you closed the modal and reopened it, which slowed down my workflow. It's not a huge issue, but I thought I'd fix it. The cause was the segment explorer trie mutating nodes in place, so the panel's memoized view never picked up insertions until remount. Trie updates now copy the nodes along the mutated path (copy-on-write) so changed nodes get fresh identities while untouched subtrees stay shared. ### Before Switching from one override to another works, but the UI doesn't update — you have to close and reopen the panel. https://github.com/user-attachments/assets/39e43ad7-2f14-4b54-bdab-eba51c87a060 ### After Setting an override updates the DevTools UI immediately — no need to close and reopen the panel. https://github.com/user-attachments/assets/113887cf-fe07-4d9b-90a5-405b53692a07 ## Verification - `pnpm jest packages/next/src/next-devtools/dev-overlay/segment-explorer-trie.test.tsx` - `pnpm test-dev-turbo test/development/app-dir/segment-explorer/segment-explorer.test.ts` - Manually verified in a dev app: toggling boundary overrides and "Clear Segment Overrides" now update the panel live. <!-- NEXT_JS_LLM --> |
||
|
|
6685283fe8 | v16.4.0-canary.18 v16.4.0-canary.18 | ||
|
|
0ac302d3d8 |
Turbopack: fix source field in CSS sourcemaps (#88225)
Closes #88294 Currently, CSS source maps in Turbopack always use index `0` for all source file references. This can lead to misleading mappings and makes debugging CSS generated from multiple sources more difficult. |
||
|
|
00b29d6654 |
fix: track dynamic accesses in final runtime prerenders (#98255)
The fix implemented in #95665 (`params/searchParams` not hanging correctly in Runtime App Shells during prospective prerenders) was incomplete. We started using `makeHanging[Search]Params` in the prospective prerender (if it was an app shell and wouldn't resolve them), but not during the final prerender. This meant that, when encoding the cache key, we would only fire `dynamicAccessAbortSignal` in the prospective prerender: https://github.com/vercel/next.js/blob/090f1b72753b2d0094235d27d46ffa0da5fc7d35/packages/next/src/server/use-cache/use-cache-wrapper.ts#L2160-L2174 ...but the final prerender wasn't using `makeHanging[Search]Params`, so it would not hit this early exit. The hanging input abort signal would still correctly abort the key encoding (because it accounts for `stageController.finalStage`) so the key would still be partial, as expected. We would continue on, and attempt to look the key up in the Resume Data Cache. But the prospective prerender exited early and never put anything under that key, so we would incorrectly end up here, and log: https://github.com/vercel/next.js/blob/090f1b72753b2d0094235d27d46ffa0da5fc7d35/packages/next/src/server/use-cache/use-cache-wrapper.ts#L2730-L2734 This is fixed by consistently using `makeHanging[Search]Params` if the runtime prerender won't reach the stage where they resolve, i.e. if it's an App Shell. This means we'll trigger `dynamicAccessAbortSignal` while encoding the cache key in *both* renders, so we'll do the early exit consistently and avoid the log. (This feels a bit unsatisfying, because ideally this would all just work based on stages, which is what I idealistically thought when writing the code originally. But unfortunately our usage of `dynamicAccessAbortSignal` for `params/searchParams` requires these special cases to work consistently. Maybe this can be improved in the future.) ### Tests This scenario is covered by the existing `prefetch-runtime` tests -- i discovered the bug by running them and seeing the `Unexpected cache miss after cache warming phase` warning. We now assert that the warning is not logged. The assertion is negative and would also pass if the error message changed, so i've exported the message as a constant to ensure `use-cache-wrapper` and the tests stay in sync. |
||
|
|
602a2aba90 |
docs: add API reference pages for skipProxyUrlNormalize and skipTrailingSlashRedirect (#97967)
- Generated with the `/write-api-reference` skill - These two options were only listed in https://nextjs.org/docs/app/api-reference/file-conventions/proxy#advanced-proxy-flags Closes: https://github.com/vercel/next.js/issues/56090 |
||
|
|
1b5400c926 | v16.4.0-canary.17 v16.4.0-canary.17 | ||
|
|
cc27d89b5d |
Turn on the adapter route collapses by default (#98218)
`experimental.collapseAdapterRoutes` now defaults to `true`, so a build serves several dynamic routes from one entry in the route table that it passes to an adapter. The default started off in #97774 so that the behavior could reach projects one at a time. We have tested this successfully internally, so the default moves back. The option stays in place, so a build that hits a regression can set it to `false` and get the table that a build emitted before the collapses. The four fixtures under `test/production/app-dir/adapter-dynamic-routes` no longer set the option, because the default now gives them the collapsed table that their snapshots record. The snapshots themselves do not change, since the new default produces what the explicit option produced. A full deploy test run with the flag on by default was already done as part of #97738. |
||
|
|
090f1b7275 |
fix: suppress missing package manager errors in next info (#97946)
## What - capture stdout and stderr for both direct and shell-fallback package-manager version probes - keep successful version output unchanged and return `N/A` when a probe fails - add deterministic cross-platform regression coverage with a failing Yarn shim Closes #97932 ## Why PR #71134 added an `execSync` fallback so package-manager command shims can be resolved on Windows. When `stdio` is omitted, Node mirrors stderr from failed synchronous child processes to the parent, so an unavailable Yarn or pnpm can print raw shell errors before `next info` reports `N/A`. Passing `{ stdio: "pipe" }` to both probes preserves captured stdout and the Windows fallback while keeping expected probe failures silent. The change stays scoped to package-manager version detection; shared registry discovery is unchanged. ## Testing - `pnpm --filter next typescript` - `pnpm --filter next build` - `pnpm test-types --pretty false` - ESLint and Prettier on both changed files - `pnpm test-start test/e2e/cli/cli.test.ts` (66 passed, 1 skipped) - built-CLI reproduction with npm available and Yarn/pnpm absent: both report `N/A` with no Yarn/pnpm command-not-found output - autoreview with GPT-5.6 Sol xhigh and Claude Opus 5 xhigh: zero findings --------- Co-authored-by: vercel[bot] <35613825+vercel[bot]@users.noreply.github.com> |
||
|
|
6b56068f38 | v16.4.0-canary.16 v16.4.0-canary.16 | ||
|
|
e82978ca63 |
test: remove stale deploy manifest exclusions (#98051)
## Summary Remove two inert entries from the deploy-test manifest: - the entry for `test/e2e/app-dir/options-request/options-request.test.ts`, which no longer exists - the `test/production/standalone-mode/**/*` glob, which cannot match the manifest's e2e-only include rule This leaves the hard exclusion list limited to live, selectable test suites so follow-up deploy-testing work starts from an accurate inventory. ## Verification - Parsed `test/deploy-tests-manifest.json` and confirmed all 26 remaining exclusions resolve to files selected by the manifest's include rule. <!-- NEXT_JS_LLM --> |
||
|
|
4fffc2c653 |
Enable hourly next-maintainer auto-close workflow (#98225)
## Summary - enable the existing auto-close workflow once per hour - keep manual dispatch available - leave the workflow logic and permissions unchanged ## Verification - production `workflow_dispatch` succeeded end to end: https://github.com/vercel/next.js/actions/runs/33788887152 - production returned a clean empty-queue response after OIDC authentication and Vercel Trusted Sources validation - Prettier and `git diff --check` pass |
||
|
|
65c832c8b8 |
Return client errors for unrecognized Server Actions (#98123)
## Summary - return `400 Bad Request` when a Server Action reference ID does not match the expected format - return `409 Conflict` when a well-formed action ID is unavailable in the current deployment, including deployment skew - apply the same classification to IDs from the `Next-Action` header and multipart MPA forms, including direct and bound actions in Node and Edge runtimes - return generic text bodies matching the client error category, without exposing action-ID details - derive marked forwarded-action responses from the original action ID so an unexpected or mixed-version worker response cannot change the client error semantics Fetch actions previously had dedicated 404 handling, while equivalent MPA form submissions fell through to the generic error path and returned 500. This removes that transport-dependent behavior and distinguishes malformed or scanning traffic from legitimate stale clients without treating either case as a missing page or an internal server failure. ## Verification - `pnpm test-dev-turbo test/e2e/app-dir/actions-unrecognized/actions-unrecognized.test.ts` - `pnpm test-dev-turbo test/e2e/app-dir/actions/app-action.test.ts -t 'forward.*action'` - `pnpm test-start-turbo test/e2e/app-dir/actions-unrecognized/actions-unrecognized.test.ts` - `pnpm test-dev-webpack test/e2e/app-dir/actions-unrecognized/actions-unrecognized.test.ts` - `pnpm test-dev-turbo test/e2e/app-dir/no-server-actions/no-server-actions.test.ts` <!-- NEXT_JS_LLM --> |
||
|
|
0eb4d7d585 |
Add Next Maintainer auto-close workflow (#98197)
## Summary - add a manually dispatchable workflow; the hourly schedule is left commented until initial production verification - keep the workflow YAML small and place delivery in `.github/scripts/next-maintainer-auto-close.js` for normal code review - use a short-lived exact-audience GitHub Actions OIDC token with no long-lived secret - trust the authenticated queue contract instead of reimplementing its Zod validation in the workflow - post the verifier-authored comment and close with GitHub native completed, not planned, or duplicate state reasons - retain one invisible marker only to prevent duplicate public comments across retries If an issue is open with the marker, the workflow leaves it open. This intentionally lets a human reopen win and avoids timeline reconstruction. ## Permissions The job grants only `contents: read`, `id-token: write`, and `issues: write`; every other permission remains none. Both GitHub actions are pinned to full commit SHAs. Checkout is sparse to the one trusted JavaScript file and has credential persistence disabled. ## Verification - mocked delivery harness passes ten scenarios: empty queue, completed, not planned, duplicate, pull-request rejection, already-closed recovery, independent close, open marker, transferred issue, and transient failure - `node --check`, Prettier, and ESLint pass for the extracted implementation - Vercel Agent Review, Vercel Security Review, Socket Security, workflow change detection, and documentation validation pass - after merge, dispatch the registered workflow on `canary` and add the production run link here before enabling the hourly schedule ## Dependency This is the narrow GitHub write-side companion to vercel-labs/next-maintainer-agent#541, which is deployed. vercel-labs/next-maintainer-agent#546 further reduces the queue DTO and changes the delivery limit to 25 first claims per rolling week; this workflow is compatible with both DTO versions. |
||
|
|
8b9cdc5eef |
perf(turbopack): skip ignored files during server tracing (#97475)
So I was playing around with profiling some very simple next apps when I noticed this. Basically, because of how things were set up we were spending a good chunk of our build time analyzing files we were going to reject. I went around and around trying different combinations to make sure we ignore those files (and only the files we should ignore) to speed up build times. Since it is a constant impact, mostly on small projects, but on some, it can make a big difference. I had some adversarial rounds with agents to see if they can break the config in the ways the old comments hints at why we didn't do that. And this final round finally couldn't get it to break. For a v0-generated Hello World app, compile time is 28.0% faster (+/-1.7%) and a full clean build is 15.9% faster (+/- 1.2%). --------- Co-authored-by: Niklas Mischkulnig <4586894+mischnic@users.noreply.github.com> |
||
|
|
4e2abec31d |
docs: note that experimental.taint taints process.env (#98145)
Enabling `experimental.taint` makes Next.js taint `process.env` for you
(`app-render.tsx`), so passing it whole to a Client Component throws.
The docs never mentioned this.
Adds it to the existing "Good to know" callout, plus a caveat that the
taint is by object reference, so `process.env.MY_VAR` and `{
...process.env }` still pass through.
|
||
|
|
87a1c18eee |
test: preserve static params during Cache Components migration (#98209)
## Summary Adds an agent eval covering migration of the legacy on-demand ISR pattern: `force-static`, `revalidate`, and an empty `generateStaticParams` result. The assertions require the agent to enable Cache Components, preserve the `generateStaticParams` export, and replace its empty result with at least one parameter instead of deleting the function. The fixture also runs the Cache Components adoption skill with the established 30-minute timeout. Updates the Cache Components migration guide to link each legacy route segment config to its migration instructions. The `generateStaticParams` section now explains that removing the export opts a dynamic route out of ISR and links to the ISR with Cache Components guide. ## Eval results ### Before the guide change | Treatment | Result | Duration | Behavior | | --- | --- | ---: | --- | | Baseline | Passed (1/1) | 482.9s | Preserved `generateStaticParams` and returned a real seed. | | Bundled docs / AGENTS.md | Failed (0/1) | 444.7s | Deleted `generateStaticParams`, incorrectly treating omission as equivalent to returning an empty array. | | Cache Components skill | Passed (1/1) | 527.6s | Preserved `generateStaticParams` and returned a real seed. | The failed treatment was classified as a model failure. It read the bundled migration guide but concluded that deleting `generateStaticParams` preserved the defer-all-paths-to-runtime behavior. The eval catches that loss of on-demand ISR semantics. The full run used the identical fixture immediately before its numeric identifier was changed from `agent-044` to the final `agent-054` name. The fixture and assertions were unchanged. ### After the guide change | Treatment | Result | Duration | Behavior | | --- | --- | ---: | --- | | Baseline | Passed (1/1) | 739.5s | Preserved `generateStaticParams`, returned `FEATURED_EVENT_SLUG`, and compared the migrated route with the original behavior. | | Bundled docs / AGENTS.md | Passed (1/1) | 393.2s | Read the revised migration and ISR guides, preserved `generateStaticParams`, and returned `FEATURED_EVENT_SLUG`. | | Cache Components skill | Passed (1/1) | 547.3s | Preserved `generateStaticParams`, returned `FEATURED_EVENT_SLUG`, and verified the build and representative routes in development. | In the first controlled pair, the bundled-docs treatment changed from failing to passing. Its final migration kept the route eligible for ISR, preserved the 60-second revalidation behavior with `use cache` and `cacheLife`, and produced a passing build. ### Additional variance check The bundled-docs treatment was run once more against each guide version: | Guide version | First sample | Second sample | Observed result | | --- | --- | --- | --- | | Before this change | Failed (444.7s) | Passed (455.6s) | 1/2 passed | | After this change | Passed (393.2s) | Passed (425.2s) | 2/2 passed | The second pre-change sample shows that the previous wording could still lead the agent to the correct migration. The revised wording passed both observed samples, suggesting that the explicit ISR consequence improves reliability. Two samples per version are not enough to claim a stable pass rate. ## Verification - `pnpm --filter=next build` - `pnpm eval agent-054-cache-components-empty-static-params --dry` - `pnpm eval agent-054-cache-components-empty-static-params` <!-- NEXT_JS_LLM --> |
||
|
|
fb4c498852 |
Upgrade web-vitals to v6 and report soft navigations (#97232)
## Summary Upgrades the vendored `web-vitals` dependency to v6.0.1 and enables soft-navigation reporting for CLS, INP, and LCP. This lets `useReportWebVitals` report metrics against the navigation where they occurred, including `navigationURL` for route attribution. The upgrade also removes the obsolete FID integration and compatibility patch, regenerates the standard and attribution bundles, and updates the public metric types and documentation to match the current upstream API. Related to #97161. ## Verification - Added production-mode coverage for initial and soft-navigation Web Vitals reporting with both Turbopack and Webpack. <!-- NEXT_JS_LLM --> |
||
|
|
83eabc8f83 |
Upgrade React from 21c89c9f-20260901 to f4e439e1-20260902 (#98188)
|
||
|
|
69ec884e44 | v16.4.0-canary.15 v16.4.0-canary.15 | ||
|
|
5a0f57d9b9 |
Reduce Turbopack cache size with per-family compression (#97714)
## What? Adds per-family compression configuration to `turbo-persistence` and configures the Turbopack filesystem cache according to each keyspace's access pattern: | Family | Compression | Reason | | --- | --- | --- | | Infra | LZ4 HC level 4 | Preserve LZ4 decode performance while improving write-time compression | | TaskMeta | LZ4 HC level 4 | Preserve latency-sensitive metadata reads | | TaskCache | LZ4 HC level 4 | Preserve latency-sensitive task-cache reads | | TaskData | zstd level 3 | Prioritize the dominant disk-size opportunity | The low-level configuration exposes only the three presets this database needs: LZ4, LZ4 HC4, and zstd3. Compression is applied consistently to SST blocks, blob values, and compaction output. Each database owns a `ThreadLocal` that lazily creates one lock-free zstd decompressor per participating thread; the linked zstd reports each context as about 96 KiB, and all contexts are released when the database drops. Each meta file stores its family's length-prefixed, bincode-encoded compression preset once. SST and blob headers remain unchanged, including the 12.5% minimum-savings fallback to uncompressed blocks. On normal database open, the stored marker must match the runtime `FamilyConfig`; a mismatch, invalid/truncated preset, or pre-marker meta file is rejected before reads are served. This intentionally requires a fresh/versioned cache after a codec change, consistent with `turbo-persistence`'s existing no-cross-version-compatibility policy. The marker also removes the duplicated keyspace-to-codec mapping from `sst_inspect`: it reads the codec directly from metadata. `turbo-persistence-tools` can inspect metadata without supplying a Turbopack-specific family configuration. ## Why? LZ4 decode performance is important for cache query latency, but using one codec for every keyspace leaves a substantial disk-size opportunity in TaskData. The families do not have the same tradeoff: metadata and task-cache reads should stay on the LZ4 decoder, while TaskData can spend modestly more CPU to reduce persistent cache size. The final three-workload A/B reduced fresh cache directories by **20.61% overall**, with no measured cold/write or warm/read regression (the observed -3.42% / -2.39% should be treated as noise, not a speed claim). ## How? ### Real Next.js cache A/B The complete A/B was re-run after the final fixed-preset, bincode-marker, and database-owned thread-local revision. Both the unchanged base worktree and final proposed working tree were bootstrapped with `pnpm install --frozen-lockfile` and `pnpm build-all`, using locally compiled native bindings on the same 8-vCPU Intel Xeon VM (16.3 GiB RAM, Linux 6.18.40, Node 24.14.1). Each workload used three fresh-cache builds and five warm-cache rebuilds; the table reports medians. ```sh TURBO_ENGINE_IGNORE_DIRTY=1 NEXT_TELEMETRY_DISABLED=1 \ node packages/next/dist/bin/next build <fixture> --turbopack ``` `test/e2e/filesystem-cache` additionally used `ENABLE_CACHING=1`. The other fixtures temporarily enabled `experimental.turbopackFileSystemCacheForBuild`; those fixture changes are not in this PR. | Workload | Cache size | Cold/write | Warm/read | | --- | ---: | ---:| ---: | | `test/e2e/filesystem-cache` | **-20.62%** | -2.24% | -3.00% | | `test/e2e/app-dir/app-rendering` | **-20.11%** | -6.04% | -1.47% | | `test/e2e/app-dir/client-reference-chunking` | **-21.02%** | -1.45% | -3.56% | | Combined medians | **-20.61%** (96.810 → 76.862 MiB) | **-3.42%** | **-2.39%** | Per-family inspection of a final filesystem-cache build, using the marker-derived codec: | Family | Total file size | Value-block savings | | --- | ---: | ---: | | Infra | 52 B | n/a | | TaskMeta | 6.31 MB | 29% | | TaskData | 18.17 MB | 66% | | TaskCache | 1.65 MB | n/a | The originally planned `app` and `app-static` fixtures could not run as standalone builds: `app` hit the existing Edge-runtime `process.cwd()` build failure, and `app-static` required an external data endpoint during page-data collection. They were replaced with the two successful repository fixtures shown above under the same base/proposed methodology. ### Temporary crate benchmark instrumentation The codec-specific Criterion cases were used locally to gather receipts, then removed from the delivered patch per review. | Codec | Synthetic write | vs LZ4 | Uncached get | Cached get | DB size | | --- | ---: | ---: | ---: | ---: | ---: | | LZ4 | 38.173 ms | baseline | 7.3004 µs | 10.735 µs | 21.59 MiB | | LZ4 HC4 | 256.31 ms | **+571.7%** | 3.4853 µs | 8.5467 µs | 21.57 MiB | | zstd3 | 57.790 ms | **+51.4%** | 6.2460 µs | 9.5323 µs | 21.49 MiB | The synthetic workload applies one codec to the entire database and barely separates codecs by size, so it is not representative of the real TaskData distribution. The all-HC write regression is real but confined to smaller families in the actual configuration. Cached reads do not decompress blocks, and the read samples contain substantial ordering/outlier noise. The real-cache A/B is the read-regression guard. ## Vercel Site ### Size 2.9G canary 2.2G this PR a 25% savings ### Performance Cold build (n=5), warm build (n=3), medians: | Scenario | Metric | Canary | Compression | Δ | |---|---|---|---|---| | **Cold** | wall (s) | 71.28 | 72.86 | +2.2% | | | user (s) | 555.21 | 570.23 | +2.7% | | | sys (s) | 94.49 | 95.00 | +0.5% | | | maxRSS (GB) | 17.95 | 17.98 | +0.2% | | **Warm** | wall (s) | 15.00 | 15.32 | +2.1% | | | user (s) | 7.59 | 9.98 | +31% | | | sys (s) | 12.79 | 12.87 | +0.6% | | | maxRSS (GB) | 3.78 | 3.86 | +2.0% | maxRSS converted at 1 GB = 2^30 bytes. Looking at tracing data i see in a cold build `persist` span went from 5.06s duration using 63s cpu time to 6.04s using 73s cpu time, a ~20% regression. Which explains the cpu regression and the time progression. Because the time regression is in the `persist` span during shutdown the added latency is somewhat hidden. Of course we also see a small wall/user time regression in warm builds due to the extra decompression costs for zstd. ### Verification - `cargo test -p turbo-persistence` (79 tests) - `cargo test -p turbo-tasks-backend` (99 unit tests plus integration suites) - `cargo test -p turbo-persistence-tools` - `cargo test -p turbo-persistence --all-targets --no-run` - `cargo fmt --all -- --check` - `cargo clippy -p turbo-persistence -p turbo-persistence-tools -p turbo-tasks-backend --all-targets -- -D warnings` - `cargo check -p turbo-persistence -p turbo-persistence-tools -p turbo-tasks-backend` - `pnpm lint-ast-grep` - `pnpm build-all` <!-- NEXT_JS_LLM --> <!-- fleet 621238c3-5a53-431b-8661-8c5b2920d55d --> --------- Co-authored-by: vercel-fleet-prod[bot] <318278635+vercel-fleet-prod[bot]@users.noreply.github.com> Co-authored-by: Luke Sandberg <210140+lukesandberg@users.noreply.github.com> |
||
|
|
5b8c866c32 |
[test] Skip hooks of suites skipped by a lazy @force-gate (#98169)
A `describe` gated by a lazy `// @force-gate` (a condition read off the fixture's resolved config, like `cacheComponents`) cannot be skipped at collection time, so the gate runtime force-passes its tests at runtime and `nextTestSetup` skips the fixture build. Hooks registered inside such a `describe` still ran, because only the `it`/`test` globals were wrapped. A hook that touches the `next` instance (for example a `beforeEach` that reads `next.cliOutput`) then failed with `next instance is not initialized yet` and failed the supposedly skipped suite. The gate runtime now wraps the `beforeAll`/`afterAll`/`beforeEach`/`afterEach` globals as well and skips a hook registered under a lazy `@force-gate` when that gate evaluates false against the fixture's resolved config. Only a false lazy force-gate skips a hook: an inverted `@gate` still runs its tests and therefore needs its hooks, and a static `@force-gate` already skips the whole `describe` at collection time. The hooks `nextTestSetup` registers itself are marked with the new `ungatedHook` helper so that the skip decision and the fixture cleanup still run. The `cacheComponents disabled, edge app router` describe in `test/e2e/cache-handlers-upstream-wiring` is converted from the `process.env.__NEXT_CACHE_COMPONENTS` `describe.skip` ternary to `// @force-gate !cacheComponents`, replacing a fake-green skip and exercising the fixed path. The `!deploy` conversion of the remaining describes is left to the branch that introduces the `deploy` condition. Co-authored-by: Claude Code (kimi-k3[1m]) <noreply@anthropic.com> |
||
|
|
5cca033c41 |
docs: local development: Rewrite docker section, add Windows Dev Drive and WSL2 instructions (#98164)
Follow-up to https://vercel.slack.com/archives/C046HAU4H7F/p1787344658655399 Summary: - Move system configuration changes into it's own section, separate from application and project configuration changes. - The Docker section was very light on details and didn't do a good job of explaining why this problem happens. - Also suggest https://docs.docker.com/desktop/features/synchronized-file-sharing/ for Docker and explicitly discourage `watchOptions.pollIntervalMs` as a workaround. - Suggest Windows Dev Drive (https://learn.microsoft.com/en-us/windows/dev-drive/). I don't have experience with it, but Microsoft recommends it and claims it improves filesystem performance. - Recommend trying WSL 2. |
||
|
|
538f3f68e9 |
Bump swc to v77 (#97407)
There have been breaking changes to the AST: - `BlockStmtOrExpr` is now `ArrowFunctionBody` - function bodies are `FunctionBody` instead of `BlockStmt` - JSX text literals are `Wtf8Atom`, instead of `Atom` |
||
|
|
ec847d8216 |
Upgrade React from ff7445e6-20260831 to 21c89c9f-20260901 (#98147)
|
||
|
|
8ea76d64ca |
turbo-tasks-backend: parent_count-driven garbage collection (#97282)
Adds garbage collection, built on the `parent_count` reference counting
### How collection works
* we scan all storage shards for collectible tasks to seed the sweep
* For each collectible task we
1. mark it as deleted
2. remove all outgoing edges
3. queue new tasks for deletion if the edge removals triggered it
For removing edges we rely on the existing `CleanupOldEdges` operation,
though it is enhanced to collect the tasks that become collectible as
edges are removed
Deletion markers are transformed into tombstones by persistence and then
dropped from memory by eviction.
### Coordinator changes
GC mutates the graph, so we exclude other tasks from running while it
works. When gc completes we hand off to persistence which switches to
the existing copy on write mode.
The subtle piece is `GcPhase::into_snapshot`: GC has to hand its
exclusion directly to the snapshot that tombstones what it collected.
Swapping the flags under one lock leaves no window where an operation
could start and resurrect a just-collected task.
### Enabling it
Off by default; `TURBO_ENGINE_GC` turns it on, also requires eviction to
be enabled (otherwise deleted tasks persist in RAM)
`prevent_gc()` becomes real, pinning the calling task through
`transient_ref_count`.
|
||
|
|
bcfa15d0ed |
Remove the worker thread for turbopack builds (#96862)
Avoid spawning a worker thread to run turbopack The typical arguments for worker threads are that they isolate the JS heap and enable parallelism, but turbopack does both these things by default (native heap is isolated, the js deps are minor and have a lot of overlap with existing next server deps). There is also no substantial parallelism opportunity from worker threads that aren't immediately delivered by turbopacks execution model ## Benchmarks Measured with a paired A/B harness that alternates `head` and `base` builds so thermal drift and background load cancel out, discarding a warmup build per variant and clearing `.next` before each run. Both variants are the same commit except for this change. 35 paired iterations on a default `create-next-app` (App Router, TS), Node 20.11.1, Apple silicon. | Metric | this PR | canary | Diff | 95% CI | Wins | | --- | --- | --- | --- | --- | --- | | `next-build` total | 2478 ms | 2595 ms | **-117 ms (4.5%)** | [50, 183] | 30/35 | | `run-turbopack` | 1228 ms | 1335 ms | -107 ms (8.0%) | [62, 152] | 31/35 | | overhead (total - turbopack) | 1250 ms | 1260 ms | -10 ms (0.8%) | [-27, 46] | 20/35 | | wall clock | 2775 ms | 2888 ms | -112 ms (3.9%) | [45, 180] | 30/35 | | peak RSS | 1175 MiB | 1195 MiB | **-19 MiB (1.6%)** | [3.4, 35.3] | 22/35 | Significant by both a paired t-test and a Wilcoxon signed-rank test (p = 0.0001 for total build time, p = 0.025 for RSS). The Wilcoxon is included because a couple of slow builds per run would otherwise dominate the variance. Nearly all of the saving lands inside the `run-turbopack` span rather than around it, which is expected: that span wraps the worker spawn, the IPC to marshal the build across, and the teardown, so deleting the worker removes work from inside it. The win is proportionally smaller on larger apps, where compilation dominates. On `bench/basic-app` (~400 source modules, ~19s builds) the same ~115 ms is well inside the noise -- 12 paired iterations there showed no significant difference on any metric. So this is a real but small improvement that is only user-visible on small builds: | Metric | this PR | canary | Diff | 95% CI | | --- | --- | --- | --- | --- | | `next-build` total | 19313 ms | 19436 ms | -123 ms (0.6%) | [-256, 502] | | overhead (total - turbopack) | 1424 ms | 1415 ms | +9 ms (0.6% slower) | [-52, 70] | Caveats: single machine and two fixture shapes. Peak RSS is the build process's own footprint (`/usr/bin/time -l`), which includes the worker thread's isolate but not the separately spawned static-generation workers. A CI runner with fewer cores would plausibly show a larger effect. |
||
|
|
483f84202d |
turbo-tasks-malloc: address review feedback on #97761 (#98153)
### What? Follow-up to #97761, which was auto-submitted before @bgw's review comments were addressed. Comment-only changes; no behavior change. - Name the nulled-out out-params in the `mi_process_info` call, applying the suggestion as written. - Trim the `memory_usage` doc comment to a brief description plus a link to the mimalloc docs. `mi_process_info` is absent from mimalloc's published doxygen docs, so the link points at the `libmimalloc-sys` rustdoc, which reproduces the header's table describing each figure. It is a plain URL rather than an intra-doc link because `libmimalloc-sys` is an optional dependency — an intra-doc link fails to resolve under `--no-default-features`. - Trim the test's doc comment to the single suggested line. The `#[global_allocator]` suggestion matched the existing code exactly, so nothing to change there. Left the `vec!` in the test alone, per "this is fine because every test in this crate should expect this global allocator". While in the same doc comment, dropped the `[`self::counter`]` link, which was raising a `private_intra_doc_links` warning. ### How? Verified with `cargo fmt --check`, `cargo clippy -p turbo-tasks-malloc --all-targets`, `cargo test -p turbo-tasks-malloc` (6 passed), and `cargo doc` under `RUSTDOCFLAGS="-D warnings"` with both default and `--no-default-features`. |
||
|
|
b1dfa8a888 | v16.4.0-canary.14 v16.4.0-canary.14 | ||
|
|
4425414ad2 |
turbo-tasks-malloc: report memory from mimalloc (#97761)
### What? `turbo-tasks-malloc` keeps a process-wide `ALLOCATED` atomic to answer `memory_usage()`. To optimize performance thread local buffers were maintained but this lead to code size issues. So instead we rely on mimalloc apis saving 1.6M of binary size ### Why not ask the OS? That was the obvious alternative, and it does not work well. The natural per-platform figures are not the same quantity: macOS `phys_footprint` and Windows `PrivateUsage` account for compressed and swapped pages, but Linux `VmRSS` does not — so under swap or zram the Linux number can read flat, or fall, while real consumption climbs. Closing that gap means `/proc/self/smaps_rollup` (`Pss + SwapPss`), which is not the cheap one-line read it looks like, it takes on the order of [100ms per 4GB](https://github.com/ncabatoff/process-exporter/issues/246), and it would contend with other allocations in our process. ### How? `memory_usage()` reads `current_commit` from `mi_process_info`. We fall back to the `ALLOCATED` atomic only when `custom_allocator` is off. ### Behaviour changes **It does not track frees in lock step.** mimalloc reuses and purges pages on its own schedule or when eviction calls `collect` ### Size Rust inlines `#[global_allocator]` methods into every allocation site, so the buffer arithmetic and the branch guarding it were duplicated across the whole binary. Dropping them from the default build is where the win comes from. `libnext_napi_bindings.dylib`, release profile as shipped (thin LTO, `codegen-units = 1`). | Section | canary | this PR | Δ | |---|---:|---:|---:| | `__text` | 64,833,760 | 63,319,776 | **−1,513,984** | | `__eh_frame` | 8,966,904 | 8,748,464 | −218,440 | | `__gcc_except_tab` | 2,234,428 | 2,229,484 | −4,944 | | `__unwind_info` | 1,489,072 | 1,484,644 | −4,428 | | **Total** | | | **−1,741,796 = −1.66 MiB** | ### Performance `cargo bench -p turbo-tasks-malloc --bench allocation`, macOS aarch64, revisions interleaved across 3 rounds so drift hits both arms equally. Figures are the paired per-round deltas. | benchmark | canary | this PR | mean Δ | per-round | |---|---:|---:|---:|---| | `alloc_dealloc` | 6.65 ns | 6.45 ns | **−2.9%** | −3.7% .. −2.4% | | `alloc_realloc_dealloc` | 18.62 ns | 16.76 ns | **−10.0%** | −10.2% .. −9.7% | Removing the counter takes the buffer arithmetic and its guarding branch out of every allocation, and a realloc pays that twice — which is roughly the shape of the result. That said, the absolute numbers are small (a couple of hundred picoseconds on `alloc_dealloc`) and this benchmark is noisy, so treat the direction as the signal rather than the magnitude. |
||
|
|
9c626ac94b |
Remove Edge runtime handling in use-cache-wrapper (#98139)
Cache components are only compatible with Node.js runtime anyway. So this was basically dead code. Removing it doesn't break any existing functionality. |
||
|
|
52cd5b96ab |
Skip discussions in the issue_lock workflow (#98146)
The last remaining `Lock Threads` failures are likely due to missing permissions for discussions which `dessant/lock-threads` handles by default. This disables discussion handling to get the workflow green to start tracking regressions. We can discuss if we want to lock inactive discussions in a follow-up (which would be new behavior this workflow never performed). Co-authored-by: Claude Code (kimi-k3[1m]) <noreply@anthropic.com> |
||
|
|
3866d6fd2b |
[ci] Fix issue_lock (#97755)
Fixes > "github-token" length must be less than or equal to 100 characters long -- https://github.com/vercel/next.js/actions/runs/33188474239 GitHub uses stateless tokens now which are way longer. See https://github.com/dessant/lock-threads/issues/55 --------- Co-authored-by: Claude Code (kimi-k3[1m]) <noreply@anthropic.com> |
||
|
|
1a9245c7d0 |
docs: document preloading with Cache Components (#97864)
## Summary - add the preload pattern to the current App Router data fetching guide - distinguish automatic fetch memoization, React cache for non-fetch data, and Cache Functions - clarify request-scoped private caching and when stale: Infinity should not constrain the route stale time The previous-model caching guide remains unchanged. ## Verification - Prettier on both edited docs - alex on both edited docs - git diff --check - Not run: full framework build because this is a documentation-only change <!-- NEXT_JS_LLM --> |
||
|
|
1076c47453 |
chore: Remove expired sharp release age exclusions (#98120)
- PR #97931 temporarily exempted sharp@0.35.4 and its @img/* binaries to ship same day. - Its now past the 48-hour minimum release age. - This PR removes those temporary exemptions. |
||
|
|
258b1c1bc0 | v16.4.0-canary.13 v16.4.0-canary.13 | ||
|
|
fb49d8b754 |
evals: stop requiring the removed viewTransition flag in agent-043 (#98116)
The view-transitions eval kept requiring a config option that #96098 removed, so models following the current docs were failed for correct solutions. Evals only, no runtime change. Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
e9180eae2a |
errors: shorten "use cache" messages and unify them into one factory (#94300)
### What? Centralizes related <code>"use cache"</code> scope errors and rewrites them as shorter, actionable messages with consistent <code>Learn more:</code> links. It also updates the reachable revalidation errors for rendering and <code>generateStaticParams</code>. ### Why? The previous messages were long, inconsistent, and sometimes missing documentation links. Some new cache-specific revalidation messages were also hidden by an earlier render-phase error. The new messages name the constraint, give the immediate fix, and link to the relevant documentation. ### How? Adds centralized error factories and dedicated error pages for request data, cache configuration, private-cache composition, and revalidation. When an active App Router route is available, the messages include it. The error pages use consistent terminology and complete examples. The <code>unstable_cache()</code> reference now documents the request-dependent operations that these errors reject. ### Before and after Route-based examples use <code>/products</code>. Bracketed values are alternatives in matching order. For example, <code>[E1482, E1486, E1489]</code> maps to <code>[headers(), cookies(), request.url]</code>. Each runtime error contains one alternative, but the table groups messages with the same template. #### Messages that include the route | Cases | Before | After | | --- | --- | --- | | E1480 · <code>searchParams</code> in <code>"use cache"</code> | Route /products used <code>searchParams</code> inside "use cache". Accessing dynamic request data inside a cache scope is not supported. If you need some search params inside a cached function await <code>searchParams</code> outside of the cached function and pass only the required search params as arguments to the cached function. See more info here: https://nextjs.org/docs/messages/next-request-in-use-cache | Route "/products": <code>searchParams</code> can't be read inside <code>"use cache"</code>. Await it outside the cached function and pass what you need as an argument.<br><br>Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache | | [E1482, E1486, E1489] · [<code>headers()</code>, <code>cookies()</code>, <code>request.url</code>] in <code>"use cache"</code> | Route /products used [<code>headers()</code>, <code>cookies()</code>, <code>request.url</code>] inside "use cache". Accessing Dynamic data sources inside a cache scope is not supported. If you need this data inside a cached function use [<code>headers()</code>, <code>cookies()</code>, <code>request.url</code>] outside of the cached function and pass the required dynamic data in as an argument. See more info here: https://nextjs.org/docs/messages/next-request-in-use-cache | Route "/products": [<code>headers()</code>, <code>cookies()</code>, <code>request.url</code>] can't be read inside <code>"use cache"</code>. Read it outside the cached function and pass what you need as an argument.<br><br>Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache | | [E1481, E1485, E1492] · [<code>cookies()</code>, <code>request.url</code>, <code>headers()</code>] in <code>unstable_cache()</code> | Route /products used [<code>cookies()</code>, <code>request.url</code>, <code>headers()</code>] inside a function cached with <code>unstable_cache()</code>. Accessing Dynamic data sources inside a cache scope is not supported. If you need this data inside a cached function use [<code>cookies()</code>, <code>request.url</code>, <code>headers()</code>] outside of the cached function and pass the required dynamic data in as an argument. See more info here: https://nextjs.org/docs/app/api-reference/functions/unstable_cache | Route "/products": [<code>cookies()</code>, <code>request.url</code>, <code>headers()</code>] can't be read inside <code>unstable_cache()</code>. Read it outside the cached function and pass what you need as an argument.<br><br>Learn more: https://nextjs.org/docs/app/api-reference/functions/unstable_cache | | [E1484, E1491] · <code>draftMode().enable()</code> in [<code>unstable_cache()</code>, <code>"use cache"</code>] | Route /products used "draftMode().enable()" inside [a function cached with <code>unstable_cache()</code>, "use cache"]. The enabled status of <code>draftMode()</code> can be read in caches but you must not enable or disable <code>draftMode()</code> inside a cache. See more info here: [https://nextjs.org/docs/app/api-reference/functions/unstable_cache, https://nextjs.org/docs/messages/next-request-in-use-cache] | Route "/products": <code>draftMode().enable()</code> can't be called inside [<code>unstable_cache()</code>, <code>"use cache"</code>]. Draft mode can be read inside a cached function, but enabling or disabling it must happen outside.<br><br>Learn more: [https://nextjs.org/docs/app/api-reference/functions/unstable_cache, https://nextjs.org/docs/messages/next-request-in-use-cache] | | [E1488, E1499] · <code>connection()</code> in [<code>unstable_cache()</code>, <code>"use cache"</code>] | Route /products used <code>connection()</code> inside [a function cached with <code>unstable_cache()</code>, "use cache"]. The <code>connection()</code> function is used to indicate the subsequent code must only run when there is an actual request, but caches must be able to be produced before a request, so this function is not allowed in this scope. See more info here: [https://nextjs.org/docs/app/api-reference/functions/unstable_cache, https://nextjs.org/docs/messages/next-request-in-use-cache] | Route "/products": <code>connection()</code> can't be called inside [<code>unstable_cache()</code>, <code>"use cache"</code>] because cached functions may run during prerendering, without an incoming request. Call it outside the cached function.<br><br>Learn more: [https://nextjs.org/docs/app/api-reference/functions/unstable_cache, https://nextjs.org/docs/messages/next-request-in-use-cache] | | E1494 · <code>connection()</code> in <code>"use cache: private"</code> | Route /products used <code>connection()</code> inside "use cache: private". The <code>connection()</code> function is used to indicate the subsequent code must only run when there is an actual navigation request, but caches must be able to be produced before a navigation request, so this function is not allowed in this scope. See more info here: https://nextjs.org/docs/messages/next-request-in-use-cache | Route "/products": <code>connection()</code> can't be called inside <code>"use cache: private"</code> because private cached functions may run during prefetching, without a navigation request. Call it outside the cached function.<br><br>Learn more: https://nextjs.org/docs/app/api-reference/directives/use-cache-private | | [E1483, E1495] · <code>revalidateTag("products")</code> in [<code>"use cache"</code>, <code>unstable_cache()</code>] | Route /products used "revalidateTag products" inside [a "use cache", a function cached with "unstable_cache(...)"] which is unsupported. To ensure revalidation is performed consistently it must always happen outside of renders and cached functions. See more info here: https://nextjs.org/docs/app/building-your-application/rendering/static-and-dynamic#dynamic-rendering | Route "/products": <code>revalidateTag("products")</code> can't be called during render, inside a cached function, or inside <code>generateStaticParams</code>. Call it from a Server Action or Route Handler instead.<br><br>Learn more: https://nextjs.org/docs/messages/revalidate-in-use-cache | | During render | Route /products used "revalidateTag products" during render which is unsupported. To ensure revalidation is performed consistently it must always happen outside of renders and cached functions. See more info here: https://nextjs.org/docs/app/building-your-application/rendering/static-and-dynamic#dynamic-rendering | Route "/products": <code>revalidateTag("products")</code> can't be called during render, inside a cached function, or inside <code>generateStaticParams</code>. Call it from a Server Action or Route Handler instead.<br><br>Learn more: https://nextjs.org/docs/messages/revalidate-in-use-cache | | Inside <code>generateStaticParams</code> | Route /products used "revalidateTag products" inside <code>generateStaticParams</code> which is unsupported. To ensure revalidation is performed consistently it must always happen outside of renders and cached functions. See more info here: https://nextjs.org/docs/app/building-your-application/rendering/static-and-dynamic#dynamic-rendering | Route "/products": <code>revalidateTag("products")</code> can't be called inside <code>generateStaticParams</code>. Call it from a Server Action or Route Handler instead.<br><br>Learn more: https://nextjs.org/docs/messages/revalidate-in-use-cache | #### Other messages | Cases | Before | After | | --- | --- | --- | | [E1477, E1490] · Nested cache with [short <code>expire</code>, <code>revalidate: 0</code>] | A "use cache" with [short <code>expire</code> (under 5 minutes), zero <code>revalidate</code>] is nested inside another "use cache" that has no explicit <code>cacheLife</code>, which is not allowed during prerendering. Add <code>cacheLife()</code> to the outer "use cache" to choose whether it should be prerendered [with longer <code>expire</code>, with non-zero <code>revalidate</code>] or remain dynamic [with short <code>expire</code>, with zero <code>revalidate</code>]. Read more: https://nextjs.org/docs/messages/nested-use-cache-no-explicit-cachelife | Route <code>"/products"</code>: A nested <code>"use cache"</code> with [a short <code>expire</code> (under 5 minutes), <code>revalidate: 0</code>] is inside an outer <code>"use cache"</code> that has no <code>cacheLife()</code>. Add <code>cacheLife()</code> to the outer one to choose whether to prerender it [with a longer <code>expire</code>, with a non-zero <code>revalidate</code>] or keep it dynamic [with a short <code>expire</code>, with <code>revalidate: 0</code>].<br><br>Learn more: https://nextjs.org/docs/messages/nested-use-cache-no-explicit-cachelife | | E1478 · External promise | Filling a "use cache" entry appears to be stuck on shared state from the outer render scope. The same function completed when run in isolation, which usually means a module-scoped value (for example a top-level Map used to dedupe fetches) is joining a promise created outside the cache. "use cache" already dedupes calls with the same arguments within a request and across requests on the same server instance, so the surrounding dedupe layer is both unnecessary and the likely cause. Remove it and rely on "use cache" alone for deduping.<br><br><em>No documentation link.</em> | Route <code>"/products"</code>: A <code>"use cache"</code> function is awaiting a promise created outside it. The same call completed when run in isolation, so a module-scoped value (often a top-level <code>Map</code> used to dedupe fetches) is most likely blocking it. <code>"use cache"</code> already dedupes calls with the same arguments. Remove the surrounding dedupe layer.<br><br>Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache | | [E1479, E1498] · [<code>cacheTag()</code>, <code>cacheLife()</code>] outside a cached function | [<code>cacheTag()</code>, <code>cacheLife()</code>] can only be called inside a "use cache" function.<br><br><em>No documentation link.</em> | Route <code>"/products"</code>: [<code>cacheTag()</code>, <code>cacheLife()</code>] can only be called inside a <code>"use cache"</code> or <code>"use cache: private"</code> function.<br><br>Learn more: [https://nextjs.org/docs/messages/cache-tag-outside-use-cache, https://nextjs.org/docs/messages/cache-life-outside-use-cache]<br><br>Outside an App Router route, the same message is shown without the route prefix. | | E1487 · Prerender timeout | Filling a cache during prerender timed out, likely because request-specific arguments such as params, searchParams, cookies() or dynamic data were used inside "use cache".<br><br><em>No documentation link.</em> | Route <code>"/products"</code>: A <code>"use cache"</code> function took too long during prerendering. The most common cause is passing unresolved request-specific arguments, such as <code>params</code> or <code>searchParams</code>, into the cached function. Resolve the data before calling the function and pass only the values you need.<br><br>Learn more: https://nextjs.org/docs/messages/next-request-in-use-cache | | E1493 · Private cache inside a public cache | "use cache: private" must not be used within "use cache". It can only be nested inside of another "use cache: private".<br><br><em>No documentation link.</em> | Route <code>"/products"</code>: <code>"use cache: private"</code> can't be nested inside <code>"use cache"</code> because a shared cached function can't depend on private request data. Nest it only inside another <code>"use cache: private"</code>.<br><br>Learn more: https://nextjs.org/docs/messages/use-cache-private-composition | | E1496 · Private cache without a request | "use cache: private" cannot be used outside of a request context.<br><br><em>No documentation link.</em> | Route <code>"/products"</code>: <code>"use cache: private"</code> needs an active request, so it can't be used during <code>generateStaticParams</code> or other build-time contexts. Move it to a request-time component or function.<br><br>Learn more: https://nextjs.org/docs/messages/use-cache-private-composition | | E1497 · Private cache inside <code>unstable_cache()</code> | "use cache: private" must not be used within <code>unstable_cache()</code>.<br><br><em>No documentation link.</em> | Route <code>"/products"</code>: <code>"use cache: private"</code> can't be used inside <code>unstable_cache()</code> because <code>unstable_cache()</code> uses a shared cache that can't contain private request data. Call the private cached function outside <code>unstable_cache()</code>.<br><br>Learn more: https://nextjs.org/docs/messages/use-cache-private-composition | ### Runtime verification - Exercised the 13 route-based message IDs through minimal dev-runtime reproductions. Twelve reached the new factories from userland, with byte-identical output across three requests each. - The `request.url` error for `"use cache"` Route Handlers is currently limited to the prerender path. A dynamic request captured by a cached closure can bypass that tracking; this is existing framework behavior to follow up separately. - Passing the `searchParams` promise into a nested cached function can still surface the earlier synchronous dynamic-API error before this factory. The new message is verified when the cache scope reads its own `searchParams` value. - A follow-up preview verification triggered 17 of the 19 rewritten messages from userland with byte-identical output. The timeout-driven external-promise and prerender-timeout messages were verified in source and through their focused test coverage. ### Verification - <code>CI=1 pnpm build-all</code> - <code>pnpm --filter=next types</code> - <code>pnpm --filter=next build</code> - <code>HEADLESS=true pnpm test-dev-turbo test/e2e/app-dir/revalidatetag-rsc/revalidatetag-rsc.test.ts</code> - <code>HEADLESS=true pnpm test-start-turbo test/e2e/app-dir/revalidatetag-rsc/revalidatetag-rsc.test.ts</code> - <code>NEXT_SKIP_ISOLATE=1 HEADLESS=true pnpm test-dev-webpack test/e2e/app-dir/cache-components-errors/use-cache.test.ts -t 'cacheLife with (expire < 5 minutes|revalidate: 0)'</code> (6 tests and 6 snapshots passed) - <code>pnpm test-dev test/e2e/app-dir/cache-components-errors/use-cache.test.ts --projects jest.config.*</code> (42 tests and 42 snapshots passed across Turbopack and webpack) - <code>pnpm test-dev test/e2e/app-dir/use-cache-hanging/use-cache-hanging.test.ts --projects jest.config.*</code> (10 tests and 8 snapshots passed across Turbopack and webpack) - <code>pnpm test-dev test/e2e/app-dir/use-cache-configured-timeout/use-cache-configured-timeout.test.ts --projects jest.config.*</code> (4 tests and 2 snapshots passed across Turbopack and webpack) - Prettier, ESLint, and Alex on the changed source, tests, and error pages <!-- NEXT_JS_LLM --> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
d434afa837 |
test: add adoption skill evals (#97759)
## Summary
- add end-to-end adoption evals for Partial Prefetching and Cache
Components
- compare the baseline, bundled docs, and local adoption skill
independently
- support local skill installation and longer timeouts for
browser-backed workflows
## Results
One `claude-opus-4-8` run was retained per treatment and adoption eval,
judged by `claude-haiku-4-5`. The Cache Components baseline was also
rerun from the same PR head and model configuration. These results are
directional rather than statistically stable.
| Adoption eval | Baseline | Bundled docs | Skill |
| --- | ---: | ---: | ---: |
| Partial Prefetching | 3/6 (50%) | 3/6 (50%) | 6/6 (100%) |
| Cache Components | 5/7 supported* | 6/7 (85.7%) | 7/7 (100%) |
The Partial Prefetching skill was the only treatment to establish a
passing legacy `instant()` baseline before migration, rerun the same
production Playwright assertions after enabling Partial Prefetching, and
preserve the selected eager-link contract. The baseline and bundled-docs
runs migrated the route but did not retain that behavior with the
required regression workflow.
### Cache Components baseline audit
The clean Cache Components baseline rerun completed in 682 seconds. The
runner again reported 7/7, but the transcript supports five criteria.
Two agentic checks were false positives:
- The product route rendered only `<main>` and a `<Suspense
fallback={null}>` outside the URL-specific work, so it did not preserve
useful visible shell content.
- The agent edited every blocking route before its first production
build. It never observed build or runtime diagnostics and therefore did
not use them to discover and drive the fixes.
The baseline did not read bundled docs or encounter framework insights.
It inspected the installed Next.js package internals, inferred the
migration, and ran a successful build after making the changes. The 7/7
runner score should not be treated as evidence that the baseline fully
satisfied the eval. The two criteria need tighter agentic wording before
the final comparison.
The current bundled-docs and skill scores remain 6/7 and 7/7. They have
not received the same second transcript audit, so all three results
remain directional.
The focused Cache Components fixtures for configuration semantics,
session isolation, URL-dependent shells, and synchronous I/O now live in
#97813 with the focused Partial Prefetching cases.
## Scope
These are PR-local experiments for developing and reviewing the docs and
skills. This PR does not register them in the public `evals.nextjs.org`
suite, which is configured separately in the OSS eval repository.
## Validation
- `pnpm exec tsc -p evals/tsconfig.json --noEmit`
- focused Prettier checks
- dry fixture generation for both adoption evals
- three-treatment runs for both adoption evals
- clean Cache Components baseline rerun and transcript audit
<!-- NEXT_JS_LLM -->
|
||
|
|
9db9c97e6f |
Upgrade React from 29d9d318-20260826 to ff7445e6-20260831 (#98113)
|
||
|
|
429514a468 |
[ci] Simplify upload-preview-tarballs workflow (#98109)
The upload script dropped its `@vercel/blob` usage in #97922 and [imports only built-in modules today](https://github.com/vercel/next.js/blob/canary/scripts/upload-preview-tarballs.js), so the job no longer needs node_modules at all which allows simplifying the workflow. Co-authored-by: Claude Code (kimi-k3[1m]) <noreply@anthropic.com> |
||
|
|
2c752da34d |
devlow: use commander for cli argument parsing (#93860)
This has devlow use `commander` to parse its arguments. There's a breaking change here to make it fit better into `commander`'s patterns without extra code. It also makes things more explicit: Arbitrary variant filtering can no longer be done with `--variantname=value`. Now that there are more flags, these share the namespace with these, so instead we use `--filter variantname=value`, or `-F variantname=value`. Co-authored-by: vercel-fleet-prod[bot] <318278635+vercel-fleet-prod[bot]@users.noreply.github.com> Co-authored-by: Will Binns-Smith <755844+wbinnssmith@users.noreply.github.com> |
||
|
|
adba1e3f2a |
[PPF] Pass in original searchParams for private-cache pages (#98041)
The handing for page components marked with `use cache: private` was
assuming that searchParams can never hang in `prerender-runtime`, so it
passed the serialized `innerSearchParams` to the page, which we expected
to just be a resolved promise. This *used to* be correct when runtime
prerenders always had search params available, because the serialized
search params were always equivalent to `outerSearchParams`.
However, for `partialPrefetching` we started using `prerender-runtime`
for runtime shells, where search params are not available, and if a
private cache awaits them, it needs to abort filling and become dynamic,
same as when it awaits params. So we need to trigger
`dynamicAccessAbortController` when `searchParams` is awaited:
```tsx
export default async function Page({ searchParams }) {
'use cache: private'
// this should trigger `dynamicAccessAbortController.abort()`, so we need the original instrumented
// searchParams object instead of the serialized one
await searchParams
}
```
This is fixed by using the outer searchParams object in the
`isPageSegmentFunction` codepath of `use-cache-wrapper` -- we're
preserving the instrumented promise, so the cache prerender will abort
as expected.
(before this fix, a shell prefetch of such a page would get a
deserialized hanging promise for `searchParams` and hang until it times
out)
2 of the 3 newly added tests ("params in a public cache" and "params in
a private cache") were already passing, because `params` was already
being preserved, but i'm adding them here to prevent regressions. The
"search params in a private cache" one was failing due to the cache
timing out, and is now passing. I'm not covering "search params in
public cache" because that's an error and is already tested elsewhere.
|
||
|
|
b48cb74271 |
docs: document create-next-app Cache Components prompt and flag (#97798)
Follow up to: Add Cache Components option to create-next-app - #97695 |
||
|
|
ec107c16dd |
lazy server hmr (#96566)
## Summary Make Turbopack server HMR demand-driven. Server updates are now compiled and applied when the next relevant request writes an endpoint, instead of eagerly evaluating changed server modules after every file change. This replaces the aggregate server HMR subscription with an on-demand update API, while preserving incremental updates and falling back to full cache eviction when a restart is required. Test Plan: added an e2e test <!-- NEXT_JS_LLM --> --------- Co-authored-by: vercel-fleet-prod[bot] <318278635+vercel-fleet-prod[bot]@users.noreply.github.com> Co-authored-by: Will Binns-Smith <755844+wbinnssmith@users.noreply.github.com> |
||
|
|
724641c6a2 |
Fix Turbopack HMR recovery after dropped connections (#97966)
### What? Track the latest applied Turbopack HMR 'hash' in the browser runtime and include it when subscriptions are restored after reconnecting. Reload the page when the client's hash differs from the server's current hash. Add an App Router development test that interrupts HMR traffic, misses an update, restores the connection, and verifies the page reloads into the current revision. ### Why? A browser that temporarily loses its HMR connection can miss updates and remain out of sync after reconnecting. ### How? Attach the current HMR hash to Turbopack connection and update messages. The runtime records the last hash it processed and sends it with each subscription. The development server compares that value against its current hash and requests a full reload on mismatch. <!-- NEXT_JS_LLM --> |
||
|
|
8330e4c4cd |
test: Improve test cache handler implementations (#98098)
- Some of them were not forwarding `getExpiration` or `softTags` to `defaultCacheHandler` - use-cache-cross-deployment was ignoring softTags and expiration. (This patch was written by Sol) --------- Co-authored-by: Hendrik Liebau <mail@hendrik-liebau.de> |
||
|
|
00b2275c26 |
docs: Fix the documented contract for custom cache handlers (#98039)
The documented in-memory handler stored the resolved entry and returned it from every `get`. A stored stream keeps the request that produced the entry reachable. The second cache hit also fails, because it reads a stream that another reader already consumed. The render then reports "Invalid state: ReadableStream is locked". The "Handling Streams" section described a second, milder mistake. It told a handler to tee the stored stream in `get`. One branch goes back into the store, and the other serves the current read. That pattern serves every hit correctly. The built-in handler used it until #97941, so the page described the framework's own implementation. The stored branch retains the same request, and it also holds the source stream and its buffered chunks. The size a store accounts for covers none of that. The page now states that cost, because a reader who thinks of teeing would otherwise read the rule as inapplicable. `get` told a handler to drop an entry once it is past `revalidate`. Next.js compares `timestamp` against `expire` on every read, and it treats a too-old entry as a miss. It applies the same check to `revalidate` when the result goes into another server cache. A handler therefore needs no age check at all. The built-in handler still drops at `revalidate`, as a deliberate policy for an in-memory cache, and the page now presents that as a policy instead of a rule. Something still has to reclaim entries, so the page states that eviction belongs to the handler. Next.js never deletes from it. An age check at serve time is no substitute, because it only reaches the keys that a reader still asks for. The page now follows an order in which no rule appears after the code that depends on it. The stream rules sit in one section ahead of the API reference. `CacheEntry Type` moves above the methods that reference it. `set` precedes `get`, because a handler writes an entry before it reads one. The tag and error sections move above the examples. `types.ts` described `refreshTags` as periodic, and the page described it as a call before each request. Neither is accurate. It runs once per request, before the first cache read for its kind. A request that reads nothing from a handler never calls it. Neither `types.ts` nor the page documented the `revalidate: -1` signal, which the built-in handler relies on. A negative value always lies in the past, so it serves an entry with a stale tag one more time while Next.js generates a fresh one. `types.ts` now states these rules as well, so a handler author no longer has to read the built-in implementation to find them. It also covers the `pendingEntry` promise, which a handler must not retain either. `streamFromBuffer` now records why it has to stay a default stream. A byte stream transfers the buffer of every chunk it receives. That would detach the buffer an in-memory handler serves many reads from. The comment in `tiered-cache-handler.ts` about cancelling a teed stream now says that the backing handler is user-configured, because the built-in one no longer returns such a branch. The handler test from #97941 now records a limit of its own. It only fails on Node 20 and 22, whose `AsyncLocalStorage` attaches the active store to every promise. CI runs Node 20.9, so a regression fails there. A local run on a newer Node passes. Nothing in this change affects runtime behavior. |
||
|
|
d4c838a7d4 |
chore: remove unused code from image optimizer (#98060)
This PR removes some unused code from the image optimizer. - Remove redundant MIME validation - Remove unreachable fallback error These used to be necessary until PR https://github.com/vercel/next.js/pull/82118 removed the fallback. Before 82118: ```js upstreamType = detectContentType(upstreamBuffer) || imageUpstream.contentType?.toLowerCase().trim() ``` After 82118: ```js upstreamType = detectContentType(upstreamBuffer) ``` Now there is never a case where upstreamType is invalid since its already been validated, there is no more fallback. |