vinext 1.0.0-beta.1 → 1.0.0-beta.2

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 (175) hide show
  1. package/README.md +4 -1
  2. package/dist/build/client-build-config.js +1 -0
  3. package/dist/build/report.d.ts +8 -1
  4. package/dist/build/report.js +12 -2
  5. package/dist/check.js +15 -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 +31 -121
  9. package/dist/config/next-config.d.ts +3 -2
  10. package/dist/config/next-config.js +3 -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-rsc-entry.js +27 -9
  14. package/dist/entries/app-rsc-manifest.js +17 -0
  15. package/dist/entries/pages-server-entry.js +3 -2
  16. package/dist/index.js +82 -58
  17. package/dist/init.d.ts +6 -4
  18. package/dist/init.js +20 -22
  19. package/dist/plugins/og-assets.js +1 -1
  20. package/dist/routing/app-route-graph.d.ts +13 -5
  21. package/dist/routing/app-route-graph.js +59 -13
  22. package/dist/routing/app-router.js +12 -6
  23. package/dist/routing/file-matcher.d.ts +11 -22
  24. package/dist/routing/file-matcher.js +21 -33
  25. package/dist/routing/pages-router.js +23 -29
  26. package/dist/routing/route-matching.d.ts +14 -1
  27. package/dist/routing/route-matching.js +13 -1
  28. package/dist/routing/route-pattern.d.ts +2 -1
  29. package/dist/routing/route-pattern.js +7 -4
  30. package/dist/routing/route-trie.d.ts +5 -1
  31. package/dist/routing/route-trie.js +5 -2
  32. package/dist/server/app-browser-entry.js +7 -6
  33. package/dist/server/app-browser-history-controller.d.ts +1 -1
  34. package/dist/server/app-browser-history-controller.js +3 -3
  35. package/dist/server/app-browser-navigation-controller.js +1 -1
  36. package/dist/server/app-browser-state.js +1 -1
  37. package/dist/server/app-browser-visible-commit.js +1 -1
  38. package/dist/server/app-fallback-renderer.d.ts +3 -0
  39. package/dist/server/app-fallback-renderer.js +4 -2
  40. package/dist/server/app-middleware.js +2 -1
  41. package/dist/server/app-page-boundary-render.d.ts +12 -1
  42. package/dist/server/app-page-boundary-render.js +84 -2
  43. package/dist/server/app-page-cache-finalizer.d.ts +1 -1
  44. package/dist/server/app-page-cache.d.ts +1 -1
  45. package/dist/server/app-page-dispatch.d.ts +11 -2
  46. package/dist/server/app-page-dispatch.js +14 -17
  47. package/dist/server/app-page-element-builder.d.ts +7 -2
  48. package/dist/server/app-page-element-builder.js +112 -27
  49. package/dist/server/app-page-head.d.ts +54 -2
  50. package/dist/server/app-page-head.js +164 -78
  51. package/dist/server/app-page-http-access-fallback-metadata.d.ts +29 -0
  52. package/dist/server/app-page-http-access-fallback-metadata.js +106 -0
  53. package/dist/server/app-page-render.d.ts +2 -2
  54. package/dist/server/app-page-render.js +1 -1
  55. package/dist/server/app-page-request.d.ts +12 -0
  56. package/dist/server/app-page-request.js +133 -13
  57. package/dist/server/app-page-route-wiring.d.ts +9 -0
  58. package/dist/server/app-page-route-wiring.js +43 -3
  59. package/dist/server/app-post-middleware-context.d.ts +1 -1
  60. package/dist/server/app-post-middleware-context.js +1 -1
  61. package/dist/server/app-route-handler-dispatch.js +31 -17
  62. package/dist/server/app-route-handler-execution.d.ts +6 -1
  63. package/dist/server/app-route-handler-execution.js +42 -16
  64. package/dist/server/app-route-handler-policy.d.ts +2 -0
  65. package/dist/server/app-route-handler-policy.js +3 -3
  66. package/dist/server/app-route-module-loader.d.ts +3 -1
  67. package/dist/server/app-route-module-loader.js +1 -0
  68. package/dist/server/app-rsc-cache-busting.d.ts +1 -1
  69. package/dist/server/app-rsc-errors.d.ts +1 -1
  70. package/dist/server/app-rsc-errors.js +12 -2
  71. package/dist/server/app-rsc-handler.d.ts +10 -5
  72. package/dist/server/app-rsc-handler.js +52 -24
  73. package/dist/server/app-rsc-request-normalization.d.ts +3 -2
  74. package/dist/server/app-rsc-request-normalization.js +8 -2
  75. package/dist/server/app-rsc-response-finalizer.d.ts +2 -2
  76. package/dist/server/app-rsc-response-finalizer.js +5 -11
  77. package/dist/server/app-rsc-route-matching.d.ts +20 -0
  78. package/dist/server/app-rsc-route-matching.js +87 -11
  79. package/dist/server/app-segment-config.js +13 -3
  80. package/dist/server/app-server-action-execution.d.ts +3 -1
  81. package/dist/server/app-server-action-execution.js +9 -7
  82. package/dist/server/app-ssr-entry.js +2 -2
  83. package/dist/server/app-ssr-stream.d.ts +1 -7
  84. package/dist/server/app-ssr-stream.js +2 -10
  85. package/dist/server/config-headers.d.ts +24 -0
  86. package/dist/server/config-headers.js +52 -0
  87. package/dist/server/dev-initial-server-error.d.ts +1 -1
  88. package/dist/server/dev-server.js +29 -13
  89. package/dist/server/isr-cache.d.ts +1 -1
  90. package/dist/server/middleware-matcher-pattern.d.ts +22 -0
  91. package/dist/server/middleware-matcher-pattern.js +219 -0
  92. package/dist/server/middleware-matcher.d.ts +2 -7
  93. package/dist/server/middleware-matcher.js +13 -60
  94. package/dist/server/middleware-path-to-regexp.d.ts +14 -0
  95. package/dist/server/middleware-path-to-regexp.js +228 -0
  96. package/dist/server/middleware-runtime.js +4 -3
  97. package/dist/server/middleware.d.ts +1 -1
  98. package/dist/server/middleware.js +2 -1
  99. package/dist/server/pages-data-route.d.ts +11 -1
  100. package/dist/server/pages-data-route.js +16 -1
  101. package/dist/server/pages-get-initial-props.d.ts +15 -2
  102. package/dist/server/pages-get-initial-props.js +16 -6
  103. package/dist/server/pages-page-data.d.ts +3 -1
  104. package/dist/server/pages-page-data.js +16 -7
  105. package/dist/server/pages-page-handler.d.ts +4 -2
  106. package/dist/server/pages-page-handler.js +16 -17
  107. package/dist/server/pages-page-response.js +1 -1
  108. package/dist/server/pages-readiness.d.ts +3 -3
  109. package/dist/server/pages-request-pipeline.d.ts +1 -0
  110. package/dist/server/pages-request-pipeline.js +25 -11
  111. package/dist/server/pages-router-entry.js +6 -0
  112. package/dist/server/prerender-route-params.js +2 -2
  113. package/dist/server/prod-server.d.ts +1 -4
  114. package/dist/server/prod-server.js +36 -23
  115. package/dist/server/request-pipeline.d.ts +15 -29
  116. package/dist/server/request-pipeline.js +24 -59
  117. package/dist/server/rsc-stream-hints.js +122 -18
  118. package/dist/shims/app.d.ts +1 -1
  119. package/dist/shims/cache-request-state.d.ts +27 -1
  120. package/dist/shims/cache-request-state.js +60 -1
  121. package/dist/shims/cache-runtime.d.ts +12 -1
  122. package/dist/shims/cache-runtime.js +22 -7
  123. package/dist/shims/cache.d.ts +5 -5
  124. package/dist/shims/cache.js +26 -10
  125. package/dist/shims/document.d.ts +42 -83
  126. package/dist/shims/document.js +62 -53
  127. package/dist/shims/dynamic.d.ts +5 -25
  128. package/dist/shims/dynamic.js +29 -8
  129. package/dist/shims/error.d.ts +1 -1
  130. package/dist/shims/error.js +1 -1
  131. package/dist/shims/fetch-cache.js +12 -3
  132. package/dist/shims/font-google-base.d.ts +11 -4
  133. package/dist/shims/font-local.d.ts +10 -3
  134. package/dist/shims/head.d.ts +13 -2
  135. package/dist/shims/head.js +3 -3
  136. package/dist/shims/headers.d.ts +28 -31
  137. package/dist/shims/headers.js +109 -49
  138. package/dist/shims/image.d.ts +27 -37
  139. package/dist/shims/image.js +21 -14
  140. package/dist/shims/internal/cookie-serialize.d.ts +3 -1
  141. package/dist/shims/internal/cookie-serialize.js +3 -1
  142. package/dist/shims/internal/hybrid-client-route-owner.js +3 -1
  143. package/dist/shims/internal/utils.d.ts +3 -3
  144. package/dist/shims/legacy-image.d.ts +3 -37
  145. package/dist/shims/legacy-image.js +6 -16
  146. package/dist/shims/link.d.ts +15 -22
  147. package/dist/shims/link.js +20 -11
  148. package/dist/shims/navigation-context-state.d.ts +1 -1
  149. package/dist/shims/navigation.d.ts +7 -3
  150. package/dist/shims/navigation.js +24 -13
  151. package/dist/shims/navigation.react-server.js +1 -1
  152. package/dist/shims/next-shims-augmentations.d.ts +1 -0
  153. package/dist/shims/next-shims-public.d.ts +1 -0
  154. package/dist/shims/public-shim-map.json.js +103 -0
  155. package/dist/shims/readonly-url-search-params.d.ts +3 -3
  156. package/dist/shims/readonly-url-search-params.js +3 -3
  157. package/dist/shims/request-state-types.d.ts +1 -1
  158. package/dist/shims/router.d.ts +55 -21
  159. package/dist/shims/router.js +162 -37
  160. package/dist/shims/script.d.ts +6 -10
  161. package/dist/shims/script.js +6 -3
  162. package/dist/shims/server.d.ts +45 -22
  163. package/dist/shims/server.js +44 -5
  164. package/dist/shims/unified-request-context.d.ts +1 -1
  165. package/dist/shims/unified-request-context.js +2 -0
  166. package/dist/shims/url-safety.d.ts +1 -1
  167. package/dist/shims/url-safety.js +2 -2
  168. package/dist/typegen.d.ts +8 -2
  169. package/dist/typegen.js +76 -15
  170. package/dist/utils/domain-locale.d.ts +2 -2
  171. package/dist/utils/external-url.d.ts +5 -0
  172. package/dist/utils/external-url.js +7 -0
  173. package/dist/utils/regex-safety.d.ts +8 -0
  174. package/dist/utils/regex-safety.js +737 -0
  175. package/package.json +13 -2
