Removes the "allow-runtime" prefetch config, and turns its behavior on
implicitly wherever Partial Prefetching is enabled.
The original motivation for "allow-runtime" was to give apps more
control over server costs triggered by prefetches. Until a route
explicitly opts in, prefetches would only be served from the CDN, not
from the server. The problem, though, was it was very confusing to know
when to add or remove this configuration. The incentive for many apps
was to add it everywhere, with no clear signal for when to remove it.
Our updated thinking is that Partial Prefetching itself already provides
sufficient protection against runaway prefetching costs: per-link
prefetches only happen on Link components that explicitly opt in with
the prefetch prop.
The optimizations landed earlier in this stack also make allow-runtime
less necessary: on pages where all the content is statically renderable,
prefetches are served from the static cache and no runtime request is
ever issued; only a page that accesses non-static data is prefetched at
runtime.
The upshot of this decision is that runtime versus static becomes an
internal optimization; the same content gets prefetched regardless of
whether or how Next.js is able to optimize it.
The cold cache dev indicator added in #94611 surfaces a load that filled
an empty cache while streaming. After the load settles it leaves a
persistent "Cold cache" badge in the corner. That badge is visually too
loud and disruptive in its current form, so we want to keep iterating on
its UI and UX before showing it to everyone. Until then it should be off
by default.
This change puts that persistent badge behind a new, default-off
`experimental.coldCacheBadge` flag, plumbed to the dev overlay as
`process.env.__NEXT_EXPERIMENTAL_COLD_CACHE_BADGE` via the define
plugin. The transient "Rendering (cold cache)" pill shown during a
navigation is intentionally kept regardless of the flag: it is transient
and clears itself once the blocking navigation transition has committed,
so it stays valuable without being disruptive. Only the permanent badge
was the problem. The pre-existing "Cache disabled" (bypass) badge and
the DevTools menu's cold-cache entry are also unaffected.
The gate lives in `computeIntent`, so when the flag is off a cold load
resolves to no badge and the indicator follows its existing empty-badge
render path. Storybook forces the flag on through its `env` hook so the
badge stories remain the surface for iterating on the design, and every
test suite that asserts on the badge opts into the flag so none of them
regress while it is disabled by default.
When `partialPrefetching` is on, the shell that we usually care about
displaying is the App Shell, which is represented by the ShellRuntime
stage. We should only show the cold cache indicator for caches that
happen up until then, and release the client-side promise appropriately.
This PR extends the existing `revealAfterStage` mechanics to account for
this.
> Note that this is incomplete: if we're navigating via `<Link
prefetch={true}>` (or have `partialPrefetching: "unstable_eager"), we
might want to use the Runtime stage instead. I'm leaving those for a
follow up -- representing the common case (a shell prefetch) seems like
the most useful thing.
I've re-worked the `revealAfterStage/holdStreamUntilRevealed` setup into
an object (`DevNavigationKind`) that represents the navigation that
we're trying to simulate -- either an initial load or a client nav. This
gets rid of the invalid `holdStreamUntilRevealed = true` +
`revealAfterStage = RenderStage.Runtime/ShellRuntime` combination and
lets us bring the logic closer to where the stream blocking tricks
actually happen.
I've also done some drive-by refactoring of `streamStagedRenderInDev` to
dedupe some repetitive code, because every task was basically doing the
same thing. I've also moved all `revealAfter.resolve()` calls into
separate tasks -- we were inconsistent about this, and the `Static`
`revealAfter.resolve()` was done together with the next stage, but the
`RenderStage.Runtime` was done in a separate task.
---
Strangely, despite the changes working as expected for the Cold Cache
indicator, I can't actually get link data (`await searchParams`) to
trigger a fallback when navigating, so there's a failing test for that
in `cache-components-dev-streaming.test.ts`. I'm not sure why what's
causing this, but I'm leaving that investigation for a follow-up as
well.