vinext 1.0.0-beta.3 → 1.0.0-beta.4

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 (90) hide show
  1. package/dist/build/precompress.js +1 -1
  2. package/dist/build/prerender.d.ts +2 -1
  3. package/dist/build/prerender.js +12 -2
  4. package/dist/cli-args.d.ts +2 -1
  5. package/dist/cli-args.js +10 -0
  6. package/dist/cli.js +14 -8
  7. package/dist/client/navigation-runtime.d.ts +6 -0
  8. package/dist/client/navigation-runtime.js +1 -1
  9. package/dist/config/dotenv.d.ts +3 -4
  10. package/dist/config/dotenv.js +30 -22
  11. package/dist/config/next-config.d.ts +4 -2
  12. package/dist/config/next-config.js +3 -0
  13. package/dist/entries/app-browser-entry.js +1 -1
  14. package/dist/entries/app-rsc-entry.js +18 -3
  15. package/dist/entries/pages-server-entry.js +4 -1
  16. package/dist/index.js +11 -7
  17. package/dist/plugins/client-reference-dedup.js +7 -5
  18. package/dist/plugins/sass.js +3 -4
  19. package/dist/server/api-handler.js +4 -3
  20. package/dist/server/app-bfcache-identity.js +28 -2
  21. package/dist/server/app-browser-entry.js +73 -45
  22. package/dist/server/app-browser-navigation-controller.d.ts +1 -1
  23. package/dist/server/app-browser-navigation-controller.js +29 -4
  24. package/dist/server/app-browser-prefetch-response.d.ts +9 -0
  25. package/dist/server/app-browser-prefetch-response.js +32 -0
  26. package/dist/server/app-browser-state.d.ts +8 -3
  27. package/dist/server/app-browser-state.js +9 -3
  28. package/dist/server/app-page-dispatch.d.ts +1 -1
  29. package/dist/server/app-page-dispatch.js +6 -2
  30. package/dist/server/app-page-element-builder.d.ts +2 -1
  31. package/dist/server/app-page-element-builder.js +1 -0
  32. package/dist/server/app-page-render.d.ts +1 -1
  33. package/dist/server/app-page-render.js +13 -3
  34. package/dist/server/app-page-request.d.ts +0 -1
  35. package/dist/server/app-page-request.js +3 -12
  36. package/dist/server/app-page-response.d.ts +2 -0
  37. package/dist/server/app-page-response.js +6 -1
  38. package/dist/server/app-page-route-wiring.d.ts +3 -2
  39. package/dist/server/app-page-route-wiring.js +61 -13
  40. package/dist/server/app-pages-bridge.d.ts +2 -1
  41. package/dist/server/app-pages-bridge.js +2 -1
  42. package/dist/server/app-rsc-cache-busting.d.ts +7 -0
  43. package/dist/server/app-rsc-cache-busting.js +6 -0
  44. package/dist/server/app-rsc-handler.d.ts +2 -0
  45. package/dist/server/app-rsc-handler.js +9 -4
  46. package/dist/server/app-rsc-render-mode.d.ts +3 -2
  47. package/dist/server/app-rsc-render-mode.js +4 -1
  48. package/dist/server/app-rsc-request-normalization.js +65 -4
  49. package/dist/server/app-server-action-execution.d.ts +1 -1
  50. package/dist/server/app-visited-response-cache.d.ts +6 -1
  51. package/dist/server/app-visited-response-cache.js +49 -1
  52. package/dist/server/dev-server.d.ts +1 -1
  53. package/dist/server/dev-server.js +61 -38
  54. package/dist/server/edge-api-runtime.d.ts +10 -1
  55. package/dist/server/edge-api-runtime.js +29 -1
  56. package/dist/server/headers.d.ts +3 -1
  57. package/dist/server/headers.js +3 -1
  58. package/dist/server/pages-api-route.d.ts +2 -0
  59. package/dist/server/pages-api-route.js +2 -2
  60. package/dist/server/pages-asset-tags.d.ts +1 -0
  61. package/dist/server/pages-asset-tags.js +6 -4
  62. package/dist/server/pages-document-asset-props.d.ts +26 -0
  63. package/dist/server/pages-document-asset-props.js +69 -0
  64. package/dist/server/pages-node-compat.js +3 -3
  65. package/dist/server/pages-page-handler.d.ts +1 -0
  66. package/dist/server/pages-page-handler.js +4 -1
  67. package/dist/server/pages-page-response.d.ts +2 -0
  68. package/dist/server/pages-page-response.js +19 -8
  69. package/dist/server/pages-router-entry.js +1 -1
  70. package/dist/server/prerender-manifest.d.ts +1 -0
  71. package/dist/server/prod-server.js +2 -1
  72. package/dist/server/seed-cache.js +1 -1
  73. package/dist/server/worker-revalidation-context.js +1 -0
  74. package/dist/shims/app-router-scroll-state.d.ts +2 -1
  75. package/dist/shims/app-router-scroll-state.js +7 -2
  76. package/dist/shims/document.js +15 -2
  77. package/dist/shims/hash-scroll.d.ts +1 -1
  78. package/dist/shims/hash-scroll.js +2 -1
  79. package/dist/shims/link.d.ts +2 -0
  80. package/dist/shims/link.js +59 -18
  81. package/dist/shims/metadata.d.ts +11 -17
  82. package/dist/shims/metadata.js +24 -32
  83. package/dist/shims/navigation.d.ts +18 -2
  84. package/dist/shims/navigation.js +69 -20
  85. package/dist/shims/request-context.d.ts +2 -1
  86. package/dist/shims/slot.d.ts +2 -1
  87. package/dist/shims/slot.js +6 -2
  88. package/dist/shims/streamed-icons.d.ts +9 -0
  89. package/dist/shims/streamed-icons.js +33 -0
  90. package/package.json +1 -1
@@ -30,6 +30,10 @@ function readRequestCacheLifeForPrerender(options) {
30
30
  if (options.isEdgeRuntime && options.revalidateSeconds === null) return (options.peekRequestCacheLife?.() ?? options.getRequestCacheLife())?.revalidate !== void 0 ? { revalidate: 0 } : null;
31
31
  return options.peekRequestCacheLife?.() ?? options.getRequestCacheLife();
32
32
  }
33
+ function resolveConfiguredDynamicStaleTimeSeconds() {
34
+ const seconds = Number(process.env.__NEXT_CLIENT_ROUTER_DYNAMIC_STALETIME);
35
+ return Number.isInteger(seconds) && seconds >= 0 ? seconds : 0;
36
+ }
33
37
  function readRequestCacheLifeForCachePolicy(options) {
34
38
  const requestCacheLife = options.getRequestCacheLife();
35
39
  if (options.isEdgeRuntime && options.revalidateSeconds === null) return null;
@@ -322,10 +326,11 @@ async function renderAppPageLifecycle(options) {
322
326
  layoutParamAccess: options.layoutParamAccess
323
327
  });
324
328
  const shouldBypassRscCacheForSkipTransport = options.isRscRequest && isSkipTransportEnabled(skipDisposition);