@@ -4,13 +4,17 @@ import "./app-elements.js";
4
4
  import { createAppPageRenderIdentity } from "./app-page-render-identity.js";
5
5
  import { matchRoutePattern } from "../routing/route-pattern.js";
6
6
  import { makeThenableParams } from "../shims/thenable-params.js";
7
- import { resolveAppPageBranchParams } from "./app-page-params.js";
8
- import { resolveActiveParallelRouteHeadInputs, resolveAppPageHead } from "./app-page-head.js";
7
+ import { resolveAppPageBranchParams, resolveAppPageSegmentParams } from "./app-page-params.js";
8
+ import { resolveAppPageSpecialError } from "./app-page-execution.js";
9
+ import { prepareAppPageHead, resolveActiveParallelRouteHeadInputs } from "./app-page-head.js";
9
10
  import { createAppPageSearchParamsObserver, makeObservedAppPageSearchParamsThenable } from "./app-page-search-params-observation.js";
10
11
  import { buildAppPageElements, createAppPageSourcePage, createAppPageTreePath } from "./app-page-route-wiring.js";
12
+ import { resolveHttpAccessFallbackMetadata, resolveHttpAccessFallbackViewport } from "./app-page-http-access-fallback-metadata.js";
13
+ import "./app-rsc-route-matching.js";
14
+ import { resolveAppPageParentHttpAccessBoundary } from "./app-page-boundary.js";
15
+ import { sanitizeErrorForClient } from "./app-rsc-errors.js";
11
16
  import { DEFAULT_GLOBAL_ERROR_MODULE } from "./default-global-error-module.js";
