mirror of
https://github.com/hanzoai/next.js.git
synced 2026-10-30 14:46:38 +00:00
This PR makes Sync IO behavior more restrictive when `partialPrefetching` is on (either globally or for a page). - Previously, we allowed `await cookies(); Date.now()` in in segments that won't ever be runtime prefetched. This was achieved by separating segments into "early" and "late" stages and only erroring in the "early" ones. - After this PR, it will always be an error to do sync IO anywhere other than after `io()`/`connection()`/uncached IO (i.e. outside the Dynamic stage). We're doing this mainly because with `partialPrefetching`, `cookies()` resolves in App Shells, and sync IO causes a severe deopt in any prerender. `allow-runtime` also opts a route into `partialPrefetching`, which means that even the segments above the runtime prefetch boundary will need an App Shell and can't tolerate Sync IO. This PR removes all early/late stage separation, because we no longer need to vary the Sync IO behavior on the segment. Instead, we now pick whether a stageController should use - `SyncIOMode.AllowedInRuntimeOrDynamic` (legacy) - `SyncIOMode.AllowedInDynamic` (`partialPrefetching`) - `SyncIOMode.Untracked` if it needs to opt out I've removed the existing tests that asserted that sync IO is allowed in segments above the `allow-runtime` boundary. Instead, we check that sync io either errors (if partialPrefetching is on) or is allowed (if partialPrefetching is off) Closes NAR-855
51 lines
1.7 KiB
TypeScript
51 lines
1.7 KiB
TypeScript
import { Instant } from 'next'
|
|
import { cookies } from 'next/headers'
|
|
import { Fragment, Suspense } from 'react'
|
|
|
|
export const instant: Instant = {
|
|
level: 'experimental-error',
|
|
unstable_samples: [{ cookies: [{ name: 'testCookie', value: 'testValue' }] }],
|
|
}
|
|
|
|
// cookies() is passed as a promise input to a public "use cache" function.
|
|
// The cache doesn't read the cookies in its body — they're only part of the cache key.
|
|
// After the cache resolves, Date.now() is sync IO that should error.
|
|
//
|
|
// This test validates that the cache input encoding resolves in the
|
|
// Runtime stage. Date.now() should either error or be allowed
|
|
// depending on if partialPrefetching is enabled or not.
|
|
|
|
async function cachedFn(cookiePromise: Promise<string>) {
|
|
'use cache'
|
|
// Intentionally not reading the cookie promise in the body.
|
|
// It's only used as part of the cache key via input encoding.
|
|
return 'cached result'
|
|
}
|
|
|
|
export default async function Page() {
|
|
return (
|
|
<main>
|
|
<p>
|
|
Page with sync IO after cache with cookie input:{' '}
|
|
<SuspenseIfNotPartialPrefetching>
|
|
<SyncIOAfterCache />
|
|
</SuspenseIfNotPartialPrefetching>
|
|
</p>
|
|
</main>
|
|
)
|
|
}
|
|
|
|
// Before partialPrefetching, cookies() is not allowed in shells,
|
|
// so it would fail instant validation unless wrapped in suspense.
|
|
// We're not interested in checking if cookies blocks,
|
|
// only in wheter the Sync IO after triggers an error.
|
|
const SuspenseIfNotPartialPrefetching = process.env.__NEXT_PARTIAL_PREFETCHING
|
|
? Fragment
|
|
: Suspense
|
|
|
|
async function SyncIOAfterCache() {
|
|
const cookiePromise = cookies().then((c) => c.get('testCookie')?.value ?? '')
|
|
await cachedFn(cookiePromise)
|
|
return Date.now()
|
|
}
|