vinext 1.0.0-beta.1 → 1.0.0-beta.3

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 (234) hide show
  1. package/README.md +10 -10
  2. package/dist/build/client-build-config.js +8 -1
  3. package/dist/build/report.d.ts +8 -1
  4. package/dist/build/report.js +12 -2
  5. package/dist/check.js +19 -2
  6. package/dist/cli.js +3 -2
  7. package/dist/config/config-matchers.d.ts +13 -37
  8. package/dist/config/config-matchers.js +35 -122
  9. package/dist/config/next-config.d.ts +15 -5
  10. package/dist/config/next-config.js +7 -1
  11. package/dist/config/request-context.d.ts +14 -0
  12. package/dist/config/request-context.js +26 -0
  13. package/dist/entries/app-browser-entry.d.ts +4 -10
  14. package/dist/entries/app-browser-entry.js +20 -4
  15. package/dist/entries/app-rsc-entry.js +30 -9
  16. package/dist/entries/app-rsc-manifest.js +36 -0
  17. package/dist/entries/pages-client-entry.js +0 -1
  18. package/dist/entries/pages-server-entry.js +13 -5
  19. package/dist/index.js +194 -101
  20. package/dist/init-cloudflare.d.ts +3 -1
  21. package/dist/init-cloudflare.js +59 -5
  22. package/dist/init.d.ts +6 -4
  23. package/dist/init.js +20 -22
  24. package/dist/plugins/extensionless-dynamic-import.js +23 -18
  25. package/dist/plugins/ignore-dynamic-requests.js +3 -1
  26. package/dist/plugins/import-meta-url.js +17 -6
  27. package/dist/plugins/og-assets.js +1 -1
  28. package/dist/plugins/require-context.js +20 -15
  29. package/dist/plugins/transform-cache.d.ts +19 -0
  30. package/dist/plugins/transform-cache.js +36 -0
  31. package/dist/routing/app-route-graph.d.ts +27 -11
  32. package/dist/routing/app-route-graph.js +158 -22
  33. package/dist/routing/app-router.js +12 -6
  34. package/dist/routing/file-matcher.d.ts +11 -22
  35. package/dist/routing/file-matcher.js +21 -33
  36. package/dist/routing/pages-router.js +23 -29
  37. package/dist/routing/route-matching.d.ts +14 -1
  38. package/dist/routing/route-matching.js +13 -1
  39. package/dist/routing/route-pattern.d.ts +2 -1
  40. package/dist/routing/route-pattern.js +7 -4
  41. package/dist/routing/route-trie.d.ts +5 -1
  42. package/dist/routing/route-trie.js +5 -2
  43. package/dist/server/api-handler.d.ts +2 -0
  44. package/dist/server/api-handler.js +3 -3
  45. package/dist/server/app-browser-entry.js +110 -37
  46. package/dist/server/app-browser-history-controller.d.ts +1 -1
  47. package/dist/server/app-browser-history-controller.js +3 -3
  48. package/dist/server/app-browser-navigation-controller.d.ts +23 -21
  49. package/dist/server/app-browser-navigation-controller.js +2 -2
  50. package/dist/server/app-browser-state.js +1 -1
  51. package/dist/server/app-browser-visible-commit.js +1 -1
  52. package/dist/server/app-fallback-renderer.d.ts +3 -0
  53. package/dist/server/app-fallback-renderer.js +4 -2
  54. package/dist/server/app-middleware.js +2 -1
  55. package/dist/server/app-page-boundary-render.d.ts +12 -1
  56. package/dist/server/app-page-boundary-render.js +84 -2
  57. package/dist/server/app-page-cache-finalizer.d.ts +1 -1
  58. package/dist/server/app-page-cache-finalizer.js +1 -1
  59. package/dist/server/app-page-cache.d.ts +2 -2
  60. package/dist/server/app-page-cache.js +16 -2
  61. package/dist/server/app-page-dispatch.d.ts +19 -2
  62. package/dist/server/app-page-dispatch.js +45 -23
  63. package/dist/server/app-page-element-builder.d.ts +9 -2
  64. package/dist/server/app-page-element-builder.js +144 -33
  65. package/dist/server/app-page-head.d.ts +56 -4
  66. package/dist/server/app-page-head.js +216 -85
  67. package/dist/server/app-page-http-access-fallback-metadata.d.ts +29 -0
  68. package/dist/server/app-page-http-access-fallback-metadata.js +114 -0
  69. package/dist/server/app-page-probe.d.ts +4 -0
  70. package/dist/server/app-page-probe.js +6 -2
  71. package/dist/server/app-page-render.d.ts +2 -2
  72. package/dist/server/app-page-render.js +1 -1
  73. package/dist/server/app-page-request.d.ts +15 -0
  74. package/dist/server/app-page-request.js +134 -14
  75. package/dist/server/app-page-response.js +1 -1
  76. package/dist/server/app-page-route-wiring.d.ts +16 -0
  77. package/dist/server/app-page-route-wiring.js +254 -52
  78. package/dist/server/app-pages-bridge.d.ts +1 -1
  79. package/dist/server/app-pages-bridge.js +2 -1
  80. package/dist/server/app-post-middleware-context.d.ts +1 -1
  81. package/dist/server/app-post-middleware-context.js +1 -1
  82. package/dist/server/app-route-handler-cache.js +4 -0
  83. package/dist/server/app-route-handler-dispatch.js +40 -20
  84. package/dist/server/app-route-handler-execution.d.ts +6 -1
  85. package/dist/server/app-route-handler-execution.js +42 -16
  86. package/dist/server/app-route-handler-policy.d.ts +2 -0
  87. package/dist/server/app-route-handler-policy.js +3 -3
  88. package/dist/server/app-route-handler-response.js +1 -1
  89. package/dist/server/app-route-module-loader.d.ts +9 -1
  90. package/dist/server/app-route-module-loader.js +4 -0
  91. package/dist/server/app-router-entry.js +6 -3
  92. package/dist/server/app-rsc-cache-busting.d.ts +1 -1
  93. package/dist/server/app-rsc-errors.d.ts +1 -1
  94. package/dist/server/app-rsc-errors.js +22 -2
  95. package/dist/server/app-rsc-handler.d.ts +10 -5
  96. package/dist/server/app-rsc-handler.js +73 -33
  97. package/dist/server/app-rsc-request-normalization.d.ts +3 -2
  98. package/dist/server/app-rsc-request-normalization.js +8 -2
  99. package/dist/server/app-rsc-response-finalizer.d.ts +2 -2
  100. package/dist/server/app-rsc-response-finalizer.js +5 -11
  101. package/dist/server/app-rsc-route-matching.d.ts +29 -0
  102. package/dist/server/app-rsc-route-matching.js +93 -11
  103. package/dist/server/app-segment-config.js +13 -3
  104. package/dist/server/app-server-action-execution.d.ts +3 -1
  105. package/dist/server/app-server-action-execution.js +9 -7
  106. package/dist/server/app-ssr-entry.js +6 -6
  107. package/dist/server/app-ssr-stream.d.ts +1 -7
  108. package/dist/server/app-ssr-stream.js +2 -10
  109. package/dist/server/cache-control.d.ts +2 -2
  110. package/dist/server/cache-control.js +2 -1
  111. package/dist/server/config-headers.d.ts +24 -0
  112. package/dist/server/config-headers.js +52 -0
  113. package/dist/server/dev-initial-server-error.d.ts +1 -1
  114. package/dist/server/dev-server.d.ts +3 -1
  115. package/dist/server/dev-server.js +170 -321
  116. package/dist/server/headers.d.ts +2 -2
  117. package/dist/server/headers.js +4 -3
  118. package/dist/server/isr-cache.d.ts +11 -34
  119. package/dist/server/isr-cache.js +33 -43
  120. package/dist/server/isr-decision.d.ts +2 -2
  121. package/dist/server/middleware-matcher-pattern.d.ts +22 -0
  122. package/dist/server/middleware-matcher-pattern.js +219 -0
  123. package/dist/server/middleware-matcher.d.ts +2 -7
  124. package/dist/server/middleware-matcher.js +13 -60
  125. package/dist/server/middleware-path-to-regexp.d.ts +14 -0
  126. package/dist/server/middleware-path-to-regexp.js +228 -0
  127. package/dist/server/middleware-runtime.js +4 -3
  128. package/dist/server/middleware.d.ts +1 -1
  129. package/dist/server/middleware.js +2 -1
  130. package/dist/server/pages-api-route.d.ts +2 -0
  131. package/dist/server/pages-api-route.js +4 -1
  132. package/dist/server/pages-data-route.d.ts +11 -1
  133. package/dist/server/pages-data-route.js +16 -1
  134. package/dist/server/pages-dev-hydration.js +0 -1
  135. package/dist/server/pages-get-initial-props.d.ts +16 -3
  136. package/dist/server/pages-get-initial-props.js +17 -10
  137. package/dist/server/pages-i18n.js +64 -16
  138. package/dist/server/pages-node-compat.d.ts +2 -0
  139. package/dist/server/pages-node-compat.js +7 -3
  140. package/dist/server/pages-page-data.d.ts +64 -10
  141. package/dist/server/pages-page-data.js +349 -80
  142. package/dist/server/pages-page-handler.d.ts +9 -3
  143. package/dist/server/pages-page-handler.js +131 -36
  144. package/dist/server/pages-page-response.d.ts +5 -3
  145. package/dist/server/pages-page-response.js +24 -27
  146. package/dist/server/pages-readiness.d.ts +3 -3
  147. package/dist/server/pages-request-pipeline.d.ts +8 -0
  148. package/dist/server/pages-request-pipeline.js +30 -12
  149. package/dist/server/pages-revalidate.d.ts +1 -1
  150. package/dist/server/pages-revalidate.js +54 -8
  151. package/dist/server/pages-router-entry.d.ts +1 -0
  152. package/dist/server/pages-router-entry.js +16 -4
  153. package/dist/server/prerender-route-params.js +2 -2
  154. package/dist/server/prod-server.d.ts +3 -5
  155. package/dist/server/prod-server.js +72 -35
  156. package/dist/server/request-pipeline.d.ts +15 -29
  157. package/dist/server/request-pipeline.js +24 -59
  158. package/dist/server/revalidation-host.d.ts +11 -0
  159. package/dist/server/revalidation-host.js +19 -0
  160. package/dist/server/rsc-stream-hints.js +122 -18
  161. package/dist/server/worker-revalidation-context.d.ts +13 -0
  162. package/dist/server/worker-revalidation-context.js +29 -0
  163. package/dist/shims/app.d.ts +1 -1
  164. package/dist/shims/cache-handler.d.ts +1 -1
  165. package/dist/shims/cache-handler.js +10 -7
  166. package/dist/shims/cache-request-state.d.ts +27 -1
  167. package/dist/shims/cache-request-state.js +60 -1
  168. package/dist/shims/cache-runtime.d.ts +18 -1
  169. package/dist/shims/cache-runtime.js +26 -8
  170. package/dist/shims/cache.d.ts +5 -5
  171. package/dist/shims/cache.js +27 -11
  172. package/dist/shims/document.d.ts +42 -83
  173. package/dist/shims/document.js +62 -53
  174. package/dist/shims/dynamic.d.ts +5 -25
  175. package/dist/shims/dynamic.js +29 -8
  176. package/dist/shims/error.d.ts +1 -1
  177. package/dist/shims/error.js +2 -2
  178. package/dist/shims/fetch-cache.js +13 -4
  179. package/dist/shims/font-google-base.d.ts +11 -4
  180. package/dist/shims/font-google-base.js +17 -8
  181. package/dist/shims/font-local.d.ts +10 -3
  182. package/dist/shims/font-local.js +41 -10
  183. package/dist/shims/head.d.ts +13 -2
  184. package/dist/shims/head.js +3 -3
  185. package/dist/shims/headers.d.ts +37 -43
  186. package/dist/shims/headers.js +183 -74
  187. package/dist/shims/image.d.ts +27 -37
  188. package/dist/shims/image.js +21 -14
  189. package/dist/shims/internal/cookie-serialize.d.ts +12 -9
  190. package/dist/shims/internal/cookie-serialize.js +10 -7
  191. package/dist/shims/internal/hybrid-client-route-owner.js +3 -1
  192. package/dist/shims/internal/interpolate-as.d.ts +15 -1
  193. package/dist/shims/internal/interpolate-as.js +27 -2
  194. package/dist/shims/internal/utils.d.ts +3 -3
  195. package/dist/shims/legacy-image.d.ts +3 -37
  196. package/dist/shims/legacy-image.js +6 -16
  197. package/dist/shims/link.d.ts +15 -22
  198. package/dist/shims/link.js +34 -13
  199. package/dist/shims/metadata.d.ts +7 -17
  200. package/dist/shims/metadata.js +74 -24
  201. package/dist/shims/navigation-context-state.d.ts +1 -1
  202. package/dist/shims/navigation.d.ts +7 -3
  203. package/dist/shims/navigation.js +25 -14
  204. package/dist/shims/navigation.react-server.js +1 -1
  205. package/dist/shims/next-shims-augmentations.d.ts +1 -0
  206. package/dist/shims/next-shims-public.d.ts +1 -0
  207. package/dist/shims/public-shim-map.json.js +103 -0
  208. package/dist/shims/readonly-url-search-params.d.ts +3 -3
  209. package/dist/shims/readonly-url-search-params.js +3 -3
  210. package/dist/shims/request-context.d.ts +4 -1
  211. package/dist/shims/request-state-types.d.ts +1 -1
  212. package/dist/shims/router.d.ts +55 -21
  213. package/dist/shims/router.js +180 -44
  214. package/dist/shims/script.d.ts +6 -10
  215. package/dist/shims/script.js +7 -4
  216. package/dist/shims/server.d.ts +50 -26
  217. package/dist/shims/server.js +193 -55
  218. package/dist/shims/unified-request-context.d.ts +22 -3
  219. package/dist/shims/unified-request-context.js +84 -1
  220. package/dist/shims/url-safety.d.ts +1 -1
  221. package/dist/shims/url-safety.js +2 -2
  222. package/dist/typegen.d.ts +8 -2
  223. package/dist/typegen.js +76 -15
  224. package/dist/utils/cache-control-metadata.d.ts +2 -1
  225. package/dist/utils/cache-control-metadata.js +5 -1
  226. package/dist/utils/domain-locale.d.ts +2 -2
  227. package/dist/utils/external-url.d.ts +5 -0
  228. package/dist/utils/external-url.js +7 -0
  229. package/dist/utils/html-limited-bots.js +1 -1
  230. package/dist/utils/protocol-headers.d.ts +7 -1
  231. package/dist/utils/protocol-headers.js +7 -1
  232. package/dist/utils/regex-safety.d.ts +8 -0
  233. package/dist/utils/regex-safety.js +737 -0
  234. package/package.json +13 -2