12
17
  import { shouldServeStreamingMetadata } from "./streaming-metadata.js";
13
- import "./app-rsc-route-matching.js";
14
18
  import { createElement } from "react";
15
19
  //#region src/server/app-page-element-builder.ts
16
20
  function resolveInterceptLayoutParams(branchSegments, layoutSegments, params) {
@@ -41,7 +45,7 @@ function isReactOwnedPageComponent(component) {
41
45
  async function buildPageElements(options) {
42
46
  const { route, params, routePath, displayPathname = routePath, pageRequest, globalErrorModule, rootNotFoundModule, rootForbiddenModule, rootUnauthorizedModule, metadataRoutes } = options;
43
47
  const slotParamOverrides = resolveSlotParamOverrides(route, routePath);
44
- const { opts, searchParams, isRscRequest, mountedSlotsHeader, renderMode = APP_RSC_RENDER_MODE_NAVIGATION, observeMetadataSearchParamsAccess = false, observePageSearchParamsAccess = false, serveStreamingMetadata } = pageRequest;
48
+ const { opts, searchParams, isRscRequest, mountedSlotsHeader, renderMode = APP_RSC_RENDER_MODE_NAVIGATION, observeMetadataSearchParamsAccess = false, observePageSearchParamsAccess = false, serveStreamingMetadata, isProduction = process.env.NODE_ENV === "production" } = pageRequest;
45
49
  const pageModule = route.page;
46
50
  const isSiblingIntercept = opts?.interceptSlotKey === "__vinext_page_intercept" && !!opts?.interceptPage;
47
51
  const effectivePageModule = isSiblingIntercept ? opts.interceptPage : pageModule;
@@ -76,38 +80,116 @@ async function buildPageElements(options) {
76
80
  [renderIdentity.routeId]: createElement("div", null, "Page has no default export")
77
81
  };
78
82
  }
79
- const { hasDynamicMetadata, metadata: resolvedMetadata, pageSearchParams, viewport: resolvedViewport } = await resolveAppPageHead({
80
- applyFileBasedMetadata: options.applyFileBasedMetadata,
81
- basePath: options.basePath ?? "",
82
- layoutModules: route.layouts,
83
+ const activeParallelRouteHeadInputs = resolveActiveParallelRouteHeadInputs({
84
+ interceptBranchSegments: opts?.interceptBranchSegments ?? null,
85
+ interceptLayouts: opts?.interceptLayouts ?? null,
86
+ interceptLayoutSegments: opts?.interceptLayoutSegments ?? null,
87
+ interceptNotFoundBranchSegments: opts?.interceptNotFoundBranchSegments ?? null,
88
+ interceptNotFound: opts?.interceptNotFound ?? null,
89
+ interceptNotFoundTreePosition: opts?.interceptNotFoundTreePosition ?? null,
90
+ interceptPage: opts?.interceptPage ?? null,
91
+ interceptParams: opts?.interceptParams ?? null,
92
+ interceptSlotKey: opts?.interceptSlotKey ?? null,
93
+ interceptSourcePageSegments: opts?.interceptSourcePageSegments ?? null,
83
94
  layoutTreePositions: route.layoutTreePositions,
84
- metadataRoutes,
85
- pageModule: isSiblingIntercept ? null : effectivePageModule ?? null,
86
- parallelRoutes: [...resolveActiveParallelRouteHeadInputs({
87
- interceptBranchSegments: opts?.interceptBranchSegments ?? null,
88
- interceptLayouts: opts?.interceptLayouts ?? null,
89
- interceptLayoutSegments: opts?.interceptLayoutSegments ?? null,
90
- interceptPage: opts?.interceptPage ?? null,
91
- interceptParams: opts?.interceptParams ?? null,
92
- interceptSlotKey: opts?.interceptSlotKey ?? null,
93
- layoutTreePositions: route.layoutTreePositions,
94
- params,
95
- routeSegments: route.routeSegments ?? [],
96
- slotParams: slotParamOverrides,
97
- slots: route.slots ?? null
98
- }), ...isSiblingIntercept ? [{
95
+ params,
96
+ routeSegments: route.routeSegments ?? [],
97
+ slotParams: slotParamOverrides,
98
+ slots: route.slots ?? null
99
+ });
100
+ const primaryParallelRouteHeadInput = isSiblingIntercept ? {
101
+ head: {
99
102
  layoutModules: opts?.interceptLayouts ?? [],
100
103
  layoutParams: (opts?.interceptLayoutSegments ?? []).map((segments) => resolveInterceptLayoutParams(opts?.interceptBranchSegments ?? segments, segments, effectiveParams)),
101
104
  pageModule: effectivePageModule ?? null,
102
105
  params: effectiveParams,
103
106
  routeSegments: opts?.interceptSourcePageSegments ?? route.routeSegments ?? []
104
- }] : []],
107
+ },
108
+ ...opts?.interceptNotFound ? {
109
+ notFoundModule: opts.interceptNotFound,
110
+ notFoundParams: resolveAppPageBranchParams(opts.interceptNotFoundBranchSegments ?? opts.interceptBranchSegments ?? route.routeSegments ?? [], opts.interceptNotFoundTreePosition ?? 0, effectiveParams)
111
+ } : {},
112
+ ownerTreePosition: route.routeSegments?.length ?? 0
113
+ } : null;
114
+ const parallelRoutes = [...activeParallelRouteHeadInputs.map((input) => input.head), ...primaryParallelRouteHeadInput ? [primaryParallelRouteHeadInput.head] : []];
115
+ const metadataSearchParamsObserver = observeMetadataSearchParamsAccess ? createAppPageSearchParamsObserver() : void 0;
116
+ const preparedHead = prepareAppPageHead({
117
+ applyFileBasedMetadata: options.applyFileBasedMetadata,
118
+ basePath: options.basePath ?? "",
119
+ layoutModules: route.layouts,
120
+ layoutTreePositions: route.layoutTreePositions,
121
+ metadataRoutes,
122
+ pageModule: isSiblingIntercept ? null : effectivePageModule ?? null,
123
+ parallelRoutes,
105
124
  params: effectiveParams,
106
125
  routePath: route.pattern,
107
126
  routeSegments: route.routeSegments ?? null,
108
127
  searchParams,
109
- searchParamsObserver: observeMetadataSearchParamsAccess ? createAppPageSearchParamsObserver() : void 0
128
+ searchParamsObserver: metadataSearchParamsObserver
110
129
  });
130
+ const { hasDynamicMetadata, pageSearchParams } = preparedHead;
131
+ const streamGeneratedHead = serveStreamingMetadata ?? shouldServeStreamingMetadata(pageRequest.request.headers.get("user-agent") ?? "", options.htmlLimitedBots);
132
+ const metadataPlacement = hasDynamicMetadata && streamGeneratedHead ? "body" : "head";
133
+ const shouldDeferMetadata = metadataPlacement === "body";
134
+ const streamingMetadata = shouldDeferMetadata ? isProduction ? preparedHead.metadata.catch((error) => {
135
+ throw sanitizeErrorForClient(error, "production");
136
+ }) : preparedHead.metadata : null;
137
+ streamingMetadata?.catch(() => null);
138
+ const resolveNotFoundFallbackPlanOptions = () => {
139
+ const routeBoundaryModule = route.notFound;
140
+ const parentBoundary = resolveAppPageParentHttpAccessBoundary({
141
+ layoutIndex: route.layouts.length,
142
+ rootForbiddenModule,
143
+ rootNotFoundModule,
144
+ rootUnauthorizedModule,
145
+ routeForbiddenModules: route.forbiddens,
146
+ routeNotFoundModules: route.notFounds,
147
+ routeUnauthorizedModules: route.unauthorizeds,
148
+ statusCode: 404
149
+ });
150
+ const boundaryModule = routeBoundaryModule ?? parentBoundary.module;
151
+ const boundaryTreePosition = routeBoundaryModule ? route.notFoundTreePosition : parentBoundary.layoutIndex === null ? null : route.layoutTreePositions?.[parentBoundary.layoutIndex];
152
+ return {
153
+ boundaryModule,
154
+ boundaryParams: boundaryModule && boundaryTreePosition != null ? resolveAppPageSegmentParams(route.routeSegments ?? [], boundaryTreePosition, effectiveParams) : {},
155
+ layoutModules: route.layouts,
156
+ layoutTreePositions: route.layoutTreePositions,
157
+ parallelBranches: activeParallelRouteHeadInputs,
158
+ params: effectiveParams,
159
+ primaryParallelBranch: primaryParallelRouteHeadInput,
160
+ routeSegments: route.routeSegments ?? null
161
+ };
162
+ };
163
+ let viewportErrorOutlet = null;
164
+ let metadataErrorOutlet = null;
165
+ const resolveMetadataErrorTags = async (error) => {
166
+ if (resolveAppPageSpecialError(error)?.kind !== "http-access-fallback") return null;
167
+ return resolveHttpAccessFallbackMetadata({
168
+ applyFileBasedMetadata: options.applyFileBasedMetadata,
169
+ basePath: options.basePath ?? "",
170
+ ...resolveNotFoundFallbackPlanOptions(),
171
+ metadataRoutes,
172
+ routePath: route.pattern
173
+ }).catch(() => null);
174
+ };
175
+ const [resolvedMetadata, resolvedViewport] = await Promise.all([shouldDeferMetadata ? Promise.resolve(null) : preparedHead.metadata.catch((error) => {
176
+ metadataErrorOutlet = Promise.reject(isProduction ? sanitizeErrorForClient(error, "production") : error);
177
+ metadataErrorOutlet.catch(() => null);
178
+ return resolveMetadataErrorTags(error);
179
+ }), preparedHead.viewport.catch(async (error) => {
180
+ const specialError = resolveAppPageSpecialError(error);
181
+ viewportErrorOutlet = Promise.reject(isProduction ? sanitizeErrorForClient(error, "production") : error);
182
+ viewportErrorOutlet.catch(() => null);
183
+ return specialError?.kind === "http-access-fallback" ? resolveHttpAccessFallbackViewport(resolveNotFoundFallbackPlanOptions()).catch(() => ({})) : {};
184
+ })]);
185
+ const streamingMetadataTags = shouldDeferMetadata ? preparedHead.metadata.catch(resolveMetadataErrorTags) : null;
186
+ const streamingMetadataOutletInputs = [
187
+ streamingMetadata,
188
+ metadataErrorOutlet,
189
+ viewportErrorOutlet
190
+ ].filter((promise) => promise !== null).map((promise) => Promise.resolve(promise));
191
+ const streamingMetadataOutlet = streamingMetadataOutletInputs.length > 0 ? Promise.all(streamingMetadataOutletInputs).then(() => null) : null;
192
+ streamingMetadataOutlet?.catch(() => null);
111
193
  const pageProps = { params: makeThenableParams(effectiveParams) };
112
194
  const hasRequestSearchParams = Object.keys(pageSearchParams).length > 0;
113
195
  const createPageElement = (PageComponent, props) => {
@@ -127,7 +209,6 @@ async function buildPageElements(options) {
127
209
  const pageSearchParamsThenable = searchParams ? makeThenableParams(pageSearchParams) : void 0;
128
210
  const mountedSlotIds = mountedSlotsHeader ? new Set(mountedSlotsHeader.split(" ")) : null;
129
211
  const slotOverrides = buildSlotOverrides(route, params, routePath, opts);
130
- const metadataPlacement = hasDynamicMetadata && (serveStreamingMetadata ?? shouldServeStreamingMetadata(pageRequest.request.headers.get("user-agent") ?? "", options.htmlLimitedBots)) ? "body" : "head";
131
212
  let siblingInterceptElement = isSiblingIntercept && EffectivePageComponent ? createPageElement(EffectivePageComponent, pageProps) : null;
132
213
  if (isSiblingIntercept && siblingInterceptElement !== null && opts?.interceptLayouts?.length) for (let i = opts.interceptLayouts.length - 1; i >= 0; i--) {
133
214
  const LayoutComponent = opts.interceptLayouts[i]?.default;
@@ -149,6 +230,10 @@ async function buildPageElements(options) {
149
230
  resolvedMetadata,
150
231
  resolvedMetadataPathname: routePath,
151
232
  resolvedViewport,
233
+ streamingMetadata,
234
+ streamingMetadataOutlet,
235
+ streamingMetadataOutletSuspended: streamGeneratedHead,
236
+ streamingMetadataTags,
152
237
  renderIdentity,
153
238
  routePath,
154
239
  sourcePageSegments,
@@ -293,4 +378,4 @@ function collectParamNameSet(params) {
293
378
  return set;
294
379
  }
295
380
  //#endregion
296
- export { buildPageElements, resolveAppPageNavigationParams, resolveInterceptedSlotSegments };
381
+ export { buildPageElements, resolveAppPageNavigationParams, resolveInterceptedSlotSegments, resolveSlotParamOverrides };
@@ -8,6 +8,14 @@ import { applyFileBasedMetadata } from "./file-based-metadata.js";
8
8
  type AppPageSearchParams = Record<string, string | string[]>;
9
9
  type AppPageHeadModule = Record<string, unknown>;
10
10
  type ApplyAppPageFileBasedMetadata = typeof applyFileBasedMetadata;
11
+ type OrderedAppPageMetadataSource<TModule extends AppPageHeadModule = AppPageHeadModule> = {
12
+ /** Preserve an empty result as the most specific file-metadata source. */includeWhenEmpty?: boolean;
13
+ module: TModule;
14
+ params: AppPageParams;
15
+ routeSegments: readonly string[];
16
+ searchParams?: AppPageSearchParams;
17
+ searchParamsObserver?: ThenableParamsObserver;
18
+ };
11
19
  type AppPageHeadParallelRoute<TModule extends AppPageHeadModule = AppPageHeadModule> = {
12
20
  layoutParams?: readonly AppPageParams[] | null;
13
21
  layoutModule?: TModule | null;
@@ -17,11 +25,19 @@ type AppPageHeadParallelRoute<TModule extends AppPageHeadModule = AppPageHeadMod
17
25
  params?: AppPageParams | null;
18
26
  routeSegments?: readonly string[] | null;
19
27
  };
28
+ type ActiveParallelRouteHeadInput<TModule extends AppPageHeadModule = AppPageHeadModule> = {
29
+ head: AppPageHeadParallelRoute<TModule>;
30
+ notFoundModule?: TModule | null;
31
+ notFoundParams?: AppPageParams | null;
32
+ ownerTreePosition: number;
33
+ };
20
34
  type AppPageHeadSlot<TModule extends AppPageHeadModule = AppPageHeadModule> = {
21
35
  configLayouts?: readonly (TModule | null | undefined)[] | null;
22
36
  configLayoutTreePositions?: readonly number[] | null;
23
37
  layout?: TModule | null;
24
38
  layoutIndex?: number;
39
+ notFound?: TModule | null;
40
+ notFoundTreePosition?: number | null;
25
41
  page?: TModule | null;
26
42
  routeSegments?: readonly string[] | null;
27
43
  };
@@ -30,8 +46,12 @@ type ResolveActiveParallelRouteHeadInputsOptions<TModule extends AppPageHeadModu
30
46
  interceptBranchSegments?: readonly string[] | null;
31
47
  interceptLayoutSegments?: readonly (readonly string[])[] | null;
32
48
  interceptPage?: TModule | null;
49
+ interceptNotFoundBranchSegments?: readonly string[] | null;
50
+ interceptNotFound?: TModule | null;
51
+ interceptNotFoundTreePosition?: number | null;
33
52
  interceptParams?: AppPageParams | null;
34
53
  interceptSlotKey?: string | null;
54
+ interceptSourcePageSegments?: readonly string[] | null;
35
55
  layoutTreePositions?: readonly number[] | null;
36
56
  params: AppPageParams;
37
57
  routeSegments: readonly string[];
@@ -59,6 +79,18 @@ type ResolveAppPageHeadOptions<TModule extends AppPageHeadModule = AppPageHeadMo
59
79
  searchParams?: URLSearchParams | null;
60
80
  searchParamsObserver?: ThenableParamsObserver;
61
81
  };
82
+ type AppPageMetadataOutputOptions = {
83
+ applyFileBasedMetadata?: ApplyAppPageFileBasedMetadata;
84
+ basePath?: string;
85
+ fallbackOnFileMetadataError?: boolean;
86
+ metadataRoutes: readonly MetadataFileRoute[];
87
+ params: AppPageParams;
88
+ routePath: string;
89
+ routeSegments?: readonly string[] | null;
90
+ };
91
+ type ResolveOrderedAppPageMetadataOptions<TModule extends AppPageHeadModule = AppPageHeadModule> = AppPageMetadataOutputOptions & {
92
+ sources: readonly OrderedAppPageMetadataSource<TModule>[];
93
+ };
62
94
  type ResolveAppPageHeadResult = {
63
95
  hasDynamicMetadata: boolean;
64
96
  hasSearchParams: boolean;
@@ -66,12 +98,32 @@ type ResolveAppPageHeadResult = {
66
98
  pageSearchParams: AppPageSearchParams;
67
99
  viewport: Viewport;
68
100
  };
101
+ type PreparedAppPageHead = Omit<ResolveAppPageHeadResult, "metadata" | "viewport"> & {
102
+ metadata: Promise<Metadata | null>;
103
+ viewport: Promise<Viewport>;
104
+ };
69
105
  type AppPageSearchParamsCollection = {
70
106
  hasSearchParams: boolean;
71
107
  pageSearchParams: AppPageSearchParams;
72
108
  };
73
- declare function resolveActiveParallelRouteHeadInputs<TModule extends AppPageHeadModule>(options: ResolveActiveParallelRouteHeadInputsOptions<TModule>): AppPageHeadParallelRoute<TModule>[];
109
+ declare function resolveActiveParallelRouteHeadInputs<TModule extends AppPageHeadModule>(options: ResolveActiveParallelRouteHeadInputsOptions<TModule>): ActiveParallelRouteHeadInput<TModule>[];
74
110
  declare function collectAppPageSearchParams(searchParams: URLSearchParams | null | undefined): AppPageSearchParamsCollection;
111
+ /**
112
+ * Resolve an explicit metadata-source sequence.
113
+ *
114
+ * Route-specific conventions own source selection and ordering. This resolver
115
+ * only supplies each source with its accumulated parent, merges the results,
116
+ * and applies file-based metadata at the end.
117
+ */
118
+ declare function resolveOrderedAppPageMetadata<TModule extends AppPageHeadModule>(options: ResolveOrderedAppPageMetadataOptions<TModule>): Promise<Metadata | null>;
75
119
  declare function resolveAppPageHead<TModule extends AppPageHeadModule>(options: ResolveAppPageHeadOptions<TModule>): Promise<ResolveAppPageHeadResult>;
120
+ /**
121
+ * Start metadata and viewport resolution without coupling their completion.
122
+ *
123
+ * Live document renders can place the metadata promise behind Suspense while
124
+ * still waiting for viewport tags before the shell is emitted. Blocking
125
+ * callers use {@link resolveAppPageHead} and observe the same result as before.
126
+ */
127
+ declare function prepareAppPageHead<TModule extends AppPageHeadModule>(options: ResolveAppPageHeadOptions<TModule>): PreparedAppPageHead;
76
128
  //#endregion
77
- export { AppPageSearchParams, ApplyAppPageFileBasedMetadata, collectAppPageSearchParams, resolveActiveParallelRouteHeadInputs, resolveAppPageHead };
129
+ export { ActiveParallelRouteHeadInput, AppPageHeadModule, AppPageHeadParallelRoute, AppPageSearchParams, ApplyAppPageFileBasedMetadata, OrderedAppPageMetadataSource, PreparedAppPageHead, ResolveAppPageHeadResult, ResolveOrderedAppPageMetadataOptions, collectAppPageSearchParams, prepareAppPageHead, resolveActiveParallelRouteHeadInputs, resolveAppPageHead, resolveOrderedAppPageMetadata };
@@ -26,33 +26,58 @@ 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
  });
58
83
  }
@@ -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++) {
@@ -132,21 +171,21 @@ async function resolveLayoutViewport(layoutInputs, params, routeSegments) {
132
171
  return resolveModuleViewport(layoutInput.module, layoutParams);
133
172
  }));
134
173
  }
135
- async function resolveParallelRouteHead(parallelRoute, fallbackParams, fallbackRouteSegments, pageSearchParams, parent, searchParamsObserver) {
174
+ function getParallelRouteModules(parallelRoute) {
175
+ return [...parallelRoute.layoutModules ?? [], parallelRoute.layoutModule].filter(isPresent);
176
+ }
177
+ function parallelRouteHasDynamicMetadata(parallelRoute) {
178
+ return getParallelRouteModules(parallelRoute).some(hasGenerateMetadata) || hasGenerateMetadata(parallelRoute.pageModule);
179
+ }
180
+ async function resolveParallelRouteMetadata(parallelRoute, fallbackParams, fallbackRouteSegments, pageSearchParams, parent, searchParamsObserver) {
136
181
  const params = parallelRoute.params ?? fallbackParams;
137
182
  const routeSegments = parallelRoute.routeSegments ?? fallbackRouteSegments;
138
183
  const metadataResults = [];
139
- const viewportResults = [];
140
184
  const metadataSources = [];
141
185
  let accumulatedMetadata = parent;
142
- const layoutModules = [...parallelRoute.layoutModules ?? [], parallelRoute.layoutModule].filter(isPresent);
186
+ const layoutModules = getParallelRouteModules(parallelRoute);
143
187
  const layoutTreePositions = parallelRoute.layoutTreePositions ?? [];
144
188
  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
189
  for (const [index, layoutModule] of layoutModules.entries()) {
151
190
  const layoutMetadata = await resolveModuleMetadata(layoutModule, layoutParams[index] ?? resolveParallelLayoutParams(routeSegments, layoutTreePositions[index] ?? 0, params), void 0, accumulatedMetadata);
152
191
  metadataResults.push(layoutMetadata);
@@ -167,20 +206,72 @@ async function resolveParallelRouteHead(parallelRoute, fallbackParams, fallbackR
167
206
  routeSegments
168
207
  });
169
208
  }
170
- viewportResults.push(...await Promise.all(layoutViewportPromises));
171
- const pageViewport = await pageViewportPromise;
172
- if (parallelRoute.pageModule) viewportResults.push(pageViewport);
173
209
  return {
174
- hasDynamicMetadata,
175
210
  metadataResults,
176
- metadataSources,
177
- viewportResults
211
+ metadataSources
178
212
  };
179
213
  }
214
+ async function resolveParallelRouteViewport(parallelRoute, fallbackParams, fallbackRouteSegments, pageSearchParams, searchParamsObserver) {
215
+ const params = parallelRoute.params ?? fallbackParams;
216
+ const routeSegments = parallelRoute.routeSegments ?? fallbackRouteSegments;
217
+ const layoutModules = getParallelRouteModules(parallelRoute);
218
+ const layoutTreePositions = parallelRoute.layoutTreePositions ?? [];
219
+ const layoutParams = parallelRoute.layoutParams ?? [];
220
+ const layoutViewportPromise = Promise.all(layoutModules.map((layoutModule, index) => resolveModuleViewport(layoutModule, layoutParams[index] ?? resolveParallelLayoutParams(routeSegments, layoutTreePositions[index] ?? 0, params))));
221
+ const pageViewportPromise = parallelRoute.pageModule ? resolveModuleViewport(parallelRoute.pageModule, params, pageSearchParams, searchParamsObserver) : Promise.resolve(null);
222
+ const [layoutViewports, pageViewport] = await Promise.all([layoutViewportPromise, pageViewportPromise]);
223
+ return parallelRoute.pageModule ? [...layoutViewports, pageViewport] : layoutViewports;
224
+ }
225
+ /**
226
+ * Resolve an explicit metadata-source sequence.
227
+ *
228
+ * Route-specific conventions own source selection and ordering. This resolver
229
+ * only supplies each source with its accumulated parent, merges the results,
230
+ * and applies file-based metadata at the end.
231
+ */
232
+ function resolveOrderedAppPageMetadata(options) {
233
+ return runWithFetchDedupe(async () => {
234
+ const metadataPromises = [];
235
+ let accumulatedEntriesPromise = Promise.resolve([]);
236
+ for (const source of options.sources) {
237
+ const parentPromise = accumulatedEntriesPromise.then((entries) => entries.length > 0 ? mergeMetadataEntries(entries) : {});
238
+ const metadataPromise = resolveModuleMetadata(source.module, source.params, source.searchParams, parentPromise, source.searchParamsObserver);
239
+ metadataPromises.push(metadataPromise);
240
+ metadataPromise.catch(() => null);
241
+ accumulatedEntriesPromise = Promise.all([accumulatedEntriesPromise, metadataPromise]).then(([entries, metadata]) => metadata ? [...entries, { metadata }] : entries);
242
+ accumulatedEntriesPromise.catch(() => null);
243
+ }
244
+ const [metadataEntries, metadataResults] = await Promise.all([accumulatedEntriesPromise, Promise.all(metadataPromises)]);
245
+ const metadataSources = options.sources.flatMap((source, index) => {
246
+ const metadata = metadataResults[index] ?? null;
247
+ return metadata || source.includeWhenEmpty ? [{
248
+ metadata,
249
+ routeSegments: source.routeSegments
250
+ }] : [];
251
+ });
252
+ return finalizeAppPageMetadata(metadataEntries.length > 0 ? mergeMetadataEntries(metadataEntries) : null, metadataSources, options);
253
+ });
254
+ }
180
255
  async function resolveAppPageHead(options) {
181
- return await runWithFetchDedupe(() => resolveAppPageHeadInner(options));
256
+ const prepared = prepareAppPageHead(options);
257
+ const [metadata, viewport] = await Promise.all([prepared.metadata, prepared.viewport]);
258
+ return {
259
+ ...prepared,
260
+ metadata,
261
+ viewport
262
+ };
263
+ }
264
+ /**
265
+ * Start metadata and viewport resolution without coupling their completion.
266
+ *
267
+ * Live document renders can place the metadata promise behind Suspense while
268
+ * still waiting for viewport tags before the shell is emitted. Blocking
269
+ * callers use {@link resolveAppPageHead} and observe the same result as before.
270
+ */
271
+ function prepareAppPageHead(options) {
272
+ return runWithFetchDedupe(() => prepareAppPageHeadInner(options));
182
273
  }
183
- async function resolveAppPageHeadInner(options) {
274
+ function prepareAppPageHeadInner(options) {
184
275
  const routeSegments = options.routeSegments ?? [];
185
276
  const layoutTreePositions = options.layoutTreePositions ?? [];
186
277
  const layoutInputs = createLayoutInputs(options.layoutModules, layoutTreePositions);
@@ -195,57 +286,52 @@ async function resolveAppPageHeadInner(options) {
195
286
  pageParentPromise.catch(() => null);
196
287
  const pageMetadataPromise = options.pageModule ? resolveModuleMetadata(options.pageModule, options.params, pageSearchParams, pageParentPromise, options.searchParamsObserver) : Promise.resolve(null);
197
288
  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([
289
+ const parallelRoutes = options.parallelRoutes ?? [];
290
+ const parallelRouteMetadataPromise = Promise.all(parallelRoutes.map((parallelRoute) => resolveParallelRouteMetadata(parallelRoute, options.params, routeSegments, pageSearchParams, pageParentPromise, options.searchParamsObserver)));
291
+ const parallelRouteViewportPromise = Promise.all(parallelRoutes.map((parallelRoute) => resolveParallelRouteViewport(parallelRoute, options.params, routeSegments, pageSearchParams, options.searchParamsObserver)));
292
+ const hasDynamicMetadata = primaryHasDynamicMetadata || parallelRoutes.some(parallelRouteHasDynamicMetadata);
293
+ const metadata = Promise.all([
200
294
  layoutMetadataPromise,
201
- layoutViewportPromise,
202
295
  pageMetadataPromise,
296
+ parallelRouteMetadataPromise
297
+ ]).then(async ([layoutMetadataResults, pageMetadata, parallelRouteMetadata]) => {
298
+ const parallelMetadataResults = parallelRouteMetadata.flatMap((head) => head.metadataResults);
299
+ const parallelMetadataSources = parallelRouteMetadata.flatMap((head) => head.metadataSources);
300
+ const primaryPageHasTitle = pageMetadata != null && pageMetadata.title !== void 0;
301
+ const metadataEntries = [
302
+ ...layoutMetadataResults.filter(isPresent).map((entry) => ({ metadata: entry })),
303
+ ...pageMetadata ? [{
304
+ isPage: true,
305
+ metadata: pageMetadata
306
+ }] : [],
307
+ ...parallelMetadataResults.filter(isPresent).map((entry) => ({
308
+ contributesTitle: !primaryPageHasTitle,
309
+ metadata: entry
310
+ }))
311
+ ];
312
+ const resolvedMetadataBase = metadataEntries.length > 0 ? mergeMetadataEntries(metadataEntries) : null;
313
+ const metadataSources = createMetadataSources(layoutMetadataResults, routeSegments, layoutSourcePositions, pageMetadata, Boolean(options.pageModule));
314
+ metadataSources.push(...parallelMetadataSources);
315
+ return finalizeAppPageMetadata(resolvedMetadataBase, metadataSources, options);
316
+ });
317
+ const viewport = Promise.all([
318
+ layoutViewportPromise,
203
319
  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 = [
320
+ parallelRouteViewportPromise
321
+ ]).then(([layoutViewportResults, pageViewport, parallelRouteViewports]) => mergeViewport([
223
322
  ...layoutViewportResults.filter(isPresent),
224
323
  ...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);
324
+ ...parallelRouteViewports.flat().filter(isPresent)
325
+ ]));
326
+ metadata.catch(() => null);
327
+ viewport.catch(() => null);
242
328
  return {
243
329
  hasDynamicMetadata,
244
330
  hasSearchParams,
245
331
  metadata,
246
332
  pageSearchParams,
247
- viewport: mergeViewport(viewportList)
333
+ viewport
248
334
  };
249
335
  }
250
336
  //#endregion
251
- export { collectAppPageSearchParams, resolveActiveParallelRouteHeadInputs, resolveAppPageHead };
337
+ 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, Viewport } 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<Viewport>;
28
+ //#endregion
29
+ export { createHttpAccessFallbackMetadataPlan, resolveHttpAccessFallbackMetadata, resolveHttpAccessFallbackViewport };