vinext 0.1.7 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/dist/check.d.ts +18 -0
  2. package/dist/check.js +37 -17
  3. package/dist/cli.js +2 -2
  4. package/dist/config/next-config.d.ts +2 -1
  5. package/dist/config/next-config.js +3 -0
  6. package/dist/config/server-external-packages.d.ts +4 -0
  7. package/dist/config/server-external-packages.js +91 -0
  8. package/dist/deploy.d.ts +7 -0
  9. package/dist/deploy.js +25 -18
  10. package/dist/entries/app-rsc-entry.js +54 -10
  11. package/dist/entries/app-rsc-manifest.js +8 -0
  12. package/dist/entries/pages-client-entry.js +1 -0
  13. package/dist/entries/pages-server-entry.js +1 -0
  14. package/dist/index.js +46 -20
  15. package/dist/init.js +2 -1
  16. package/dist/plugins/middleware-server-only.d.ts +8 -6
  17. package/dist/plugins/middleware-server-only.js +8 -7
  18. package/dist/plugins/optimize-imports.js +1 -1
  19. package/dist/routing/app-route-graph.d.ts +6 -2
  20. package/dist/routing/app-route-graph.js +61 -13
  21. package/dist/routing/app-router.d.ts +5 -0
  22. package/dist/routing/app-router.js +5 -0
  23. package/dist/routing/file-matcher.d.ts +5 -0
  24. package/dist/routing/file-matcher.js +7 -1
  25. package/dist/server/app-browser-entry.js +42 -147
  26. package/dist/server/app-browser-history-controller.d.ts +2 -1
  27. package/dist/server/app-browser-history-controller.js +6 -2
  28. package/dist/server/app-browser-server-action-client.d.ts +32 -0
  29. package/dist/server/app-browser-server-action-client.js +128 -0
  30. package/dist/server/app-fallback-renderer.d.ts +3 -1
  31. package/dist/server/app-fallback-renderer.js +6 -2
  32. package/dist/server/app-page-boundary-render.d.ts +3 -0
  33. package/dist/server/app-page-boundary-render.js +14 -3
  34. package/dist/server/app-page-cache-finalizer.d.ts +1 -0
  35. package/dist/server/app-page-cache-finalizer.js +8 -2
  36. package/dist/server/app-page-dispatch.d.ts +33 -4
  37. package/dist/server/app-page-dispatch.js +62 -90
  38. package/dist/server/app-page-element-builder.d.ts +7 -1
  39. package/dist/server/app-page-element-builder.js +56 -19
  40. package/dist/server/app-page-head.d.ts +16 -1
  41. package/dist/server/app-page-head.js +44 -22
  42. package/dist/server/app-page-params.d.ts +2 -1
  43. package/dist/server/app-page-params.js +8 -1
  44. package/dist/server/app-page-ppr-runtime.d.ts +7 -0
  45. package/dist/server/app-page-ppr-runtime.js +70 -0
  46. package/dist/server/app-page-probe.d.ts +1 -0
  47. package/dist/server/app-page-probe.js +1 -1
  48. package/dist/server/app-page-render.d.ts +4 -1
  49. package/dist/server/app-page-render.js +8 -3
  50. package/dist/server/app-page-request.d.ts +8 -1
  51. package/dist/server/app-page-request.js +23 -11
  52. package/dist/server/app-page-route-wiring.d.ts +6 -1
  53. package/dist/server/app-page-route-wiring.js +30 -8
  54. package/dist/server/app-page-search-params-observation.d.ts +4 -2
  55. package/dist/server/app-page-search-params-observation.js +11 -7
  56. package/dist/server/app-ppr-fallback-shell-render.d.ts +4 -3
  57. package/dist/server/app-route-handler-dispatch.js +1 -0
  58. package/dist/server/app-route-handler-execution.js +2 -1
  59. package/dist/server/app-route-module-loader.d.ts +2 -0
  60. package/dist/server/app-route-module-loader.js +1 -0
  61. package/dist/server/app-router-entry.js +7 -6
  62. package/dist/server/app-rsc-errors.js +7 -1
  63. package/dist/server/app-rsc-handler.d.ts +2 -1
  64. package/dist/server/app-rsc-handler.js +6 -9
  65. package/dist/server/app-rsc-route-matching.d.ts +7 -0
  66. package/dist/server/app-rsc-route-matching.js +36 -3
  67. package/dist/server/app-segment-config.d.ts +1 -0
  68. package/dist/server/app-segment-config.js +32 -2
  69. package/dist/server/app-server-action-execution.d.ts +4 -0
  70. package/dist/server/app-server-action-execution.js +41 -10
  71. package/dist/server/app-ssr-entry.js +4 -1
  72. package/dist/server/app-static-generation.d.ts +1 -0
  73. package/dist/server/app-static-generation.js +1 -0
  74. package/dist/server/headers.d.ts +3 -1
  75. package/dist/server/headers.js +3 -1
  76. package/dist/server/prod-server.js +15 -6
  77. package/dist/server/worker-utils.d.ts +2 -1
  78. package/dist/server/worker-utils.js +7 -1
  79. package/dist/shims/error-boundary.d.ts +19 -1
  80. package/dist/shims/error-boundary.js +11 -1
  81. package/dist/shims/headers.d.ts +3 -1
  82. package/dist/shims/headers.js +16 -5
  83. package/dist/shims/link.js +16 -12
  84. package/dist/shims/metadata.d.ts +3 -2
  85. package/dist/shims/metadata.js +8 -4
  86. package/dist/shims/router.js +13 -2
  87. package/dist/typegen.js +6 -5
  88. package/dist/utils/path.d.ts +2 -1
  89. package/dist/utils/path.js +1 -1
  90. package/dist/utils/project.d.ts +4 -0
  91. package/dist/utils/project.js +5 -1
  92. package/package.json +7 -3
