This is everything except `turbo-tasks-[backend]`
Using ast-grep replacement:
- search: `|$$$ARGS| async move {$$$BODY}`
- replace: `async |$$$ARGS| {$$$BODY}`
This also made it obvious in some places that we don't actually need
async-await,
e.g. `.map(async |v| v.to_resolved.await)`
## What
Adds `scope_unbounded `, a parallel scope to turbo-tasks: jobs run on a
shared work-queue, any job may enqueue more, and the pool is drained by
the runtime worker threads plus the calling thread until empty.
## Why
The garbage collector (later in this stack) needs to process a pool of
work that *discovers more work as it runs* — collecting a task cascades
into collecting newly-parentless children. A fixed `parallel::for_each`
can't express that. `scope_self_feeding` is the general primitive for
"parallel pool where jobs spawn jobs".
## Design notes
- **Deadlock-safe on thread-limited / contended runtimes.** Helper
workers are a pure optimization: the calling thread always makes
progress on the shared queue on its own, so the pool completes even when
no worker threads are available.
- Supports growing/shrinking the set of workers pulling from the queue
based on the amount of work available.
- Supports a simple mostly lock free way to aggregate data from the
tasks
- Supports a way for tasks to abort the whole queue, which is important
for making GC interruptible
## Testing
There are a fair number of new unit tests and i have done some runs
under miri
- The `brower.eval(callback)` version was a bit weird, use
beforePageLoad as we do everywhere else
- Properly parse the URL and match against the pathname, to not break
with `?dpl`
## Summary
Turbopack currently builds an App Router page without a root layout,
leaving the problem to surface later at runtime. Report it while
building the app structure instead:
`route/page.js doesn't have a root layout. To fix this error, make sure
every page has a root layout.`
The check uses the layouts already inherited during directory-tree
traversal, so route groups and apps with multiple root layouts keep
working. Route handlers are unaffected.
Webpack behavior is intentionally unchanged in this PR.
## Verification
- `HEADLESS=true pnpm test-dev-turbo
test/e2e/app-dir/create-root-layout/create-root-layout.test.ts`
- `HEADLESS=true pnpm test-start-turbo
test/e2e/app-dir/create-root-layout/create-root-layout.test.ts`
- `HEADLESS=true pnpm test-dev-webpack
test/e2e/app-dir/create-root-layout/create-root-layout.test.ts`
- `HEADLESS=true pnpm test-start-webpack
test/e2e/app-dir/create-root-layout/create-root-layout.test.ts`
- `HEADLESS=true pnpm test-dev-turbo
test/e2e/app-dir/root-layout/root-layout.test.ts`
## Summary
Dogfooding the Partial Prefetching adoption skill on v0 exposed two
preservation checks that should be part of the main workflow:
- trace custom Link wrapper consumers and distinguish declarative
prefetch props from imperative hover or touch prefetching
- treat existing instant() assertions as preservation contracts, and
expand caching only to the smallest coherent rendered subtree when a
data-only cache does not restore the prefetched UI
## Verification
- pnpm prettier --with-node-modules --ignore-path .prettierignore
--check skills/next-partial-prefetching-adoption/SKILL.md
- Commit hook lint-staged formatting passed
- Not run: quick_validate.py because PyYAML is unavailable in the local
skill validator runtime
<!-- NEXT_JS_LLM -->
### What?
Stabilizes the RSC poisoned-import error-overlay test for `proxy.js`
without weakening or retrying its redbox assertion.
### Why?
An initially broken proxy can emit the expected build error and then
force a startup full reload. That reload can clear the overlay before
the test begins observing it, causing an intermittent `Expected Redbox
but found no visible one` failure even though validation worked.
### How?
The proxy parameter now starts from a valid module and introduces the
poisoned import only after the sandbox page has hydrated. This moves the
assertion onto the live HMR path and removes the startup-reload race.
Middleware and instrumentation retain their initial-compile setup
because their error overlays are reliable there and instrumentation does
not reliably surface this edit through Turbopack HMR.
### Verification
- `pnpm test-dev-webpack
test/development/acceptance-app/rsc-build-errors-poisoned-imports.test.ts`
- `pnpm test-dev-turbo
test/development/acceptance-app/rsc-build-errors-poisoned-imports.test.ts`
- Proxy case repeated 5 times with Webpack and 3 times with Turbopack
- Prettier, ESLint, and `git diff --check`
<!-- NEXT_JS_LLM -->
---------
Co-authored-by: vercel-fleet-prod[bot] <318278635+vercel-fleet-prod[bot]@users.noreply.github.com>
Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
Clarifying that:
> - **`stale` from 30 seconds up to 5 minutes**: included in prerenders,
but excluded from the route's [App Shell](/docs/app/glossary#app-shell).
Means, 30 <= stale and stale < 5minutes
Implements `unstable_prefetch()`, which is intended for use in
`partialPrefetching`. `await unstable_prefetch()` excludes content from
the app shell -- it will only be available when using `prefetch={true}`
(speculative prefetch) or during navigations.
As a rule of thumb, `unstable_prefetch()` resolves whenever static
`params` would:
- in a static prerender
- but NOT the app shell extracted from it, which is param-less
- in a runtime prefetch (`prefetch={true}`)
- but NOT a runtime app shell, which is param-less
Note that `prefetch()` is URL data, so using it in an App Shell without
Suspense will trigger an instant insight.
### Implementation notes
`prefetch()` is treated like URL data, so it resolves in the
`PrefetchStatic/PrefetchRuntime` stages added in #96908. The
implementation is basically analogous to `unstable_navigation()` except
using different stages. i've considered abstracting them into one
implementation, but decided against that for now, we can deduplicate
later.
Error messages about URL data have not been updated to mention it yet --
we will do that as a follow up, along with docs.
`await prefetch()` does not count as a runtime data access, meaning that
it won't affect the static prefetch hint for a route. however `await
prefetch(); await cookies()` does deopt the route, because using a
speculative runtime prefetch would reveal more content. Note that this
may cause us to unnecessarily deopt a shell to runtime even if only the
speculative part of the content would be improved by a runtime request;
this is not a new issue, but it's something we should optimize.
Scaffolds a new API, `unstable_prefetch()`. It's only allowed in server
code, so we ban it from being imported in the client.
Implementation follows upstack. Intended to be merged together.
Eager glob values were built with a plain CommonJS require, so the value
was a
module's `exports` object rather than its ESM namespace — for JSON (or
any
CommonJS) module that meant no CommonJS interop ran, so
`modules[path].default`
(the shape our own docs show) and `{ import: 'default' }` were
`undefined`. A
hand-written `import * as ns from './data.json'` did have `default`.
Eager values now use the same namespace-import primitive a static
`import * as ns from '…'` produces.
<!-- NEXT_JS_LLM -->
Co-authored-by: vercel-fleet-prod[bot] <318278635+vercel-fleet-prod[bot]@users.noreply.github.com>
Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
## Summary
When two Next.js microfrontend child applications run in development
mode at the same time, their HMR clients conflict because they share the
same global chunk-update listener registry.
Since turbopack added support for the chunkloadingglobal configuration,
I think this configuration can also consume HMR's global object
simultaneously: PR: https://github.com/vercel/next.js/pull/88790 and
https://github.com/vercel/next.js/pull/93488
This change scopes the listener registry to each runtime chunk-loading
global so the applications can receive HMR updates independently.
## Test
Update snapshot test case
Previously, Turbopack treated `_app` and Pages router pages as
completely separate.
So using code both in `_app` and `pages/foo.tsx` would lead to a lot of
duplicated code to be loaded at runtime.
But in reality, `_app` is always loaded for Pages, so we can thread the
availability info and skip chunking modules that were already loaded by
`_app`.
Recreation of https://github.com/vercel/next.js/pull/97549
Co-authored-by: vercel-fleet-prod[bot] <318278635+vercel-fleet-prod[bot]@users.noreply.github.com>
On scheduled runs of the Update React workflow, `github.actor` often
resolves to `github-actions[bot]`, which cannot be assigned to pull
requests in `vercel/next.js`. Since the sync switched to the GitHub App
token, adding a non-assignable assignee fails the request with 403 e.g.
https://github.com/vercel/next.js/actions/runs/32392135342/job/96500520936#step:8:781
(user tokens silently ignored it instead), which failed the entire sync
run and raced the in-flight reviewer request, leaving the created pull
request without a reviewer.
The script now checks assignability and skips assignment with a warning
when the actor cannot be assigned, and the finalize requests (assign,
request reviewers, add labels) run through `Promise.allSettled`, with
any failures rethrown together as an `AggregateError` so the run still
fails on real errors.
Co-authored-by: Claude Code (kimi-k3[1m]) <noreply@anthropic.com>
Another attempt at cleanup, similar to
https://github.com/vercel/next.js/pull/96955.
We need different information to write symlinks or junction points (most
notably, if it's a directory or not) than we get when reading symlinks.
We were trying to use the same data structure for both, but this lead us
to always call `get_type()` as part of reading a link, which was just
silly.
This also meant that `write_link` had to handle `NotFound` and
`Invalid`. `NotFound` is somewhat reasonable (just delete the file), but
`Invalid` is awkward.
This is a follow-up to https://github.com/vercel/next.js/pull/96116
If we're waiting a long time for your filesystem to settle, we should
show you the last modified path, so at least you can have an idea of why
this is happening.
Relocates `useDynamicRouteParams` and `useDynamicSearchParams` to avoid
having to update `client-hook-abort-reasons.test.ts` every time there's
changes in `dynamic-rendering.ts`. Webpack leaked the source location of
the `React.use` calls inside, so any code change that moved the source
of the hook would require a snapshot update.
For reasons i don't quite understand, moving the hooks to a different
file improved the ignore-listing, i.e. we no longer point to the
internals of the hooks. This is surprising but not unwelcome.
`navigation()` is a new API that allows omitting contents from runtime
shells and runtime prefetches. Conceptually, the point is to express
that something is expensive to compute, so we shouldn't do it for
requests that may not get used (shells and prefetches). Notably, this
means that it's fine to include it in a static prerender -- it'll be
computed once and used for many requests, so it doesn't make sense to
exclude it.
## Implementation
The split in behavior across static and runtime prerenders is a
departure from how most of our APIs behave -- usually, if something
resolves statically, then it also resolves in "more complete" prerender.
Departing from this leads to some implementation complexity.
We include three new stages, used by two facets of the implementation:
```diff
export enum RenderStage {
Before = 1,
//
ShellStatic = 10,
+ PrefetchStatic = 11, <------- params, prefetch() [static prerenders]
+ NavigationStatic = 12 <------navigation() [static prerenders]
Static = 13, <--------------- finish accumulators [static prerenders]
//
ShellRuntime = 20,
Runtime = 21, <-------------- params, prefetch() [runtime prerenders]
+ NavigationRuntime = 22, <---- navigation() [runtime prerenders]
//
Dynamic = 30,
Abandoned = 40,
}
```
### NavigationRuntime
In runtime prerenders (or dev renders that simulate them),
`navigation()` resolves in `NavigationRuntime`
We only reach this stage in 1. the embedded runtime prerender produced
for Cached Navigations and 2. during dev/prod full staged renders --
runtime shells end in `ShellRuntime`, and runtime prefetches end in
`Runtime`.
Notably, this means that content gated behind `navigation()` is included
in the embedded runtime prefetch stream.
### PrefetchStatic & NavigationStatic
This is a helper stage added before `Static`. Static prefetches still
use the `Static` stage for their output. This new stage exists so that
we can resolve static `params` (and `prefetch()` when we implement it)
which the stage is named after) separately from `navigation()`, which
resolves in `NavigationStatic`, after which the prerender ends in
`Static`. This separation is important, because during static prerenders
we track whether or not runtime APIs are used (see
`trackRuntimeDataAccessed`) to determine if a runtime shell (or runtime
prefetch) might give us more content than the static ones. However, a
runtime shell/prefethc **would not resolve navigation()**, so `await
navigation(); await cookies()` would not reveal more content, and thus
shouldn't count as a usage that prevents static optimization.
We achieve this by checking the stage inside
`trackRuntimeDataAccessedImpl` and not tracking anything if we reached
the `NavigationStatic` stage.
### Behavior of shells and validation
As noted before, `navigation()` has an incompatible resolution order
between static and runtime prerenders. In #97040, we did some groundwork
to deal with this in validation.
Static prerenders resolve `navigation()`, which means that static shells
include content gated behind navigation(). This means that Static Shell
Validation allows them.
On the other hand, App shells **do not** resolve `navigation()`. This
leads to an inconsistency for Instant Validation -- a `await
navigation()` might be fine if a page is prefetched statically, but
would become blocking as soon as the page starts using runtime data and
switches to a runtime shell. To avoid this pitfall, we pessimistically
assume that any `navigation()` _might_ be part of a runtime
shell/prefetch, so any `navigation()` unguarded by Suspense will error
in IV.
In practice, this is handled analogously to static params: we do a dev
render with `needsAppShell: true`, which makes `navigation()` resolve in
`NavigationRuntime`, and then we use the `ShellRuntime` stage when
validating, which means that `navigation()` will be a hole. Note that
the discriminated error message logic currently only retries errors
using the `Runtime` stage, which won't have `navigation()` resolved
either, so it will be incorrectly reported as dynamic data. This will be
improved in a follow up.
Scaffolds a new API, `unstable_navigation()`. It's only allowed in
server code, so we ban it from being imported in the client.
Implementation follows upstack. Intended to be merged together.
Completing the input-output pair for cached functions.
This is addressing user confusion around what happens to a cached
function, and functions within the scope.
## Summary
- keep incompatible segment config migration before the codemod
- move synchronous IO fixes after the codemod and normal build so
`connection()` is applied only to reported blockers
- use a scoped debug build only when the normal build does not locate
the reported call
## Verification
- Not run: product tests (agent skill guidance only)
<!-- NEXT_JS_LLM -->
Using `keyv` instead of `sqlite3`. This test never installed the package
but it just looked like it's native binding related due to its choice of
package.
### What?
Use GitHub's raw content endpoint to validate the `package.json` for
create-next-app examples supplied as repository URLs.
### Why?
The previous validation used GitHub's unauthenticated Contents API.
Parallel create-next-app tests can exhaust the shared runner IP's low
API quota, after which valid examples are reported as missing before
download or package-manager installation begins.
This surfaced as `EPERM` because execa labels a child exit code of `1`
with Node's matching errno name; the underlying create-next-app output
showed the repository lookup failure.
### How?
Probe the example's `package.json` directly on
`raw.githubusercontent.com`. This preserves the existing existence check
while removing the rate-limited API request from URL-based example
creation. The actual archive download continues to use
`codeload.github.com` as before.
### Verification
- `IS_WEBPACK_TEST=1 NEXT_TEST_MODE=start pnpm test-start
test/production/create-next-app/package-manager/yarn.test.ts`
- `pnpm build-all`
- `pnpm --filter create-next-app build`
- `pnpm types`
<!-- NEXT_JS_LLM -->
Co-authored-by: vercel-fleet-prod[bot] <318278635+vercel-fleet-prod[bot]@users.noreply.github.com>
Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
`experimental.workerThreads` decides whether static generation runs in
real worker threads or forked child processes, and a native addon
declared with `NODE_MODULE` can only be loaded once per process. That is
why the flag defaults to false (#9199) and why the static export worker
was fixed to respect it rather than hardcoding threads on (#25063).
Nothing tested it: the only suite that tried, `firebase-grpc`, had its
assertion skipped since 2019, and modern `firebase` ships no native
module at all, so its remaining test asserted only that a build
succeeds.
This adds a `single-context-addon` fixture, deliberately declared with
`NODE_MODULE`, and a production suite asserting both directions: the
build succeeds by default and fails with "Module did not self-register"
once worker threads are enabled. The fixture loads the addon from
`next.config.js` as well as from the page, because that failure only
happens on a second `dlopen` within one process, so an addon loaded only
inside the worker would register there and the build would pass.
A third case documents a bug rather than intended behaviour. `next
build` runs Turbopack in a worker thread and that worker re-evaluates
`next.config.js`, so requiring a non-context-aware addon from the config
breaks the build even with `experimental.workerThreads` off. Webpack and
rspack are unaffected, since their build workers are forked child
processes. Isolated with an unguarded `require` and default flags,
Turbopack exits 1 with "Module did not self-register" where webpack
exits 0. It is the same class of failure #9199 and #25063 fixed, in a
worker those PRs did not touch. The assertion is branched on the bundler
and carries a note to drop the branch once Turbopack stops evaluating
the config on a worker thread. The `isMainThread` guard in the fixture's
config keeps the first two cases pointed at the static generation worker
instead.
A development suite covers the other direction, asserting that
evaluating a route does not put such an addon on one of the threads
`next dev` uses regardless of the flag, so it would catch a change that
moved route evaluation onto the dev validation pool. The expectation is
the same with and without Cache Components; both were checked by logging
the thread id from the page's module scope, and the route is evaluated
on the main thread either way.
`firebase-grpc` is removed, since it covered the same flag with a
skipped assertion and a vacuous one.
This suite installed `sqlite` and `sqlite3` on every run to prerender a
page from a checked-in SQLite database. Neither package was needed for
what the test covers. The pinned `sqlite3@5.0.2` has no linux-arm64
prebuild, and every job that runs this suite is linux-arm64, so each one
compiled the SQLite amalgamation from source. That pin is also Node-API
based and therefore context-aware, so it could no longer reproduce the
abort the suite was originally added for.
The fixture now uses its own compiled `native-addon` plus a
dependency-free JS wrapper standing in for `sqlite`'s role, and reads
its rows from a plain JSON file. The `path.join(process.cwd(), ...)`
expression stays in the page, because output file tracing only follows
it from the app's own code, so moving it into the wrapper would stop the
data file being traced.
The emitted traces were read rather than assumed. Turbopack and
`@vercel/nft` produce the same fixture entries, including the compiled
binary at `native-addon/build/Release/native_addon.node` and the
`process.cwd()`-derived `users.json`.
The trace assertion now checks each pattern separately instead of
collapsing them into a single `every(...)`, which could only report that
something did not match. The `notTests` block went away with it, since
`[].some(...)` asserted nothing.
The suite installed `sqlite3` only to get a package that locates its
compiled binary through `require('bindings')(...)`, which is the shape
turbopack issue 5913 was about. It now carries its own `native-addon`
and `bindings` packages instead, both installed as relative `file:`
dependencies, with node-gyp compiling the addon during install.
The addon is compiled rather than stubbed because the assertion reads a
value off the loaded binary, so a real `process.dlopen()` has to happen.
Compiling at install time also keeps the binary matched to whichever
Node ABI is running, which a checked-in binary could not do, since a
non-context-aware addon cannot use Node-API and is therefore ABI-locked.
The page now renders the addon's constant, so a module that resolved to
nothing fails the test instead of passing quietly.
Both packages are `file:` rather than `link:` dependencies. A linked
package resolves to a path inside the app, which the bundler then treats
as app code and tries to bundle, and `bindings` contains a `require` it
cannot resolve statically. `serverExternalPackages` is needed because
the app router bundles `node_modules` by default, and `sqlite3` only
avoided that by being on the built-in list in
`server-external-packages.jsonc`.
The fixture packages are registered in `modulePathIgnorePatterns`,
following the entries already there. Jest is configured with
`throwOnModuleCollision`, so a package name appearing twice outside
`node_modules` aborts the whole test run, and the layers above add more
copies of `bindings`.
CI authenticated to Vercel Remote Cache with a long-lived Personal
Access Token in the `TURBO_TOKEN` repository secret. That token never
expired, is scoped to a team member rather than the team, and is
readable by every job that inherits secrets.
Each job now mints its own short-lived, cache-only token instead, using
`vercel/setup-turborepo-remote-cache-action` against a Turborepo CLI
OIDC policy configured on the Vercel team following
https://vercel.com/docs/monorepos/remote-caching/external-ci-cd#openid-connect-oidc
Forks skip the step entirely since they won't have access to repository
variables. During outages or any other permission errors, the steps
outcome will simply be ignore and we fall back to uncached behavior.
This could lead to silent regressions or hiding new, incorrect callsites
lacking necessary permissions. A Datadog monitor is not as simple as I'd
like since DD does not track outcome but conclusion (which is always
success for continue-on-error). Adding custom tags via DD CLI feels to
heavy. We'll revisit if this becomes a recurring issue.
With #94948 we intended to move the client over to the firehose feed of
HMR events with the intent of unifying the code paths for maintenance.
However, now that Server HMR is moving to a pull-based model (which
client HMR will not be able to implement), let's keep the split.
There's no need to encode the HmrTarget into each surface, and we can
just use the function name to indicate which mode of HMR it's for.
Stacked on #96686.
## What?
Keep HMR update instructions as typed Rust values until they cross the
client protocol serialization boundary. The JSON wire format remains
unchanged.
## Why?
HMR aggregation currently operates on `serde_json::Value`, so it has to
inspect serialized `type` fields and clone values out of JSON objects.
This loses type information before aggregation and makes unsupported
instruction variants easy to overlook.
Keeping instructions typed lets aggregation use concrete equality and
hashing, preserves stable ordering while deduplicating updates, and
falls back to a total update when it encounters an instruction type it
cannot aggregate.
## How?
- Add `UpdateInstructionValue`, a type-erased serializable value with
typed downcasting and equality.
- Represent chunk-list and merged ECMAScript updates with owned,
hashable values backed by frozen collections.
- Aggregate typed instructions with first-seen ordering for merged
updates and last-writer-wins values for chunk updates.
- Serialize instructions only when producing the HMR protocol message.
- Add unit coverage for downcasting, equality, hashing, aggregation
behavior, and the existing JSON protocol shape.
## Verification
- Unit tests cover the new typed values and aggregation behavior.
- Serialization tests confirm the client-facing HMR messages are
unchanged.
- CI
<!-- NEXT_JS_LLM -->
## What?
Serialize `FrozenMap` as a map and `FrozenSet` as a sequence instead of
exposing their internal fields.
Add JSON shape and round-trip coverage for both collection types.
## Why?
Keep serialized formats tied to collection semantics rather than their
current storage representation. This matches Serde's implementations for
`HashMap` and `HashSet`, which serialize through `collect_map` and
`collect_seq` respectively.
---------
Co-authored-by: Benjamin Woodruff <github@benjam.info>
The implementation of `unstable_eager` is currently incomplete, causes
bugs in the main Partial Prefetching flow (see #97469), and is generally
not worth the complexity it adds, so we're removing it. We may add
something like this back later if we think it's useful.
Importantly, removing it fixes a bug that caused apps that use
incremental opt-in (i.e. no global `partialPrefetching` config and
`export const prefetch = 'partial'`) to issue speculative prefetches for
all links without `prefetch={true}`, because any segment without an
explicit `prefetch = "partial"` was marked as needing eager prefetching,
which was then propagated to the whole route. See #97469 for a full
explanation.
This fix causes a lot of changes in tests, which were previously
incorrectly asserting that we should be seeing two requests. Now that
the unnecesary speculative prefetch is gone, most of them only see one.
The router code did not respect the isPartial byte for runtime app
shells for some reason, potentially leading to unnecessary speculative
prefetch requests even if the shell was in fact complete.
The comments say that the byte on those can't be trusted:
https://github.com/vercel/next.js/blob/d00eb6b6c471c9c51ef997f643b049210d9940a5/packages/next/src/client/components/segment-cache/cache.ts#L2694-L2696
which is not true -- the byte is definitely set to partial when a shell
contains holes (due to URL data or link data).
Updates some tests that now no longer trigger a speculative prefetch
because the shell is complete.
(note that our tests use `prefetch = 'partial'` a lot, so some of them
were triggering speculative prefetches even when they shouldn't have.
see #97469 for more)
## Summary
This PR improves the accessibility of the Ant Design example by adding
an accessible label to the icon-only `Link`.
### Changes
- Added `aria-label="Home"` to the icon-only link.
### Why
The link currently renders only an icon, so it does not provide an
accessible name for assistive technologies. Adding an `aria-label` makes
its purpose clear to screen reader users without changing the visual
appearance.
### Before
Screen readers announce the element as an unlabeled link.
### After
Screen readers announce the link as **"Home"**.
Co-authored-by: Marcos Hernanz <96699542+marcoshernanz@users.noreply.github.com>
### Why?
generateMetadata can be marked with use cache, but Cache Function return
values must be serializable. The metadataBase examples use URL objects,
so the interaction needs co-located guidance for cached metadata.
### How?
Add a Good to know note that recommends returning metadataBase as a
string, such as with url.toString(), and links to the use cache
serialization requirements. Runtime behavior is unchanged.
<!-- NEXT_JS_LLM -->
---------
Co-authored-by: Joseph <joseph.chamochumbi@vercel.com>
Store/load the encryption keys at `.next/server/preview-props.json`
(which are always needed) as opposed to including the giant
`.next/prerender-manifest.json`
The manifest is added to `required-server-file.json#files` so it keeps
getting included in prod
`.next/prerender-manifest.json` is unchanged for backwards compatiblity
reasons
This is work towards the goal of not including the one giant
prerender-manifest at runtime in the serverless function
## What?
Updates the Interactive Apps guide to show how an optimistic mutation
handles an expected server error.
## Why?
The optimistic card move automatically returns to the confirmed data
when the write fails, but the guide did not show how to explain that
failure to the user. A success toast would duplicate the visible card
movement, so the example only adds feedback for the error case.
## How?
- Return a structured error result when the task no longer exists.
- Display that expected error in a toast while `useOptimistic` reverts
the card.
- Forward unexpected thrown errors to the nearest error boundary.