@@ -26,35 +26,60 @@ function resolveActiveParallelRouteHeadInputs(options) {
26
26
  return Object.entries(options.slots ?? {}).map(([slotKey, slot]) => {
27
27
  const ownerTreePosition = options.layoutTreePositions?.[slot.layoutIndex ?? 0] ?? 0;
28
28
  const ownerParams = resolveAppPageSegmentParams(options.routeSegments, ownerTreePosition, options.params);
29
+ const slotParams = options.slotParams?.[slotKey] ?? options.params;
30
+ const notFoundParams = slot.notFound ? {
31
+ ...ownerParams,
32
+ ...resolveParallelLayoutParams(slot.routeSegments ?? options.routeSegments, slot.notFoundTreePosition ?? 0, slotParams)
33
+ } : null;
29
34
  if (options.interceptSlotKey === slotKey && options.interceptPage) {
30
35
  const interceptLayouts = options.interceptLayouts ?? [];
36
+ const inheritedSlotNotFound = slot.notFoundTreePosition === 0 ? slot.notFound ?? null : null;
37
+ const interceptNotFound = options.interceptNotFound ?? inheritedSlotNotFound;
38
+ const interceptNotFoundParams = interceptNotFound ? {
39
+ ...ownerParams,
40
+ ...resolveParallelLayoutParams(options.interceptNotFoundBranchSegments ?? options.interceptBranchSegments ?? options.routeSegments, options.interceptNotFound ? options.interceptNotFoundTreePosition ?? 0 : slot.notFoundTreePosition ?? 0, options.interceptParams ?? options.params)
41
+ } : null;
31
42
  return {
32
- layoutModules: [slot.layout, ...interceptLayouts].filter(isPresent),
33
- layoutParams: [...slot.layout ? [ownerParams] : [], ...interceptLayouts.filter(isPresent).map((_, index) => {
34
- const segments = options.interceptLayoutSegments?.[index] ?? [];
35
- return {
36
- ...ownerParams,
37
- ...resolveParallelLayoutParams(options.interceptBranchSegments ?? segments, segments.length, options.interceptParams ?? options.params)
38
- };
39
- })],
40
- layoutTreePositions: [...slot.layout ? [0] : [], ...interceptLayouts.filter(isPresent).map(() => options.routeSegments.length)],
41
- pageModule: options.interceptPage,
42
- params: options.interceptParams ?? options.params,
43
- routeSegments: options.routeSegments
43
+ head: {
44
+ layoutModules: [slot.layout, ...interceptLayouts].filter(isPresent),
45
+ layoutParams: [...slot.layout ? [ownerParams] : [], ...interceptLayouts.filter(isPresent).map((_, index) => {
46
+ const segments = options.interceptLayoutSegments?.[index] ?? [];
47
+ return {
48
+ ...ownerParams,
49
+ ...resolveParallelLayoutParams(options.interceptBranchSegments ?? segments, segments.length, options.interceptParams ?? options.params)
50
+ };
51
+ })],
52
+ layoutTreePositions: [...slot.layout ? [0] : [], ...interceptLayouts.filter(isPresent).map(() => options.routeSegments.length)],
53
+ pageModule: options.interceptPage,
54
+ params: options.interceptParams ?? options.params,
55
+ routeSegments: options.interceptSourcePageSegments ?? options.routeSegments
56
+ },
57
+ ...interceptNotFound ? {
58
+ notFoundModule: interceptNotFound,
59
+ notFoundParams: interceptNotFoundParams
60
+ } : {},
61
+ ownerTreePosition
44
62
  };
45
63
  }
46
64
  return {
47
- layoutModules: [slot.layout, ...slot.configLayouts ?? []].filter(isPresent),
48
- layoutParams: [...slot.layout ? [ownerParams] : [], ...(slot.configLayoutTreePositions ?? []).map((treePosition) => ({
49
- ...ownerParams,
50
- ...resolveParallelLayoutParams(slot.routeSegments ?? options.routeSegments, treePosition, options.slotParams?.[slotKey] ?? options.params)
51
- }))],
52
- layoutTreePositions: [...slot.layout ? [0] : [], ...slot.configLayoutTreePositions ?? []],
53
- pageModule: slot.page,
54
- params: options.slotParams?.[slotKey] ?? options.params,
55
- routeSegments: slot.routeSegments ?? options.routeSegments
65
+ head: {
66
+ layoutModules: [slot.layout, ...slot.configLayouts ?? []].filter(isPresent),
67
+ layoutParams: [...slot.layout ? [ownerParams] : [], ...(slot.configLayoutTreePositions ?? []).map((treePosition) => ({
68
+ ...ownerParams,
69
+ ...resolveParallelLayoutParams(slot.routeSegments ?? options.routeSegments, treePosition, options.slotParams?.[slotKey] ?? options.params)
70
+ }))],
71
+ layoutTreePositions: [...slot.layout ? [0] : [], ...slot.configLayoutTreePositions ?? []],
72
+ pageModule: slot.page,
73
+ params: slotParams,
74
+ routeSegments: slot.routeSegments ?? options.routeSegments
75
+ },
76
+ ...slot.notFound ? {
77
+ notFoundModule: slot.notFound,
78
+ notFoundParams
79
+ } : {},
80
+ ownerTreePosition
56
81
  };
57
- });
82
+ }).sort((left, right) => right.ownerTreePosition - left.ownerTreePosition);
58
83
  }
