@@ -173,6 +173,11 @@ import type { RouteModule } from './route-modules/route-module'
173173import { FallbackMode , parseFallbackField } from '../lib/fallback'
174174import { toResponseCacheEntry } from './response-cache/utils'
175175import { scheduleOnNextTick } from '../lib/scheduler'
176+ import { PrefetchCacheScopes } from './lib/prefetch-cache-scopes'
177+ import {
178+ runWithCacheScope ,
179+ type CacheScopeStore ,
180+ } from './async-storage/cache-scope'
176181
177182export type FindComponentsResult = {
178183 components : LoadComponentsReturnType
@@ -454,6 +459,14 @@ export default abstract class Server<
454459
455460 private readonly isAppPPREnabled : boolean
456461
462+ private readonly prefetchCacheScopesDev = new PrefetchCacheScopes ( )
463+
464+ /**
465+ * This is used to persist cache scopes across
466+ * prefetch -> full route requests for dynamic IO
467+ * it's only fully used in dev
468+ */
469+
457470 public constructor ( options : ServerOptions ) {
458471 const {
459472 dir = '.' ,
@@ -2082,6 +2095,11 @@ export default abstract class Server<
20822095 typeof query . __nextppronly !== 'undefined' &&
20832096 couldSupportPPR
20842097
2098+ // When enabled, this will allow the use of the `?__nextppronly` query
2099+ // to enable debugging of the fallback shell.
2100+ const hasDebugFallbackShellQuery =
2101+ hasDebugStaticShellQuery && query . __nextppronly === 'fallback'
2102+
20852103 // This page supports PPR if it is marked as being `PARTIALLY_STATIC` in the
20862104 // prerender manifest and this is an app page.
20872105 const isRoutePPREnabled : boolean =
@@ -2106,6 +2124,8 @@ export default abstract class Server<
21062124 const isDebugDynamicAccesses =
21072125 isDebugStaticShell && this . renderOpts . dev === true
21082126
2127+ const isDebugFallbackShell = hasDebugFallbackShellQuery && isRoutePPREnabled
2128+
21092129 // If we're in minimal mode, then try to get the postponed information from
21102130 // the request metadata. If available, use it for resuming the postponed
21112131 // render.
@@ -2741,7 +2761,7 @@ export default abstract class Server<
27412761 }
27422762 }
27432763
2744- const responseGenerator : ResponseGenerator = async ( {
2764+ let responseGenerator : ResponseGenerator = async ( {
27452765 hasResolved,
27462766 previousCacheEntry,
27472767 isRevalidating,
@@ -2974,7 +2994,8 @@ export default abstract class Server<
29742994 const fallbackRouteParams =
29752995 isDynamic &&
29762996 isRoutePPREnabled &&
2977- getRequestMeta ( req , 'didSetDefaultRouteMatches' )
2997+ ( getRequestMeta ( req , 'didSetDefaultRouteMatches' ) ||
2998+ isDebugFallbackShell )
29782999 ? getFallbackRouteParams ( pathname )
29793000 : null
29803001
@@ -2991,6 +3012,54 @@ export default abstract class Server<
29913012 }
29923013 }
29933014
3015+ if ( this . nextConfig . experimental . dynamicIO ) {
3016+ const originalResponseGenerator = responseGenerator
3017+
3018+ responseGenerator = async (
3019+ ...args : Parameters < typeof responseGenerator >
3020+ ) : ReturnType < typeof responseGenerator > => {
3021+ let cache : CacheScopeStore [ 'cache' ] | undefined
3022+
3023+ if ( this . renderOpts . dev ) {
3024+ cache = this . prefetchCacheScopesDev . get ( urlPathname )
3025+
3026+ // we need to seed the prefetch cache scope in dev
3027+ // since we did not have a prefetch cache available
3028+ // and this is not a prefetch request
3029+ if (
3030+ ! cache &&
3031+ ! isPrefetchRSCRequest &&
3032+ routeModule ?. definition . kind === RouteKind . APP_PAGE
3033+ ) {
3034+ req . headers [ RSC_HEADER ] = '1'
3035+ req . headers [ NEXT_ROUTER_PREFETCH_HEADER ] = '1'
3036+
3037+ cache = new Map ( )
3038+
3039+ await runWithCacheScope ( { cache } , ( ) =>
3040+ originalResponseGenerator ( ...args )
3041+ )
3042+ this . prefetchCacheScopesDev . set ( urlPathname , cache )
3043+
3044+ delete req . headers [ RSC_HEADER ]
3045+ delete req . headers [ NEXT_ROUTER_PREFETCH_HEADER ]
3046+ }
3047+ }
3048+
3049+ return runWithCacheScope ( { cache } , ( ) =>
3050+ originalResponseGenerator ( ...args )
3051+ ) . finally ( ( ) => {
3052+ if ( this . renderOpts . dev ) {
3053+ if ( isPrefetchRSCRequest ) {
3054+ this . prefetchCacheScopesDev . set ( urlPathname , cache )
3055+ } else {
3056+ this . prefetchCacheScopesDev . del ( urlPathname )
3057+ }
3058+ }
3059+ } )
3060+ }
3061+ }
3062+
29943063 const cacheEntry = await this . responseCache . get (
29953064 ssgCacheKey ,
29963065 responseGenerator ,
0 commit comments