@@ -3,8 +3,7 @@ import { getRequestExecutionContext } from "../shims/request-context.js";
3
3
  import { AppElementsWire } from "./app-elements-wire.js";
4
4
  import { shouldSuppressLoadingBoundaries } from "./app-rsc-render-mode.js";
5
5
  import "./app-elements.js";
6
- import { beginPprFallbackShellFinalRender, createPprFallbackShellState, getPprFallbackShellState, runWithPprFallbackShellState } from "../shims/ppr-fallback-shell.js";
7
- import { consumeDynamicUsage, consumeInvalidDynamicUsageError, getAndClearPendingCookies, getDraftModeCookieHeader, isDraftModeRequest, markDynamicUsage, peekRenderRequestApiUsage, setHeadersContext } from "../shims/headers.js";
6
+ import { consumeDynamicUsage, consumeInvalidDynamicUsageError, getAndClearPendingCookies, getDraftModeCookieHeader, getHeadersContext, isDraftModeRequest, markDynamicUsage, peekDynamicUsage, peekRenderRequestApiUsage, setHeadersContext } from "../shims/headers.js";
8
7
  import { _consumeRequestScopedCacheLife, _peekRequestScopedCacheLife } from "../shims/cache-request-state.js";
9
8
  import { ensureFetchPatch, getCollectedFetchTags, peekDynamicFetchObservations, runWithFetchDedupe, setCurrentFetchCacheMode, setCurrentFetchSoftTags, setCurrentForceDynamicFetchDefault } from "../shims/fetch-cache.js";
10
9
  import { VINEXT_RSC_CONTENT_TYPE, VINEXT_RSC_VARY_HEADER, applyRscCompatibilityIdHeader } from "./app-rsc-cache-busting.js";