59
84
  function isPresent(value) {
60
85
  return value !== null && value !== void 0;
@@ -97,6 +122,20 @@ function createMetadataSources(metadataResults, routeSegments, layoutTreePositio
97
122
  });
98
123
  return metadataSources;
99
124
  }
125
+ async function finalizeAppPageMetadata(metadata, metadataSources, options) {
126
+ let resolvedMetadata = metadata;
127
+ if (options.applyFileBasedMetadata && options.metadataRoutes.length > 0) try {
128
+ resolvedMetadata = await options.applyFileBasedMetadata(metadata, options.routePath, options.params, options.metadataRoutes, {
129
+ routeSegments: options.routeSegments ?? [],
130
+ metadataSources,
131
+ basePath: options.basePath ?? ""
132
+ });
133
+ } catch (error) {
134
+ if (!options.fallbackOnFileMetadataError) throw error;
135
+ console.error(`[vinext] File-based metadata resolution failed while rendering error boundary for ${options.routePath}:`, error);
136
+ }
137
+ return resolvedMetadata ? postProcessMetadata(resolvedMetadata) : null;
138
+ }
100
139
  function createLayoutInputs(layoutModules, layoutTreePositions) {
101
140
  const layoutInputs = [];
102
141
  for (let index = 0; index < layoutModules.length; index++) {
@@ -126,27 +165,41 @@ async function resolveLayoutMetadata(layoutInputs, params, routeSegments) {
126
165
  }
127
166
  return Promise.all(layoutMetadataPromises);
128
167
  }
129
- async function resolveLayoutViewport(layoutInputs, params, routeSegments) {
130
- return Promise.all(layoutInputs.map((layoutInput) => {
168
+ function resolveLayoutViewport(layoutInputs, params, routeSegments) {
169
+ const viewportPromises = [];
170
+ let accumulatedViewport = Promise.resolve(mergeViewport([]));
171
+ for (const layoutInput of layoutInputs) {
172
+ const parentForLayout = accumulatedViewport;
131
173
  const layoutParams = resolveAppPageSegmentParams(routeSegments, layoutInput.treePosition, params);
132
- return resolveModuleViewport(layoutInput.module, layoutParams);
133
- }));
174
+ const viewportPromise = resolveModuleViewport(layoutInput.module, layoutParams, void 0, parentForLayout);
175
+ viewportPromises.push(viewportPromise);
176
+ viewportPromise.catch(() => null);
177
+ accumulatedViewport = mergeResolvedViewport(parentForLayout, viewportPromise);
178
+ accumulatedViewport.catch(() => null);
179
+ }
180
+ return {
181
+ resolvedViewport: accumulatedViewport,
182
+ viewportResults: Promise.all(viewportPromises)
183
+ };
184
+ }
185
+ function mergeResolvedViewport(parent, viewport) {
186
+ return Promise.all([parent, viewport]).then(([resolvedParent, resolvedViewport]) => resolvedViewport ? mergeViewport([resolvedParent, resolvedViewport]) : resolvedParent);
187
+ }
188
+ function getParallelRouteModules(parallelRoute) {
189
+ return [...parallelRoute.layoutModules ?? [], parallelRoute.layoutModule].filter(isPresent);
134
190
  }
135
- async function resolveParallelRouteHead(parallelRoute, fallbackParams, fallbackRouteSegments, pageSearchParams, parent, searchParamsObserver) {
191
+ function parallelRouteHasDynamicMetadata(parallelRoute) {
192
+ return getParallelRouteModules(parallelRoute).some(hasGenerateMetadata) || hasGenerateMetadata(parallelRoute.pageModule);
193
+ }
194
+ async function resolveParallelRouteMetadata(parallelRoute, fallbackParams, fallbackRouteSegments, pageSearchParams, parent, searchParamsObserver) {
136
195
  const params = parallelRoute.params ?? fallbackParams;
137
196
  const routeSegments = parallelRoute.routeSegments ?? fallbackRouteSegments;
138
197
  const metadataResults = [];
139
- const viewportResults = [];
140
198
  const metadataSources = [];
141
199
  let accumulatedMetadata = parent;
142
- const layoutModules = [...parallelRoute.layoutModules ?? [], parallelRoute.layoutModule].filter(isPresent);
200
+ const layoutModules = getParallelRouteModules(parallelRoute);
143
201
  const layoutTreePositions = parallelRoute.layoutTreePositions ?? [];
144
202
  const layoutParams = parallelRoute.layoutParams ?? [];
145
- const hasDynamicMetadata = layoutModules.some(hasGenerateMetadata) || hasGenerateMetadata(parallelRoute.pageModule);
146
- const layoutViewportPromises = layoutModules.map((layoutModule, index) => resolveModuleViewport(layoutModule, layoutParams[index] ?? resolveParallelLayoutParams(routeSegments, layoutTreePositions[index] ?? 0, params)));
147
- const pageViewportPromise = parallelRoute.pageModule ? resolveModuleViewport(parallelRoute.pageModule, params, pageSearchParams, searchParamsObserver) : Promise.resolve(null);
148
- for (const layoutViewportPromise of layoutViewportPromises) layoutViewportPromise.catch(() => null);
149
- pageViewportPromise.catch(() => null);
150
203
  for (const [index, layoutModule] of layoutModules.entries()) {
151
204
  const layoutMetadata = await resolveModuleMetadata(layoutModule, layoutParams[index] ?? resolveParallelLayoutParams(routeSegments, layoutTreePositions[index] ?? 0, params), void 0, accumulatedMetadata);
152
205
  metadataResults.push(layoutMetadata);
@@ -167,20 +220,90 @@ async function resolveParallelRouteHead(parallelRoute, fallbackParams, fallbackR
167
220
  routeSegments
168
221
  });
169
222
  }
170
- viewportResults.push(...await Promise.all(layoutViewportPromises));
171
- const pageViewport = await pageViewportPromise;
172
- if (parallelRoute.pageModule) viewportResults.push(pageViewport);
173
223
  return {
174
- hasDynamicMetadata,
175
224
  metadataResults,
176
- metadataSources,
177
- viewportResults
225
+ metadataSources
178
226
  };
179
227
  }
228
+ function resolveParallelRouteViewport(parallelRoute, fallbackParams, fallbackRouteSegments, pageSearchParams, parent, searchParamsObserver) {
229
+ const params = parallelRoute.params ?? fallbackParams;
230
+ const routeSegments = parallelRoute.routeSegments ?? fallbackRouteSegments;
231
+ const layoutModules = getParallelRouteModules(parallelRoute);
232
+ const layoutTreePositions = parallelRoute.layoutTreePositions ?? [];
233
+ const layoutParams = parallelRoute.layoutParams ?? [];
234
+ const viewportPromises = [];
235
+ let accumulatedViewport = parent;
236
+ for (const [index, layoutModule] of layoutModules.entries()) {
237
+ const parentForLayout = accumulatedViewport;
238
+ const viewportPromise = resolveModuleViewport(layoutModule, layoutParams[index] ?? resolveParallelLayoutParams(routeSegments, layoutTreePositions[index] ?? 0, params), void 0, parentForLayout);
239
+ viewportPromises.push(viewportPromise);
240
+ viewportPromise.catch(() => null);
241
+ accumulatedViewport = mergeResolvedViewport(parentForLayout, viewportPromise);
242
+ accumulatedViewport.catch(() => null);
243
+ }
244
+ if (parallelRoute.pageModule) {
245
+ const parentForPage = accumulatedViewport;
246
+ const viewportPromise = resolveModuleViewport(parallelRoute.pageModule, params, pageSearchParams, parentForPage, searchParamsObserver);
247
+ viewportPromises.push(viewportPromise);
248
+ viewportPromise.catch(() => null);
249
+ accumulatedViewport = mergeResolvedViewport(parentForPage, viewportPromise);
250
+ accumulatedViewport.catch(() => null);
251
+ }
252
+ return {
253
+ resolvedViewport: accumulatedViewport,
254
+ viewportResults: Promise.all(viewportPromises)
255
+ };
256
+ }
257
+ /**
258
+ * Resolve an explicit metadata-source sequence.
259
+ *
260
+ * Route-specific conventions own source selection and ordering. This resolver
261
+ * only supplies each source with its accumulated parent, merges the results,
262
+ * and applies file-based metadata at the end.
263
+ */
264
+ function resolveOrderedAppPageMetadata(options) {
265
+ return runWithFetchDedupe(async () => {
266
+ const metadataPromises = [];
267
+ let accumulatedEntriesPromise = Promise.resolve([]);
268
+ for (const source of options.sources) {
269
+ const parentPromise = accumulatedEntriesPromise.then((entries) => entries.length > 0 ? mergeMetadataEntries(entries) : {});
270
+ const metadataPromise = resolveModuleMetadata(source.module, source.params, source.searchParams, parentPromise, source.searchParamsObserver);
271
+ metadataPromises.push(metadataPromise);
272
+ metadataPromise.catch(() => null);
273
+ accumulatedEntriesPromise = Promise.all([accumulatedEntriesPromise, metadataPromise]).then(([entries, metadata]) => metadata ? [...entries, { metadata }] : entries);
274
+ accumulatedEntriesPromise.catch(() => null);
275
+ }
276
+ const [metadataEntries, metadataResults] = await Promise.all([accumulatedEntriesPromise, Promise.all(metadataPromises)]);
277
+ const metadataSources = options.sources.flatMap((source, index) => {
278
+ const metadata = metadataResults[index] ?? null;
279
+ return metadata || source.includeWhenEmpty ? [{
280
+ metadata,
281
+ routeSegments: source.routeSegments
282
+ }] : [];
283
+ });
284
+ return finalizeAppPageMetadata(metadataEntries.length > 0 ? mergeMetadataEntries(metadataEntries) : null, metadataSources, options);
285
+ });
286
+ }
180
287
  async function resolveAppPageHead(options) {
181
- return await runWithFetchDedupe(() => resolveAppPageHeadInner(options));
288
+ const prepared = prepareAppPageHead(options);
289
+ const [metadata, viewport] = await Promise.all([prepared.metadata, prepared.viewport]);
290
+ return {
291
+ ...prepared,
292
+ metadata,
293
+ viewport
294
+ };
295
+ }
296
+ /**
297
+ * Start metadata and viewport resolution without coupling their completion.
298
+ *
299
+ * Live document renders can place the metadata promise behind Suspense while
300
+ * still waiting for viewport tags before the shell is emitted. Blocking
301
+ * callers use {@link resolveAppPageHead} and observe the same result as before.
302
+ */
303
+ function prepareAppPageHead(options) {
304
+ return runWithFetchDedupe(() => prepareAppPageHeadInner(options));
182
305
  }
183
- async function resolveAppPageHeadInner(options) {
306
+ function prepareAppPageHeadInner(options) {
184
307
  const routeSegments = options.routeSegments ?? [];
185
308
  const layoutTreePositions = options.layoutTreePositions ?? [];
186
309
  const layoutInputs = createLayoutInputs(options.layoutModules, layoutTreePositions);
@@ -188,64 +311,72 @@ async function resolveAppPageHeadInner(options) {
188
311
  const primaryHasDynamicMetadata = layoutInputs.some((input) => hasGenerateMetadata(input.module)) || hasGenerateMetadata(options.pageModule);
189
312
  const { hasSearchParams, pageSearchParams } = collectAppPageSearchParams(options.searchParams);
190
313
  const layoutMetadataPromise = resolveLayoutMetadata(layoutInputs, options.params, routeSegments);
191
- const layoutViewportPromise = resolveLayoutViewport(layoutInputs, options.params, routeSegments);
314
+ const layoutViewport = resolveLayoutViewport(layoutInputs, options.params, routeSegments);
315
+ const layoutViewportPromise = layoutViewport.viewportResults;
192
316
  const layoutMetadataResultsForParent = layoutMetadataPromise.then((metadataResults) => metadataResults.filter(isPresent));
193
317
  layoutMetadataResultsForParent.catch(() => null);
194
318
  const pageParentPromise = layoutMetadataResultsForParent.then((metadataResults) => metadataResults.length > 0 ? mergeMetadataEntries(metadataResults.map((metadata) => ({ metadata }))) : {});
195
319
  pageParentPromise.catch(() => null);
196
320
  const pageMetadataPromise = options.pageModule ? resolveModuleMetadata(options.pageModule, options.params, pageSearchParams, pageParentPromise, options.searchParamsObserver) : Promise.resolve(null);
197
- const pageViewportPromise = options.pageModule ? resolveModuleViewport(options.pageModule, options.params, pageSearchParams, options.searchParamsObserver) : Promise.resolve(null);
198
- const parallelRouteHeadPromise = Promise.all((options.parallelRoutes ?? []).map((parallelRoute) => resolveParallelRouteHead(parallelRoute, options.params, routeSegments, pageSearchParams, pageParentPromise, options.searchParamsObserver)));
199
- const [layoutMetadataResults, layoutViewportResults, pageMetadata, pageViewport, parallelRouteHeads] = await Promise.all([
321
+ const parallelRoutes = options.parallelRoutes ?? [];
322
+ const parallelRouteMetadataPromise = Promise.all(parallelRoutes.map((parallelRoute) => resolveParallelRouteMetadata(parallelRoute, options.params, routeSegments, pageSearchParams, pageParentPromise, options.searchParamsObserver)));
323
+ const parallelRouteViewportPromises = [];
324
+ let accumulatedViewport = layoutViewport.resolvedViewport;
325
+ const pageParentViewport = accumulatedViewport;
326
+ const pageViewportPromise = options.pageModule ? resolveModuleViewport(options.pageModule, options.params, pageSearchParams, pageParentViewport, options.searchParamsObserver) : Promise.resolve(null);
327
+ if (options.pageModule) {
328
+ accumulatedViewport = mergeResolvedViewport(pageParentViewport, pageViewportPromise);
329
+ accumulatedViewport.catch(() => null);
330
+ }
331
+ for (const parallelRoute of parallelRoutes) {
332
+ const parallelViewport = resolveParallelRouteViewport(parallelRoute, options.params, routeSegments, pageSearchParams, accumulatedViewport, options.searchParamsObserver);
333
+ parallelRouteViewportPromises.push(parallelViewport.viewportResults);
334
+ accumulatedViewport = parallelViewport.resolvedViewport;
335
+ }
336
+ const parallelRouteViewportPromise = Promise.all(parallelRouteViewportPromises);
337
+ const hasDynamicMetadata = primaryHasDynamicMetadata || parallelRoutes.some(parallelRouteHasDynamicMetadata);
338
+ const metadata = Promise.all([
200
339
  layoutMetadataPromise,
201
- layoutViewportPromise,
202
340
  pageMetadataPromise,
341
+ parallelRouteMetadataPromise
342
+ ]).then(async ([layoutMetadataResults, pageMetadata, parallelRouteMetadata]) => {
343
+ const parallelMetadataResults = parallelRouteMetadata.flatMap((head) => head.metadataResults);
344
+ const parallelMetadataSources = parallelRouteMetadata.flatMap((head) => head.metadataSources);
345
+ const primaryPageHasTitle = pageMetadata != null && pageMetadata.title !== void 0;
346
+ const metadataEntries = [
347
+ ...layoutMetadataResults.filter(isPresent).map((entry) => ({ metadata: entry })),
348
+ ...pageMetadata ? [{
349
+ isPage: true,
350
+ metadata: pageMetadata
351
+ }] : [],
352
+ ...parallelMetadataResults.filter(isPresent).map((entry) => ({
353
+ contributesTitle: !primaryPageHasTitle,
354
+ metadata: entry
355
+ }))
356
+ ];
357
+ const resolvedMetadataBase = metadataEntries.length > 0 ? mergeMetadataEntries(metadataEntries) : null;
358
+ const metadataSources = createMetadataSources(layoutMetadataResults, routeSegments, layoutSourcePositions, pageMetadata, Boolean(options.pageModule));
359
+ metadataSources.push(...parallelMetadataSources);
360
+ return finalizeAppPageMetadata(resolvedMetadataBase, metadataSources, options);
361
+ });
362
+ const viewport = Promise.all([
363
+ layoutViewportPromise,
203
364
  pageViewportPromise,
204
- parallelRouteHeadPromise
205
- ]);
206
- const parallelMetadataResults = parallelRouteHeads.flatMap((head) => head.metadataResults);
207
- const parallelViewportResults = parallelRouteHeads.flatMap((head) => head.viewportResults);
208
- const parallelMetadataSources = parallelRouteHeads.flatMap((head) => head.metadataSources);
209
- const hasDynamicMetadata = primaryHasDynamicMetadata || parallelRouteHeads.some((head) => head.hasDynamicMetadata);
210
- const primaryPageHasTitle = pageMetadata != null && pageMetadata.title !== void 0;
211
- const metadataEntries = [
212
- ...layoutMetadataResults.filter(isPresent).map((metadata) => ({ metadata })),
213
- ...pageMetadata ? [{
214
- isPage: true,
215
- metadata: pageMetadata
216
- }] : [],
217
- ...parallelMetadataResults.filter(isPresent).map((metadata) => ({
218
- contributesTitle: !primaryPageHasTitle,
219
- metadata
220
- }))
221
- ];
222
- const viewportList = [
365
+ parallelRouteViewportPromise
366
+ ]).then(([layoutViewportResults, pageViewport, parallelRouteViewports]) => mergeViewport([
223
367
  ...layoutViewportResults.filter(isPresent),
224
368
  ...pageViewport ? [pageViewport] : [],
225
- ...parallelViewportResults.filter(isPresent)
226
- ];
227
- const resolvedMetadataBase = metadataEntries.length > 0 ? mergeMetadataEntries(metadataEntries) : null;
228
- const metadataSources = createMetadataSources(layoutMetadataResults, routeSegments, layoutSourcePositions, pageMetadata, Boolean(options.pageModule));
229
- metadataSources.push(...parallelMetadataSources);
230
- let metadata = resolvedMetadataBase;
231
- if (options.applyFileBasedMetadata && options.metadataRoutes.length > 0) try {
232
- metadata = await options.applyFileBasedMetadata(resolvedMetadataBase, options.routePath, options.params, options.metadataRoutes, {
233
- routeSegments,
234
- metadataSources,
235
- basePath: options.basePath ?? ""
236
- });
237
- } catch (error) {
238
- if (!options.fallbackOnFileMetadataError) throw error;
239
- console.error(`[vinext] File-based metadata resolution failed while rendering error boundary for ${options.routePath}:`, error);
240
- }
241
- if (metadata) metadata = postProcessMetadata(metadata);
369
+ ...parallelRouteViewports.flat().filter(isPresent)
370
+ ]));
371
+ metadata.catch(() => null);
372
+ viewport.catch(() => null);
242
373
  return {
243
374
  hasDynamicMetadata,
244
375
  hasSearchParams,
245
376
  metadata,
246
377
  pageSearchParams,
247
- viewport: mergeViewport(viewportList)
378
+ viewport
248
379
  };
249
380
  }
250
381
  //#endregion
251
- export { collectAppPageSearchParams, resolveActiveParallelRouteHeadInputs, resolveAppPageHead };
382
+ export { collectAppPageSearchParams, prepareAppPageHead, resolveActiveParallelRouteHeadInputs, resolveAppPageHead, resolveOrderedAppPageMetadata };
@@ -0,0 +1,29 @@
1
+ import { MetadataFileRoute } from "./metadata-routes.js";
2
+ import { AppPageParams } from "./app-page-boundary.js";
3
+ import { Metadata, ResolvedViewport } from "../shims/metadata.js";
4
+ import { ActiveParallelRouteHeadInput, AppPageHeadModule, ApplyAppPageFileBasedMetadata, OrderedAppPageMetadataSource } from "./app-page-head.js";
5
+
6
+ //#region src/server/app-page-http-access-fallback-metadata.d.ts
7
+ type HttpAccessFallbackMetadataPlanOptions<TModule extends AppPageHeadModule = AppPageHeadModule> = {
8
+ boundaryModule?: TModule | null;
9
+ boundaryParams: AppPageParams; /** Whether active branches may replace the fallback with their local not-found convention. */
10
+ branchNotFoundConventions?: boolean;
11
+ layoutModules: readonly (TModule | null | undefined)[];
12
+ layoutTreePositions?: readonly number[] | null;
13
+ parallelBranches?: readonly ActiveParallelRouteHeadInput<TModule>[] | null;
14
+ params: AppPageParams;
15
+ primaryParallelBranch?: ActiveParallelRouteHeadInput<TModule> | null;
16
+ routeSegments?: readonly string[] | null;
17
+ };
18
+ type ResolveHttpAccessFallbackMetadataOptions<TModule extends AppPageHeadModule = AppPageHeadModule> = HttpAccessFallbackMetadataPlanOptions<TModule> & {
19
+ applyFileBasedMetadata?: ApplyAppPageFileBasedMetadata;
20
+ basePath?: string;
21
+ fallbackOnFileMetadataError?: boolean;
22
+ metadataRoutes: readonly MetadataFileRoute[];
23
+ routePath: string;
24
+ };
25
+ declare function createHttpAccessFallbackMetadataPlan<TModule extends AppPageHeadModule>(options: HttpAccessFallbackMetadataPlanOptions<TModule>): OrderedAppPageMetadataSource<TModule>[];
26
+ declare function resolveHttpAccessFallbackMetadata<TModule extends AppPageHeadModule>(options: ResolveHttpAccessFallbackMetadataOptions<TModule>): Promise<Metadata | null>;
27
+ declare function resolveHttpAccessFallbackViewport<TModule extends AppPageHeadModule>(options: HttpAccessFallbackMetadataPlanOptions<TModule>): Promise<ResolvedViewport>;
28
+ //#endregion
29
+ export { createHttpAccessFallbackMetadataPlan, resolveHttpAccessFallbackMetadata, resolveHttpAccessFallbackViewport };
@@ -0,0 +1,114 @@
1
+ import { mergeViewport, resolveModuleViewport } from "../shims/metadata.js";
2
+ import { resolveAppPageBranchParams, resolveAppPageSegmentParams } from "./app-page-params.js";
3
+ import { resolveOrderedAppPageMetadata } from "./app-page-head.js";
4
+ //#region src/server/app-page-http-access-fallback-metadata.ts
5
+ function isPresent(value) {
6
+ return value !== null && value !== void 0;
7
+ }
8
+ /**
9
+ * Translate HTTP-access boundary semantics into the exact metadata source
10
+ * order used by Next.js's loader-tree walk.
11
+ *
12
+ * The not-found convention is appended at every active leaf. A sibling
13
+ * intercept is the primary leaf and therefore precedes the ordinary slot
14
+ * branches; otherwise the convention also represents the primary page leaf.
15
+ */
16
+ function createHttpAccessFallbackPlan(options, fallbackLeafMode) {
17
+ const routeSegments = options.routeSegments ?? [];
18
+ const plan = [];
19
+ for (const [index, layoutModule] of options.layoutModules.entries()) {
20
+ if (!isPresent(layoutModule)) continue;
21
+ const treePosition = options.layoutTreePositions?.[index] ?? 0;
22
+ plan.push({
23
+ kind: "source",
24
+ source: {
25
+ includeWhenEmpty: true,
26
+ module: layoutModule,
27
+ params: resolveAppPageSegmentParams(routeSegments, treePosition, options.params),
28
+ routeSegments: routeSegments.slice(0, treePosition)
29
+ }
30
+ });
31
+ }
32
+ let activeBoundaryModule = options.boundaryModule;
33
+ let activeBoundaryParams = options.boundaryParams;
34
+ let activeBoundaryRouteSegments = routeSegments;
35
+ const appendFallbackLeaf = () => {
36
+ if (fallbackLeafMode === "final") {
37
+ plan.push({ kind: "fallback-leaf" });
38
+ return;
39
+ }
40
+ if (!activeBoundaryModule) return;
41
+ plan.push({
42
+ kind: "source",
43
+ source: {
44
+ includeWhenEmpty: true,
45
+ module: activeBoundaryModule,
46
+ params: activeBoundaryParams,
47
+ routeSegments: activeBoundaryRouteSegments
48
+ }
49
+ });
50
+ };
51
+ if (!options.primaryParallelBranch) appendFallbackLeaf();
52
+ const parallelBranches = [...options.primaryParallelBranch ? [options.primaryParallelBranch] : [], ...[...options.parallelBranches ?? []].sort((left, right) => right.ownerTreePosition - left.ownerTreePosition)];
53
+ for (const branch of parallelBranches) {
54
+ const parallelRoute = branch.head;
55
+ const parallelParams = parallelRoute.params ?? options.params;
56
+ const parallelRouteSegments = parallelRoute.routeSegments ?? routeSegments;
57
+ const layoutModules = [...parallelRoute.layoutModules ?? [], parallelRoute.layoutModule].filter(isPresent);
58
+ const layoutTreePositions = parallelRoute.layoutTreePositions ?? [];
59
+ const layoutParams = parallelRoute.layoutParams ?? [];
60
+ for (const [index, layoutModule] of layoutModules.entries()) plan.push({
61
+ kind: "source",
62
+ source: {
63
+ includeWhenEmpty: true,
64
+ module: layoutModule,
65
+ params: layoutParams[index] ?? resolveAppPageBranchParams(parallelRouteSegments, layoutTreePositions[index] ?? 0, parallelParams),
66
+ routeSegments: parallelRouteSegments
67
+ }
68
+ });
69
+ if (options.branchNotFoundConventions !== false && branch.notFoundModule) {
70
+ activeBoundaryModule = branch.notFoundModule;
71
+ activeBoundaryParams = branch.notFoundParams ?? parallelParams;
72
+ activeBoundaryRouteSegments = parallelRouteSegments;
73
+ }
74
+ appendFallbackLeaf();
75
+ }
76
+ return plan.flatMap((item) => {
77
+ if (item.kind === "source") return [item.source];
78
+ if (!activeBoundaryModule) return [];
79
+ return [{
80
+ includeWhenEmpty: true,
81
+ module: activeBoundaryModule,
82
+ params: activeBoundaryParams,
83
+ routeSegments: activeBoundaryRouteSegments
84
+ }];
85
+ });
86
+ }
87
+ function createHttpAccessFallbackMetadataPlan(options) {
88
+ return createHttpAccessFallbackPlan(options, "final");
89
+ }
90
+ function resolveHttpAccessFallbackMetadata(options) {
91
+ return resolveOrderedAppPageMetadata({
92
+ applyFileBasedMetadata: options.applyFileBasedMetadata,
93
+ basePath: options.basePath,
94
+ fallbackOnFileMetadataError: options.fallbackOnFileMetadataError,
95
+ metadataRoutes: options.metadataRoutes,
96
+ params: options.params,
97
+ routePath: options.routePath,
98
+ routeSegments: options.routeSegments,
99
+ sources: createHttpAccessFallbackMetadataPlan(options)
100
+ });
101
+ }
102
+ async function resolveHttpAccessFallbackViewport(options) {
103
+ let accumulatedViewport = Promise.resolve(mergeViewport([]));
104
+ for (const source of createHttpAccessFallbackPlan(options, "snapshot")) {
105
+ const parentForSource = accumulatedViewport;
106
+ const viewportPromise = resolveModuleViewport(source.module, source.params, void 0, parentForSource);
107
+ viewportPromise.catch(() => null);
108
+ accumulatedViewport = Promise.all([parentForSource, viewportPromise]).then(([parent, viewport]) => viewport ? mergeViewport([parent, viewport]) : parent);
109
+ accumulatedViewport.catch(() => null);
110
+ }
111
+ return accumulatedViewport;
112
+ }
113
+ //#endregion
114
+ export { createHttpAccessFallbackMetadataPlan, resolveHttpAccessFallbackMetadata, resolveHttpAccessFallbackViewport };
@@ -40,12 +40,16 @@ type AppPageProbeModule = Readonly<{
40
40
  }> | null | undefined;
41
41
  type AppPageProbeSlot = Readonly<{
42
42
  page?: AppPageProbeModule;
43
+ loading?: AppPageProbeModule;
44
+ loadings?: readonly AppPageProbeModule[] | null;
45
+ loadingTreePositions?: readonly number[] | null;
43
46
  }> | null | undefined;
44
47
  type AppPageProbeRoute = Readonly<{
45
48
  slots?: Readonly<Record<string, AppPageProbeSlot>> | null;
46
49
  }>;
47
50
  type AppPageProbeIntercept = Readonly<{
48
51
  page?: AppPageProbeModule;
52
+ interceptLoadings?: readonly AppPageProbeModule[] | null;
49
53
  matchedParams?: unknown;
50
54
  /**
51
55
  * Key of the parallel-route slot this interception overrides. At render
@@ -1,6 +1,6 @@
1
- import { markAppPagePropsForUseCache } from "../shims/internal/app-page-props-cache-key.js";
2
1
  import { isNextRouterError } from "../shims/navigation-errors.js";
3
2
  import "../shims/navigation-server.js";
3
+ import { markAppPagePropsForUseCache } from "../shims/internal/app-page-props-cache-key.js";
4
4
  import { isPromiseLike } from "../utils/promise.js";
5
5
  import { probeAppPageComponent, probeAppPageLayouts } from "./app-page-execution.js";
6
6
  import { collectAppPageSearchParams } from "./app-page-head.js";
@@ -218,13 +218,17 @@ function buildAppPageProbes(options) {
218
218
  const overriddenSlotKey = intercept?.slotKey ?? null;
219
219
  for (const [slotKey, slot] of Object.entries(route.slots ?? {})) {
220
220
  if (overriddenSlotKey !== null && slotKey === overriddenSlotKey) continue;
221
+ if (slot?.loading?.default || slot?.loadings?.some((loading) => loading?.default)) continue;
221
222
  probes.push(probeAppPage({
222
223
  pageComponent: slot?.page?.default,
223
224
  asyncRouteParams,
224
225
  searchParams
225
226
  }));
226
227
  }
227
- if (intercept) probes.push(probeAppPage({
228
+ const interceptedSlot = intercept?.slotKey ? route.slots?.[intercept.slotKey] : null;
229
+ const interceptedSlotHasRootLoading = Boolean(interceptedSlot?.loading?.default || interceptedSlot?.loadings?.some((loading, index) => loading?.default && interceptedSlot.loadingTreePositions?.[index] === 0));
230
+ const interceptHasLoadingBoundary = Boolean(intercept?.interceptLoadings?.some((loading) => loading?.default) || interceptedSlotHasRootLoading);
231
+ if (intercept && !interceptHasLoadingBoundary) probes.push(probeAppPage({
228
232
  pageComponent: intercept.page?.default,
229
233
  asyncRouteParams: options.makeThenableParams(intercept.matchedParams ?? matchedParams),
230
234
  searchParams
@@ -3,11 +3,11 @@ import { AppOutgoingElements } from "./app-elements-wire.js";
3
3
  import { NavigationContext } from "../shims/navigation-context-state.js";
4
4
  import { AppPageFontPreload, AppPageSpecialError, LayoutClassificationOptions } from "./app-page-execution.js";
5
5
  import { AppPageMiddlewareContext } from "./app-page-response.js";
6
+ import { AppLayoutParamAccessTracker } from "./app-layout-param-observation.js";
7
+ import { AppRscRenderMode } from "./app-rsc-render-mode.js";
6
8
  import { RootParams } from "../shims/root-params.js";
7
9
  import { AppPageSsrHandler } from "./app-page-stream.js";
8
- import { AppLayoutParamAccessTracker } from "./app-layout-param-observation.js";
9
10
  import { CachedAppPageValue } from "../shims/cache-handler.js";
10
- import { AppRscRenderMode } from "./app-rsc-render-mode.js";
11
11
  import { AppPageRenderObservationState } from "./app-page-render-observation.js";
12
12
  import { ReactNode } from "react";
13
13
  import { ReactFormState } from "react-dom/client";
@@ -11,10 +11,10 @@ import { DEFAULT_CACHE_VARIANT_BUDGET, buildCacheVariantWithRouteBudget, buildRe
11
11
  import { createAppPageHtmlOutputScope, createAppPageRenderObservation, createAppPageRscOutputScope, createEmptyAppPageRenderObservationState } from "./app-page-render-observation.js";
12
12
  import { finalizeAppPageHtmlCacheResponse, finalizeAppPageRscCacheResponse } from "./app-page-cache-finalizer.js";
13
13
  import { createStaticLayoutClientReuseArtifactCompatibility, createStaticLayoutClientReusePayloadHash, createStaticLayoutClientReuseRouteId } from "./static-layout-client-reuse-proof.js";
14
+ import { hasDigest } from "./app-rsc-errors.js";
14
15
  import { deferUntilStreamConsumed } from "./defer-until-stream-consumed.js";
15
16
  import { buildAppPageLinkHeader, createAppPageFontData, createAppPageRscErrorTracker, renderAppPageHtmlStream, renderAppPageHtmlStreamWithRecovery } from "./app-page-stream.js";
16
17
  import { getStaticLayoutObservationSkipRejection } from "./app-layout-param-observation.js";
17
- import { hasDigest } from "./app-rsc-errors.js";
18
18
  import { createClientReuseSkipTransportPlan, crossCheckClientReuseManifestEntryWithCache } from "./skip-cache-proof.js";
19
19
  //#region src/server/app-page-render.ts
20
20
  function buildResponseTiming(options) {
@@ -11,7 +11,13 @@ type GenerateStaticParamsModule = {
11
11
  generateStaticParams?: GenerateStaticParams | null;
12
12
  };
13
13
  type GenerateStaticParamsSource = {
14
+ /**
15
+ * Primary loader-tree sources execute top-down as one chain. Each source
16
+ * receives the complete params object produced by the sources before it.
17
+ */
18
+ chained?: true;
14
19
  generateStaticParams: GenerateStaticParams;
20
+ independentChain?: number;
15
21
  paramAliases?: Readonly<Record<string, string>>;
16
22
  paramPatternParts?: readonly string[];
17
23
  routePatternParts?: readonly string[];
@@ -56,14 +62,23 @@ type AppPageInterceptMatch<TPage = unknown> = {
56
62
  interceptLayouts?: readonly unknown[] | null;
57
63
  interceptLayoutSegments?: readonly (readonly string[])[] | null;
58
64
  interceptBranchSegments?: readonly string[] | null;
65
+ interceptLoadings?: readonly unknown[] | null;
66
+ interceptLoadingTreePositions?: readonly number[] | null;
67
+ interceptNotFoundBranchSegments?: readonly string[] | null;
59
68
  __loadInterceptLayouts?: readonly (() => Promise<unknown>)[] | null;
69
+ __loadInterceptLoadings?: readonly (() => Promise<unknown>)[] | null;
60
70
  matchedParams: AppPageParams;
61
71
  sourceMatchedParams?: AppPageParams;
62
72
  page: TPage;
63
73
  __pageLoader?: (() => Promise<TPage>) | null;
74
+ notFound?: unknown;
75
+ __loadNotFound?: (() => Promise<unknown>) | null;
76
+ notFoundTreePosition?: number | null;
64
77
  __loadState?: {
65
78
  page: TPage;
66
79
  pageLoading: Promise<TPage> | null;
80
+ notFound?: unknown;
81
+ notFoundLoading?: Promise<unknown> | null;
67
82
  interceptLayoutsLoading: Promise<readonly unknown[]> | null;
68
83
  };
69
84
  slotId?: string | null;