329
+ const dynamicStaleTimeSeconds = options.dynamicStaleTimeSeconds ?? (options.isForceDynamic ? resolveConfiguredDynamicStaleTimeSeconds() : void 0);
325
330
  const outgoingElement = AppElementsWire.encodeOutgoingPayload({
326
331
  element: options.element,
327
332
  layoutFlags,
328
- ...options.dynamicStaleTimeSeconds !== void 0 && options.isPrerender !== true && !options.isForceStatic ? { dynamicStaleTimeSeconds: options.dynamicStaleTimeSeconds } : {},
333
+ ...dynamicStaleTimeSeconds !== void 0 && options.isPrerender !== true && !options.isForceStatic ? { dynamicStaleTimeSeconds } : {},
329
334
  ...artifactCompatibility ? { artifactCompatibility } : {},
330
335
  renderObservation: payloadRenderObservation,
331
336
  skipDisposition: options.isRscRequest ? skipDisposition : void 0
@@ -385,8 +390,10 @@ async function renderAppPageLifecycle(options) {
385
390
  revalidateSeconds
386
391
  });
387
392
  if (shouldBypassRscCacheForSkipTransport) options.isrDebug?.("RSC cache write skipped (skip transport payload)", options.cleanPathname);
393
+ const shouldEmitDynamicStaleTime = dynamicStaleTimeSeconds !== void 0 && options.isPrerender !== true && !options.isForceStatic && (dynamicUsedDuringBuild || options.isForceDynamic || !shouldCaptureRscForCacheMetadata);
388
394
  const rscResponse = buildAppPageRscResponse(rscForResponse, {
389
- dynamicStaleTimeSeconds: options.dynamicStaleTimeSeconds !== void 0 && options.isPrerender !== true && !options.isForceStatic && (dynamicUsedDuringBuild || !shouldCaptureRscForCacheMetadata) ? options.dynamicStaleTimeSeconds : void 0,
395
+ cacheTags: options.isPrerender === true ? options.getPageTags() : void 0,
396
+ dynamicStaleTimeSeconds: shouldEmitDynamicStaleTime ? dynamicStaleTimeSeconds : void 0,
390
397
  isEdgeRuntime: options.isEdgeRuntime,
391
398
  middlewareContext: options.middlewareContext,
392
399
  mountedSlotsHeader: options.mountedSlotsHeader,
@@ -474,7 +481,7 @@ async function renderAppPageLifecycle(options) {
474
481
  }
475
482
  return {
476
483
  kind,
477
- ...kind === "dynamic" && options.dynamicStaleTimeSeconds !== void 0 && options.isPrerender !== true && !shouldCaptureRscForCacheMetadata ? { dynamicStaleTimeSeconds: options.dynamicStaleTimeSeconds } : {}
484
+ ...kind === "dynamic" && dynamicStaleTimeSeconds !== void 0 && options.isPrerender !== true && !shouldCaptureRscForCacheMetadata ? { dynamicStaleTimeSeconds } : {}
478
485
  };
479
486
  },
480
487
  fontData,
@@ -564,6 +571,7 @@ async function renderAppPageLifecycle(options) {
564
571
  });
565
572
  if (htmlRender.shellErrorRecovered) {
566
573
  const response = buildAppPageHtmlResponse(safeHtmlStream, {
574
+ cacheTags: options.isPrerender === true ? options.getPageTags() : void 0,
567
575
  draftCookie,
568
576
  linkHeader,
569
577
  isEdgeRuntime: options.isEdgeRuntime,
@@ -581,6 +589,7 @@ async function renderAppPageLifecycle(options) {
581
589
  const shouldSpeculativelyWriteCache = options.isProduction && shouldCaptureRscForCacheMetadata && !options.isEdgeRuntime && revalidateSeconds === null && !options.isDynamicError && !options.isForceStatic && !options.scriptNonce && options.isProgressiveActionRender !== true && !dynamicUsedDuringRender;
582
590
  if (htmlResponsePolicy.shouldWriteToCache || shouldSpeculativelyWriteCache) {
583
591
  const isrResponse = buildAppPageHtmlResponse(safeHtmlStream, {
592
+ cacheTags: options.isPrerender === true ? options.getPageTags() : void 0,
584
593
  draftCookie,
585
594
  linkHeader,
586
595
  isEdgeRuntime: options.isEdgeRuntime,
@@ -647,6 +656,7 @@ async function renderAppPageLifecycle(options) {
647
656
  });
648
657
  }
649
658
  return buildAppPageHtmlResponse(safeHtmlStream, {
659
+ cacheTags: options.isPrerender === true ? options.getPageTags() : void 0,
650
660
  draftCookie,
651
661
  linkHeader,
652
662
  isEdgeRuntime: options.isEdgeRuntime,
@@ -33,7 +33,6 @@ type ParallelGenerateStaticParamsBranch = {
33
33
  routeSegments?: readonly string[] | null;
34
34
  };
35
35
  type ValidateAppPageDynamicParamsOptions = {
36
- clearRequestContext: () => void;
37
36
  enforceStaticParamsOnly: boolean;
38
37
  generateStaticParams?: GenerateStaticParams | GenerateStaticParamsSource | readonly (GenerateStaticParams | GenerateStaticParamsSource | null | undefined)[] | null;
39
38
  isDynamicRoute: boolean;
@@ -212,10 +212,7 @@ function normalizeGenerateStaticParams(generateStaticParams) {
212
212
  async function validateAppPageDynamicParams(options) {
213
213
  if (!options.enforceStaticParamsOnly || !options.isDynamicRoute) return null;
214
214
  const generateStaticParamsSources = normalizeGenerateStaticParams(options.generateStaticParams);
215
- if (generateStaticParamsSources.length === 0) {
216
- options.clearRequestContext();
217
- return notFoundResponse();
218
- }
215
+ if (generateStaticParamsSources.length === 0) return notFoundResponse();
219
216
  const chainedSources = generateStaticParamsSources.filter((source) => source.chained);
220
217
  let chainedStaticParams = null;
221
218
  if (chainedSources.length > 0) chainedStaticParams = await generateChainedStaticParams(chainedSources);
@@ -230,17 +227,11 @@ async function validateAppPageDynamicParams(options) {
230
227
  const result = await generateIndependentStaticParams(sources, chainedStaticParams, options.params);
231
228
  if (result.validated) {
232
229
  validatedIndependentResults = true;
233
- if (!areStaticParamsAllowed(options.params, result.staticParams, true)) {
234
- options.clearRequestContext();
235
- return notFoundResponse();
236
- }
230
+ if (!areStaticParamsAllowed(options.params, result.staticParams, true)) return notFoundResponse();
237
231
  }
238
232
  }
239
233
  if (chainedStaticParams && !validatedIndependentResults) {
240
- if (!areStaticParamsAllowed(options.params, chainedStaticParams)) {
241
- options.clearRequestContext();
242
- return notFoundResponse();
243
- }
234
+ if (!areStaticParamsAllowed(options.params, chainedStaticParams)) return notFoundResponse();
244
235
  }
245
236
  return null;
246
237
  }
@@ -40,6 +40,7 @@ type AppPageHtmlResponsePolicy = {
40
40
  shouldWriteToCache: boolean;
41
41
  } & AppPageResponsePolicy;
42
42
  type BuildAppPageRscResponseOptions = {
43
+ cacheTags?: readonly string[];
43
44
  dynamicStaleTimeSeconds?: number;
44
45
  isEdgeRuntime?: boolean;
45
46
  middlewareContext: AppPageMiddlewareContext;
@@ -51,6 +52,7 @@ type BuildAppPageRscResponseOptions = {
51
52
  timing?: AppPageResponseTiming;
52
53
  };
53
54
  type BuildAppPageHtmlResponseOptions = {
55
+ cacheTags?: readonly string[];
54
56
  draftCookie?: string | null; /** Combined preload `Link` header value (React hints + font preloads), already capped. */
55
57
  linkHeader?: string;
56
58
  isEdgeRuntime?: boolean;
@@ -1,4 +1,4 @@
1
- import { VINEXT_DYNAMIC_STALE_TIME_HEADER, VINEXT_MOUNTED_SLOTS_HEADER, VINEXT_PARAMS_HEADER, VINEXT_PRERENDER_CACHE_LIFE_HEADER, VINEXT_RENDERED_PATH_AND_SEARCH_HEADER, VINEXT_TIMING_HEADER } from "./headers.js";
1
+ import { NEXT_CACHE_TAGS_HEADER, VINEXT_DYNAMIC_STALE_TIME_HEADER, VINEXT_MOUNTED_SLOTS_HEADER, VINEXT_PARAMS_HEADER, VINEXT_PRERENDER_CACHE_LIFE_HEADER, VINEXT_RENDERED_PATH_AND_SEARCH_HEADER, VINEXT_TIMING_HEADER } from "./headers.js";
2
2
  import { NO_STORE_CACHE_CONTROL, STATIC_CACHE_CONTROL, buildRevalidateCacheControl } from "./cache-control.js";
3
3
  import { setCacheStateHeaders } from "./cache-headers.js";
4
4
  import { VINEXT_RSC_CONTENT_TYPE, VINEXT_RSC_VARY_HEADER, applyRscCompatibilityIdHeader, applyRscDeploymentIdHeader } from "./app-rsc-cache-busting.js";
@@ -22,6 +22,9 @@ function applyPrerenderCacheLifeHeader(headers, requestCacheLife) {
22
22
  if (payload.revalidate === void 0 && payload.expire === void 0) return;
23
23
  headers.set(VINEXT_PRERENDER_CACHE_LIFE_HEADER, JSON.stringify(payload));
24
24
  }
25
+ function applyPrerenderCacheTagsHeader(headers, cacheTags) {
26
+ if (cacheTags && cacheTags.length > 0) headers.set(NEXT_CACHE_TAGS_HEADER, cacheTags.join(","));
27
+ }
25
28
  function resolveAppPageRscResponsePolicy(options) {
26
29
  if (options.isDraftMode) return { cacheControl: NO_STORE_CACHE_CONTROL };
27
30
  if (options.isForceDynamic || options.dynamicUsedDuringBuild) return { cacheControl: NO_STORE_CACHE_CONTROL };
@@ -104,6 +107,7 @@ function buildAppPageRscResponse(body, options) {
104
107
  applyRscCompatibilityIdHeader(headers);
105
108
  applyRscDeploymentIdHeader(headers);
106
109
  applyPrerenderCacheLifeHeader(headers, options.requestCacheLife);
110
+ applyPrerenderCacheTagsHeader(headers, options.cacheTags);
107
111
  applyTimingHeader(headers, options.timing);
108
112
  return new Response(body, {
109
113
  status: options.middlewareContext.status ?? 200,
@@ -122,6 +126,7 @@ function buildAppPageHtmlResponse(body, options) {
122
126
  if (options.linkHeader) headers.set("Link", options.linkHeader);
123
127
  mergeMiddlewareResponseHeaders(headers, options.middlewareContext.headers);
124
128
  applyPrerenderCacheLifeHeader(headers, options.requestCacheLife);
129
+ applyPrerenderCacheTagsHeader(headers, options.cacheTags);
125
130
  applyTimingHeader(headers, options.timing);
126
131
  return new Response(body, {
127
132
  status: options.middlewareContext.status ?? 200,
@@ -1,10 +1,10 @@
1
1
  import { AppRouteSemanticIds } from "../routing/app-route-graph.js";
2
2
  import { AppElements, AppElementsInterception, AppElementsSlotBinding } from "./app-elements-wire.js";
3
+ import { AppRscRenderMode } from "./app-rsc-render-mode.js";
3
4
  import { AppPageParams } from "./app-page-boundary.js";
4
5
  import { ThenableParamsObserver } from "../shims/thenable-params.js";
5
6
  import { Metadata, Viewport } from "../shims/metadata.js";
6
7
  import { AppLayoutParamAccessTracker } from "./app-layout-param-observation.js";
7
- import { AppRscRenderMode } from "./app-rsc-render-mode.js";
8
8
  import { AppPageRenderIdentity } from "./app-page-render-identity.js";
9
9
  import { resolveAppPageChildSegments } from "./app-page-segment-state.js";
10
10
  import { ComponentType, ReactNode } from "react";
@@ -136,6 +136,7 @@ type BuildAppPageRouteElementOptions<TModule extends AppPageModule = AppPageModu
136
136
  resolvedMetadata: Metadata | null;
137
137
  resolvedMetadataPathname?: string;
138
138
  resolvedViewport: Viewport;
139
+ scriptNonce?: string;
139
140
  streamingMetadata?: Promise<Metadata | null> | null;
140
141
  streamingMetadataOutlet?: Promise<unknown> | null;
141
142
  streamingMetadataOutletSuspended?: boolean;
@@ -173,7 +174,7 @@ declare function createAppPageLayoutEntries<TModule extends AppPageModule, TErro
173
174
  unauthorizeds?: readonly (TModule | null | undefined)[] | null;
174
175
  }): AppPageLayoutEntry<TModule, TErrorModule>[];
175
176
  declare function createAppPageSourcePage(routeSegments: readonly string[] | null | undefined): string;
176
- declare function createAppPageRouteBodyMetadata(metadata: Metadata | null, pathname: string, metadataPlacement: "body" | "head", trailingSlash?: boolean): ReactNode;
177
+ declare function createAppPageRouteBodyMetadata(metadata: Metadata | null, pathname: string, metadataPlacement: "body" | "head", trailingSlash?: boolean, scriptNonce?: string): ReactNode;
177
178
  declare function buildAppPageElements<TModule extends AppPageModule, TErrorModule extends AppPageErrorModule>(options: BuildAppPageElementsOptions<TModule, TErrorModule>): AppElements;
178
179
  //#endregion
179
180
  export { AppPageErrorModule, AppPageModule, AppPageRouteWiringRoute, AppPageSlotOverride, buildAppPageElements, createAppPageLayoutEntries, createAppPageRouteBodyMetadata, createAppPageSourcePage, createAppPageTreePath, probeAppPageLayoutWithTracking, resolveAppPageChildSegments };
@@ -1,13 +1,16 @@
1
1
  import { createAppRenderDependency, registerAppElementRenderDependencies, renderAfterAppDependencies, renderWithAppDependencyBarrier } from "./app-render-dependency.js";
2
- import { APP_STATIC_SIBLINGS_KEY, AppElementsWire, normalizeAppElementsSlotBindings } from "./app-elements-wire.js";
3
- import { APP_RSC_RENDER_MODE_PREFETCH_LOADING_SHELL } from "./app-rsc-render-mode.js";
2
+ import { APP_LAYOUT_IDS_KEY, APP_ROOT_LAYOUT_KEY, APP_STATIC_SIBLINGS_KEY, AppElementsWire, normalizeAppElementsSlotBindings } from "./app-elements-wire.js";
3
+ import { APP_RSC_RENDER_MODE_PREFETCH_EMPTY, APP_RSC_RENDER_MODE_PREFETCH_LOADING_SHELL } from "./app-rsc-render-mode.js";
4
4
  import { APP_PREFETCH_LOADING_SHELL_MARKER_KEY } from "./app-elements.js";
5
+ import { createInlineScriptTag, escapeHtmlAttr } from "./html.js";
5
6
  import DefaultGlobalError from "../shims/default-global-error.js";
6
7
  import { ErrorBoundary, ForbiddenBoundary, GlobalErrorBoundary, NotFoundBoundary, RedirectBoundary, UnauthorizedBoundary } from "../shims/error-boundary.js";
7
8
  import { AppRouterScrollTarget } from "../shims/app-router-scroll.js";
9
+ import DefaultNotFound from "../shims/default-not-found.js";
8
10
  import { LayoutSegmentProvider } from "../shims/layout-segment-context.js";
9
11
  import { MetadataHead, ViewportHead, renderMetadataToHtml } from "../shims/metadata.js";
10
12
  import { Children as Children$1, ParallelSlot, Slot } from "../shims/slot.js";
13
+ import { StreamedIconsInsertion } from "../shims/streamed-icons.js";
11
14
  import { resolveAppPageBranchParams, resolveAppPageSegmentParamScopeKeys, resolveAppPageSegmentParams } from "./app-page-params.js";
12
15
  import { probeReactServerSubtree } from "./app-page-probe.js";
13
16
  import { APP_PAGE_SEGMENT_KEY, resolveAppPageChildSegments, resolveAppPageRouteStateKey, resolveAppPageSegmentStateKey } from "./app-page-segment-state.js";
@@ -16,6 +19,7 @@ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
16
19
  //#region src/server/app-page-route-wiring.tsx
17
20
  const APP_PAGE_LAYOUT_PROBE_CHILD = /* @__PURE__ */ jsx(Fragment, {});
18
21
  const DEFAULT_GLOBAL_ERROR_COMPONENT = DefaultGlobalError;
22
+ const DEFAULT_NOT_FOUND_COMPONENT = DefaultNotFound;
19
23
  function resolveSlotLayoutParams(routeSegments, treePosition, params) {
20
24
  return resolveAppPageBranchParams(routeSegments, treePosition, params);
21
25
  }
@@ -120,17 +124,23 @@ function createAppPageLoadingEntries(route) {
120
124
  });
121
125
  }
122
126
  function getPrefetchLoadingEntry(route) {
123
- let firstEntry = null;
127
+ let rootEntry = null;
128
+ let firstNestedEntry = null;
124
129
  for (const [index, loadingModule] of (route.loadings ?? []).entries()) {
125
130
  if (!getDefaultExport(loadingModule)) continue;
126
131
  const treePosition = route.loadingTreePositions?.[index];
127
132
  if (treePosition === void 0) continue;
128
- if (firstEntry === null || treePosition < firstEntry.treePosition) firstEntry = {
133
+ if (treePosition === 0) rootEntry ??= {
134
+ loadingModule,
135
+ treePosition
136
+ };
137
+ else if (firstNestedEntry === null || treePosition < firstNestedEntry.treePosition) firstNestedEntry = {
129
138
  loadingModule,
130
139
  treePosition
131
140
  };
132
141
  }
133
- if (firstEntry) return firstEntry;
142
+ if (firstNestedEntry) return firstNestedEntry;
143
+ if (rootEntry) return rootEntry;
134
144
  return getDefaultExport(route.loading) ? {
135
145
  loadingModule: route.loading,
136
146
  treePosition: route.routeSegments?.length ?? 0
@@ -194,6 +204,7 @@ function createAppPageSlotBindings(route, layoutEntries, resolveSlotOverride, op
194
204
  if (route.childrenSlot) {
195
205
  const ownerLayoutId = layoutEntries.find((layoutEntry) => layoutEntry.treePath === route.childrenSlot?.ownerTreePath)?.id;
196
206
  bindings.push({
207
+ ...route.childrenSlot.state === "active" ? { activeRouteId: options.routeId } : {},
197
208
  ownerLayoutId: ownerLayoutId ?? null,
198
209
  slotId: route.childrenSlot.id,
199
210
  state: route.childrenSlot.state
@@ -226,16 +237,40 @@ function createAppPageRouteHead(metadata, viewport, pathname, metadataPlacement,
226
237
  /* @__PURE__ */ jsx(ViewportHead, { viewport })
227
238
  ] });
228
239
  }
229
- function createAppPageRouteBodyMetadata(metadata, pathname, metadataPlacement, trailingSlash) {
240
+ function hasStreamedIcons(metadata) {
241
+ const icons = metadata.icons;
242
+ if (!icons) return false;
243
+ if (typeof icons === "string" || icons instanceof URL || Array.isArray(icons)) return !Array.isArray(icons) || icons.length > 0;
244
+ if ("url" in icons) return true;
245
+ return Boolean(icons.shortcut || icons.icon || icons.apple || icons.other);
246
+ }
247
+ function createStreamedIconKey(pathname, metadataHtml) {
248
+ let hash = 2166136261;
249
+ for (let index = 0; index < metadataHtml.length; index++) {
250
+ hash ^= metadataHtml.charCodeAt(index);
251
+ hash = Math.imul(hash, 16777619);
252
+ }
253
+ return `${pathname}:${(hash >>> 0).toString(36)}`;
254
+ }
255
+ const STREAMED_ICON_KEY_PLACEHOLDER = "vinext-pending-streamed-icon-key";
256
+ const REINSERT_STREAMED_ICONS_SCRIPT = `document.querySelectorAll('body link[rel="icon"], body link[rel="apple-touch-icon"]').forEach(el => document.head.appendChild(el));const a='data-vinext-streamed-icon',o=el=>{const m=el.getAttribute(a),i=m.lastIndexOf(':');return Number(m.slice(i+1))};[...document.querySelectorAll('link['+a+']')].sort((l,r)=>o(l)-o(r)).forEach(el=>document.head.appendChild(el))`;
257
+ function createAppPageRouteBodyMetadata(metadata, pathname, metadataPlacement, trailingSlash, scriptNonce) {
230
258
  if (!metadata || metadataPlacement !== "body") return null;
231
- return /* @__PURE__ */ jsx("div", {
232
- hidden: true,
233
- dangerouslySetInnerHTML: { __html: renderMetadataToHtml(metadata, pathname, { trailingSlash }) }
259
+ const streamedIconKey = hasStreamedIcons(metadata) ? STREAMED_ICON_KEY_PLACEHOLDER : void 0;
260
+ const renderedMetadataHtml = renderMetadataToHtml(metadata, pathname, {
261
+ trailingSlash,
262
+ streamedIconKey
234
263
  });
264
+ const metadataKey = streamedIconKey ? createStreamedIconKey(pathname, renderedMetadataHtml) : "";
265
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("div", {
266
+ hidden: true,
267
+ suppressHydrationWarning: true,
268
+ dangerouslySetInnerHTML: { __html: (streamedIconKey ? renderedMetadataHtml.replaceAll(`<link data-vinext-streamed-icon="${STREAMED_ICON_KEY_PLACEHOLDER}:`, `<link data-vinext-streamed-icon="${escapeHtmlAttr(metadataKey)}:`) : renderedMetadataHtml) + createInlineScriptTag(REINSERT_STREAMED_ICONS_SCRIPT, scriptNonce) }
269
+ }), /* @__PURE__ */ jsx(StreamedIconsInsertion, { metadataKey })] });
235
270
  }
236
271
  async function AppPageStreamingMetadata(props) {
237
272
  try {
238
- return createAppPageRouteBodyMetadata(await props.metadata, props.pathname, "body", props.trailingSlash);
273
+ return createAppPageRouteBodyMetadata(await props.metadata, props.pathname, "body", props.trailingSlash, props.scriptNonce);
239
274
  } catch {
240
275
  return null;
241
276
  }
@@ -287,6 +322,7 @@ function buildAppPageElements(options) {
287
322
  for (const entry of loadingEntries) if (entry.treePosition <= treePosition && (!nearest || entry.treePosition > nearest.treePosition)) nearest = entry;
288
323
  return nearest;
289
324
  };
325
+ const isPrefetchEmpty = renderMode === APP_RSC_RENDER_MODE_PREFETCH_EMPTY;
290
326
  const isPrefetchLoadingShell = renderMode === APP_RSC_RENDER_MODE_PREFETCH_LOADING_SHELL;
291
327
  const prefetchLoadingEntry = isPrefetchLoadingShell ? getPrefetchLoadingEntry(options.route) : null;
292
328
  const metadataPlacement = options.metadataPlacement ?? "head";
@@ -339,13 +375,22 @@ function buildAppPageElements(options) {
339
375
  slotBindings: createAppPageSlotBindings(options.route, layoutEntries, resolveSlotOverride, {
340
376
  interception: renderIdentity?.interception ?? options.interception ?? null,
341
377
  interceptionContext,
378
+ routeId,
342
379
  routePath: options.routePath
343
380
  })
344
381
  }) };
382
+ if (isPrefetchEmpty) {
383
+ elements[APP_LAYOUT_IDS_KEY] = [];
384
+ elements[APP_ROOT_LAYOUT_KEY] = null;
385
+ elements[pageElementId] = null;
386
+ elements[routeId] = null;
387
+ return elements;
388
+ }
345
389
  if (options.route.staticSiblings && options.route.staticSiblings.length > 0) elements[APP_STATIC_SIBLINGS_KEY] = options.route.staticSiblings;
346
390
  if (options.streamingMetadata && streamingMetadataBodyId) elements[streamingMetadataBodyId] = /* @__PURE__ */ jsx(AppPageStreamingMetadata, {
347
391
  metadata: options.streamingMetadataTags ?? options.streamingMetadata,
348
392
  pathname: options.resolvedMetadataPathname ?? options.routePath,
393
+ scriptNonce: options.scriptNonce,
349
394
  trailingSlash: options.trailingSlash
350
395
  });
351
396
  if (options.streamingMetadataOutlet && streamingMetadataOutletId) elements[streamingMetadataOutletId] = /* @__PURE__ */ jsx(AppPageMetadataOutlet, { metadata: options.streamingMetadataOutlet });
@@ -468,6 +513,7 @@ function buildAppPageElements(options) {
468
513
  slotElement = options.createPageElement ? options.createPageElement(slotComponent, slotProps) : (() => {
469
514
  return /* @__PURE__ */ jsx(slotComponent, { ...slotProps });
470
515
  })();
516
+ if (overrideOrPageComponent) slotElement = /* @__PURE__ */ jsx(Fragment, { children: slotElement }, slotResetKey);
471
517
  }
472
518
  const branchSegments = slotOverride?.branchSegments ?? slotRouteSegments;
473
519
  const branchLayouts = /* @__PURE__ */ new Map();
@@ -573,7 +619,9 @@ function buildAppPageElements(options) {
573
619
  routeChildren = /* @__PURE__ */ jsx(AppRouterScrollTarget, { children: routeChildren });
574
620
  }
575
621
  const lastLayoutErrorModule = errorEntries.length > 0 ? errorEntries[errorEntries.length - 1].errorModule : null;
576
- const notFoundComponent = getDefaultExport(options.route.notFound) ?? getDefaultExport(options.rootNotFoundModule);
622
+ const configuredNotFoundComponent = getDefaultExport(options.route.notFound) ?? getDefaultExport(options.rootNotFoundModule);
623
+ const defaultNotFoundOwnerLayoutId = configuredNotFoundComponent === null ? layoutEntries[0]?.id ?? null : null;
624
+ const notFoundComponent = configuredNotFoundComponent ?? (defaultNotFoundOwnerLayoutId === null ? DEFAULT_NOT_FOUND_COMPONENT : null);
577
625
  if (notFoundComponent) routeChildren = /* @__PURE__ */ jsx(NotFoundBoundary, {
578
626
  resetKey: routeResetKey,
579
627
  fallback: /* @__PURE__ */ jsx(notFoundComponent, {}),
@@ -614,7 +662,7 @@ function buildAppPageElements(options) {
614
662
  }, segmentResetKey || routeResetKey);
615
663
  }
616
664
  if (layoutEntry) {
617
- const layoutNotFoundComponent = getDefaultExport(layoutEntry.notFoundModule);
665
+ const layoutNotFoundComponent = getDefaultExport(layoutEntry.notFoundModule) ?? (layoutEntry.id === defaultNotFoundOwnerLayoutId ? DEFAULT_NOT_FOUND_COMPONENT : null);
618
666
  if (layoutNotFoundComponent) segmentChildren = /* @__PURE__ */ jsx(NotFoundBoundary, {
619
667
  resetKey: segmentResetKey,
620
668
  fallback: /* @__PURE__ */ jsx(layoutNotFoundComponent, {}),
@@ -680,7 +728,7 @@ function buildAppPageElements(options) {
680
728
  elements[routeId] = /* @__PURE__ */ jsxs(Fragment$1, { children: [
681
729
  createAppPageRouteHead(options.resolvedMetadata, options.resolvedViewport, options.resolvedMetadataPathname ?? options.routePath, metadataPlacement, options.trailingSlash),
682
730
  routeChildren,
683
- createAppPageRouteBodyMetadata(options.resolvedMetadata, options.resolvedMetadataPathname ?? options.routePath, metadataPlacement, options.trailingSlash),
731
+ createAppPageRouteBodyMetadata(options.resolvedMetadata, options.resolvedMetadataPathname ?? options.routePath, metadataPlacement, options.trailingSlash, options.scriptNonce),
684
732
  createAppPageStreamingMetadataBody(streamingMetadataBodyId)
685
733
  ] });
686
734
  registerAppElementRenderDependencies(elements, renderDependenciesByElementId);
@@ -1,8 +1,9 @@
1
1
  import { AppMiddlewareContext } from "./app-middleware.js";
2
+ import { EdgeApiExecutionRuntime } from "./edge-api-runtime.js";
2
3
 
3
4
  //#region src/server/app-pages-bridge.d.ts
4
5
  type PagesEntry = {
5
- handleApiRoute?: (request: Request, url: string, ctx?: unknown, trustedRevalidateOrigin?: string) => Promise<Response> | Response;
6
+ handleApiRoute?: (request: Request, url: string, ctx: unknown, trustedRevalidateOrigin: string | undefined, edgeRuntime: EdgeApiExecutionRuntime) => Promise<Response> | Response;
6
7
  matchApiRoute?: (url: string, request: Request) => PagesRouteMatch | null;
7
8
  matchPageRoute?: (url: string, request: Request) => PagesRouteMatch | null;
8
9
  renderPage?: (request: Request, url: string, query: Record<string, unknown>, parsedUrl: unknown, middlewareRequestHeaders?: Headers | null, options?: {
@@ -27,7 +27,8 @@ async function renderPagesFallback(options, dependencies) {
27
27
  if (appRouteMatch !== null) {
28
28
  if (apiMatch === null || !pagesRouteHasPriorityOverAppRoute(apiMatch.route, appRouteMatch.route)) return null;
29
29
  }
30
- const pagesApiResponse = await pagesEntry.handleApiRoute(pagesRequest, pagesUrl, void 0, getRequestExecutionContext()?.trustedRevalidateOrigin ?? new URL(pagesRequest.url).origin);
30
+ const executionContext = getRequestExecutionContext();
31
+ const pagesApiResponse = await pagesEntry.handleApiRoute(pagesRequest, pagesUrl, void 0, executionContext?.trustedRevalidateOrigin ?? new URL(pagesRequest.url).origin, executionContext?.hostRuntime ?? "node");
31
32
  const draftCookie = getDraftModeCookieHeader();
32
33
  return applyDraftModeCookie(applyRouteHandlerMiddlewareContext(pagesApiResponse, middlewareContext), draftCookie);
33
34
  }
@@ -16,7 +16,14 @@ type CreateRscRequestHeadersOptions = {
16
16
  clientReuseManifestHeader?: string | null;
17
17
  interceptionContext?: string | null;
18
18
  mountedSlotsHeader?: string | null;
19
+ includePrefetchHeader?: boolean;
19
20
  renderMode?: AppRscRenderMode;
21
+ fetchPriority?: "auto" | "high" | "low";
22
+ nextUrl?: string | null;
23
+ prefetchRouterState?: {
24
+ pathAndSearch: string;
25
+ routeId: string;
26
+ } | null;
20
27
  };
21
28
  type ResolveInvalidRscCacheBustingRequestOptions = {
22
29
  isRscRequest: boolean;
@@ -154,6 +154,12 @@ function createRscRequestHeaders(options = {}) {
154
154
  ["RSC"]: "1"
155
155
  });
156
156
  applyDeploymentIdHeader(headers);
157
+ if (options.prefetchRouterState) {
158
+ if (options.includePrefetchHeader !== false) headers.set(NEXT_ROUTER_PREFETCH_HEADER, "1");
159
+ headers.set(NEXT_ROUTER_STATE_TREE_HEADER, encodeURIComponent(JSON.stringify(options.prefetchRouterState)));
160
+ }
161
+ if (options.nextUrl) headers.set(NEXT_URL_HEADER, options.nextUrl);
162
+ if (process.env.__NEXT_TEST_MODE && options.fetchPriority) headers.set("Next-Test-Fetch-Priority", options.fetchPriority);
157
163
  if (options.interceptionContext !== void 0 && options.interceptionContext !== null) headers.set(VINEXT_INTERCEPTION_CONTEXT_HEADER, options.interceptionContext);
158
164
  if (options.mountedSlotsHeader !== void 0 && options.mountedSlotsHeader !== null) headers.set(VINEXT_MOUNTED_SLOTS_HEADER, options.mountedSlotsHeader);
159
165
  if (options.clientReuseManifestHeader !== void 0 && options.clientReuseManifestHeader !== null) headers.set(VINEXT_CLIENT_REUSE_MANIFEST_HEADER, options.clientReuseManifestHeader);
@@ -125,6 +125,7 @@ type HandleServerActionRequestOptions<TRoute> = {
125
125
  middlewareContext: AppRscMiddlewareContext;
126
126
  mountedSlotsHeader: string | null;
127
127
  request: Request;
128
+ scriptNonce?: string;
128
129
  routeMatch: AppRscRouteMatch<TRoute> | null;
129
130
  routePathname: string;
130
131
  searchParams: URLSearchParams;
@@ -195,6 +196,7 @@ type CreateAppRscHandlerOptions<TRoute extends AppRscHandlerRoute> = {
195
196
  imageConfig?: ImageConfig;
196
197
  isDev: boolean;
197
198
  loadPrerenderPagesRoutes?: () => Promise<unknown>;
199
+ matchInterceptRoute?: (pathname: string, sourcePathname: string) => AppRscRouteMatch<TRoute> | null;
198
200
  matchRoute: (pathname: string) => AppRscRouteMatch<TRoute> | null;
199
201
  matchRequestRoute?: (pathname: string) => AppRscRouteMatch<TRoute> | null;
200
202
  runMiddleware?: (options: RunAppMiddlewareOptions) => Promise<ApplyAppMiddlewareResult>;
@@ -328,7 +328,11 @@ async function handleAppRscRequest(options, request, preMiddlewareRequestContext
328
328
  searchParams: getResolvedSearchParams(),
329
329
  params: {}
330
330
  });
331
- const preActionMatch = filesystemRouteEligible ? matchCleanPathname() : null;
331
+ const directPreActionMatch = filesystemRouteEligible ? matchCleanPathname() : null;
332
+ const preActionRoutePathname = cleanPathnameIsRequestPathname ? requestCleanPathname : cleanPathname;
333
+ const interceptionPreActionMatch = filesystemRouteEligible && directPreActionMatch === null && isRscRequest && interceptionContextHeader !== null ? options.matchInterceptRoute?.(preActionRoutePathname, interceptionContextHeader) ?? null : null;
334
+ const preActionMatch = directPreActionMatch ?? interceptionPreActionMatch;
335
+ const isInterceptionMatch = interceptionPreActionMatch !== null;
332
336
  if (preActionMatch) setRootParams(pickRootParams(preActionMatch.params, preActionMatch.route.rootParamNames));
333
337
  if (pagesDataRequest && didMiddlewareRewritePathname && preActionMatch) {
334
338
  const headers = new Headers();
@@ -374,8 +378,9 @@ async function handleAppRscRequest(options, request, preMiddlewareRequestContext
374
378
  middlewareContext,
375
379
  mountedSlotsHeader,
376
380
  request,
381
+ scriptNonce,
377
382
  routeMatch: preActionMatch,
378
- routePathname: cleanPathnameIsRequestPathname ? requestCleanPathname : cleanPathname,
383
+ routePathname: preActionRoutePathname,
379
384
  searchParams: getResolvedSearchParams()
380
385
  }) : null;
381
386
  if (serverActionResponse) return serverActionResponse;
@@ -383,7 +388,7 @@ async function handleAppRscRequest(options, request, preMiddlewareRequestContext
383
388
  let match = preActionMatch;
384
389
  const renderPagesForMatchKind = async (matchKind) => {
385
390
  if (!filesystemRouteEligible) return null;
386
- const response = match === null || match.route.isDynamic ? await options.renderPagesFallback?.({
391
+ const response = !isInterceptionMatch && (match === null || match.route.isDynamic) ? await options.renderPagesFallback?.({
387
392
  appRouteMatch: match ?? null,
388
393
  allowRscDocumentFallback: didMiddlewareRewritePathname,
389
394
  isDataRequest,
@@ -409,7 +414,7 @@ async function handleAppRscRequest(options, request, preMiddlewareRequestContext
409
414
  options.clearRequestContext();
410
415
  return staticPagesFallbackResponse;
411
416
  }
412
- if (!resolvedLateRewritesForAction && (!match || match.route.isDynamic)) for (const rewrite of options.configRewrites.afterFiles) {
417
+ if (!isInterceptionMatch && !resolvedLateRewritesForAction && (!match || match.route.isDynamic)) for (const rewrite of options.configRewrites.afterFiles) {
413
418
  const afterFilesRewrite = await applyRewrite({
414
419
  basePathState,
415
420
  clearRequestContext: options.clearRequestContext,
@@ -1,9 +1,10 @@
1
1
  //#region src/server/app-rsc-render-mode.d.ts
2
- type AppRscRenderMode = "navigation" | "prefetch-dynamic-shell" | "prefetch-loading-shell";
2
+ type AppRscRenderMode = "navigation" | "prefetch-empty" | "prefetch-dynamic-shell" | "prefetch-loading-shell";
3
3
  declare const APP_RSC_RENDER_MODE_NAVIGATION = "navigation";
4
+ declare const APP_RSC_RENDER_MODE_PREFETCH_EMPTY = "prefetch-empty";
4
5
  declare const APP_RSC_RENDER_MODE_PREFETCH_DYNAMIC_SHELL = "prefetch-dynamic-shell";
5
6
  declare const APP_RSC_RENDER_MODE_PREFETCH_LOADING_SHELL = "prefetch-loading-shell";
6
7
  declare function getRscRenderModeCacheVariant(mode: AppRscRenderMode): string | null;
7
8
  declare function parseAppRscRenderMode(value: string | null): AppRscRenderMode;
8
9
  //#endregion
9
- export { APP_RSC_RENDER_MODE_NAVIGATION, APP_RSC_RENDER_MODE_PREFETCH_DYNAMIC_SHELL, APP_RSC_RENDER_MODE_PREFETCH_LOADING_SHELL, AppRscRenderMode, getRscRenderModeCacheVariant, parseAppRscRenderMode };
10
+ export { APP_RSC_RENDER_MODE_NAVIGATION, APP_RSC_RENDER_MODE_PREFETCH_DYNAMIC_SHELL, APP_RSC_RENDER_MODE_PREFETCH_EMPTY, APP_RSC_RENDER_MODE_PREFETCH_LOADING_SHELL, AppRscRenderMode, getRscRenderModeCacheVariant, parseAppRscRenderMode };
@@ -1,18 +1,21 @@
1
1
  //#region src/server/app-rsc-render-mode.ts
2
2
  const APP_RSC_RENDER_MODE_NAVIGATION = "navigation";
3
+ const APP_RSC_RENDER_MODE_PREFETCH_EMPTY = "prefetch-empty";
3
4
  const APP_RSC_RENDER_MODE_PREFETCH_DYNAMIC_SHELL = "prefetch-dynamic-shell";
4
5
  const APP_RSC_RENDER_MODE_PREFETCH_LOADING_SHELL = "prefetch-loading-shell";
5
6
  function getRscRenderModeCacheVariant(mode) {
7
+ if (mode === "prefetch-empty") return "prefetch-empty";
6
8
  if (mode === "prefetch-dynamic-shell") return "prefetch-dynamic-shell";
7
9
  if (mode === "prefetch-loading-shell") return "prefetch-loading-shell";
8
10
  return null;
9
11
  }
10
12
  function parseAppRscRenderMode(value) {
11
13
  switch (value) {
14
+ case APP_RSC_RENDER_MODE_PREFETCH_EMPTY: return APP_RSC_RENDER_MODE_PREFETCH_EMPTY;
12
15
  case APP_RSC_RENDER_MODE_PREFETCH_DYNAMIC_SHELL: return APP_RSC_RENDER_MODE_PREFETCH_DYNAMIC_SHELL;
13
16
  case APP_RSC_RENDER_MODE_PREFETCH_LOADING_SHELL: return APP_RSC_RENDER_MODE_PREFETCH_LOADING_SHELL;
14
17
  default: return APP_RSC_RENDER_MODE_NAVIGATION;
15
18
  }
16
19
  }
17
20
  //#endregion
18
- export { APP_RSC_RENDER_MODE_NAVIGATION, APP_RSC_RENDER_MODE_PREFETCH_DYNAMIC_SHELL, APP_RSC_RENDER_MODE_PREFETCH_LOADING_SHELL, getRscRenderModeCacheVariant, parseAppRscRenderMode };
21
+ export { APP_RSC_RENDER_MODE_NAVIGATION, APP_RSC_RENDER_MODE_PREFETCH_DYNAMIC_SHELL, APP_RSC_RENDER_MODE_PREFETCH_EMPTY, APP_RSC_RENDER_MODE_PREFETCH_LOADING_SHELL, getRscRenderModeCacheVariant, parseAppRscRenderMode };
@@ -1,15 +1,63 @@
1
1
  import { normalizePathnameForRouteMatchStrict } from "../routing/utils.js";
2
2
  import { hasBasePath, stripBasePath } from "../utils/base-path.js";
3
- import { VINEXT_CLIENT_REUSE_MANIFEST_HEADER, VINEXT_INTERCEPTION_CONTEXT_HEADER, VINEXT_MOUNTED_SLOTS_HEADER, VINEXT_RSC_RENDER_MODE_HEADER } from "./headers.js";
3
+ import { NEXT_ROUTER_STATE_TREE_HEADER, NEXT_URL_HEADER, VINEXT_CLIENT_REUSE_MANIFEST_HEADER, VINEXT_INTERCEPTION_CONTEXT_HEADER, VINEXT_MOUNTED_SLOTS_HEADER, VINEXT_RSC_RENDER_MODE_HEADER } from "./headers.js";
4
4
  import { normalizePath } from "./normalize-path.js";
5
5
  import { badRequestResponse, notFoundResponse } from "./http-error-responses.js";
6
6
  import { guardProtocolRelativeUrl } from "./request-pipeline.js";
7
7
  import { normalizeMountedSlotsHeader } from "./app-mounted-slots-header.js";
8
- import { APP_RSC_RENDER_MODE_NAVIGATION, parseAppRscRenderMode } from "./app-rsc-render-mode.js";
9
- import { stripRscSuffix } from "./app-rsc-cache-busting.js";
8
+ import { APP_RSC_RENDER_MODE_NAVIGATION, APP_RSC_RENDER_MODE_PREFETCH_EMPTY, APP_RSC_RENDER_MODE_PREFETCH_LOADING_SHELL, parseAppRscRenderMode } from "./app-rsc-render-mode.js";
9
+ import { VINEXT_RSC_CACHE_BUSTING_SEARCH_PARAM, stripRscSuffix } from "./app-rsc-cache-busting.js";
10
10
  import { parseClientReuseManifestHeader } from "./client-reuse-manifest.js";
11
11
  import { normalizeInterceptionContextHeader } from "./app-interception-context-header.js";
12
12
  //#region src/server/app-rsc-request-normalization.ts
13
+ function extractFlightRouterStatePath(value, depth = 0) {
14
+ if (!Array.isArray(value) || value.length < 2 || depth > 64) return null;
15
+ const rawSegment = value[0];
16
+ const segment = Array.isArray(rawSegment) ? rawSegment[1] : rawSegment;
17
+ if (typeof segment !== "string") return null;
18
+ if (segment === "__DEFAULT__" || /^(?:\(\.\)|\(\.\.\)|\(\.\.\.\))/.test(segment)) return null;
19
+ const parallelRoutes = value[1];
20
+ if (!parallelRoutes || typeof parallelRoutes !== "object" || Array.isArray(parallelRoutes)) return null;
21
+ let childPath = null;
22
+ const children = Reflect.get(parallelRoutes, "children");
23
+ if (children !== void 0) childPath = extractFlightRouterStatePath(children, depth + 1);
24
+ if (childPath === null) for (const [key, child] of Object.entries(parallelRoutes)) {
25
+ if (key === "children") continue;
26
+ childPath = extractFlightRouterStatePath(child, depth + 1);
27
+ if (childPath !== null) break;
28
+ }
29
+ return `/${[segment === "" || segment === "children" || segment.startsWith("__PAGE__") || segment.startsWith("(") && segment.endsWith(")") ? "" : segment.replace(/^\/+/, ""), ...childPath === null ? [] : childPath.split("/")].filter(Boolean).join("/")}`;
30
+ }
31
+ function parsePrefetchRouterState(value) {
32
+ if (!value) return null;
33
+ try {
34
+ const parsed = JSON.parse(decodeURIComponent(value));
35
+ if (Array.isArray(parsed)) {
36
+ const pathAndSearch = extractFlightRouterStatePath(parsed);
37
+ return pathAndSearch === null ? null : { pathAndSearch };
38
+ }
39
+ if (!parsed || typeof parsed !== "object") return null;
40
+ const pathAndSearch = Reflect.get(parsed, "pathAndSearch");
41
+ const routeId = Reflect.get(parsed, "routeId");
42
+ if (typeof pathAndSearch !== "string" || !pathAndSearch.startsWith("/") || typeof routeId !== "string" || routeId.length === 0) return null;
43
+ return { pathAndSearch };
44
+ } catch {
45
+ return null;
46
+ }
47
+ }
48
+ function normalizeComparablePathAndSearch(value, basePath, baseUrl) {
49
+ const parsed = new URL(value, baseUrl);
50
+ const pathname = basePath && hasBasePath(parsed.pathname, basePath) ? stripBasePath(parsed.pathname, basePath) : parsed.pathname;
51
+ const search = parsed.searchParams.toString();
52
+ return `${pathname}${search ? `?${search}` : ""}`;
53
+ }
54
+ function tryNormalizeComparablePathAndSearch(value, basePath, baseUrl) {
55
+ try {
56
+ return normalizeComparablePathAndSearch(value, basePath, baseUrl);
57
+ } catch {
58
+ return null;
59
+ }
60
+ }
13
61
  /**
14
62
  * Normalize an App Router RSC request.
15
63
  *
@@ -65,7 +113,20 @@ function normalizeRscRequest(request, basePath, allowOutsideBasePath = false) {
65
113
  const requestCleanPathname = stripRscSuffix(requestPathname);
66
114
  const interceptionContextHeader = normalizeInterceptionContextHeader(request.headers.get(VINEXT_INTERCEPTION_CONTEXT_HEADER));
67
115
  const mountedSlotsHeader = normalizeMountedSlotsHeader(request.headers.get(VINEXT_MOUNTED_SLOTS_HEADER));
68
- const renderMode = isRscRequest ? parseAppRscRenderMode(request.headers.get(VINEXT_RSC_RENDER_MODE_HEADER)) : APP_RSC_RENDER_MODE_NAVIGATION;
116
+ let renderMode = isRscRequest ? parseAppRscRenderMode(request.headers.get(VINEXT_RSC_RENDER_MODE_HEADER)) : APP_RSC_RENDER_MODE_NAVIGATION;
117
+ if (isRscRequest && renderMode === "navigation" && request.headers.get("Next-Router-Prefetch") === "1" && request.headers.get("Next-Router-Segment-Prefetch") === null) {
118
+ const nextUrl = request.headers.get(NEXT_URL_HEADER);
119
+ const routerState = parsePrefetchRouterState(request.headers.get(NEXT_ROUTER_STATE_TREE_HEADER));
120
+ if (nextUrl && routerState) {
121
+ const targetUrl = new URL(url);
122
+ targetUrl.pathname = cleanPathname;
123
+ targetUrl.searchParams.delete(VINEXT_RSC_CACHE_BUSTING_SEARCH_PARAM);
124
+ const routerPathAndSearch = tryNormalizeComparablePathAndSearch(routerState.pathAndSearch, basePath, url);
125
+ const nextPathAndSearch = tryNormalizeComparablePathAndSearch(nextUrl, basePath, url);
126
+ const targetPathAndSearch = normalizeComparablePathAndSearch(targetUrl.href, basePath, url);
127
+ if (routerPathAndSearch !== null && nextPathAndSearch !== null) renderMode = routerPathAndSearch === nextPathAndSearch && routerPathAndSearch === targetPathAndSearch ? APP_RSC_RENDER_MODE_PREFETCH_EMPTY : APP_RSC_RENDER_MODE_PREFETCH_LOADING_SHELL;
128
+ }
129
+ }
69
130
  return {
70
131
  clientReuseManifest: isRscRequest ? parseClientReuseManifestHeader(request.headers.get(VINEXT_CLIENT_REUSE_MANIFEST_HEADER)) : { kind: "absent" },
71
132
  hadBasePath,
@@ -1,5 +1,5 @@
1
- import { ActionRevalidationKind } from "../shims/cache-request-state.js";
2
1
  import { AppRscRenderMode } from "./app-rsc-render-mode.js";
2
+ import { ActionRevalidationKind } from "../shims/cache-request-state.js";
3
3
  import { FetchCacheMode } from "../shims/fetch-cache.js";
4
4
  import { HeadersAccessPhase } from "../shims/headers.js";
5
5
  import { ReactFormState } from "react-dom/client";
@@ -25,5 +25,10 @@ declare function isVisitedResponseCacheEntryFresh(entry: VisitedResponseCacheEnt
25
25
  navigationKind: VisitedResponseCacheNavigationKind;
26
26
  now: number;
27
27
  }): boolean;
28
+ declare function findVisitedResponseCacheEntry(cache: Map<string, VisitedResponseCacheEntry>, rscUrl: string, interceptionContext: string | null): {
29
+ cacheKey: string;
30
+ entry: VisitedResponseCacheEntry;
31
+ } | null;
32
+ declare function deleteVisitedResponseCacheEntry(cache: Map<string, VisitedResponseCacheEntry>, rscUrl: string, interceptionContext: string | null): boolean;
28
33
  //#endregion
29
- export { MAX_TRAVERSAL_CACHE_TTL, VISITED_RESPONSE_CACHE_TTL, VisitedResponseCacheEntry, createVisitedResponseCacheEntry, isVisitedResponseCacheEntryFresh };
34
+ export { MAX_TRAVERSAL_CACHE_TTL, VISITED_RESPONSE_CACHE_TTL, VisitedResponseCacheEntry, createVisitedResponseCacheEntry, deleteVisitedResponseCacheEntry, findVisitedResponseCacheEntry, isVisitedResponseCacheEntryFresh };