@@ -108,6 +107,7 @@ async function runAppPageRevalidationContext(options, renderFn) {
108
107
  const { createStaticGenerationHeadersContext } = await import("./app-static-generation.js");
109
108
  return runWithRequestContext(createRequestContext({
110
109
  headersContext: createStaticGenerationHeadersContext({
110
+ draftModeEnabled: false,
111
111
  draftModeSecret: options.draftModeSecret,
112
112
  dynamicConfig: options.dynamicConfig,
113
113
  routeKind: "page",
@@ -132,6 +132,8 @@ function toInterceptOptions(interceptionContext, intercept) {
132
132
  return {
133
133
  interceptionContext,
134
134
  interceptLayouts: intercept.interceptLayouts,
135
+ interceptLayoutSegments: intercept.interceptLayoutSegments,
136
+ interceptBranchSegments: intercept.interceptBranchSegments,
135
137
  interceptPage: intercept.page,
136
138
  interceptParams: intercept.matchedParams,
137
139
  interceptSlotId: intercept.slotId ?? null,
@@ -140,75 +142,10 @@ function toInterceptOptions(interceptionContext, intercept) {
140
142
  interceptSourcePageSegments: intercept.sourcePageSegments ?? null
141
143
  };
142
144
  }
143
- /**
144
- * Request-phase fallback-shell gate. Callers must run the exact cache read and
145
- * static-param validation before this classification step.
146
- */
147
- function classifyPprFallbackShellEligibility(options, currentRevalidateSeconds, isDraftMode, isForceStatic, isForceDynamic) {
148
- if (options.renderedConcreteUrlPaths?.has(options.cleanPathname) === true) return { kind: "skip-known-pregenerated-route" };
149
- const fallbackShells = options.pprFallbackCacheShells;
150
- if (!fallbackShells || fallbackShells.length === 0) return { kind: "skip-no-fallback-shells" };
151
- if (options.isRscRequest) return { kind: "skip-rsc-request" };
152
- if (options.request.method !== "GET") return { kind: "skip-non-get" };
153
- if (!isForceStatic && hasSearchParams(options.searchParams)) return { kind: "skip-search-params" };
154
- if (!shouldReadAppPageCache({
155
- isDraftMode,
156
- isForceDynamic,
157
- isProgressiveActionRender: options.isProgressiveActionRender === true,
158
- isProduction: options.isProduction,
159
- isRscRequest: false,
160
- revalidateSeconds: currentRevalidateSeconds,
161
- scriptNonce: options.scriptNonce
162
- })) return { kind: "skip-cache-disabled" };
163
- return {
164
- kind: "probe-fallback-shells",
165
- fallbackShells
166
- };
167
- }
168
- async function probePprFallbackShellCache(options, route, fallbackShells, currentRevalidateSeconds) {
169
- const { readAppPageFallbackShellCacheResponse } = await import("./app-page-cache.js");
170
- const { rewriteAppPprFallbackShellHtmlNavigation } = await import("./app-ppr-fallback-shell.js");
171
- for (const fallbackShell of fallbackShells) {
172
- const fallbackShellResponse = await readAppPageFallbackShellCacheResponse({
173
- clearRequestContext: options.clearRequestContext,
174
- expireSeconds: options.expireSeconds,
175
- fallbackPathname: fallbackShell.pathname,
176
- isEdgeRuntime: options.isEdgeRuntime,
177
- isrDebug: options.isrDebug,
178
- isrGet: options.isrGet,
179
- isrHtmlKey: options.isrHtmlKey,
180
- middlewareHeaders: options.middlewareContext.headers,
181
- middlewareStatus: options.middlewareContext.status,
182
- revalidateSeconds: currentRevalidateSeconds ?? 0,
183
- rewriteHtml(html) {
184
- return rewriteAppPprFallbackShellHtmlNavigation({
185
- html,
186
- params: options.params,
187
- pathname: options.cleanPathname,
188
- searchParams: options.searchParams
189
- });
190
- }
191
- });
192
- if (fallbackShellResponse) return fallbackShellResponse;
193
- }
194
- return null;
195
- }
196
- async function tryServePprFallbackShell(options, route, currentRevalidateSeconds, isDraftMode, isForceStatic, isForceDynamic) {
197
- const decision = classifyPprFallbackShellEligibility(options, currentRevalidateSeconds, isDraftMode, isForceStatic, isForceDynamic);
198
- switch (decision.kind) {
199
- case "skip-known-pregenerated-route":
200
- case "skip-no-fallback-shells":
201
- case "skip-rsc-request":
202
- case "skip-non-get":
203
- case "skip-search-params":
204
- case "skip-cache-disabled": return null;
205
- case "probe-fallback-shells": return await probePprFallbackShellCache(options, route, decision.fallbackShells, currentRevalidateSeconds);
206
- }
207
- }
208
145
  async function dispatchAppPage(options) {
209
146
  const dispatch = () => runWithFetchDedupe(() => dispatchAppPageInner(options));
210
- if (!options.pprFallbackShell) return await dispatch();
211
- return await runWithPprFallbackShellState(createPprFallbackShellState(options.pprFallbackShell), dispatch);
147
+ if (!options.pprFallbackShell || !options.pprRuntime) return await dispatch();
148
+ return await options.pprRuntime.run(options.pprFallbackShell, dispatch);
212
149
  }
213
150
  async function dispatchAppPageInner(options) {
214
151
  const route = options.route;
@@ -218,7 +155,11 @@ async function dispatchAppPageInner(options) {
218
155
  const isDynamicError = dynamicConfig === "error";
219
156
  const isForceDynamic = dynamicConfig === "force-dynamic";
220
157
  const isDraftMode = isDraftModeRequest(options.request, options.draftModeSecret);
158
+ const requestHeadersContext = getHeadersContext();
159
+ const hasRequestSearchParams = !isForceStatic && hasSearchParams(options.searchParams);
160
+ const pageSearchParams = isForceStatic ? new URLSearchParams() : options.searchParams;
221
161
  const layoutParamAccess = createAppLayoutParamAccessTracker();
162
+ const hasActiveLoadingBoundary = shouldSuppressLoadingBoundaries(options.renderMode ?? "navigation") ? false : Boolean(route.loading?.default);
222
163
  setCurrentFetchSoftTags(buildAppPageTags(options.cleanPathname, [], route.routeSegments));
223
164
  setCurrentFetchCacheMode(options.fetchCache ?? null);
224
165
  setCurrentForceDynamicFetchDefault(isForceDynamic);
@@ -238,9 +179,10 @@ async function dispatchAppPageInner(options) {
238
179
  options.clearRequestContext();
239
180
  return methodResponse;
240
181
  }
241
- if ((isForceStatic || isDynamicError) && !isDraftMode) {
182
+ if (isForceStatic || isDynamicError) {
242
183
  const { createStaticGenerationHeadersContext } = await import("./app-static-generation.js");
243
184
  setHeadersContext(createStaticGenerationHeadersContext({
185
+ draftModeEnabled: isDraftMode,
244
186
  draftModeSecret: options.draftModeSecret,
245
187
  dynamicConfig,
246
188
  routeKind: "page",
@@ -266,7 +208,7 @@ async function dispatchAppPageInner(options) {
266
208
  const cachedPageResponse = await readAppPageCacheResponse({
267
209
  cleanPathname: options.cleanPathname,
268
210
  clearRequestContext: options.clearRequestContext,
269
- hasRequestSearchParams: !isForceStatic && hasSearchParams(options.searchParams),
211
+ hasRequestSearchParams,
270
212
  isEdgeRuntime: options.isEdgeRuntime,
271
213
  isRscRequest: options.isRscRequest,
272
214
  isrDebug: options.isrDebug,
@@ -300,19 +242,23 @@ async function dispatchAppPageInner(options) {
300
242
  });
301
243
  revalidationTarget.navigationParams = resolveAppPageNavigationParams(revalidationTarget.route, revalidationTarget.navigationParams, options.cleanPathname, revalidationTarget.interceptOpts);
302
244
  await options.ensureRouteLoaded?.(revalidationTarget.route);
245
+ const revalidationDynamicConfig = options.resolveRouteDynamicConfig?.(revalidationTarget.route) ?? (revalidationTarget.route === route ? dynamicConfig : void 0);
303
246
  return runAppPageRevalidationContext({
304
247
  cleanPathname: options.cleanPathname,
305
248
  displayPathname: options.displayPathname,
306
249
  currentFetchCacheMode: options.resolveRouteFetchCacheMode?.(revalidationTarget.route) ?? (revalidationTarget.route === route ? options.fetchCache ?? null : null),
307
250
  draftModeSecret: options.draftModeSecret,
308
- dynamicConfig: options.resolveRouteDynamicConfig?.(revalidationTarget.route) ?? (revalidationTarget.route === route ? dynamicConfig : void 0),
251
+ dynamicConfig: revalidationDynamicConfig,
309
252
  params: revalidationTarget.navigationParams,
310
253
  routePattern: revalidationTarget.route.pattern,
311
254
  routeSegments: revalidationTarget.route.routeSegments,
312
255
  setNavigationContext: options.setNavigationContext
313
256
  }, async () => {
314
257
  const { renderAppPageCacheArtifacts } = await import("./app-page-cache-render.js");
315
- const revalidatedElement = await options.buildPageElement(revalidationTarget.route, revalidationTarget.params, revalidationTarget.interceptOpts, new URLSearchParams());
258
+ const revalidatedElement = await options.buildPageElement(revalidationTarget.route, revalidationTarget.params, revalidationTarget.interceptOpts, new URLSearchParams(), void 0, {
259
+ observeMetadataSearchParamsAccess: revalidationDynamicConfig !== "force-static",
260
+ observePageSearchParamsAccess: revalidationDynamicConfig !== "force-static"
261
+ });
316
262
  const revalidatedOnError = options.createRscOnErrorHandler(options.cleanPathname, revalidationTarget.route.pattern);
317
263
  const rendered = await renderAppPageCacheArtifacts({
318
264
  basePath: options.basePath,
@@ -365,14 +311,29 @@ async function dispatchAppPageInner(options) {
365
311
  });
366
312
  if (dynamicParamsResponse) return dynamicParamsResponse;
367
313
  }
368
- const fallbackShellResponse = await tryServePprFallbackShell(options, route, currentRevalidateSeconds, isDraftMode, isForceStatic, isForceDynamic);
314
+ const fallbackShellResponse = options.pprRuntime ? await options.pprRuntime.tryServe(options, currentRevalidateSeconds, isDraftMode, isForceStatic, isForceDynamic) : null;
369
315
  if (fallbackShellResponse) return fallbackShellResponse;
316
+ let interceptDynamicConfig;
317
+ let interceptDynamicConfigResolved = false;
370
318
  const interceptResult = await resolveAppPageIntercept({
371
319
  async buildPageElement(interceptRoute, interceptParams, interceptOpts, interceptSearchParams, interceptLayoutParamAccess) {
372
- await options.ensureRouteLoaded?.(interceptRoute);
320
+ const sourceDynamicConfig = interceptDynamicConfigResolved ? interceptDynamicConfig : options.resolveRouteDynamicConfig?.(interceptRoute);
321
+ if (sourceDynamicConfig === "force-static" || sourceDynamicConfig === "error") {
322
+ const { createStaticGenerationHeadersContext } = await import("./app-static-generation.js");
323
+ setHeadersContext(createStaticGenerationHeadersContext({
324
+ draftModeEnabled: isDraftMode,
325
+ draftModeSecret: options.draftModeSecret,
326
+ dynamicConfig: sourceDynamicConfig,
327
+ routeKind: "page",
328
+ routePattern: interceptRoute.pattern
329
+ }));
330
+ } else setHeadersContext(requestHeadersContext);
373
331
  setCurrentFetchCacheMode(options.resolveRouteFetchCacheMode?.(interceptRoute) ?? null);
374
- setCurrentForceDynamicFetchDefault(options.resolveRouteDynamicConfig?.(interceptRoute) === "force-dynamic");
375
- return options.buildPageElement(interceptRoute, interceptParams, interceptOpts, interceptSearchParams, interceptLayoutParamAccess);
332
+ setCurrentForceDynamicFetchDefault(sourceDynamicConfig === "force-dynamic");
333
+ return options.buildPageElement(interceptRoute, interceptParams, interceptOpts, interceptSearchParams, interceptLayoutParamAccess, {
334
+ observeMetadataSearchParamsAccess: sourceDynamicConfig !== "force-static",
335
+ observePageSearchParamsAccess: sourceDynamicConfig !== "force-static"
336
+ });
376
337
  },
377
338
  cleanPathname: options.cleanPathname,
378
339
  currentRoute: route,
@@ -404,6 +365,12 @@ async function dispatchAppPageInner(options) {
404
365
  headers: interceptHeaders
405
366
  });
406
367
  },
368
+ async resolveSearchParams(sourceRoute, searchParams) {
369
+ await options.ensureRouteLoaded?.(sourceRoute);
370
+ interceptDynamicConfig = options.resolveRouteDynamicConfig?.(sourceRoute);
371
+ interceptDynamicConfigResolved = true;
372
+ return interceptDynamicConfig === "force-static" ? new URLSearchParams() : searchParams;
373
+ },
407
374
  searchParams: options.searchParams,
408
375
  setNavigationContext: options.setNavigationContext,
409
376
  toInterceptOpts(intercept) {
@@ -414,12 +381,15 @@ async function dispatchAppPageInner(options) {
414
381
  const buildCurrentPageElement = () => buildAppPageElement({
415
382
  buildPageElement() {
416
383
  if (options.actionFailed) throw options.actionError;
417
- return options.buildPageElement(route, options.params, interceptResult.interceptOpts, options.searchParams, layoutParamAccess);
384
+ return options.buildPageElement(route, options.params, interceptResult.interceptOpts, pageSearchParams, layoutParamAccess, {
385
+ observeMetadataSearchParamsAccess: !isForceStatic,
386
+ observePageSearchParamsAccess: !isForceStatic
387
+ });
418
388
  },
419
389
  async probePageSpecialError() {
420
390
  if (!shouldSuppressLoadingBoundaries(options.renderMode ?? "navigation") && route.loading?.default) return null;
421
391
  return resolveAppPageSpecialError(await probeAppPageThrownError({
422
- probePage: options.probePage,
392
+ probePage: () => options.probePage(pageSearchParams),
423
393
  runWithSuppressedHookWarning(probe) {
424
394
  return options.runWithSuppressedHookWarning(probe);
425
395
  }
@@ -433,12 +403,11 @@ async function dispatchAppPageInner(options) {
433
403
  },
434
404
  resolveSpecialError: resolveAppPageSpecialError
435
405
  });
436
- const fallbackShellState = getPprFallbackShellState();
406
+ const fallbackShellState = options.pprRuntime?.getState() ?? null;
437
407
  if (fallbackShellState && process.env.VINEXT_PRERENDER === "1" && !options.isRscRequest) {
438
408
  const warmupBuildResult = await buildCurrentPageElement();
439
409
  if (warmupBuildResult.response) return warmupBuildResult.response;
440
- const { warmPprFallbackShellCaches } = await import("./app-ppr-fallback-shell-render.js");
441
- await warmPprFallbackShellCaches({
410
+ await options.pprRuntime.warm({
442
411
  element: warmupBuildResult.element,
443
412
  onError: options.createRscOnErrorHandler(options.cleanPathname, route.pattern),
444
413
  renderToReadableStream: options.renderToReadableStream,
@@ -451,11 +420,11 @@ async function dispatchAppPageInner(options) {
451
420
  const navigationParams = resolveAppPageNavigationParams(route, options.params, options.cleanPathname, interceptResult.interceptOpts);
452
421
  options.setNavigationContext({
453
422
  pathname: options.displayPathname ?? options.cleanPathname,
454
- searchParams: options.searchParams,
423
+ searchParams: pageSearchParams,
455
424
  params: navigationParams
456
425
  });
457
426
  const layoutClassifications = getEffectiveLayoutClassifications(route, options.debugClassification);
458
- const activeFallbackShellState = getPprFallbackShellState();
427
+ const activeFallbackShellState = options.pprRuntime?.getState() ?? null;
459
428
  const pprFallbackShellSignal = activeFallbackShellState?.abortController.signal;
460
429
  const pprFallbackShellReactSignal = activeFallbackShellState?.reactAbortController.signal;
461
430
  return renderAppPageLifecycle({
@@ -465,6 +434,7 @@ async function dispatchAppPageInner(options) {
465
434
  cleanPathname: options.cleanPathname,
466
435
  clearRequestContext: options.clearRequestContext,
467
436
  consumeDynamicUsage,
437
+ peekDynamicUsage,
468
438
  consumeInvalidDynamicUsageError,
469
439
  consumeRenderObservationState: consumeAppPageRenderObservationState,
470
440
  createRscOnErrorHandler(pathname, routePath) {
@@ -487,7 +457,8 @@ async function dispatchAppPageInner(options) {
487
457
  return _peekRequestScopedCacheLife();
488
458
  },
489
459
  handlerStart: options.handlerStart,
490
- hasLoadingBoundary: shouldSuppressLoadingBoundaries(options.renderMode ?? "navigation") ? false : Boolean(route.loading?.default),
460
+ hasLoadingBoundary: hasActiveLoadingBoundary,
461
+ omitPendingDynamicCacheState: !options.isRscRequest && hasRequestSearchParams,
491
462
  formState: options.formState ?? null,
492
463
  isProgressiveActionRender: options.isProgressiveActionRender === true,
493
464
  isDynamicError,
@@ -512,7 +483,7 @@ async function dispatchAppPageInner(options) {
512
483
  pprFallbackShellSignal,
513
484
  pprFallbackShellReactSignal,
514
485
  abortPprFallbackShell: activeFallbackShellState ? () => {
515
- beginPprFallbackShellFinalRender(activeFallbackShellState);
486
+ options.pprRuntime.beginFinalRender(activeFallbackShellState);
516
487
  } : void 0,
517
488
  layoutParamAccess,
518
489
  rootParams: options.rootParams,
@@ -526,8 +497,9 @@ async function dispatchAppPageInner(options) {
526
497
  return options.probeLayoutAt(layoutIndex, layoutParamAccess);
527
498
  },
528
499
  probePage() {
529
- return options.probePage();
500
+ return options.probePage(pageSearchParams);
530
501
  },
502
+ probePageBeforeRender: options.isRscRequest,
531
503
  classification: {
532
504
  getLayoutId(index) {
533
505
  const treePosition = route.layoutTreePositions?.[index] ?? 0;
@@ -556,8 +528,8 @@ async function dispatchAppPageInner(options) {
556
528
  revalidateSeconds: currentRevalidateSeconds,
557
529
  mountedSlotsHeader: options.mountedSlotsHeader,
558
530
  renderMode: options.renderMode ?? "navigation",
559
- renderErrorBoundaryResponse(renderError) {
560
- return options.renderErrorBoundaryPage(renderError);
531
+ renderErrorBoundaryResponse(renderError, errorOrigin) {
532
+ return options.renderErrorBoundaryPage(renderError, errorOrigin);
561
533
  },
562
534
  renderLayoutSpecialError(specialError, layoutIndex) {
563
535
  return renderLayoutSpecialError(options, specialError, layoutIndex);
@@ -674,4 +646,4 @@ async function renderPageSpecialError(options, specialError) {
674
646
  });
675
647
  }
676
648
  //#endregion
677
- export { dispatchAppPage };
649
+ export { dispatchAppPage, hasSearchParams, shouldReadAppPageCache };
@@ -1,6 +1,7 @@
1
1
  import { MetadataFileRoute } from "./metadata-routes.js";
2
2
  import { AppElements } from "./app-elements-wire.js";
3
3
  import { AppPageParams } from "./app-page-boundary.js";
4
+ import { ApplyAppPageFileBasedMetadata } from "./app-page-head.js";
4
5
  import { AppLayoutParamAccessTracker } from "./app-layout-param-observation.js";
5
6
  import { AppRscRenderMode } from "./app-rsc-render-mode.js";
6
7
  import { AppPageErrorModule, AppPageModule, AppPageRouteWiringRoute } from "./app-page-route-wiring.js";
@@ -19,6 +20,8 @@ type AppPageBuildRoute<TModule extends AppPageModule = AppPageModule, TErrorModu
19
20
  type AppPageInterceptOptions<TModule extends AppPageModule = AppPageModule> = {
20
21
  interceptionContext?: string | null;
21
22
  interceptLayouts?: readonly (TModule | null | undefined)[] | null;
23
+ interceptLayoutSegments?: readonly (readonly string[])[] | null;
24
+ interceptBranchSegments?: readonly string[] | null;
22
25
  interceptPage?: TModule | null;
23
26
  interceptParams?: AppPageParams | null;
24
27
  interceptSlotId?: string | null;
@@ -32,9 +35,12 @@ type AppPagePageRequest<TModule extends AppPageModule = AppPageModule> = {
32
35
  isRscRequest: boolean; /** The incoming HTTP request (available but unused by this module). */
33
36
  request: Request; /** Normalized x-vinext-mounted-slots header value. */
34
37
  mountedSlotsHeader: string | null; /** Semantic RSC payload mode for this page render. */
35
- renderMode?: AppRscRenderMode;
38
+ renderMode?: AppRscRenderMode; /** Observe page `searchParams` access for cache-safety classification. */
39
+ observePageSearchParamsAccess?: boolean; /** Observe page metadata `searchParams` access for cache-safety classification. */
40
+ observeMetadataSearchParamsAccess?: boolean;
36
41
  };
37
42
  type BuildPageElementsOptions<TModule extends AppPageModule = AppPageModule, TErrorModule extends AppPageErrorModule = AppPageErrorModule> = {
43
+ applyFileBasedMetadata?: ApplyAppPageFileBasedMetadata;
38
44
  route: AppPageBuildRoute<TModule, TErrorModule>;
39
45
  params: AppPageParams;
40
46
  routePath: string;
@@ -1,17 +1,27 @@
1
1
  import { matchRoutePattern } from "../routing/route-pattern.js";
2
2
  import { AppElementsWire } from "./app-elements-wire.js";
3
- import { APP_RSC_RENDER_MODE_NAVIGATION, shouldSuppressLoadingBoundaries } from "./app-rsc-render-mode.js";
3
+ import { APP_RSC_RENDER_MODE_NAVIGATION } from "./app-rsc-render-mode.js";
4
4
  import "./app-elements.js";
5
5
  import { createAppPageRenderIdentity } from "./app-page-render-identity.js";
6
6
  import { makeThenableParams } from "../shims/thenable-params.js";
7
+ import { resolveAppPageBranchParams } from "./app-page-params.js";
7
8
  import { resolveActiveParallelRouteHeadInputs, resolveAppPageHead } from "./app-page-head.js";
8
- import { makeObservedAppPageSearchParamsThenable } from "./app-page-search-params-observation.js";
9
+ import { createAppPageSearchParamsObserver, makeObservedAppPageSearchParamsThenable } from "./app-page-search-params-observation.js";
9
10
  import { buildAppPageElements, createAppPageSourcePage, createAppPageTreePath } from "./app-page-route-wiring.js";
10
11
  import { DEFAULT_GLOBAL_ERROR_MODULE } from "./default-global-error-module.js";
11
12
  import "./app-rsc-route-matching.js";
12
13
  import { shouldServeStreamingMetadata } from "./streaming-metadata.js";
13
14
  import { createElement } from "react";
14
15
  //#region src/server/app-page-element-builder.ts
16
+ function resolveInterceptLayoutParams(branchSegments, layoutSegments, params) {
17
+ return resolveAppPageBranchParams(branchSegments, layoutSegments.length, params, layoutSegments);
18
+ }
19
+ const REACT_CLIENT_REFERENCE = Symbol.for("react.client.reference");
20
+ function isReactOwnedPageComponent(component) {
21
+ if (typeof component !== "function") return true;
22
+ const candidate = component;
23
+ return candidate.$$typeof === REACT_CLIENT_REFERENCE || candidate.prototype?.isReactComponent != null;
24
+ }
15
25
  /**
16
26
  * Build the App Router element tree for a matched route.
17
27
  *
@@ -30,7 +40,8 @@ import { createElement } from "react";
30
40
  */
31
41
  async function buildPageElements(options) {
32
42
  const { route, params, routePath, displayPathname = routePath, pageRequest, globalErrorModule, rootNotFoundModule, rootForbiddenModule, rootUnauthorizedModule, metadataRoutes } = options;
33
- const { opts, searchParams, isRscRequest, mountedSlotsHeader, renderMode = APP_RSC_RENDER_MODE_NAVIGATION } = pageRequest;
43
+ const slotParamOverrides = resolveSlotParamOverrides(route, routePath);
44
+ const { opts, searchParams, isRscRequest, mountedSlotsHeader, renderMode = APP_RSC_RENDER_MODE_NAVIGATION, observeMetadataSearchParamsAccess = false, observePageSearchParamsAccess = false } = pageRequest;
34
45
  const pageModule = route.page;
35
46
  const isSiblingIntercept = opts?.interceptSlotKey === "__vinext_page_intercept" && !!opts?.interceptPage;
36
47
  const effectivePageModule = isSiblingIntercept ? opts.interceptPage : pageModule;
@@ -64,44 +75,68 @@ async function buildPageElements(options) {
64
75
  };
65
76
  }
66
77
  const { hasDynamicMetadata, metadata: resolvedMetadata, pageSearchParams, viewport: resolvedViewport } = await resolveAppPageHead({
78
+ applyFileBasedMetadata: options.applyFileBasedMetadata,
67
79
  basePath: options.basePath ?? "",
68
80
  layoutModules: route.layouts,
69
81
  layoutTreePositions: route.layoutTreePositions,
70
82
  metadataRoutes,
71
- pageModule: effectivePageModule ?? null,
72
- parallelRoutes: resolveActiveParallelRouteHeadInputs({
83
+ pageModule: isSiblingIntercept ? null : effectivePageModule ?? null,
84
+ parallelRoutes: [...resolveActiveParallelRouteHeadInputs({
85
+ interceptBranchSegments: opts?.interceptBranchSegments ?? null,
73
86
  interceptLayouts: opts?.interceptLayouts ?? null,
87
+ interceptLayoutSegments: opts?.interceptLayoutSegments ?? null,
74
88
  interceptPage: opts?.interceptPage ?? null,
75
89
  interceptParams: opts?.interceptParams ?? null,
76
90
  interceptSlotKey: opts?.interceptSlotKey ?? null,
91
+ layoutTreePositions: route.layoutTreePositions,
77
92
  params,
78
93
  routeSegments: route.routeSegments ?? [],
94
+ slotParams: slotParamOverrides,
79
95
  slots: route.slots ?? null
80
- }),
96
+ }), ...isSiblingIntercept ? [{
97
+ layoutModules: opts?.interceptLayouts ?? [],
98
+ layoutParams: (opts?.interceptLayoutSegments ?? []).map((segments) => resolveInterceptLayoutParams(opts?.interceptBranchSegments ?? segments, segments, effectiveParams)),
99
+ pageModule: effectivePageModule ?? null,
100
+ params: effectiveParams,
101
+ routeSegments: opts?.interceptSourcePageSegments ?? route.routeSegments ?? []
102
+ }] : []],
81
103
  params: effectiveParams,
82
104
  routePath: route.pattern,
83
105
  routeSegments: route.routeSegments ?? null,
84
- searchParams
106
+ searchParams,
107
+ searchParamsObserver: observeMetadataSearchParamsAccess ? createAppPageSearchParamsObserver() : void 0
85
108
  });
86
109
  const pageProps = { params: makeThenableParams(effectiveParams) };
87
- let pageSearchParamsThenable;
88
- if (searchParams) {
89
- pageSearchParamsThenable = !shouldSuppressLoadingBoundaries(renderMode) && Boolean(route.loading?.default) ? makeObservedAppPageSearchParamsThenable(pageSearchParams) : makeThenableParams(pageSearchParams);
90
- pageProps.searchParams = pageSearchParamsThenable;
91
- }
110
+ const hasRequestSearchParams = Object.keys(pageSearchParams).length > 0;
111
+ const createPageElement = (PageComponent, props) => {
112
+ if (isReactOwnedPageComponent(PageComponent)) {
113
+ const invocationProps = { ...props };
114
+ if (searchParams) invocationProps.searchParams = observePageSearchParamsAccess ? makeObservedAppPageSearchParamsThenable(pageSearchParams, { markDynamic: hasRequestSearchParams }) : makeThenableParams(pageSearchParams);
115
+ return createElement(PageComponent, invocationProps);
116
+ }
117
+ const ServerPageComponent = PageComponent;
118
+ const PageInvoker = () => {
119
+ const invocationProps = { ...props };
120
+ if (searchParams) invocationProps.searchParams = observePageSearchParamsAccess ? makeObservedAppPageSearchParamsThenable(pageSearchParams) : makeThenableParams(pageSearchParams);
121
+ return ServerPageComponent(invocationProps);
122
+ };
123
+ return createElement(PageInvoker);
124
+ };
125
+ const pageSearchParamsThenable = searchParams ? makeThenableParams(pageSearchParams) : void 0;
92
126
  const mountedSlotIds = mountedSlotsHeader ? new Set(mountedSlotsHeader.split(" ")) : null;
93
127
  const slotOverrides = buildSlotOverrides(route, params, routePath, opts);
94
128
  const metadataPlacement = hasDynamicMetadata && shouldServeStreamingMetadata(pageRequest.request.headers.get("user-agent") ?? "", options.htmlLimitedBots) ? "body" : "head";
95
- let siblingInterceptElement = isSiblingIntercept && EffectivePageComponent ? createElement(EffectivePageComponent, pageProps) : null;
96
- if (isSiblingIntercept && siblingInterceptElement !== null && opts?.interceptLayouts?.length) {
97
- const siblingThenableParams = makeThenableParams(effectiveParams);
98
- for (let i = opts.interceptLayouts.length - 1; i >= 0; i--) {
99
- const LayoutComponent = opts.interceptLayouts[i]?.default;
100
- if (LayoutComponent) siblingInterceptElement = createElement(LayoutComponent, { params: siblingThenableParams }, siblingInterceptElement);
129
+ let siblingInterceptElement = isSiblingIntercept && EffectivePageComponent ? createPageElement(EffectivePageComponent, pageProps) : null;
130
+ if (isSiblingIntercept && siblingInterceptElement !== null && opts?.interceptLayouts?.length) for (let i = opts.interceptLayouts.length - 1; i >= 0; i--) {
131
+ const LayoutComponent = opts.interceptLayouts[i]?.default;
132
+ if (LayoutComponent) {
133
+ const interceptLayoutSegments = opts.interceptLayoutSegments?.[i] ?? [];
134
+ siblingInterceptElement = createElement(LayoutComponent, { params: makeThenableParams(resolveInterceptLayoutParams(opts.interceptBranchSegments ?? interceptLayoutSegments, interceptLayoutSegments, effectiveParams)) }, siblingInterceptElement);
101
135
  }
102
136
  }
103
137
  return buildAppPageElements({
104
- element: isSiblingIntercept ? siblingInterceptElement : EffectivePageComponent ? createElement(EffectivePageComponent, pageProps) : null,
138
+ element: isSiblingIntercept ? siblingInterceptElement : EffectivePageComponent ? createPageElement(EffectivePageComponent, pageProps) : null,
139
+ createPageElement,
105
140
  globalErrorModule: globalErrorModule ?? DEFAULT_GLOBAL_ERROR_MODULE,
106
141
  isRscRequest,
107
142
  layoutParamAccess: options.layoutParamAccess,
@@ -142,7 +177,9 @@ async function buildPageElements(options) {
142
177
  function buildSlotOverrides(route, routeParams, routePath, opts) {
143
178
  const overrides = {};
144
179
  if (opts && opts.interceptSlotKey && opts.interceptPage && opts.interceptSlotKey !== "__vinext_page_intercept") overrides[opts.interceptSlotKey] = {
180
+ branchSegments: opts.interceptBranchSegments ?? null,
145
181
  layoutModules: opts.interceptLayouts || null,
182
+ layoutSegments: opts.interceptLayoutSegments ?? null,
146
183
  pageModule: opts.interceptPage,
147
184
  params: opts.interceptParams || routeParams
148
185
  };
@@ -1,31 +1,45 @@
1
1
  import { MetadataFileRoute } from "./metadata-routes.js";
2
2
  import { AppPageParams } from "./app-page-boundary.js";
3
+ import { ThenableParamsObserver } from "../shims/thenable-params.js";
3
4
  import { Metadata, Viewport } from "../shims/metadata.js";
5
+ import { applyFileBasedMetadata } from "./file-based-metadata.js";
4
6
 
5
7
  //#region src/server/app-page-head.d.ts
6
8
  type AppPageSearchParams = Record<string, string | string[]>;
7
9
  type AppPageHeadModule = Record<string, unknown>;
10
+ type ApplyAppPageFileBasedMetadata = typeof applyFileBasedMetadata;
8
11
  type AppPageHeadParallelRoute<TModule extends AppPageHeadModule = AppPageHeadModule> = {
12
+ layoutParams?: readonly AppPageParams[] | null;
9
13
  layoutModule?: TModule | null;
10
14
  layoutModules?: readonly (TModule | null | undefined)[] | null;
15
+ layoutTreePositions?: readonly number[] | null;
11
16
  pageModule?: TModule | null;
12
17
  params?: AppPageParams | null;
13
18
  routeSegments?: readonly string[] | null;
14
19
  };
15
20
  type AppPageHeadSlot<TModule extends AppPageHeadModule = AppPageHeadModule> = {
21
+ configLayouts?: readonly (TModule | null | undefined)[] | null;
22
+ configLayoutTreePositions?: readonly number[] | null;
16
23
  layout?: TModule | null;
24
+ layoutIndex?: number;
17
25
  page?: TModule | null;
26
+ routeSegments?: readonly string[] | null;
18
27
  };
19
28
  type ResolveActiveParallelRouteHeadInputsOptions<TModule extends AppPageHeadModule = AppPageHeadModule> = {
20
29
  interceptLayouts?: readonly (TModule | null | undefined)[] | null;
30
+ interceptBranchSegments?: readonly string[] | null;
31
+ interceptLayoutSegments?: readonly (readonly string[])[] | null;
21
32
  interceptPage?: TModule | null;
22
33
  interceptParams?: AppPageParams | null;
23
34
  interceptSlotKey?: string | null;
35
+ layoutTreePositions?: readonly number[] | null;
24
36
  params: AppPageParams;
25
37
  routeSegments: readonly string[];
38
+ slotParams?: Readonly<Record<string, AppPageParams>> | null;
26
39
  slots?: Record<string, AppPageHeadSlot<TModule>> | null;
27
40
  };
28
41
  type ResolveAppPageHeadOptions<TModule extends AppPageHeadModule = AppPageHeadModule> = {
42
+ applyFileBasedMetadata?: ApplyAppPageFileBasedMetadata;
29
43
  /**
30
44
  * Configured next.config `basePath`. Threaded into `applyFileBasedMetadata`
31
45
  * so file-based metadata route URLs (icon, opengraph-image, manifest, ...)
@@ -43,6 +57,7 @@ type ResolveAppPageHeadOptions<TModule extends AppPageHeadModule = AppPageHeadMo
43
57
  routePath: string;
44
58
  routeSegments?: readonly string[] | null;
45
59
  searchParams?: URLSearchParams | null;
60
+ searchParamsObserver?: ThenableParamsObserver;
46
61
  };
47
62
  type ResolveAppPageHeadResult = {
48
63
  hasDynamicMetadata: boolean;
@@ -59,4 +74,4 @@ declare function resolveActiveParallelRouteHeadInputs<TModule extends AppPageHea
59
74
  declare function collectAppPageSearchParams(searchParams: URLSearchParams | null | undefined): AppPageSearchParamsCollection;
60
75
  declare function resolveAppPageHead<TModule extends AppPageHeadModule>(options: ResolveAppPageHeadOptions<TModule>): Promise<ResolveAppPageHeadResult>;
61
76
  //#endregion
62
- export { AppPageSearchParams, collectAppPageSearchParams, resolveActiveParallelRouteHeadInputs, resolveAppPageHead };
77
+ export { AppPageSearchParams, ApplyAppPageFileBasedMetadata, collectAppPageSearchParams, resolveActiveParallelRouteHeadInputs, resolveAppPageHead };