vinext 0.0.52 → 0.0.54

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 (330) hide show
  1. package/README.md +1 -1
  2. package/dist/build/clean-output.d.ts +14 -0
  3. package/dist/build/clean-output.js +36 -0
  4. package/dist/build/clean-output.js.map +1 -0
  5. package/dist/build/inline-css.d.ts +7 -0
  6. package/dist/build/inline-css.js +50 -0
  7. package/dist/build/inline-css.js.map +1 -0
  8. package/dist/build/prerender.d.ts +6 -2
  9. package/dist/build/prerender.js +51 -12
  10. package/dist/build/prerender.js.map +1 -1
  11. package/dist/build/run-prerender.js +10 -1
  12. package/dist/build/run-prerender.js.map +1 -1
  13. package/dist/build/static-export.d.ts +5 -0
  14. package/dist/build/static-export.js +8 -3
  15. package/dist/build/static-export.js.map +1 -1
  16. package/dist/check.js +4 -0
  17. package/dist/check.js.map +1 -1
  18. package/dist/cli.js +19 -4
  19. package/dist/cli.js.map +1 -1
  20. package/dist/client/instrumentation-client-inject.d.ts +34 -0
  21. package/dist/client/instrumentation-client-inject.js +57 -0
  22. package/dist/client/instrumentation-client-inject.js.map +1 -0
  23. package/dist/client/navigation-runtime.d.ts +16 -2
  24. package/dist/client/navigation-runtime.js +16 -1
  25. package/dist/client/navigation-runtime.js.map +1 -1
  26. package/dist/client/vinext-next-data.d.ts +2 -1
  27. package/dist/client/vinext-next-data.js.map +1 -1
  28. package/dist/client/window-next.d.ts +17 -2
  29. package/dist/client/window-next.js.map +1 -1
  30. package/dist/cloudflare/tpr.js +1 -1
  31. package/dist/cloudflare/tpr.js.map +1 -1
  32. package/dist/config/config-matchers.js +2 -1
  33. package/dist/config/config-matchers.js.map +1 -1
  34. package/dist/config/next-config.d.ts +95 -4
  35. package/dist/config/next-config.js +173 -14
  36. package/dist/config/next-config.js.map +1 -1
  37. package/dist/deploy.js +42 -7
  38. package/dist/deploy.js.map +1 -1
  39. package/dist/entries/app-browser-entry.d.ts +11 -1
  40. package/dist/entries/app-browser-entry.js +16 -6
  41. package/dist/entries/app-browser-entry.js.map +1 -1
  42. package/dist/entries/app-rsc-entry.d.ts +12 -3
  43. package/dist/entries/app-rsc-entry.js +41 -8
  44. package/dist/entries/app-rsc-entry.js.map +1 -1
  45. package/dist/entries/app-rsc-manifest.d.ts +21 -1
  46. package/dist/entries/app-rsc-manifest.js +6 -4
  47. package/dist/entries/app-rsc-manifest.js.map +1 -1
  48. package/dist/entries/pages-client-entry.d.ts +4 -1
  49. package/dist/entries/pages-client-entry.js +40 -3
  50. package/dist/entries/pages-client-entry.js.map +1 -1
  51. package/dist/entries/pages-server-entry.js +292 -34
  52. package/dist/entries/pages-server-entry.js.map +1 -1
  53. package/dist/entries/runtime-entry-module.d.ts +1 -10
  54. package/dist/entries/runtime-entry-module.js +2 -12
  55. package/dist/entries/runtime-entry-module.js.map +1 -1
  56. package/dist/index.js +91 -10
  57. package/dist/index.js.map +1 -1
  58. package/dist/plugins/fonts.js +25 -2
  59. package/dist/plugins/fonts.js.map +1 -1
  60. package/dist/plugins/remove-console.d.ts +16 -0
  61. package/dist/plugins/remove-console.js +176 -0
  62. package/dist/plugins/remove-console.js.map +1 -0
  63. package/dist/routing/app-route-graph.d.ts +24 -1
  64. package/dist/routing/app-route-graph.js +52 -4
  65. package/dist/routing/app-route-graph.js.map +1 -1
  66. package/dist/routing/app-router.d.ts +2 -2
  67. package/dist/routing/app-router.js +2 -2
  68. package/dist/routing/app-router.js.map +1 -1
  69. package/dist/routing/file-matcher.d.ts +21 -1
  70. package/dist/routing/file-matcher.js +39 -1
  71. package/dist/routing/file-matcher.js.map +1 -1
  72. package/dist/routing/pages-router.d.ts +1 -1
  73. package/dist/routing/pages-router.js +10 -3
  74. package/dist/routing/pages-router.js.map +1 -1
  75. package/dist/routing/route-trie.js +13 -18
  76. package/dist/routing/route-trie.js.map +1 -1
  77. package/dist/routing/utils.d.ts +11 -1
  78. package/dist/routing/utils.js +15 -1
  79. package/dist/routing/utils.js.map +1 -1
  80. package/dist/server/api-handler.js +19 -10
  81. package/dist/server/api-handler.js.map +1 -1
  82. package/dist/server/app-browser-action-result.d.ts +16 -1
  83. package/dist/server/app-browser-action-result.js +15 -1
  84. package/dist/server/app-browser-action-result.js.map +1 -1
  85. package/dist/server/app-browser-entry.js +47 -28
  86. package/dist/server/app-browser-entry.js.map +1 -1
  87. package/dist/server/app-browser-navigation-controller.d.ts +2 -0
  88. package/dist/server/app-browser-navigation-controller.js +4 -0
  89. package/dist/server/app-browser-navigation-controller.js.map +1 -1
  90. package/dist/server/app-elements-wire.d.ts +13 -4
  91. package/dist/server/app-elements-wire.js +10 -1
  92. package/dist/server/app-elements-wire.js.map +1 -1
  93. package/dist/server/app-elements.d.ts +2 -2
  94. package/dist/server/app-elements.js +2 -2
  95. package/dist/server/app-elements.js.map +1 -1
  96. package/dist/server/app-fallback-renderer.d.ts +27 -8
  97. package/dist/server/app-fallback-renderer.js +19 -8
  98. package/dist/server/app-fallback-renderer.js.map +1 -1
  99. package/dist/server/app-history-state.js +6 -2
  100. package/dist/server/app-history-state.js.map +1 -1
  101. package/dist/server/app-inline-css-client.d.ts +7 -0
  102. package/dist/server/app-inline-css-client.js +37 -0
  103. package/dist/server/app-inline-css-client.js.map +1 -0
  104. package/dist/server/app-interception-context-header.d.ts +33 -0
  105. package/dist/server/app-interception-context-header.js +44 -0
  106. package/dist/server/app-interception-context-header.js.map +1 -0
  107. package/dist/server/app-mounted-slots-header.d.ts +19 -0
  108. package/dist/server/app-mounted-slots-header.js +40 -1
  109. package/dist/server/app-mounted-slots-header.js.map +1 -1
  110. package/dist/server/app-optimistic-routing.js +26 -18
  111. package/dist/server/app-optimistic-routing.js.map +1 -1
  112. package/dist/server/app-page-boundary-render.d.ts +1 -0
  113. package/dist/server/app-page-boundary-render.js +2 -0
  114. package/dist/server/app-page-boundary-render.js.map +1 -1
  115. package/dist/server/app-page-boundary.d.ts +22 -1
  116. package/dist/server/app-page-boundary.js +30 -3
  117. package/dist/server/app-page-boundary.js.map +1 -1
  118. package/dist/server/app-page-cache.d.ts +9 -3
  119. package/dist/server/app-page-cache.js +14 -8
  120. package/dist/server/app-page-cache.js.map +1 -1
  121. package/dist/server/app-page-dispatch.d.ts +13 -1
  122. package/dist/server/app-page-dispatch.js +136 -82
  123. package/dist/server/app-page-dispatch.js.map +1 -1
  124. package/dist/server/app-page-element-builder.d.ts +2 -1
  125. package/dist/server/app-page-element-builder.js +17 -30
  126. package/dist/server/app-page-element-builder.js.map +1 -1
  127. package/dist/server/app-page-execution.d.ts +1 -0
  128. package/dist/server/app-page-execution.js +2 -0
  129. package/dist/server/app-page-execution.js.map +1 -1
  130. package/dist/server/app-page-head.d.ts +1 -0
  131. package/dist/server/app-page-head.js +8 -0
  132. package/dist/server/app-page-head.js.map +1 -1
  133. package/dist/server/app-page-render-identity.d.ts +22 -0
  134. package/dist/server/app-page-render-identity.js +42 -0
  135. package/dist/server/app-page-render-identity.js.map +1 -0
  136. package/dist/server/app-page-render-observation.js +1 -1
  137. package/dist/server/app-page-render.d.ts +9 -1
  138. package/dist/server/app-page-render.js +8 -2
  139. package/dist/server/app-page-render.js.map +1 -1
  140. package/dist/server/app-page-request.d.ts +6 -3
  141. package/dist/server/app-page-request.js +5 -2
  142. package/dist/server/app-page-request.js.map +1 -1
  143. package/dist/server/app-page-response.d.ts +11 -1
  144. package/dist/server/app-page-response.js +16 -4
  145. package/dist/server/app-page-response.js.map +1 -1
  146. package/dist/server/app-page-route-wiring.d.ts +16 -0
  147. package/dist/server/app-page-route-wiring.js +25 -10
  148. package/dist/server/app-page-route-wiring.js.map +1 -1
  149. package/dist/server/app-page-stream.d.ts +12 -0
  150. package/dist/server/app-page-stream.js +3 -0
  151. package/dist/server/app-page-stream.js.map +1 -1
  152. package/dist/server/app-route-handler-dispatch.d.ts +1 -0
  153. package/dist/server/app-route-handler-dispatch.js +3 -0
  154. package/dist/server/app-route-handler-dispatch.js.map +1 -1
  155. package/dist/server/app-route-handler-execution.d.ts +1 -0
  156. package/dist/server/app-route-handler-execution.js +1 -0
  157. package/dist/server/app-route-handler-execution.js.map +1 -1
  158. package/dist/server/app-route-handler-response.js +38 -6
  159. package/dist/server/app-route-handler-response.js.map +1 -1
  160. package/dist/server/app-rsc-handler.d.ts +16 -3
  161. package/dist/server/app-rsc-handler.js +60 -11
  162. package/dist/server/app-rsc-handler.js.map +1 -1
  163. package/dist/server/app-rsc-request-normalization.d.ts +2 -1
  164. package/dist/server/app-rsc-request-normalization.js +6 -4
  165. package/dist/server/app-rsc-request-normalization.js.map +1 -1
  166. package/dist/server/app-segment-config.d.ts +4 -1
  167. package/dist/server/app-segment-config.js +6 -1
  168. package/dist/server/app-segment-config.js.map +1 -1
  169. package/dist/server/app-server-action-execution.d.ts +22 -3
  170. package/dist/server/app-server-action-execution.js +46 -7
  171. package/dist/server/app-server-action-execution.js.map +1 -1
  172. package/dist/server/app-ssr-entry.d.ts +6 -0
  173. package/dist/server/app-ssr-entry.js +57 -6
  174. package/dist/server/app-ssr-entry.js.map +1 -1
  175. package/dist/server/app-ssr-error-meta.js +3 -3
  176. package/dist/server/app-ssr-error-meta.js.map +1 -1
  177. package/dist/server/app-ssr-stream.d.ts +25 -1
  178. package/dist/server/app-ssr-stream.js +237 -19
  179. package/dist/server/app-ssr-stream.js.map +1 -1
  180. package/dist/server/app-static-generation.d.ts +1 -0
  181. package/dist/server/app-static-generation.js +2 -1
  182. package/dist/server/app-static-generation.js.map +1 -1
  183. package/dist/server/client-trace-metadata.d.ts +31 -0
  184. package/dist/server/client-trace-metadata.js +83 -0
  185. package/dist/server/client-trace-metadata.js.map +1 -0
  186. package/dist/server/cookie-utils.d.ts +13 -0
  187. package/dist/server/cookie-utils.js +20 -0
  188. package/dist/server/cookie-utils.js.map +1 -0
  189. package/dist/server/default-not-found-module.d.ts +20 -0
  190. package/dist/server/default-not-found-module.js +20 -0
  191. package/dist/server/default-not-found-module.js.map +1 -0
  192. package/dist/server/dev-server.d.ts +8 -1
  193. package/dist/server/dev-server.js +56 -11
  194. package/dist/server/dev-server.js.map +1 -1
  195. package/dist/server/headers.d.ts +5 -1
  196. package/dist/server/headers.js +5 -1
  197. package/dist/server/headers.js.map +1 -1
  198. package/dist/server/html.d.ts +2 -1
  199. package/dist/server/html.js +6 -1
  200. package/dist/server/html.js.map +1 -1
  201. package/dist/server/image-optimization.d.ts +13 -4
  202. package/dist/server/image-optimization.js +15 -4
  203. package/dist/server/image-optimization.js.map +1 -1
  204. package/dist/server/isr-cache.d.ts +7 -5
  205. package/dist/server/isr-cache.js +17 -6
  206. package/dist/server/isr-cache.js.map +1 -1
  207. package/dist/server/middleware-runtime.js +1 -2
  208. package/dist/server/middleware-runtime.js.map +1 -1
  209. package/dist/server/middleware.js +1 -1
  210. package/dist/server/middleware.js.map +1 -1
  211. package/dist/server/pages-api-route.d.ts +18 -0
  212. package/dist/server/pages-api-route.js +3 -1
  213. package/dist/server/pages-api-route.js.map +1 -1
  214. package/dist/server/pages-body-parser-config.d.ts +60 -0
  215. package/dist/server/pages-body-parser-config.js +79 -0
  216. package/dist/server/pages-body-parser-config.js.map +1 -0
  217. package/dist/server/pages-data-route.js +1 -0
  218. package/dist/server/pages-data-route.js.map +1 -1
  219. package/dist/server/pages-default-404.d.ts +31 -0
  220. package/dist/server/pages-default-404.js +40 -0
  221. package/dist/server/pages-default-404.js.map +1 -0
  222. package/dist/server/pages-document-initial-props.d.ts +7 -0
  223. package/dist/server/pages-document-initial-props.js +14 -0
  224. package/dist/server/pages-document-initial-props.js.map +1 -0
  225. package/dist/server/pages-node-compat.d.ts +10 -0
  226. package/dist/server/pages-node-compat.js +12 -1
  227. package/dist/server/pages-node-compat.js.map +1 -1
  228. package/dist/server/pages-page-data.d.ts +40 -0
  229. package/dist/server/pages-page-data.js +19 -14
  230. package/dist/server/pages-page-data.js.map +1 -1
  231. package/dist/server/pages-page-method.d.ts +48 -0
  232. package/dist/server/pages-page-method.js +19 -0
  233. package/dist/server/pages-page-method.js.map +1 -0
  234. package/dist/server/pages-page-response.d.ts +8 -0
  235. package/dist/server/pages-page-response.js +21 -11
  236. package/dist/server/pages-page-response.js.map +1 -1
  237. package/dist/server/pages-serializable-props.d.ts +25 -0
  238. package/dist/server/pages-serializable-props.js +69 -0
  239. package/dist/server/pages-serializable-props.js.map +1 -0
  240. package/dist/server/prerender-route-params.d.ts +14 -0
  241. package/dist/server/prerender-route-params.js +94 -0
  242. package/dist/server/prerender-route-params.js.map +1 -0
  243. package/dist/server/prod-server.d.ts +3 -23
  244. package/dist/server/prod-server.js +43 -57
  245. package/dist/server/prod-server.js.map +1 -1
  246. package/dist/server/proxy-trust.d.ts +41 -0
  247. package/dist/server/proxy-trust.js +70 -0
  248. package/dist/server/proxy-trust.js.map +1 -0
  249. package/dist/server/request-pipeline.d.ts +3 -3
  250. package/dist/server/request-pipeline.js +5 -4
  251. package/dist/server/request-pipeline.js.map +1 -1
  252. package/dist/server/seed-cache.js +12 -6
  253. package/dist/server/seed-cache.js.map +1 -1
  254. package/dist/server/server-action-not-found.js +3 -2
  255. package/dist/server/server-action-not-found.js.map +1 -1
  256. package/dist/server/static-file-cache.js +2 -1
  257. package/dist/server/static-file-cache.js.map +1 -1
  258. package/dist/server/streaming-metadata.d.ts +5 -0
  259. package/dist/server/streaming-metadata.js +10 -0
  260. package/dist/server/streaming-metadata.js.map +1 -0
  261. package/dist/shims/app-router-scroll-state.d.ts +14 -0
  262. package/dist/shims/app-router-scroll-state.js +51 -0
  263. package/dist/shims/app-router-scroll-state.js.map +1 -0
  264. package/dist/shims/app-router-scroll.d.ts +28 -0
  265. package/dist/shims/app-router-scroll.js +115 -0
  266. package/dist/shims/app-router-scroll.js.map +1 -0
  267. package/dist/shims/before-interactive-context.d.ts +30 -0
  268. package/dist/shims/before-interactive-context.js +10 -0
  269. package/dist/shims/before-interactive-context.js.map +1 -0
  270. package/dist/shims/cache-runtime.d.ts +1 -1
  271. package/dist/shims/cache-runtime.js +14 -1
  272. package/dist/shims/cache-runtime.js.map +1 -1
  273. package/dist/shims/cache.d.ts +6 -0
  274. package/dist/shims/cache.js +7 -0
  275. package/dist/shims/cache.js.map +1 -1
  276. package/dist/shims/default-not-found.d.ts +12 -0
  277. package/dist/shims/default-not-found.js +61 -0
  278. package/dist/shims/default-not-found.js.map +1 -0
  279. package/dist/shims/error.js +3 -0
  280. package/dist/shims/error.js.map +1 -1
  281. package/dist/shims/font-local.d.ts +5 -0
  282. package/dist/shims/font-local.js +6 -2
  283. package/dist/shims/font-local.js.map +1 -1
  284. package/dist/shims/head.js +4 -4
  285. package/dist/shims/head.js.map +1 -1
  286. package/dist/shims/headers.d.ts +13 -2
  287. package/dist/shims/headers.js +73 -22
  288. package/dist/shims/headers.js.map +1 -1
  289. package/dist/shims/image.d.ts +1 -1
  290. package/dist/shims/image.js +4 -4
  291. package/dist/shims/image.js.map +1 -1
  292. package/dist/shims/internal/app-route-detection.d.ts +37 -0
  293. package/dist/shims/internal/app-route-detection.js +69 -0
  294. package/dist/shims/internal/app-route-detection.js.map +1 -0
  295. package/dist/shims/internal/pages-data-target.d.ts +58 -0
  296. package/dist/shims/internal/pages-data-target.js +91 -0
  297. package/dist/shims/internal/pages-data-target.js.map +1 -0
  298. package/dist/shims/internal/pages-data-url.d.ts +42 -0
  299. package/dist/shims/internal/pages-data-url.js +73 -0
  300. package/dist/shims/internal/pages-data-url.js.map +1 -0
  301. package/dist/shims/link.d.ts +18 -2
  302. package/dist/shims/link.js +129 -15
  303. package/dist/shims/link.js.map +1 -1
  304. package/dist/shims/metadata.d.ts +9 -7
  305. package/dist/shims/metadata.js +70 -7
  306. package/dist/shims/metadata.js.map +1 -1
  307. package/dist/shims/navigation.d.ts +1 -2
  308. package/dist/shims/navigation.js +94 -20
  309. package/dist/shims/navigation.js.map +1 -1
  310. package/dist/shims/router.d.ts +5 -0
  311. package/dist/shims/router.js +389 -80
  312. package/dist/shims/router.js.map +1 -1
  313. package/dist/shims/script.d.ts +11 -1
  314. package/dist/shims/script.js +158 -15
  315. package/dist/shims/script.js.map +1 -1
  316. package/dist/shims/server.js +1 -0
  317. package/dist/shims/server.js.map +1 -1
  318. package/dist/shims/url-utils.d.ts +2 -1
  319. package/dist/shims/url-utils.js +15 -4
  320. package/dist/shims/url-utils.js.map +1 -1
  321. package/dist/utils/html-limited-bots.d.ts +5 -0
  322. package/dist/utils/html-limited-bots.js +15 -0
  323. package/dist/utils/html-limited-bots.js.map +1 -0
  324. package/dist/utils/path.d.ts +13 -0
  325. package/dist/utils/path.js +16 -0
  326. package/dist/utils/path.js.map +1 -0
  327. package/dist/utils/query.d.ts +6 -0
  328. package/dist/utils/query.js +10 -1
  329. package/dist/utils/query.js.map +1 -1
  330. package/package.json +1 -1
@@ -1,14 +1,14 @@
1
- import { normalizePathnameForRouteMatch } from "../routing/utils.js";
2
- import { isInterceptionMatchedUrlPath, normalizePath } from "./normalize-path.js";
3
1
  import { matchRoutePattern } from "../routing/route-pattern.js";
4
2
  import { markDynamicUsage, markRenderRequestApiUsage } from "../shims/headers.js";
5
- import { APP_RSC_RENDER_MODE_NAVIGATION } from "./app-rsc-render-mode.js";
6
3
  import { AppElementsWire } from "./app-elements-wire.js";
4
+ import { APP_RSC_RENDER_MODE_NAVIGATION } from "./app-rsc-render-mode.js";
7
5
  import "./app-elements.js";
6
+ import { createAppPageRenderIdentity } from "./app-page-render-identity.js";
8
7
  import { makeThenableParams } from "../shims/thenable-params.js";
9
8
  import { buildAppPageElements, createAppPageTreePath } from "./app-page-route-wiring.js";
10
9
  import { resolveActiveParallelRouteHeadInputs, resolveAppPageHead } from "./app-page-head.js";
11
10
  import { DEFAULT_GLOBAL_ERROR_MODULE } from "./default-global-error-module.js";
11
+ import { shouldServeStreamingMetadata } from "./streaming-metadata.js";
12
12
  import { createElement } from "react";
13
13
  //#region src/server/app-page-element-builder.ts
14
14
  /**
@@ -33,10 +33,13 @@ async function buildPageElements(options) {
33
33
  const pageModule = route.page;
34
34
  const PageComponent = pageModule?.default;
35
35
  const hasPageModule = !!pageModule;
36
- const interception = createAppPageInterceptionProof(routePath, opts);
36
+ const renderIdentity = createAppPageRenderIdentity({
37
+ displayPathname: routePath,
38
+ interceptionContext: opts?.interceptionContext ?? null,
39
+ interceptSourceMatchedUrl: opts?.interceptSourceMatchedUrl ?? null,
40
+ interceptSlotId: opts?.interceptSlotId ?? null
41
+ });
37
42
  if (hasPageModule && !PageComponent) {
38
- const interceptionContext = opts?.interceptionContext ?? null;
39
- const noExportRouteId = AppElementsWire.encodeRouteId(routePath, interceptionContext);
40
43
  let noExportRootLayout = null;
41
44
  const noExportLayoutIds = route.ids?.layouts ?? route.layouts.map((_, index) => AppElementsWire.encodeLayoutId(createAppPageTreePath(route.routeSegments, route.layoutTreePositions?.[index] ?? 0)));
42
45
  if (route.layouts?.length > 0) {
@@ -45,16 +48,16 @@ async function buildPageElements(options) {
45
48
  }
46
49
  return {
47
50
  ...AppElementsWire.createMetadataEntries({
48
- interception,
49
- interceptionContext,
51
+ interception: renderIdentity.interception,
52
+ interceptionContext: renderIdentity.interceptionContext,
50
53
  layoutIds: noExportLayoutIds,
51
54
  rootLayoutTreePath: noExportRootLayout,
52
- routeId: noExportRouteId
55
+ routeId: renderIdentity.routeId
53
56
  }),
54
- [noExportRouteId]: createElement("div", null, "Page has no default export")
57
+ [renderIdentity.routeId]: createElement("div", null, "Page has no default export")
55
58
  };
56
59
  }
57
- const { hasSearchParams, metadata: resolvedMetadata, pageSearchParams, viewport: resolvedViewport } = await resolveAppPageHead({
60
+ const { hasSearchParams, hasDynamicMetadata, metadata: resolvedMetadata, pageSearchParams, viewport: resolvedViewport } = await resolveAppPageHead({
58
61
  basePath: options.basePath ?? "",
59
62
  layoutModules: route.layouts,
60
63
  layoutTreePositions: route.layoutTreePositions,
@@ -84,6 +87,7 @@ async function buildPageElements(options) {
84
87
  }
85
88
  const mountedSlotIds = mountedSlotsHeader ? new Set(mountedSlotsHeader.split(" ")) : null;
86
89
  const slotOverrides = buildSlotOverrides(route, params, routePath, opts);
90
+ const metadataPlacement = hasDynamicMetadata && shouldServeStreamingMetadata(pageRequest.request.headers.get("user-agent") ?? "", options.htmlLimitedBots) ? "body" : "head";
87
91
  return buildAppPageElements({
88
92
  element: PageComponent ? createElement(PageComponent, pageProps) : null,
89
93
  globalErrorModule: globalErrorModule ?? DEFAULT_GLOBAL_ERROR_MODULE,
@@ -91,11 +95,11 @@ async function buildPageElements(options) {
91
95
  mountedSlotIds,
92
96
  makeThenableParams,
93
97
  matchedParams: params,
98
+ metadataPlacement,
94
99
  resolvedMetadata,
95
100
  resolvedMetadataPathname: routePath,
96
101
  resolvedViewport,
97
- interceptionContext: opts?.interceptionContext ?? null,
98
- interception,
102
+ renderIdentity,
99
103
  routePath,
100
104
  rootNotFoundModule: rootNotFoundModule ?? null,
101
105
  rootForbiddenModule: rootForbiddenModule ?? null,
@@ -105,23 +109,6 @@ async function buildPageElements(options) {
105
109
  renderMode
106
110
  });
107
111
  }
108
- function createAppPageInterceptionProof(routePath, opts) {
109
- const sourceMatchedUrl = normalizeInterceptionProofMatchedUrl(opts?.interceptSourceMatchedUrl ?? null);
110
- const targetMatchedUrl = normalizeInterceptionProofMatchedUrl(routePath);
111
- const slotId = opts?.interceptSlotId ?? null;
112
- if (sourceMatchedUrl === null || targetMatchedUrl === null || slotId === null) return null;
113
- return {
114
- sourceMatchedUrl,
115
- sourceRouteId: AppElementsWire.encodeRouteId(sourceMatchedUrl, null),
116
- slotId,
117
- targetMatchedUrl,
118
- targetRouteId: AppElementsWire.encodeRouteId(targetMatchedUrl, null)
119
- };
120
- }
121
- function normalizeInterceptionProofMatchedUrl(value) {
122
- if (value === null || !isInterceptionMatchedUrlPath(value)) return null;
123
- return normalizePath(normalizePathnameForRouteMatch(value));
124
- }
125
112
  /**
126
113
  * Build the per-request `slotOverrides` map. Combines:
127
114
  * - Interception overrides (existing behavior — swap in the intercepting page
@@ -1 +1 @@
1
- {"version":3,"file":"app-page-element-builder.js","names":[],"sources":["../../src/server/app-page-element-builder.ts"],"sourcesContent":["import { createElement } from \"react\";\nimport { markDynamicUsage, markRenderRequestApiUsage } from \"vinext/shims/headers\";\nimport { makeThenableParams } from \"vinext/shims/thenable-params\";\nimport { resolveActiveParallelRouteHeadInputs, resolveAppPageHead } from \"./app-page-head.js\";\nimport {\n buildAppPageElements,\n createAppPageTreePath,\n type AppPageErrorModule,\n type AppPageModule,\n type AppPageRouteWiringRoute,\n type AppPageSlotOverride,\n} from \"./app-page-route-wiring.js\";\nimport { AppElementsWire, type AppElements, type AppElementsInterception } from \"./app-elements.js\";\nimport type { AppPageParams } from \"./app-page-boundary.js\";\nimport { DEFAULT_GLOBAL_ERROR_MODULE } from \"./default-global-error-module.js\";\nimport { matchRoutePattern } from \"../routing/route-pattern.js\";\nimport { normalizePathnameForRouteMatch } from \"../routing/utils.js\";\nimport type { MetadataFileRoute } from \"./metadata-routes.js\";\nimport { APP_RSC_RENDER_MODE_NAVIGATION, type AppRscRenderMode } from \"./app-rsc-render-mode.js\";\nimport { isInterceptionMatchedUrlPath, normalizePath } from \"./normalize-path.js\";\n\nexport type { AppPageErrorModule, AppPageRouteWiringRoute } from \"./app-page-route-wiring.js\";\n\n/**\n * Route shape passed from the generated entry. Extends the wiring route with\n * the page module reference (used to extract the default export for the page\n * element) and the URL pattern (used as the route path in head resolution).\n */\nexport type AppPageBuildRoute<\n TModule extends AppPageModule = AppPageModule,\n TErrorModule extends AppPageErrorModule = AppPageErrorModule,\n> = AppPageRouteWiringRoute<TModule, TErrorModule> & {\n page?: TModule | null;\n pattern: string;\n /** Param names captured by the route's URL pattern, in order. */\n params?: readonly string[] | null;\n};\n\nexport type AppPageInterceptOptions<TModule extends AppPageModule = AppPageModule> = {\n interceptionContext?: string | null;\n interceptLayouts?: readonly (TModule | null | undefined)[] | null;\n interceptPage?: TModule | null;\n interceptParams?: AppPageParams | null;\n interceptSlotId?: string | null;\n interceptSlotKey?: string | null;\n interceptSourceMatchedUrl?: string | null;\n};\n\nexport type AppPagePageRequest<TModule extends AppPageModule = AppPageModule> = {\n /** Interception context from current-route navigation (null for direct visits). */\n opts?: AppPageInterceptOptions<TModule> | null;\n /** URL search params from the incoming request (null when unavailable). */\n searchParams?: URLSearchParams | null;\n /** Whether the incoming request is an RSC (client-side navigation) request. */\n isRscRequest: boolean;\n /** The incoming HTTP request (available but unused by this module). */\n request: Request;\n /** Normalized x-vinext-mounted-slots header value. */\n mountedSlotsHeader: string | null;\n /** Semantic RSC payload mode for this page render. */\n renderMode?: AppRscRenderMode;\n};\n\nexport type BuildPageElementsOptions<\n TModule extends AppPageModule = AppPageModule,\n TErrorModule extends AppPageErrorModule = AppPageErrorModule,\n> = {\n route: AppPageBuildRoute<TModule, TErrorModule>;\n params: AppPageParams;\n routePath: string;\n pageRequest: AppPagePageRequest<TModule>;\n /** Root-level global-error.tsx module. Present when the app defines this file. */\n globalErrorModule?: TErrorModule | null;\n /** Root-level not-found.tsx module. Present when the app defines this file. */\n rootNotFoundModule?: TModule | null;\n /** Root-level forbidden.tsx module. Present when the app defines this file. */\n rootForbiddenModule?: TModule | null;\n /** Root-level unauthorized.tsx module. Present when the app defines this file. */\n rootUnauthorizedModule?: TModule | null;\n /** File-based metadata routes (favicon, manifest, sitemap, etc.). */\n metadataRoutes: readonly MetadataFileRoute[];\n /**\n * Configured next.config `basePath`. Threaded through `resolveAppPageHead`\n * so file-based metadata route URLs emitted in <head> are prefixed.\n */\n basePath?: string;\n};\n\n/**\n * Build the App Router element tree for a matched route.\n *\n * This is the central element-construction path for the App Router RSC\n * handler. It resolves page head metadata (including parallel route metadata),\n * creates the page React element, and wires it into the nested layout +\n * boundary tree via {@link buildAppPageElements}.\n *\n * The function is extracted from the generated RSC entry template so it can\n * be unit-tested independently of the code-generation machinery.\n *\n * Next.js equivalent: the component tree construction in\n * {@link https://github.com/vercel/next.js/blob/canary/packages/next/src/server/app-render/create-component-tree.tsx|create-component-tree.tsx}\n * and the page head resolution in\n * {@link https://github.com/vercel/next.js/blob/canary/packages/next/src/server/app-render/create-metadata.tsx|create-metadata.tsx}.\n */\nexport async function buildPageElements<\n TModule extends AppPageModule = AppPageModule,\n TErrorModule extends AppPageErrorModule = AppPageErrorModule,\n>(options: BuildPageElementsOptions<TModule, TErrorModule>): Promise<AppElements> {\n const {\n route,\n params,\n routePath,\n pageRequest,\n globalErrorModule,\n rootNotFoundModule,\n rootForbiddenModule,\n rootUnauthorizedModule,\n metadataRoutes,\n } = options;\n const {\n opts,\n searchParams,\n isRscRequest,\n mountedSlotsHeader,\n renderMode = APP_RSC_RENDER_MODE_NAVIGATION,\n } = pageRequest;\n\n const pageModule: AppPageModule | null | undefined = route.page;\n const PageComponent = pageModule?.default;\n const hasPageModule = !!pageModule;\n const interception = createAppPageInterceptionProof(routePath, opts);\n\n if (hasPageModule && !PageComponent) {\n const interceptionContext = opts?.interceptionContext ?? null;\n const noExportRouteId = AppElementsWire.encodeRouteId(routePath, interceptionContext);\n let noExportRootLayout: string | null = null;\n const noExportLayoutIds =\n route.ids?.layouts ??\n route.layouts.map((_, index) =>\n AppElementsWire.encodeLayoutId(\n createAppPageTreePath(route.routeSegments, route.layoutTreePositions?.[index] ?? 0),\n ),\n );\n if (route.layouts?.length > 0) {\n const treePosition = route.layoutTreePositions?.[0] ?? 0;\n noExportRootLayout = createAppPageTreePath(route.routeSegments, treePosition);\n }\n return {\n ...AppElementsWire.createMetadataEntries({\n interception,\n interceptionContext,\n layoutIds: noExportLayoutIds,\n rootLayoutTreePath: noExportRootLayout,\n routeId: noExportRouteId,\n }),\n [noExportRouteId]: createElement(\"div\", null, \"Page has no default export\"),\n };\n }\n\n const {\n hasSearchParams,\n metadata: resolvedMetadata,\n pageSearchParams,\n viewport: resolvedViewport,\n } = await resolveAppPageHead({\n basePath: options.basePath ?? \"\",\n layoutModules: route.layouts,\n layoutTreePositions: route.layoutTreePositions,\n metadataRoutes,\n pageModule: route.page ?? null,\n parallelRoutes: resolveActiveParallelRouteHeadInputs({\n interceptLayouts: opts?.interceptLayouts ?? null,\n interceptPage: opts?.interceptPage ?? null,\n interceptParams: opts?.interceptParams ?? null,\n interceptSlotKey: opts?.interceptSlotKey ?? null,\n params,\n routeSegments: route.routeSegments ?? [],\n slots: route.slots ?? null,\n }),\n params,\n routePath: route.pattern,\n routeSegments: route.routeSegments ?? null,\n searchParams,\n });\n\n const pageProps: Record<string, unknown> = { params: makeThenableParams(params) };\n if (searchParams) {\n pageProps.searchParams = makeThenableParams(pageSearchParams);\n if (hasSearchParams) {\n markDynamicUsage();\n markRenderRequestApiUsage(\"searchParams\");\n }\n }\n\n const mountedSlotIds = mountedSlotsHeader ? new Set(mountedSlotsHeader.split(\" \")) : null;\n\n const slotOverrides = buildSlotOverrides(route, params, routePath, opts);\n\n return buildAppPageElements({\n element: PageComponent ? createElement(PageComponent, pageProps) : null,\n // Fall back to vinext's built-in default global error module so that\n // uncaught client render errors are caught by the route-level\n // <ErrorBoundary> wrapper in app-page-route-wiring.tsx, mirroring\n // Next.js's behavior when the user has not defined app/global-error.tsx.\n globalErrorModule:\n globalErrorModule ?? (DEFAULT_GLOBAL_ERROR_MODULE as unknown as TErrorModule),\n isRscRequest,\n mountedSlotIds,\n makeThenableParams,\n matchedParams: params,\n resolvedMetadata,\n resolvedMetadataPathname: routePath,\n resolvedViewport,\n interceptionContext: opts?.interceptionContext ?? null,\n interception,\n routePath,\n rootNotFoundModule: rootNotFoundModule ?? null,\n rootForbiddenModule: rootForbiddenModule ?? null,\n rootUnauthorizedModule: rootUnauthorizedModule ?? null,\n route,\n slotOverrides,\n renderMode,\n });\n}\n\nfunction createAppPageInterceptionProof<TModule extends AppPageModule>(\n routePath: string,\n opts?: AppPageInterceptOptions<TModule> | null,\n): AppElementsInterception | null {\n const sourceMatchedUrl = normalizeInterceptionProofMatchedUrl(\n opts?.interceptSourceMatchedUrl ?? null,\n );\n const targetMatchedUrl = normalizeInterceptionProofMatchedUrl(routePath);\n const slotId = opts?.interceptSlotId ?? null;\n if (sourceMatchedUrl === null || targetMatchedUrl === null || slotId === null) return null;\n\n return {\n sourceMatchedUrl,\n sourceRouteId: AppElementsWire.encodeRouteId(sourceMatchedUrl, null),\n slotId,\n targetMatchedUrl,\n targetRouteId: AppElementsWire.encodeRouteId(targetMatchedUrl, null),\n };\n}\n\nfunction normalizeInterceptionProofMatchedUrl(value: string | null): string | null {\n if (value === null || !isInterceptionMatchedUrlPath(value)) return null;\n\n return normalizePath(normalizePathnameForRouteMatch(value));\n}\n\n/**\n * Build the per-request `slotOverrides` map. Combines:\n * - Interception overrides (existing behavior — swap in the intercepting page\n * and its layouts when the request is intercepted into this slot).\n * - Slot-specific param extraction for inherited slots whose URL pattern\n * has different param names than the route's. The runtime matches the\n * cleaned request path against `slot.slotPatternParts` to produce\n * slot-scoped params, which `app-page-route-wiring` then hands to the\n * slot page instead of the route's matched params.\n *\n * `routePath` is the already-normalized request pathname (basePath stripped,\n * RSC suffix removed). Re-parsing `request.url` here would re-introduce the\n * basePath and silently break the match for any app that configures one.\n */\nfunction buildSlotOverrides<TModule extends AppPageModule, TErrorModule extends AppPageErrorModule>(\n route: AppPageBuildRoute<TModule, TErrorModule>,\n routeParams: AppPageParams,\n routePath: string,\n opts?: AppPageInterceptOptions<TModule> | null,\n): Readonly<Record<string, AppPageSlotOverride<TModule>>> | null {\n const overrides: Record<string, AppPageSlotOverride<TModule>> = {};\n\n if (opts && opts.interceptSlotKey && opts.interceptPage) {\n overrides[opts.interceptSlotKey] = {\n layoutModules: opts.interceptLayouts || null,\n pageModule: opts.interceptPage,\n params: opts.interceptParams || routeParams,\n };\n }\n\n const slots = route.slots;\n if (slots) {\n let urlParts: string[] | null = null;\n const routeParamSet = collectParamNameSet(route.params);\n for (const [slotKey, slot] of Object.entries(slots)) {\n const patternParts = slot.slotPatternParts;\n const paramNames = slot.slotParamNames;\n if (!patternParts || patternParts.length === 0) continue;\n // Skip when every slot param is already a route param — the route's\n // matched params already carry the values the slot page expects.\n // Empty `paramNames` (slot pattern has no dynamic markers) also skips:\n // there's nothing to extract, so the route's matched params suffice.\n if (paramNames && paramNames.every((name) => routeParamSet.has(name))) continue;\n\n if (urlParts === null) {\n urlParts = routePath.split(\"/\").filter(Boolean);\n }\n const matched = matchRoutePattern(urlParts, patternParts);\n if (!matched) continue;\n\n const existing = overrides[slotKey];\n overrides[slotKey] = existing ? { ...existing, params: matched } : { params: matched };\n }\n }\n\n return Object.keys(overrides).length > 0 ? overrides : null;\n}\n\nfunction collectParamNameSet(params: readonly string[] | undefined | null): Set<string> {\n const set = new Set<string>();\n if (params) {\n for (const name of params) set.add(name);\n }\n return set;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"app-page-element-builder.js","names":[],"sources":["../../src/server/app-page-element-builder.ts"],"sourcesContent":["import { createElement } from \"react\";\nimport { markDynamicUsage, markRenderRequestApiUsage } from \"vinext/shims/headers\";\nimport { makeThenableParams } from \"vinext/shims/thenable-params\";\nimport { resolveActiveParallelRouteHeadInputs, resolveAppPageHead } from \"./app-page-head.js\";\nimport {\n buildAppPageElements,\n createAppPageTreePath,\n type AppPageErrorModule,\n type AppPageModule,\n type AppPageRouteWiringRoute,\n type AppPageSlotOverride,\n} from \"./app-page-route-wiring.js\";\nimport { AppElementsWire, type AppElements } from \"./app-elements.js\";\nimport type { AppPageParams } from \"./app-page-boundary.js\";\nimport { DEFAULT_GLOBAL_ERROR_MODULE } from \"./default-global-error-module.js\";\nimport { matchRoutePattern } from \"../routing/route-pattern.js\";\nimport type { MetadataFileRoute } from \"./metadata-routes.js\";\nimport { APP_RSC_RENDER_MODE_NAVIGATION, type AppRscRenderMode } from \"./app-rsc-render-mode.js\";\nimport { createAppPageRenderIdentity } from \"./app-page-render-identity.js\";\nimport { shouldServeStreamingMetadata } from \"./streaming-metadata.js\";\n\nexport type { AppPageErrorModule, AppPageRouteWiringRoute } from \"./app-page-route-wiring.js\";\n\n/**\n * Route shape passed from the generated entry. Extends the wiring route with\n * the page module reference (used to extract the default export for the page\n * element) and the URL pattern (used as the route path in head resolution).\n */\nexport type AppPageBuildRoute<\n TModule extends AppPageModule = AppPageModule,\n TErrorModule extends AppPageErrorModule = AppPageErrorModule,\n> = AppPageRouteWiringRoute<TModule, TErrorModule> & {\n page?: TModule | null;\n pattern: string;\n /** Param names captured by the route's URL pattern, in order. */\n params?: readonly string[] | null;\n};\n\nexport type AppPageInterceptOptions<TModule extends AppPageModule = AppPageModule> = {\n interceptionContext?: string | null;\n interceptLayouts?: readonly (TModule | null | undefined)[] | null;\n interceptPage?: TModule | null;\n interceptParams?: AppPageParams | null;\n interceptSlotId?: string | null;\n interceptSlotKey?: string | null;\n interceptSourceMatchedUrl?: string | null;\n};\n\nexport type AppPagePageRequest<TModule extends AppPageModule = AppPageModule> = {\n /** Interception context from current-route navigation (null for direct visits). */\n opts?: AppPageInterceptOptions<TModule> | null;\n /** URL search params from the incoming request (null when unavailable). */\n searchParams?: URLSearchParams | null;\n /** Whether the incoming request is an RSC (client-side navigation) request. */\n isRscRequest: boolean;\n /** The incoming HTTP request (available but unused by this module). */\n request: Request;\n /** Normalized x-vinext-mounted-slots header value. */\n mountedSlotsHeader: string | null;\n /** Semantic RSC payload mode for this page render. */\n renderMode?: AppRscRenderMode;\n};\n\nexport type BuildPageElementsOptions<\n TModule extends AppPageModule = AppPageModule,\n TErrorModule extends AppPageErrorModule = AppPageErrorModule,\n> = {\n route: AppPageBuildRoute<TModule, TErrorModule>;\n params: AppPageParams;\n routePath: string;\n pageRequest: AppPagePageRequest<TModule>;\n /** Root-level global-error.tsx module. Present when the app defines this file. */\n globalErrorModule?: TErrorModule | null;\n /** Root-level not-found.tsx module. Present when the app defines this file. */\n rootNotFoundModule?: TModule | null;\n /** Root-level forbidden.tsx module. Present when the app defines this file. */\n rootForbiddenModule?: TModule | null;\n /** Root-level unauthorized.tsx module. Present when the app defines this file. */\n rootUnauthorizedModule?: TModule | null;\n /** File-based metadata routes (favicon, manifest, sitemap, etc.). */\n metadataRoutes: readonly MetadataFileRoute[];\n /**\n * Configured next.config `basePath`. Threaded through `resolveAppPageHead`\n * so file-based metadata route URLs emitted in <head> are prefixed.\n */\n basePath?: string;\n /** Serialized next.config `htmlLimitedBots` regexp source. */\n htmlLimitedBots?: string;\n};\n\n/**\n * Build the App Router element tree for a matched route.\n *\n * This is the central element-construction path for the App Router RSC\n * handler. It resolves page head metadata (including parallel route metadata),\n * creates the page React element, and wires it into the nested layout +\n * boundary tree via {@link buildAppPageElements}.\n *\n * The function is extracted from the generated RSC entry template so it can\n * be unit-tested independently of the code-generation machinery.\n *\n * Next.js equivalent: the component tree construction in\n * {@link https://github.com/vercel/next.js/blob/canary/packages/next/src/server/app-render/create-component-tree.tsx|create-component-tree.tsx}\n * and the page head resolution in\n * {@link https://github.com/vercel/next.js/blob/canary/packages/next/src/server/app-render/create-metadata.tsx|create-metadata.tsx}.\n */\nexport async function buildPageElements<\n TModule extends AppPageModule = AppPageModule,\n TErrorModule extends AppPageErrorModule = AppPageErrorModule,\n>(options: BuildPageElementsOptions<TModule, TErrorModule>): Promise<AppElements> {\n const {\n route,\n params,\n routePath,\n pageRequest,\n globalErrorModule,\n rootNotFoundModule,\n rootForbiddenModule,\n rootUnauthorizedModule,\n metadataRoutes,\n } = options;\n const {\n opts,\n searchParams,\n isRscRequest,\n mountedSlotsHeader,\n renderMode = APP_RSC_RENDER_MODE_NAVIGATION,\n } = pageRequest;\n\n const pageModule: AppPageModule | null | undefined = route.page;\n const PageComponent = pageModule?.default;\n const hasPageModule = !!pageModule;\n const renderIdentity = createAppPageRenderIdentity({\n displayPathname: routePath,\n interceptionContext: opts?.interceptionContext ?? null,\n interceptSourceMatchedUrl: opts?.interceptSourceMatchedUrl ?? null,\n interceptSlotId: opts?.interceptSlotId ?? null,\n });\n\n if (hasPageModule && !PageComponent) {\n let noExportRootLayout: string | null = null;\n const noExportLayoutIds =\n route.ids?.layouts ??\n route.layouts.map((_, index) =>\n AppElementsWire.encodeLayoutId(\n createAppPageTreePath(route.routeSegments, route.layoutTreePositions?.[index] ?? 0),\n ),\n );\n if (route.layouts?.length > 0) {\n const treePosition = route.layoutTreePositions?.[0] ?? 0;\n noExportRootLayout = createAppPageTreePath(route.routeSegments, treePosition);\n }\n return {\n ...AppElementsWire.createMetadataEntries({\n interception: renderIdentity.interception,\n interceptionContext: renderIdentity.interceptionContext,\n layoutIds: noExportLayoutIds,\n rootLayoutTreePath: noExportRootLayout,\n routeId: renderIdentity.routeId,\n }),\n [renderIdentity.routeId]: createElement(\"div\", null, \"Page has no default export\"),\n };\n }\n\n const {\n hasSearchParams,\n hasDynamicMetadata,\n metadata: resolvedMetadata,\n pageSearchParams,\n viewport: resolvedViewport,\n } = await resolveAppPageHead({\n basePath: options.basePath ?? \"\",\n layoutModules: route.layouts,\n layoutTreePositions: route.layoutTreePositions,\n metadataRoutes,\n pageModule: route.page ?? null,\n parallelRoutes: resolveActiveParallelRouteHeadInputs({\n interceptLayouts: opts?.interceptLayouts ?? null,\n interceptPage: opts?.interceptPage ?? null,\n interceptParams: opts?.interceptParams ?? null,\n interceptSlotKey: opts?.interceptSlotKey ?? null,\n params,\n routeSegments: route.routeSegments ?? [],\n slots: route.slots ?? null,\n }),\n params,\n routePath: route.pattern,\n routeSegments: route.routeSegments ?? null,\n searchParams,\n });\n\n const pageProps: Record<string, unknown> = { params: makeThenableParams(params) };\n if (searchParams) {\n pageProps.searchParams = makeThenableParams(pageSearchParams);\n if (hasSearchParams) {\n markDynamicUsage();\n markRenderRequestApiUsage(\"searchParams\");\n }\n }\n\n const mountedSlotIds = mountedSlotsHeader ? new Set(mountedSlotsHeader.split(\" \")) : null;\n\n const slotOverrides = buildSlotOverrides(route, params, routePath, opts);\n const metadataPlacement =\n hasDynamicMetadata &&\n shouldServeStreamingMetadata(\n pageRequest.request.headers.get(\"user-agent\") ?? \"\",\n options.htmlLimitedBots,\n )\n ? \"body\"\n : \"head\";\n\n return buildAppPageElements({\n element: PageComponent ? createElement(PageComponent, pageProps) : null,\n // Fall back to vinext's built-in default global error module so that\n // uncaught client render errors are caught by the route-level\n // <ErrorBoundary> wrapper in app-page-route-wiring.tsx, mirroring\n // Next.js's behavior when the user has not defined app/global-error.tsx.\n globalErrorModule:\n globalErrorModule ?? (DEFAULT_GLOBAL_ERROR_MODULE as unknown as TErrorModule),\n isRscRequest,\n mountedSlotIds,\n makeThenableParams,\n matchedParams: params,\n metadataPlacement,\n resolvedMetadata,\n resolvedMetadataPathname: routePath,\n resolvedViewport,\n renderIdentity,\n routePath,\n rootNotFoundModule: rootNotFoundModule ?? null,\n rootForbiddenModule: rootForbiddenModule ?? null,\n rootUnauthorizedModule: rootUnauthorizedModule ?? null,\n route,\n slotOverrides,\n renderMode,\n });\n}\n\n/**\n * Build the per-request `slotOverrides` map. Combines:\n * - Interception overrides (existing behavior — swap in the intercepting page\n * and its layouts when the request is intercepted into this slot).\n * - Slot-specific param extraction for inherited slots whose URL pattern\n * has different param names than the route's. The runtime matches the\n * cleaned request path against `slot.slotPatternParts` to produce\n * slot-scoped params, which `app-page-route-wiring` then hands to the\n * slot page instead of the route's matched params.\n *\n * `routePath` is the already-normalized request pathname (basePath stripped,\n * RSC suffix removed). Re-parsing `request.url` here would re-introduce the\n * basePath and silently break the match for any app that configures one.\n */\nfunction buildSlotOverrides<TModule extends AppPageModule, TErrorModule extends AppPageErrorModule>(\n route: AppPageBuildRoute<TModule, TErrorModule>,\n routeParams: AppPageParams,\n routePath: string,\n opts?: AppPageInterceptOptions<TModule> | null,\n): Readonly<Record<string, AppPageSlotOverride<TModule>>> | null {\n const overrides: Record<string, AppPageSlotOverride<TModule>> = {};\n\n if (opts && opts.interceptSlotKey && opts.interceptPage) {\n overrides[opts.interceptSlotKey] = {\n layoutModules: opts.interceptLayouts || null,\n pageModule: opts.interceptPage,\n params: opts.interceptParams || routeParams,\n };\n }\n\n const slots = route.slots;\n if (slots) {\n let urlParts: string[] | null = null;\n const routeParamSet = collectParamNameSet(route.params);\n for (const [slotKey, slot] of Object.entries(slots)) {\n const patternParts = slot.slotPatternParts;\n const paramNames = slot.slotParamNames;\n if (!patternParts || patternParts.length === 0) continue;\n // Skip when every slot param is already a route param — the route's\n // matched params already carry the values the slot page expects.\n // Empty `paramNames` (slot pattern has no dynamic markers) also skips:\n // there's nothing to extract, so the route's matched params suffice.\n if (paramNames && paramNames.every((name) => routeParamSet.has(name))) continue;\n\n if (urlParts === null) {\n urlParts = routePath.split(\"/\").filter(Boolean);\n }\n const matched = matchRoutePattern(urlParts, patternParts);\n if (!matched) continue;\n\n const existing = overrides[slotKey];\n overrides[slotKey] = existing ? { ...existing, params: matched } : { params: matched };\n }\n }\n\n return Object.keys(overrides).length > 0 ? overrides : null;\n}\n\nfunction collectParamNameSet(params: readonly string[] | undefined | null): Set<string> {\n const set = new Set<string>();\n if (params) {\n for (const name of params) set.add(name);\n }\n return set;\n}\n"],"mappings":"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"}
@@ -65,6 +65,7 @@ type BuildAppPageSpecialErrorResponseOptions = {
65
65
  * the http-access-fallback path leaves cookies to the rendered boundary.
66
66
  */
67
67
  getAndClearPendingCookies?: () => string[];
68
+ isEdgeRuntime?: boolean;
68
69
  isRscRequest: boolean;
69
70
  middlewareContext?: {
70
71
  headers: Headers | null;
@@ -2,6 +2,7 @@ import { addBasePathToPathname } from "../utils/base-path.js";
2
2
  import { VINEXT_RSC_REDIRECT_HEADER } from "./headers.js";
3
3
  import { VINEXT_RSC_CONTENT_TYPE, applyRscCompatibilityIdHeader, createRscRedirectLocation } from "./app-rsc-cache-busting.js";
4
4
  import { mergeMiddlewareResponseHeaders } from "./middleware-response-headers.js";
5
+ import { applyEdgeRuntimeHeader } from "./app-page-response.js";
5
6
  import { parseNextHttpErrorDigest, parseNextRedirectDigest } from "./next-error-digest.js";
6
7
  //#region src/server/app-page-execution.ts
7
8
  /**
@@ -134,6 +135,7 @@ async function buildAppPageSpecialErrorResponse(options) {
134
135
  "Content-Type": VINEXT_RSC_CONTENT_TYPE,
135
136
  [VINEXT_RSC_REDIRECT_HEADER]: digestUrl
136
137
  });
138
+ applyEdgeRuntimeHeader(headers, options.isEdgeRuntime);
137
139
  applyRscCompatibilityIdHeader(headers);
138
140
  mergeMiddlewareResponseHeaders(headers, options.middlewareContext?.headers ?? null);
139
141
  const pendingCookies = options.getAndClearPendingCookies?.() ?? [];
@@ -1 +1 @@
1
- {"version":3,"file":"app-page-execution.js","names":[],"sources":["../../src/server/app-page-execution.ts"],"sourcesContent":["import type { LayoutFlags } from \"./app-elements.js\";\nimport type { ClassificationReason } from \"../build/layout-classification-types.js\";\nimport {\n applyRscCompatibilityIdHeader,\n createRscRedirectLocation,\n VINEXT_RSC_CONTENT_TYPE,\n} from \"./app-rsc-cache-busting.js\";\nimport { VINEXT_RSC_REDIRECT_HEADER } from \"./headers.js\";\nimport { mergeMiddlewareResponseHeaders } from \"./middleware-response-headers.js\";\nimport { parseNextHttpErrorDigest, parseNextRedirectDigest } from \"./next-error-digest.js\";\nimport { addBasePathToPathname } from \"../utils/base-path.js\";\n\n/**\n * Builds the canonical `NEXT_REDIRECT;<type>;<url>;<status>;` digest that\n * Next.js encodes on `redirect()` / `permanentRedirect()` throws. Used when\n * we synthesize a flight payload for an RSC navigation: the digest must\n * round-trip through the client's `RedirectErrorBoundary` so the same\n * `getURLFromRedirectError` / `getRedirectTypeFromError` helpers decode it.\n *\n * The URL is included verbatim, not encoded — Next.js's `getRedirectError`\n * sets `digest = ${CODE};${type};${url};${status};` with the raw URL, and the\n * client decodes via `error.digest.split(';').slice(2, -2).join(';')`. We\n * default `type=replace` because `redirect()` is replace-style outside of\n * server actions, matching Next.js's `getRedirectError` default.\n *\n * Reference:\n * `.nextjs-ref/packages/next/src/client/components/redirect.ts:20-23`\n * `.nextjs-ref/packages/next/src/client/components/redirect-error.ts`\n */\nfunction formatNextRedirectDigest(options: { url: string; statusCode: number }): string {\n return `NEXT_REDIRECT;replace;${options.url};${options.statusCode};`;\n}\n\nexport type { LayoutFlags };\nexport type { ClassificationReason };\n\n/**\n * Marker we tag onto a thrown redirect/notFound error when it originates from\n * `generateMetadata()` (vs. a server component itself). Metadata resolution is\n * suspended/streamed in Next.js, so a redirect from metadata never becomes an\n * HTTP-level 307 — it rides inside the flight payload with a 200 status,\n * regardless of whether the request is RSC or a full document SSR. Page-level\n * redirect()s, by contrast, still produce a 307 for SSR document requests.\n *\n * See Next.js test:\n * test/e2e/app-dir/metadata-navigation/metadata-navigation.test.ts\n * (\"should support redirect in generateMetadata\")\n */\nconst APP_PAGE_METADATA_ERROR_MARKER = Symbol.for(\"vinext.appPage.metadataError\");\n\nexport function tagAppPageMetadataError<T>(error: T): T {\n if (error && typeof error === \"object\") {\n try {\n Object.defineProperty(error, APP_PAGE_METADATA_ERROR_MARKER, {\n value: true,\n enumerable: false,\n configurable: true,\n writable: false,\n });\n } catch {\n // The error object may be frozen (rare). The marker is best-effort —\n // callers fall back to the page-level 307 path when missing, which\n // matches the historical behavior.\n }\n }\n return error;\n}\n\nexport type AppPageSpecialError =\n | { kind: \"redirect\"; location: string; statusCode: number; fromMetadata?: boolean }\n | { kind: \"http-access-fallback\"; statusCode: number; fromMetadata?: boolean };\n\nexport type AppPageFontPreload = {\n href: string;\n type: string;\n};\n\ntype AppPageRscStreamCapture = {\n /** Stream for createFromReadableStream (SSR). Always set. */\n ssrStream: ReadableStream<Uint8Array>;\n /** When capturing, the combined embed+capture stream. handleSsr consumes this. */\n sideStream?: ReadableStream<Uint8Array>;\n};\n\n/**\n * Builds an RSC flight payload that encodes a `redirect()` as a React error\n * with the canonical `NEXT_REDIRECT;<type>;<url>;<status>;` digest. Mirrors\n * Next.js's behavior in `app-render.tsx generateDynamicFlightRenderResult`\n * where a redirect thrown during RSC rendering propagates through\n * `renderToFlightStream`'s `onError` handler and is serialized into the\n * stream — the HTTP response stays 200 because the redirect rides in the\n * flight body, not the status line.\n *\n * Returns a stream that the caller wraps in a 200 response with the standard\n * `text/x-component` content type. The client's `RedirectErrorBoundary`\n * decodes the digest and performs the navigation.\n */\ntype BuildRscRedirectFlightStream = (options: { digest: string }) => ReadableStream<Uint8Array>;\n\ntype BuildAppPageSpecialErrorResponseOptions = {\n /**\n * Optional configured basePath (e.g. \"/blog\"). When set, redirect Locations\n * pointing at app-internal paths get prefixed so callers see e.g.\n * `Location: /blog/about` for `redirect(\"/about\")`. Mirrors Next.js's\n * `addPathPrefix(getURLFromRedirectError(err), basePath)` in app-render.tsx.\n * External URLs (those that resolve to a different origin than the request)\n * are left untouched.\n */\n basePath?: string;\n /**\n * Builds the RSC flight payload used when a redirect must be encoded inside\n * the response body instead of the status line — required for RSC navigations\n * and for `generateMetadata()` redirects (always 200, never 307). When\n * omitted, redirect responses fall back to the 307 + Location path; callers\n * that handle RSC requests must supply this.\n */\n buildRscRedirectFlightStream?: BuildRscRedirectFlightStream;\n clearRequestContext: () => void;\n /**\n * Drains and returns Set-Cookie header values that were accumulated during\n * this render via cookies().set() / cookies().delete(). Appended to redirect\n * responses so an auth flow that does `cookies().set(\"session\", \"...\");\n * redirect(\"/\")` preserves the cookie on the 307. Mirrors Next.js's\n * `appendMutableCookies(headers, requestStore.mutableCookies)` in\n * app-render.tsx. Only applied to redirect responses to match Next.js;\n * the http-access-fallback path leaves cookies to the rendered boundary.\n */\n getAndClearPendingCookies?: () => string[];\n isRscRequest: boolean;\n middlewareContext?: { headers: Headers | null };\n renderFallbackPage?: (statusCode: number) => Promise<Response | null>;\n request: Request;\n specialError: AppPageSpecialError;\n};\n\ntype ProbeAppPageLayoutsResult = {\n response: Response | null;\n layoutFlags: LayoutFlags;\n};\n\nexport type LayoutClassificationOptions = {\n /** Build-time classifications from segment config or module graph, keyed by layout index. */\n buildTimeClassifications?: ReadonlyMap<number, \"static\" | \"dynamic\"> | null;\n /**\n * Per-layout classification reasons keyed by layout index. Requires\n * `VINEXT_DEBUG_CLASSIFICATION` at BOTH lifecycle points: at build time so\n * the plugin patches the `__VINEXT_CLASS_REASONS` dispatch stub, and at\n * runtime so the route object actually calls it. Setting the flag only at\n * runtime leaves the stub returning `null`, and every build-time classified\n * layout will fall through to `{ layer: \"no-classifier\" }` in the debug\n * channel. The hot path never reads this and the wire payload is unchanged.\n */\n buildTimeReasons?: ReadonlyMap<number, ClassificationReason> | null;\n /**\n * Emits one log line per layout with the classification reason, keyed by\n * layout ID. Set by the generator when `VINEXT_DEBUG_CLASSIFICATION` is\n * active. When undefined, the probe loop skips debug emission entirely.\n */\n debugClassification?: (layoutId: string, reason: ClassificationReason) => void;\n /** Maps layout index to its layout ID (e.g. \"layout:/blog\"). */\n getLayoutId: (layoutIndex: number) => string;\n /** Runs a function with isolated dynamic usage tracking per layout. */\n runWithIsolatedDynamicScope: <T>(fn: () => T) => Promise<{ result: T; dynamicDetected: boolean }>;\n};\n\ntype ProbeAppPageLayoutsOptions = {\n layoutCount: number;\n onLayoutError: (error: unknown, layoutIndex: number) => Promise<Response | null>;\n probeLayoutAt: (layoutIndex: number) => unknown;\n runWithSuppressedHookWarning<T>(probe: () => Promise<T>): Promise<T>;\n /** When provided, enables per-layout static/dynamic classification. */\n classification?: LayoutClassificationOptions | null;\n};\n\ntype ProbeAppPageComponentOptions = {\n awaitAsyncResult: boolean;\n onError: (error: unknown) => Promise<Response | null>;\n probePage: () => unknown;\n runWithSuppressedHookWarning<T>(probe: () => Promise<T>): Promise<T>;\n};\n\nfunction isPromiseLike(value: unknown): value is PromiseLike<unknown> {\n return Boolean(\n value &&\n (typeof value === \"object\" || typeof value === \"function\") &&\n \"then\" in value &&\n typeof value.then === \"function\",\n );\n}\n\nfunction getAppPageStatusText(statusCode: number): string {\n return statusCode === 403 ? \"Forbidden\" : statusCode === 401 ? \"Unauthorized\" : \"Not Found\";\n}\n\nfunction mergeAppPageSpecialErrorHeaders(\n response: Response,\n middlewareContext: { headers: Headers | null } | undefined,\n): Response {\n const headers = new Headers(response.headers);\n mergeMiddlewareResponseHeaders(headers, middlewareContext?.headers ?? null);\n\n return new Response(response.body, {\n headers,\n status: response.status,\n statusText: response.statusText,\n });\n}\n\nexport function resolveAppPageSpecialError(error: unknown): AppPageSpecialError | null {\n if (!(error && typeof error === \"object\" && \"digest\" in error)) {\n return null;\n }\n\n const digest = String(error.digest);\n const fromMetadata = (error as Record<symbol, unknown>)[APP_PAGE_METADATA_ERROR_MARKER] === true;\n\n const redirect = parseNextRedirectDigest(digest);\n if (redirect) {\n return {\n kind: \"redirect\",\n location: redirect.url,\n statusCode: redirect.status,\n ...(fromMetadata ? { fromMetadata: true } : {}),\n };\n }\n\n const httpError = parseNextHttpErrorDigest(digest);\n if (httpError) {\n return {\n kind: \"http-access-fallback\",\n statusCode: httpError.status,\n ...(fromMetadata ? { fromMetadata: true } : {}),\n };\n }\n\n return null;\n}\n\n/**\n * Resolves a redirect() target against the request URL and prepends the\n * configured basePath when the target is an app-internal absolute path.\n *\n * Mirrors Next.js's `addPathPrefix(getURLFromRedirectError(err), basePath)`\n * in `app-render.tsx`: a `redirect(\"/about\")` call from a page mounted at\n * `/blog` (basePath) produces `Location: /blog/about`.\n *\n * Skips prefixing when:\n * - basePath is unset / empty\n * - the target is a full URL pointing at a different origin (external redirect)\n * - the target already starts with the basePath (caller did the work themselves)\n */\nfunction applyAppPageRedirectBasePath(\n location: string,\n requestUrl: string,\n basePath: string | undefined,\n): string {\n const resolved = new URL(location, requestUrl);\n const requestOrigin = new URL(requestUrl).origin;\n if (!basePath || resolved.origin !== requestOrigin) {\n return resolved.toString();\n }\n resolved.pathname = addBasePathToPathname(resolved.pathname, basePath);\n return resolved.toString();\n}\n\n/**\n * Returns a path-relative form (`/foo?bar`) of an absolute URL when it shares\n * the request's origin; otherwise returns the URL verbatim. Used so the digest\n * we embed in the flight payload matches Next.js's convention — the digest\n * stores the path the developer passed to `redirect(\"/about\")`, not a\n * fully-qualified URL like `https://example.com/about`.\n */\nfunction sameOriginPathOrAbsolute(location: string, requestUrl: string): string {\n try {\n const resolved = new URL(location, requestUrl);\n const requestOrigin = new URL(requestUrl).origin;\n if (resolved.origin !== requestOrigin) {\n return resolved.toString();\n }\n return `${resolved.pathname}${resolved.search}${resolved.hash}`;\n } catch {\n return location;\n }\n}\n\nexport async function buildAppPageSpecialErrorResponse(\n options: BuildAppPageSpecialErrorResponseOptions,\n): Promise<Response> {\n if (options.specialError.kind === \"redirect\") {\n options.clearRequestContext();\n // Apply configured basePath first so app-internal targets land at\n // /<basePath>/<target> before the RSC cache-busting transform sees them.\n const prefixedLocation = applyAppPageRedirectBasePath(\n options.specialError.location,\n options.request.url,\n options.basePath,\n );\n\n // Two cases need a 200 + flight-payload encoding instead of an HTTP 307:\n // 1. RSC navigation requests (`Rsc: 1` header) — the client router\n // decodes the redirect digest from the flight stream. A raw 307\n // bypasses that path and breaks cache-busting validation.\n // 2. `generateMetadata()` redirects — metadata is suspended in Next.js,\n // so the redirect rides inside the streamed flight payload even for\n // full document SSR. The status line stays 200.\n // Mirrors Next.js's `generateDynamicFlightRenderResult` path in\n // `app-render.tsx`, where the redirect error propagates through\n // `renderToFlightStream` and is serialized with its digest.\n const shouldEmbedRedirectInFlight =\n Boolean(options.buildRscRedirectFlightStream) &&\n (options.isRscRequest || options.specialError.fromMetadata === true);\n\n if (shouldEmbedRedirectInFlight && options.buildRscRedirectFlightStream) {\n // Reduce the resolved (absolute) URL back to a path-only form for\n // same-origin redirects. Next.js's digest stores the raw URL passed to\n // `redirect()` (typically a path like \"/about\"), and the client router's\n // `router.push(url)` happily accepts paths. Cross-origin targets keep\n // their absolute form, matching Next.js's external-redirect handling.\n const digestUrl = sameOriginPathOrAbsolute(prefixedLocation, options.request.url);\n const digest = formatNextRedirectDigest({\n url: digestUrl,\n statusCode: options.specialError.statusCode,\n });\n const stream = options.buildRscRedirectFlightStream({ digest });\n\n const headers = new Headers({\n \"Content-Type\": VINEXT_RSC_CONTENT_TYPE,\n // Side-channel signal so vinext's client loop can detect the redirect\n // without having to decode the flight body first. See\n // `VINEXT_RSC_REDIRECT_HEADER` in server/headers.ts for the rationale.\n [VINEXT_RSC_REDIRECT_HEADER]: digestUrl,\n });\n // Mirror the regular RSC response by stamping the build-time compatibility\n // ID. Without it, the client treats the response as cross-build and hard-\n // navigates instead of following the redirect through the soft-nav loop.\n applyRscCompatibilityIdHeader(headers);\n // Preserve middleware response headers (Set-Cookie, custom headers, etc.)\n // exactly like the 307 path does — the client will still see them.\n mergeMiddlewareResponseHeaders(headers, options.middlewareContext?.headers ?? null);\n const pendingCookies = options.getAndClearPendingCookies?.() ?? [];\n for (const cookie of pendingCookies) {\n headers.append(\"Set-Cookie\", cookie);\n }\n\n return new Response(stream, {\n headers,\n status: 200,\n });\n }\n\n const location = options.isRscRequest\n ? await createRscRedirectLocation(prefixedLocation, options.request)\n : prefixedLocation;\n const headers = new Headers({\n Location: location,\n });\n // Middleware may contribute response headers here, but redirect() owns the\n // status. Do not apply middlewareContext.status on special-error responses.\n mergeMiddlewareResponseHeaders(headers, options.middlewareContext?.headers ?? null);\n // Preserve cookies set via cookies().set() / cookies().delete() during the\n // page render — auth flows commonly set a session cookie and immediately\n // redirect, and those Set-Cookie values must ride on the 307.\n const pendingCookies = options.getAndClearPendingCookies?.() ?? [];\n for (const cookie of pendingCookies) {\n headers.append(\"Set-Cookie\", cookie);\n }\n\n return new Response(null, {\n headers,\n status: options.specialError.statusCode,\n });\n }\n\n if (options.renderFallbackPage) {\n const fallbackResponse = await options.renderFallbackPage(options.specialError.statusCode);\n if (fallbackResponse) {\n return mergeAppPageSpecialErrorHeaders(fallbackResponse, options.middlewareContext);\n }\n }\n\n options.clearRequestContext();\n return mergeAppPageSpecialErrorHeaders(\n new Response(getAppPageStatusText(options.specialError.statusCode), {\n status: options.specialError.statusCode,\n }),\n options.middlewareContext,\n );\n}\n\n/** See `LayoutFlags` type docblock in app-elements.ts for lifecycle. */\nexport async function probeAppPageLayouts(\n options: ProbeAppPageLayoutsOptions,\n): Promise<ProbeAppPageLayoutsResult> {\n const layoutFlags: Record<string, \"s\" | \"d\"> = {};\n const cls = options.classification ?? null;\n\n const response = await options.runWithSuppressedHookWarning(async () => {\n for (let layoutIndex = options.layoutCount - 1; layoutIndex >= 0; layoutIndex--) {\n const buildTimeResult = cls?.buildTimeClassifications?.get(layoutIndex);\n\n if (cls && buildTimeResult) {\n // Build-time classified (Layer 1 or Layer 2): skip dynamic isolation,\n // but still probe for special errors (redirects, not-found).\n layoutFlags[cls.getLayoutId(layoutIndex)] = buildTimeResult === \"static\" ? \"s\" : \"d\";\n if (cls.debugClassification) {\n // `no-classifier` is the documented fallback for a layout that was\n // build-time classified but whose reason payload is absent — either\n // because the build was run without `VINEXT_DEBUG_CLASSIFICATION` or\n // because no Layer 1/2 classifier attached a reason. This is the sole\n // producer of the variant; see `layout-classification-types.ts`.\n cls.debugClassification(\n cls.getLayoutId(layoutIndex),\n cls.buildTimeReasons?.get(layoutIndex) ?? { layer: \"no-classifier\" },\n );\n }\n const errorResponse = await probeLayoutForErrors(options, layoutIndex);\n if (errorResponse) return errorResponse;\n continue;\n }\n\n if (cls) {\n // Layer 3: probe with isolated dynamic scope to detect per-layout\n // dynamic API usage (headers(), cookies(), connection(), etc.)\n try {\n const { dynamicDetected } = await cls.runWithIsolatedDynamicScope(() =>\n options.probeLayoutAt(layoutIndex),\n );\n layoutFlags[cls.getLayoutId(layoutIndex)] = dynamicDetected ? \"d\" : \"s\";\n if (cls.debugClassification) {\n cls.debugClassification(cls.getLayoutId(layoutIndex), {\n layer: \"runtime-probe\",\n outcome: dynamicDetected ? \"dynamic\" : \"static\",\n });\n }\n } catch (error) {\n // Probe failed — conservatively treat as dynamic.\n layoutFlags[cls.getLayoutId(layoutIndex)] = \"d\";\n if (cls.debugClassification) {\n cls.debugClassification(cls.getLayoutId(layoutIndex), {\n layer: \"runtime-probe\",\n outcome: \"dynamic\",\n error: error instanceof Error ? error.message : String(error),\n });\n }\n const errorResponse = await options.onLayoutError(error, layoutIndex);\n if (errorResponse) return errorResponse;\n }\n continue;\n }\n\n // No classification options — original behavior\n const errorResponse = await probeLayoutForErrors(options, layoutIndex);\n if (errorResponse) return errorResponse;\n }\n\n return null;\n });\n\n return { response, layoutFlags };\n}\n\nasync function probeLayoutForErrors(\n options: ProbeAppPageLayoutsOptions,\n layoutIndex: number,\n): Promise<Response | null> {\n try {\n const layoutResult = options.probeLayoutAt(layoutIndex);\n if (isPromiseLike(layoutResult)) {\n await layoutResult;\n }\n } catch (error) {\n return options.onLayoutError(error, layoutIndex);\n }\n return null;\n}\n\nexport async function probeAppPageComponent(\n options: ProbeAppPageComponentOptions,\n): Promise<Response | null> {\n return options.runWithSuppressedHookWarning(async () => {\n try {\n const pageResult = options.probePage();\n if (isPromiseLike(pageResult)) {\n if (options.awaitAsyncResult) {\n await pageResult;\n } else {\n void Promise.resolve(pageResult).catch(() => {});\n }\n }\n } catch (error) {\n return options.onError(error);\n }\n\n return null;\n });\n}\n\nexport async function readAppPageBinaryStream(\n stream: ReadableStream<Uint8Array>,\n): Promise<ArrayBuffer> {\n const reader = stream.getReader();\n const chunks: Uint8Array[] = [];\n let totalLength = 0;\n\n for (;;) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n chunks.push(value);\n totalLength += value.byteLength;\n }\n\n const buffer = new Uint8Array(totalLength);\n let offset = 0;\n for (const chunk of chunks) {\n buffer.set(chunk, offset);\n offset += chunk.byteLength;\n }\n\n return buffer.buffer;\n}\n\nexport function teeAppPageRscStreamForCapture(\n stream: ReadableStream<Uint8Array>,\n shouldCapture: boolean,\n): AppPageRscStreamCapture {\n if (!shouldCapture) {\n return {\n ssrStream: stream,\n };\n }\n\n const [ssrStream, sideStream] = stream.tee();\n return {\n ssrStream,\n sideStream,\n };\n}\n\nexport function buildAppPageFontLinkHeader(\n preloads: readonly AppPageFontPreload[] | null | undefined,\n): string {\n if (!preloads || preloads.length === 0) {\n return \"\";\n }\n\n return preloads\n .map((preload) => `<${preload.href}>; rel=preload; as=font; type=${preload.type}; crossorigin`)\n .join(\", \");\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"app-page-execution.js","names":[],"sources":["../../src/server/app-page-execution.ts"],"sourcesContent":["import type { LayoutFlags } from \"./app-elements.js\";\nimport type { ClassificationReason } from \"../build/layout-classification-types.js\";\nimport {\n applyRscCompatibilityIdHeader,\n createRscRedirectLocation,\n VINEXT_RSC_CONTENT_TYPE,\n} from \"./app-rsc-cache-busting.js\";\nimport { VINEXT_RSC_REDIRECT_HEADER } from \"./headers.js\";\nimport { applyEdgeRuntimeHeader } from \"./app-page-response.js\";\nimport { mergeMiddlewareResponseHeaders } from \"./middleware-response-headers.js\";\nimport { parseNextHttpErrorDigest, parseNextRedirectDigest } from \"./next-error-digest.js\";\nimport { addBasePathToPathname } from \"../utils/base-path.js\";\n\n/**\n * Builds the canonical `NEXT_REDIRECT;<type>;<url>;<status>;` digest that\n * Next.js encodes on `redirect()` / `permanentRedirect()` throws. Used when\n * we synthesize a flight payload for an RSC navigation: the digest must\n * round-trip through the client's `RedirectErrorBoundary` so the same\n * `getURLFromRedirectError` / `getRedirectTypeFromError` helpers decode it.\n *\n * The URL is included verbatim, not encoded — Next.js's `getRedirectError`\n * sets `digest = ${CODE};${type};${url};${status};` with the raw URL, and the\n * client decodes via `error.digest.split(';').slice(2, -2).join(';')`. We\n * default `type=replace` because `redirect()` is replace-style outside of\n * server actions, matching Next.js's `getRedirectError` default.\n *\n * Reference:\n * `.nextjs-ref/packages/next/src/client/components/redirect.ts:20-23`\n * `.nextjs-ref/packages/next/src/client/components/redirect-error.ts`\n */\nfunction formatNextRedirectDigest(options: { url: string; statusCode: number }): string {\n return `NEXT_REDIRECT;replace;${options.url};${options.statusCode};`;\n}\n\nexport type { LayoutFlags };\nexport type { ClassificationReason };\n\n/**\n * Marker we tag onto a thrown redirect/notFound error when it originates from\n * `generateMetadata()` (vs. a server component itself). Metadata resolution is\n * suspended/streamed in Next.js, so a redirect from metadata never becomes an\n * HTTP-level 307 — it rides inside the flight payload with a 200 status,\n * regardless of whether the request is RSC or a full document SSR. Page-level\n * redirect()s, by contrast, still produce a 307 for SSR document requests.\n *\n * See Next.js test:\n * test/e2e/app-dir/metadata-navigation/metadata-navigation.test.ts\n * (\"should support redirect in generateMetadata\")\n */\nconst APP_PAGE_METADATA_ERROR_MARKER = Symbol.for(\"vinext.appPage.metadataError\");\n\nexport function tagAppPageMetadataError<T>(error: T): T {\n if (error && typeof error === \"object\") {\n try {\n Object.defineProperty(error, APP_PAGE_METADATA_ERROR_MARKER, {\n value: true,\n enumerable: false,\n configurable: true,\n writable: false,\n });\n } catch {\n // The error object may be frozen (rare). The marker is best-effort —\n // callers fall back to the page-level 307 path when missing, which\n // matches the historical behavior.\n }\n }\n return error;\n}\n\nexport type AppPageSpecialError =\n | { kind: \"redirect\"; location: string; statusCode: number; fromMetadata?: boolean }\n | { kind: \"http-access-fallback\"; statusCode: number; fromMetadata?: boolean };\n\nexport type AppPageFontPreload = {\n href: string;\n type: string;\n};\n\ntype AppPageRscStreamCapture = {\n /** Stream for createFromReadableStream (SSR). Always set. */\n ssrStream: ReadableStream<Uint8Array>;\n /** When capturing, the combined embed+capture stream. handleSsr consumes this. */\n sideStream?: ReadableStream<Uint8Array>;\n};\n\n/**\n * Builds an RSC flight payload that encodes a `redirect()` as a React error\n * with the canonical `NEXT_REDIRECT;<type>;<url>;<status>;` digest. Mirrors\n * Next.js's behavior in `app-render.tsx generateDynamicFlightRenderResult`\n * where a redirect thrown during RSC rendering propagates through\n * `renderToFlightStream`'s `onError` handler and is serialized into the\n * stream — the HTTP response stays 200 because the redirect rides in the\n * flight body, not the status line.\n *\n * Returns a stream that the caller wraps in a 200 response with the standard\n * `text/x-component` content type. The client's `RedirectErrorBoundary`\n * decodes the digest and performs the navigation.\n */\ntype BuildRscRedirectFlightStream = (options: { digest: string }) => ReadableStream<Uint8Array>;\n\ntype BuildAppPageSpecialErrorResponseOptions = {\n /**\n * Optional configured basePath (e.g. \"/blog\"). When set, redirect Locations\n * pointing at app-internal paths get prefixed so callers see e.g.\n * `Location: /blog/about` for `redirect(\"/about\")`. Mirrors Next.js's\n * `addPathPrefix(getURLFromRedirectError(err), basePath)` in app-render.tsx.\n * External URLs (those that resolve to a different origin than the request)\n * are left untouched.\n */\n basePath?: string;\n /**\n * Builds the RSC flight payload used when a redirect must be encoded inside\n * the response body instead of the status line — required for RSC navigations\n * and for `generateMetadata()` redirects (always 200, never 307). When\n * omitted, redirect responses fall back to the 307 + Location path; callers\n * that handle RSC requests must supply this.\n */\n buildRscRedirectFlightStream?: BuildRscRedirectFlightStream;\n clearRequestContext: () => void;\n /**\n * Drains and returns Set-Cookie header values that were accumulated during\n * this render via cookies().set() / cookies().delete(). Appended to redirect\n * responses so an auth flow that does `cookies().set(\"session\", \"...\");\n * redirect(\"/\")` preserves the cookie on the 307. Mirrors Next.js's\n * `appendMutableCookies(headers, requestStore.mutableCookies)` in\n * app-render.tsx. Only applied to redirect responses to match Next.js;\n * the http-access-fallback path leaves cookies to the rendered boundary.\n */\n getAndClearPendingCookies?: () => string[];\n isEdgeRuntime?: boolean;\n isRscRequest: boolean;\n middlewareContext?: { headers: Headers | null };\n renderFallbackPage?: (statusCode: number) => Promise<Response | null>;\n request: Request;\n specialError: AppPageSpecialError;\n};\n\ntype ProbeAppPageLayoutsResult = {\n response: Response | null;\n layoutFlags: LayoutFlags;\n};\n\nexport type LayoutClassificationOptions = {\n /** Build-time classifications from segment config or module graph, keyed by layout index. */\n buildTimeClassifications?: ReadonlyMap<number, \"static\" | \"dynamic\"> | null;\n /**\n * Per-layout classification reasons keyed by layout index. Requires\n * `VINEXT_DEBUG_CLASSIFICATION` at BOTH lifecycle points: at build time so\n * the plugin patches the `__VINEXT_CLASS_REASONS` dispatch stub, and at\n * runtime so the route object actually calls it. Setting the flag only at\n * runtime leaves the stub returning `null`, and every build-time classified\n * layout will fall through to `{ layer: \"no-classifier\" }` in the debug\n * channel. The hot path never reads this and the wire payload is unchanged.\n */\n buildTimeReasons?: ReadonlyMap<number, ClassificationReason> | null;\n /**\n * Emits one log line per layout with the classification reason, keyed by\n * layout ID. Set by the generator when `VINEXT_DEBUG_CLASSIFICATION` is\n * active. When undefined, the probe loop skips debug emission entirely.\n */\n debugClassification?: (layoutId: string, reason: ClassificationReason) => void;\n /** Maps layout index to its layout ID (e.g. \"layout:/blog\"). */\n getLayoutId: (layoutIndex: number) => string;\n /** Runs a function with isolated dynamic usage tracking per layout. */\n runWithIsolatedDynamicScope: <T>(fn: () => T) => Promise<{ result: T; dynamicDetected: boolean }>;\n};\n\ntype ProbeAppPageLayoutsOptions = {\n layoutCount: number;\n onLayoutError: (error: unknown, layoutIndex: number) => Promise<Response | null>;\n probeLayoutAt: (layoutIndex: number) => unknown;\n runWithSuppressedHookWarning<T>(probe: () => Promise<T>): Promise<T>;\n /** When provided, enables per-layout static/dynamic classification. */\n classification?: LayoutClassificationOptions | null;\n};\n\ntype ProbeAppPageComponentOptions = {\n awaitAsyncResult: boolean;\n onError: (error: unknown) => Promise<Response | null>;\n probePage: () => unknown;\n runWithSuppressedHookWarning<T>(probe: () => Promise<T>): Promise<T>;\n};\n\nfunction isPromiseLike(value: unknown): value is PromiseLike<unknown> {\n return Boolean(\n value &&\n (typeof value === \"object\" || typeof value === \"function\") &&\n \"then\" in value &&\n typeof value.then === \"function\",\n );\n}\n\nfunction getAppPageStatusText(statusCode: number): string {\n return statusCode === 403 ? \"Forbidden\" : statusCode === 401 ? \"Unauthorized\" : \"Not Found\";\n}\n\nfunction mergeAppPageSpecialErrorHeaders(\n response: Response,\n middlewareContext: { headers: Headers | null } | undefined,\n): Response {\n const headers = new Headers(response.headers);\n mergeMiddlewareResponseHeaders(headers, middlewareContext?.headers ?? null);\n\n return new Response(response.body, {\n headers,\n status: response.status,\n statusText: response.statusText,\n });\n}\n\nexport function resolveAppPageSpecialError(error: unknown): AppPageSpecialError | null {\n if (!(error && typeof error === \"object\" && \"digest\" in error)) {\n return null;\n }\n\n const digest = String(error.digest);\n const fromMetadata = (error as Record<symbol, unknown>)[APP_PAGE_METADATA_ERROR_MARKER] === true;\n\n const redirect = parseNextRedirectDigest(digest);\n if (redirect) {\n return {\n kind: \"redirect\",\n location: redirect.url,\n statusCode: redirect.status,\n ...(fromMetadata ? { fromMetadata: true } : {}),\n };\n }\n\n const httpError = parseNextHttpErrorDigest(digest);\n if (httpError) {\n return {\n kind: \"http-access-fallback\",\n statusCode: httpError.status,\n ...(fromMetadata ? { fromMetadata: true } : {}),\n };\n }\n\n return null;\n}\n\n/**\n * Resolves a redirect() target against the request URL and prepends the\n * configured basePath when the target is an app-internal absolute path.\n *\n * Mirrors Next.js's `addPathPrefix(getURLFromRedirectError(err), basePath)`\n * in `app-render.tsx`: a `redirect(\"/about\")` call from a page mounted at\n * `/blog` (basePath) produces `Location: /blog/about`.\n *\n * Skips prefixing when:\n * - basePath is unset / empty\n * - the target is a full URL pointing at a different origin (external redirect)\n * - the target already starts with the basePath (caller did the work themselves)\n */\nfunction applyAppPageRedirectBasePath(\n location: string,\n requestUrl: string,\n basePath: string | undefined,\n): string {\n const resolved = new URL(location, requestUrl);\n const requestOrigin = new URL(requestUrl).origin;\n if (!basePath || resolved.origin !== requestOrigin) {\n return resolved.toString();\n }\n resolved.pathname = addBasePathToPathname(resolved.pathname, basePath);\n return resolved.toString();\n}\n\n/**\n * Returns a path-relative form (`/foo?bar`) of an absolute URL when it shares\n * the request's origin; otherwise returns the URL verbatim. Used so the digest\n * we embed in the flight payload matches Next.js's convention — the digest\n * stores the path the developer passed to `redirect(\"/about\")`, not a\n * fully-qualified URL like `https://example.com/about`.\n */\nfunction sameOriginPathOrAbsolute(location: string, requestUrl: string): string {\n try {\n const resolved = new URL(location, requestUrl);\n const requestOrigin = new URL(requestUrl).origin;\n if (resolved.origin !== requestOrigin) {\n return resolved.toString();\n }\n return `${resolved.pathname}${resolved.search}${resolved.hash}`;\n } catch {\n return location;\n }\n}\n\nexport async function buildAppPageSpecialErrorResponse(\n options: BuildAppPageSpecialErrorResponseOptions,\n): Promise<Response> {\n if (options.specialError.kind === \"redirect\") {\n options.clearRequestContext();\n // Apply configured basePath first so app-internal targets land at\n // /<basePath>/<target> before the RSC cache-busting transform sees them.\n const prefixedLocation = applyAppPageRedirectBasePath(\n options.specialError.location,\n options.request.url,\n options.basePath,\n );\n\n // Two cases need a 200 + flight-payload encoding instead of an HTTP 307:\n // 1. RSC navigation requests (`Rsc: 1` header) — the client router\n // decodes the redirect digest from the flight stream. A raw 307\n // bypasses that path and breaks cache-busting validation.\n // 2. `generateMetadata()` redirects — metadata is suspended in Next.js,\n // so the redirect rides inside the streamed flight payload even for\n // full document SSR. The status line stays 200.\n // Mirrors Next.js's `generateDynamicFlightRenderResult` path in\n // `app-render.tsx`, where the redirect error propagates through\n // `renderToFlightStream` and is serialized with its digest.\n const shouldEmbedRedirectInFlight =\n Boolean(options.buildRscRedirectFlightStream) &&\n (options.isRscRequest || options.specialError.fromMetadata === true);\n\n if (shouldEmbedRedirectInFlight && options.buildRscRedirectFlightStream) {\n // Reduce the resolved (absolute) URL back to a path-only form for\n // same-origin redirects. Next.js's digest stores the raw URL passed to\n // `redirect()` (typically a path like \"/about\"), and the client router's\n // `router.push(url)` happily accepts paths. Cross-origin targets keep\n // their absolute form, matching Next.js's external-redirect handling.\n const digestUrl = sameOriginPathOrAbsolute(prefixedLocation, options.request.url);\n const digest = formatNextRedirectDigest({\n url: digestUrl,\n statusCode: options.specialError.statusCode,\n });\n const stream = options.buildRscRedirectFlightStream({ digest });\n\n const headers = new Headers({\n \"Content-Type\": VINEXT_RSC_CONTENT_TYPE,\n // Side-channel signal so vinext's client loop can detect the redirect\n // without having to decode the flight body first. See\n // `VINEXT_RSC_REDIRECT_HEADER` in server/headers.ts for the rationale.\n [VINEXT_RSC_REDIRECT_HEADER]: digestUrl,\n });\n applyEdgeRuntimeHeader(headers, options.isEdgeRuntime);\n // Mirror the regular RSC response by stamping the build-time compatibility\n // ID. Without it, the client treats the response as cross-build and hard-\n // navigates instead of following the redirect through the soft-nav loop.\n applyRscCompatibilityIdHeader(headers);\n // Preserve middleware response headers (Set-Cookie, custom headers, etc.)\n // exactly like the 307 path does — the client will still see them.\n mergeMiddlewareResponseHeaders(headers, options.middlewareContext?.headers ?? null);\n const pendingCookies = options.getAndClearPendingCookies?.() ?? [];\n for (const cookie of pendingCookies) {\n headers.append(\"Set-Cookie\", cookie);\n }\n\n return new Response(stream, {\n headers,\n status: 200,\n });\n }\n\n const location = options.isRscRequest\n ? await createRscRedirectLocation(prefixedLocation, options.request)\n : prefixedLocation;\n const headers = new Headers({\n Location: location,\n });\n // Middleware may contribute response headers here, but redirect() owns the\n // status. Do not apply middlewareContext.status on special-error responses.\n mergeMiddlewareResponseHeaders(headers, options.middlewareContext?.headers ?? null);\n // Preserve cookies set via cookies().set() / cookies().delete() during the\n // page render — auth flows commonly set a session cookie and immediately\n // redirect, and those Set-Cookie values must ride on the 307.\n const pendingCookies = options.getAndClearPendingCookies?.() ?? [];\n for (const cookie of pendingCookies) {\n headers.append(\"Set-Cookie\", cookie);\n }\n\n return new Response(null, {\n headers,\n status: options.specialError.statusCode,\n });\n }\n\n if (options.renderFallbackPage) {\n const fallbackResponse = await options.renderFallbackPage(options.specialError.statusCode);\n if (fallbackResponse) {\n return mergeAppPageSpecialErrorHeaders(fallbackResponse, options.middlewareContext);\n }\n }\n\n options.clearRequestContext();\n return mergeAppPageSpecialErrorHeaders(\n new Response(getAppPageStatusText(options.specialError.statusCode), {\n status: options.specialError.statusCode,\n }),\n options.middlewareContext,\n );\n}\n\n/** See `LayoutFlags` type docblock in app-elements.ts for lifecycle. */\nexport async function probeAppPageLayouts(\n options: ProbeAppPageLayoutsOptions,\n): Promise<ProbeAppPageLayoutsResult> {\n const layoutFlags: Record<string, \"s\" | \"d\"> = {};\n const cls = options.classification ?? null;\n\n const response = await options.runWithSuppressedHookWarning(async () => {\n for (let layoutIndex = options.layoutCount - 1; layoutIndex >= 0; layoutIndex--) {\n const buildTimeResult = cls?.buildTimeClassifications?.get(layoutIndex);\n\n if (cls && buildTimeResult) {\n // Build-time classified (Layer 1 or Layer 2): skip dynamic isolation,\n // but still probe for special errors (redirects, not-found).\n layoutFlags[cls.getLayoutId(layoutIndex)] = buildTimeResult === \"static\" ? \"s\" : \"d\";\n if (cls.debugClassification) {\n // `no-classifier` is the documented fallback for a layout that was\n // build-time classified but whose reason payload is absent — either\n // because the build was run without `VINEXT_DEBUG_CLASSIFICATION` or\n // because no Layer 1/2 classifier attached a reason. This is the sole\n // producer of the variant; see `layout-classification-types.ts`.\n cls.debugClassification(\n cls.getLayoutId(layoutIndex),\n cls.buildTimeReasons?.get(layoutIndex) ?? { layer: \"no-classifier\" },\n );\n }\n const errorResponse = await probeLayoutForErrors(options, layoutIndex);\n if (errorResponse) return errorResponse;\n continue;\n }\n\n if (cls) {\n // Layer 3: probe with isolated dynamic scope to detect per-layout\n // dynamic API usage (headers(), cookies(), connection(), etc.)\n try {\n const { dynamicDetected } = await cls.runWithIsolatedDynamicScope(() =>\n options.probeLayoutAt(layoutIndex),\n );\n layoutFlags[cls.getLayoutId(layoutIndex)] = dynamicDetected ? \"d\" : \"s\";\n if (cls.debugClassification) {\n cls.debugClassification(cls.getLayoutId(layoutIndex), {\n layer: \"runtime-probe\",\n outcome: dynamicDetected ? \"dynamic\" : \"static\",\n });\n }\n } catch (error) {\n // Probe failed — conservatively treat as dynamic.\n layoutFlags[cls.getLayoutId(layoutIndex)] = \"d\";\n if (cls.debugClassification) {\n cls.debugClassification(cls.getLayoutId(layoutIndex), {\n layer: \"runtime-probe\",\n outcome: \"dynamic\",\n error: error instanceof Error ? error.message : String(error),\n });\n }\n const errorResponse = await options.onLayoutError(error, layoutIndex);\n if (errorResponse) return errorResponse;\n }\n continue;\n }\n\n // No classification options — original behavior\n const errorResponse = await probeLayoutForErrors(options, layoutIndex);\n if (errorResponse) return errorResponse;\n }\n\n return null;\n });\n\n return { response, layoutFlags };\n}\n\nasync function probeLayoutForErrors(\n options: ProbeAppPageLayoutsOptions,\n layoutIndex: number,\n): Promise<Response | null> {\n try {\n const layoutResult = options.probeLayoutAt(layoutIndex);\n if (isPromiseLike(layoutResult)) {\n await layoutResult;\n }\n } catch (error) {\n return options.onLayoutError(error, layoutIndex);\n }\n return null;\n}\n\nexport async function probeAppPageComponent(\n options: ProbeAppPageComponentOptions,\n): Promise<Response | null> {\n return options.runWithSuppressedHookWarning(async () => {\n try {\n const pageResult = options.probePage();\n if (isPromiseLike(pageResult)) {\n if (options.awaitAsyncResult) {\n await pageResult;\n } else {\n void Promise.resolve(pageResult).catch(() => {});\n }\n }\n } catch (error) {\n return options.onError(error);\n }\n\n return null;\n });\n}\n\nexport async function readAppPageBinaryStream(\n stream: ReadableStream<Uint8Array>,\n): Promise<ArrayBuffer> {\n const reader = stream.getReader();\n const chunks: Uint8Array[] = [];\n let totalLength = 0;\n\n for (;;) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n chunks.push(value);\n totalLength += value.byteLength;\n }\n\n const buffer = new Uint8Array(totalLength);\n let offset = 0;\n for (const chunk of chunks) {\n buffer.set(chunk, offset);\n offset += chunk.byteLength;\n }\n\n return buffer.buffer;\n}\n\nexport function teeAppPageRscStreamForCapture(\n stream: ReadableStream<Uint8Array>,\n shouldCapture: boolean,\n): AppPageRscStreamCapture {\n if (!shouldCapture) {\n return {\n ssrStream: stream,\n };\n }\n\n const [ssrStream, sideStream] = stream.tee();\n return {\n ssrStream,\n sideStream,\n };\n}\n\nexport function buildAppPageFontLinkHeader(\n preloads: readonly AppPageFontPreload[] | null | undefined,\n): string {\n if (!preloads || preloads.length === 0) {\n return \"\";\n }\n\n return preloads\n .map((preload) => `<${preload.href}>; rel=preload; as=font; type=${preload.type}; crossorigin`)\n .join(\", \");\n}\n"],"mappings":"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"}
@@ -45,6 +45,7 @@ type ResolveAppPageHeadOptions<TModule extends AppPageHeadModule = AppPageHeadMo
45
45
  searchParams?: URLSearchParams | null;
46
46
  };
47
47
  type ResolveAppPageHeadResult = {
48
+ hasDynamicMetadata: boolean;
48
49
  hasSearchParams: boolean;
49
50
  metadata: Metadata | null;
50
51
  pageSearchParams: AppPageSearchParams;
@@ -40,6 +40,9 @@ function resolveActiveParallelRouteHeadInputs(options) {
40
40
  function isPresent(value) {
41
41
  return value !== null && value !== void 0;
42
42
  }
43
+ function hasGenerateMetadata(module) {
44
+ return typeof module?.generateMetadata === "function";
45
+ }
43
46
  function collectAppPageSearchParams(searchParams) {
44
47
  const pageSearchParams = Object.create(null);
45
48
  let hasSearchParams = false;
@@ -115,6 +118,7 @@ async function resolveParallelRouteHead(parallelRoute, fallbackParams, fallbackR
115
118
  const metadataSources = [];
116
119
  let accumulatedMetadata = parent;
117
120
  const layoutModules = [...parallelRoute.layoutModules ?? [], parallelRoute.layoutModule].filter(isPresent);
121
+ const hasDynamicMetadata = layoutModules.some(hasGenerateMetadata) || hasGenerateMetadata(parallelRoute.pageModule);
118
122
  const layoutViewportPromises = layoutModules.map((layoutModule) => resolveModuleViewport(layoutModule, params));
119
123
  const pageViewportPromise = parallelRoute.pageModule ? resolveModuleViewport(parallelRoute.pageModule, params) : Promise.resolve(null);
120
124
  for (const layoutViewportPromise of layoutViewportPromises) layoutViewportPromise.catch(() => null);
@@ -143,6 +147,7 @@ async function resolveParallelRouteHead(parallelRoute, fallbackParams, fallbackR
143
147
  const pageViewport = await pageViewportPromise;
144
148
  if (parallelRoute.pageModule) viewportResults.push(pageViewport);
145
149
  return {
150
+ hasDynamicMetadata,
146
151
  metadataResults,
147
152
  metadataSources,
148
153
  viewportResults
@@ -156,6 +161,7 @@ async function resolveAppPageHeadInner(options) {
156
161
  const layoutTreePositions = options.layoutTreePositions ?? [];
157
162
  const layoutInputs = createLayoutInputs(options.layoutModules, layoutTreePositions);
158
163
  const layoutSourcePositions = layoutInputs.map((input) => input.treePosition);
164
+ const primaryHasDynamicMetadata = layoutInputs.some((input) => hasGenerateMetadata(input.module)) || hasGenerateMetadata(options.pageModule);
159
165
  const { hasSearchParams, pageSearchParams } = collectAppPageSearchParams(options.searchParams);
160
166
  const layoutMetadataPromise = resolveLayoutMetadata(layoutInputs, options.params, routeSegments);
161
167
  const layoutViewportPromise = resolveLayoutViewport(layoutInputs, options.params, routeSegments);
@@ -176,6 +182,7 @@ async function resolveAppPageHeadInner(options) {
176
182
  const parallelMetadataResults = parallelRouteHeads.flatMap((head) => head.metadataResults);
177
183
  const parallelViewportResults = parallelRouteHeads.flatMap((head) => head.viewportResults);
178
184
  const parallelMetadataSources = parallelRouteHeads.flatMap((head) => head.metadataSources);
185
+ const hasDynamicMetadata = primaryHasDynamicMetadata || parallelRouteHeads.some((head) => head.hasDynamicMetadata);
179
186
  const primaryPageHasTitle = pageMetadata != null && pageMetadata.title !== void 0;
180
187
  const metadataEntries = [
181
188
  ...layoutMetadataResults.filter(isPresent).map((metadata) => ({ metadata })),
@@ -209,6 +216,7 @@ async function resolveAppPageHeadInner(options) {
209
216
  }
210
217
  if (metadata) metadata = postProcessMetadata(metadata);
211
218
  return {
219
+ hasDynamicMetadata,
212
220
  hasSearchParams,
213
221
  metadata,
214
222
  pageSearchParams,
@@ -1 +1 @@
1
- {"version":3,"file":"app-page-head.js","names":["_resolveModuleMetadata","parentForLayout"],"sources":["../../src/server/app-page-head.ts"],"sourcesContent":["import {\n mergeMetadataEntries,\n mergeViewport,\n postProcessMetadata,\n resolveModuleMetadata as _resolveModuleMetadata,\n resolveModuleViewport,\n type Metadata,\n type MetadataMergeEntry,\n type Viewport,\n} from \"vinext/shims/metadata\";\nimport { runWithFetchDedupe } from \"vinext/shims/fetch-cache\";\nimport { applyFileBasedMetadata } from \"./file-based-metadata.js\";\nimport type { AppPageParams } from \"./app-page-boundary.js\";\nimport { tagAppPageMetadataError } from \"./app-page-execution.js\";\nimport { resolveAppPageSegmentParams } from \"./app-page-params.js\";\nimport type { MetadataFileRoute } from \"./metadata-routes.js\";\n\n/**\n * Wrapped {@link _resolveModuleMetadata} that tags any thrown error with the\n * `APP_PAGE_METADATA_ERROR_MARKER` symbol. The marker lets downstream special-\n * error handling distinguish a `generateMetadata()` redirect/notFound from a\n * page-component redirect/notFound, which matters because metadata is\n * suspended/streamed in Next.js: its redirects ride inside the flight payload\n * with a 200 status code even for document SSR, whereas page redirects still\n * emit a 307 for SSR. See https://github.com/cloudflare/vinext/issues/1347\n * and Next.js test/e2e/app-dir/metadata-navigation.\n */\nasync function resolveModuleMetadata(\n ...args: Parameters<typeof _resolveModuleMetadata>\n): Promise<Metadata | null> {\n try {\n return await _resolveModuleMetadata(...args);\n } catch (error) {\n throw tagAppPageMetadataError(error);\n }\n}\n\ntype AppPageSearchParams = Record<string, string | string[]>;\n\ntype AppPageHeadModule = Record<string, unknown>;\n\ntype AppPageHeadSource = {\n metadata: Metadata | null;\n routeSegments: readonly string[];\n};\n\ntype AppPageHeadLayout<TModule extends AppPageHeadModule> = {\n module: TModule;\n treePosition: number;\n};\n\ntype AppPageHeadParallelRoute<TModule extends AppPageHeadModule = AppPageHeadModule> = {\n layoutModule?: TModule | null;\n layoutModules?: readonly (TModule | null | undefined)[] | null;\n pageModule?: TModule | null;\n params?: AppPageParams | null;\n routeSegments?: readonly string[] | null;\n};\n\ntype AppPageHeadSlot<TModule extends AppPageHeadModule = AppPageHeadModule> = {\n layout?: TModule | null;\n page?: TModule | null;\n};\n\ntype ResolveActiveParallelRouteHeadInputsOptions<\n TModule extends AppPageHeadModule = AppPageHeadModule,\n> = {\n interceptLayouts?: readonly (TModule | null | undefined)[] | null;\n interceptPage?: TModule | null;\n interceptParams?: AppPageParams | null;\n interceptSlotKey?: string | null;\n params: AppPageParams;\n routeSegments: readonly string[];\n slots?: Record<string, AppPageHeadSlot<TModule>> | null;\n};\n\ntype ResolveAppPageHeadOptions<TModule extends AppPageHeadModule = AppPageHeadModule> = {\n /**\n * Configured next.config `basePath`. Threaded into `applyFileBasedMetadata`\n * so file-based metadata route URLs (icon, opengraph-image, manifest, ...)\n * emitted in <head> are prefixed with the basePath. Empty string when no\n * basePath is configured.\n */\n basePath?: string;\n fallbackOnFileMetadataError?: boolean;\n layoutModules: readonly (TModule | null | undefined)[];\n layoutTreePositions?: readonly number[] | null;\n metadataRoutes: readonly MetadataFileRoute[];\n pageModule?: TModule | null;\n parallelRoutes?: readonly AppPageHeadParallelRoute<TModule>[] | null;\n params: AppPageParams;\n routePath: string;\n routeSegments?: readonly string[] | null;\n searchParams?: URLSearchParams | null;\n};\n\ntype ResolveAppPageHeadResult = {\n hasSearchParams: boolean;\n metadata: Metadata | null;\n pageSearchParams: AppPageSearchParams;\n viewport: Viewport;\n};\n\ntype AppPageSearchParamsCollection = {\n hasSearchParams: boolean;\n pageSearchParams: AppPageSearchParams;\n};\n\ntype ResolvedParallelRouteHead = {\n metadataResults: (Metadata | null)[];\n metadataSources: AppPageHeadSource[];\n viewportResults: (Viewport | null)[];\n};\n\nexport function resolveActiveParallelRouteHeadInputs<TModule extends AppPageHeadModule>(\n options: ResolveActiveParallelRouteHeadInputsOptions<TModule>,\n): AppPageHeadParallelRoute<TModule>[] {\n return Object.entries(options.slots ?? {}).map(([slotKey, slot]) => {\n if (options.interceptSlotKey === slotKey && options.interceptPage) {\n return {\n layoutModules: options.interceptLayouts ?? [],\n pageModule: options.interceptPage,\n params: options.interceptParams ?? options.params,\n routeSegments: options.routeSegments,\n };\n }\n\n return {\n layoutModules: slot.layout ? [slot.layout] : [],\n pageModule: slot.page,\n params: options.params,\n routeSegments: options.routeSegments,\n };\n });\n}\n\nfunction isPresent<T>(value: T | null | undefined): value is T {\n return value !== null && value !== undefined;\n}\n\nexport function collectAppPageSearchParams(\n searchParams: URLSearchParams | null | undefined,\n): AppPageSearchParamsCollection {\n const pageSearchParams: AppPageSearchParams = Object.create(null);\n let hasSearchParams = false;\n\n searchParams?.forEach((value, key) => {\n hasSearchParams = true;\n const currentValue = pageSearchParams[key];\n if (Array.isArray(currentValue)) {\n pageSearchParams[key] = [...currentValue, value];\n return;\n }\n if (currentValue !== undefined) {\n pageSearchParams[key] = [currentValue, value];\n return;\n }\n pageSearchParams[key] = value;\n });\n\n return { hasSearchParams, pageSearchParams };\n}\n\nfunction createMetadataSources(\n metadataResults: readonly (Metadata | null)[],\n routeSegments: readonly string[],\n layoutTreePositions: readonly number[],\n pageMetadata: Metadata | null,\n includePageSource: boolean,\n): AppPageHeadSource[] {\n const metadataSources: AppPageHeadSource[] = metadataResults.map((metadata, index) => ({\n routeSegments: routeSegments.slice(0, layoutTreePositions[index] ?? 0),\n metadata,\n }));\n\n if (includePageSource) {\n metadataSources.push({\n routeSegments,\n metadata: pageMetadata,\n });\n }\n\n return metadataSources;\n}\n\nfunction createLayoutInputs<TModule extends AppPageHeadModule>(\n layoutModules: readonly (TModule | null | undefined)[],\n layoutTreePositions: readonly number[],\n): AppPageHeadLayout<TModule>[] {\n const layoutInputs: AppPageHeadLayout<TModule>[] = [];\n\n for (let index = 0; index < layoutModules.length; index++) {\n const layoutModule = layoutModules[index];\n if (!isPresent(layoutModule)) {\n continue;\n }\n layoutInputs.push({\n module: layoutModule,\n treePosition: layoutTreePositions[index] ?? 0,\n });\n }\n\n return layoutInputs;\n}\n\nasync function resolveLayoutMetadata<TModule extends AppPageHeadModule>(\n layoutInputs: readonly AppPageHeadLayout<TModule>[],\n params: AppPageParams,\n routeSegments: readonly string[],\n): Promise<(Metadata | null)[]> {\n const layoutMetadataPromises: Promise<Metadata | null>[] = [];\n let accumulatedMetadata = Promise.resolve<Metadata>({});\n\n for (const layoutInput of layoutInputs) {\n const parentForLayout = accumulatedMetadata;\n const layoutParams = resolveAppPageSegmentParams(\n routeSegments,\n layoutInput.treePosition,\n params,\n );\n const metadataPromise = resolveModuleMetadata(\n layoutInput.module,\n layoutParams,\n undefined,\n parentForLayout,\n );\n layoutMetadataPromises.push(metadataPromise);\n void metadataPromise.catch(() => null);\n\n accumulatedMetadata = metadataPromise.then(async (metadataResult) => {\n if (metadataResult) {\n return mergeMetadataEntries([\n { metadata: await parentForLayout },\n { metadata: metadataResult },\n ]);\n }\n return parentForLayout;\n });\n void accumulatedMetadata.catch(() => null);\n }\n\n return Promise.all(layoutMetadataPromises);\n}\n\nasync function resolveLayoutViewport<TModule extends AppPageHeadModule>(\n layoutInputs: readonly AppPageHeadLayout<TModule>[],\n params: AppPageParams,\n routeSegments: readonly string[],\n): Promise<(Viewport | null)[]> {\n return Promise.all(\n layoutInputs.map((layoutInput) => {\n const layoutParams = resolveAppPageSegmentParams(\n routeSegments,\n layoutInput.treePosition,\n params,\n );\n return resolveModuleViewport(layoutInput.module, layoutParams);\n }),\n );\n}\n\nasync function resolveParallelRouteHead<TModule extends AppPageHeadModule>(\n parallelRoute: AppPageHeadParallelRoute<TModule>,\n fallbackParams: AppPageParams,\n fallbackRouteSegments: readonly string[],\n pageSearchParams: AppPageSearchParams,\n parent: Promise<Metadata>,\n): Promise<ResolvedParallelRouteHead> {\n const params = parallelRoute.params ?? fallbackParams;\n const routeSegments = parallelRoute.routeSegments ?? fallbackRouteSegments;\n const metadataResults: (Metadata | null)[] = [];\n const viewportResults: (Viewport | null)[] = [];\n const metadataSources: AppPageHeadSource[] = [];\n let accumulatedMetadata = parent;\n const layoutModules = [...(parallelRoute.layoutModules ?? []), parallelRoute.layoutModule].filter(\n isPresent,\n );\n const layoutViewportPromises = layoutModules.map((layoutModule) =>\n resolveModuleViewport(layoutModule, params),\n );\n const pageViewportPromise = parallelRoute.pageModule\n ? resolveModuleViewport(parallelRoute.pageModule, params)\n : Promise.resolve(null);\n for (const layoutViewportPromise of layoutViewportPromises) {\n void layoutViewportPromise.catch(() => null);\n }\n void pageViewportPromise.catch(() => null);\n\n for (const layoutModule of layoutModules) {\n const layoutMetadata = await resolveModuleMetadata(\n layoutModule,\n params,\n undefined,\n accumulatedMetadata,\n );\n metadataResults.push(layoutMetadata);\n // Parallel route metadata sources are scoped to the active slot branch because\n // the route tree input does not carry per-layout segment positions inside that branch.\n metadataSources.push({ metadata: layoutMetadata, routeSegments });\n if (layoutMetadata) {\n const parentForLayout = accumulatedMetadata;\n accumulatedMetadata = parentForLayout.then(async (parentMetadata) =>\n mergeMetadataEntries([{ metadata: parentMetadata }, { metadata: layoutMetadata }]),\n );\n void accumulatedMetadata.catch(() => null);\n }\n }\n\n if (parallelRoute.pageModule) {\n const pageMetadata = await resolveModuleMetadata(\n parallelRoute.pageModule,\n params,\n pageSearchParams,\n accumulatedMetadata,\n );\n metadataResults.push(pageMetadata);\n // Keep the page source scoped to the same active slot branch as its layouts.\n metadataSources.push({ metadata: pageMetadata, routeSegments });\n }\n\n viewportResults.push(...(await Promise.all(layoutViewportPromises)));\n const pageViewport = await pageViewportPromise;\n if (parallelRoute.pageModule) {\n viewportResults.push(pageViewport);\n }\n\n return { metadataResults, metadataSources, viewportResults };\n}\n\nexport async function resolveAppPageHead<TModule extends AppPageHeadModule>(\n options: ResolveAppPageHeadOptions<TModule>,\n): Promise<ResolveAppPageHeadResult> {\n return await runWithFetchDedupe(() => resolveAppPageHeadInner(options));\n}\n\nasync function resolveAppPageHeadInner<TModule extends AppPageHeadModule>(\n options: ResolveAppPageHeadOptions<TModule>,\n): Promise<ResolveAppPageHeadResult> {\n const routeSegments = options.routeSegments ?? [];\n const layoutTreePositions = options.layoutTreePositions ?? [];\n const layoutInputs = createLayoutInputs(options.layoutModules, layoutTreePositions);\n const layoutSourcePositions = layoutInputs.map((input) => input.treePosition);\n const { hasSearchParams, pageSearchParams } = collectAppPageSearchParams(options.searchParams);\n const layoutMetadataPromise = resolveLayoutMetadata(layoutInputs, options.params, routeSegments);\n const layoutViewportPromise = resolveLayoutViewport(layoutInputs, options.params, routeSegments);\n\n const layoutMetadataResultsForParent = layoutMetadataPromise.then((metadataResults) =>\n metadataResults.filter(isPresent),\n );\n void layoutMetadataResultsForParent.catch(() => null);\n const pageParentPromise = layoutMetadataResultsForParent.then((metadataResults) =>\n metadataResults.length > 0\n ? mergeMetadataEntries(metadataResults.map((metadata) => ({ metadata })))\n : {},\n );\n void pageParentPromise.catch(() => null);\n const pageMetadataPromise = options.pageModule\n ? resolveModuleMetadata(options.pageModule, options.params, pageSearchParams, pageParentPromise)\n : Promise.resolve(null);\n const pageViewportPromise = options.pageModule\n ? resolveModuleViewport(options.pageModule, options.params)\n : Promise.resolve(null);\n const parallelRouteHeadPromise = Promise.all(\n (options.parallelRoutes ?? []).map((parallelRoute) =>\n resolveParallelRouteHead(\n parallelRoute,\n options.params,\n routeSegments,\n pageSearchParams,\n pageParentPromise,\n ),\n ),\n );\n\n const [\n layoutMetadataResults,\n layoutViewportResults,\n pageMetadata,\n pageViewport,\n parallelRouteHeads,\n ] = await Promise.all([\n layoutMetadataPromise,\n layoutViewportPromise,\n pageMetadataPromise,\n pageViewportPromise,\n parallelRouteHeadPromise,\n ]);\n const parallelMetadataResults = parallelRouteHeads.flatMap((head) => head.metadataResults);\n const parallelViewportResults = parallelRouteHeads.flatMap((head) => head.viewportResults);\n const parallelMetadataSources = parallelRouteHeads.flatMap((head) => head.metadataSources);\n\n // Active parallel slot metadata is suppressed from contributing the primary\n // <title> when the matched page already provides one. This preserves Next.js\n // behavior where slot pages (typically modals/sidebars rendered alongside the\n // main page) don't clobber the page title. When the route has no children\n // page providing a title (e.g. a parallel layout that doesn't render\n // `{children}`, or a parent that only has `default.tsx`), the slot page's\n // title is the most specific signal and is allowed to contribute — matching\n // Next.js's loader-tree walk which appends slot metadata items in tree order\n // with no title suppression.\n // Reference: https://github.com/vercel/next.js/blob/canary/packages/next/src/lib/metadata/resolve-metadata.ts\n const primaryPageHasTitle = pageMetadata != null && pageMetadata.title !== undefined;\n const metadataEntries: MetadataMergeEntry[] = [\n ...layoutMetadataResults.filter(isPresent).map((metadata) => ({ metadata })),\n ...(pageMetadata ? [{ isPage: true, metadata: pageMetadata }] : []),\n ...parallelMetadataResults\n .filter(isPresent)\n .map((metadata) => ({ contributesTitle: !primaryPageHasTitle, metadata })),\n ];\n const viewportList = [\n ...layoutViewportResults.filter(isPresent),\n ...(pageViewport ? [pageViewport] : []),\n ...parallelViewportResults.filter(isPresent),\n ];\n\n const resolvedMetadataBase =\n metadataEntries.length > 0 ? mergeMetadataEntries(metadataEntries) : null;\n const metadataSources = createMetadataSources(\n layoutMetadataResults,\n routeSegments,\n layoutSourcePositions,\n pageMetadata,\n Boolean(options.pageModule),\n );\n metadataSources.push(...parallelMetadataSources);\n let metadata = resolvedMetadataBase;\n\n try {\n metadata = await applyFileBasedMetadata(\n resolvedMetadataBase,\n options.routePath,\n options.params,\n options.metadataRoutes,\n {\n routeSegments,\n metadataSources,\n basePath: options.basePath ?? \"\",\n },\n );\n } catch (error) {\n if (!options.fallbackOnFileMetadataError) {\n throw error;\n }\n console.error(\n `[vinext] File-based metadata resolution failed while rendering error boundary for ${options.routePath}:`,\n error,\n );\n }\n\n if (metadata) {\n metadata = postProcessMetadata(metadata);\n }\n\n return {\n hasSearchParams,\n metadata,\n pageSearchParams,\n viewport: mergeViewport(viewportList),\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"app-page-head.js","names":["_resolveModuleMetadata","parentForLayout"],"sources":["../../src/server/app-page-head.ts"],"sourcesContent":["import {\n mergeMetadataEntries,\n mergeViewport,\n postProcessMetadata,\n resolveModuleMetadata as _resolveModuleMetadata,\n resolveModuleViewport,\n type Metadata,\n type MetadataMergeEntry,\n type Viewport,\n} from \"vinext/shims/metadata\";\nimport { runWithFetchDedupe } from \"vinext/shims/fetch-cache\";\nimport { applyFileBasedMetadata } from \"./file-based-metadata.js\";\nimport type { AppPageParams } from \"./app-page-boundary.js\";\nimport { tagAppPageMetadataError } from \"./app-page-execution.js\";\nimport { resolveAppPageSegmentParams } from \"./app-page-params.js\";\nimport type { MetadataFileRoute } from \"./metadata-routes.js\";\n\n/**\n * Wrapped {@link _resolveModuleMetadata} that tags any thrown error with the\n * `APP_PAGE_METADATA_ERROR_MARKER` symbol. The marker lets downstream special-\n * error handling distinguish a `generateMetadata()` redirect/notFound from a\n * page-component redirect/notFound, which matters because metadata is\n * suspended/streamed in Next.js: its redirects ride inside the flight payload\n * with a 200 status code even for document SSR, whereas page redirects still\n * emit a 307 for SSR. See https://github.com/cloudflare/vinext/issues/1347\n * and Next.js test/e2e/app-dir/metadata-navigation.\n */\nasync function resolveModuleMetadata(\n ...args: Parameters<typeof _resolveModuleMetadata>\n): Promise<Metadata | null> {\n try {\n return await _resolveModuleMetadata(...args);\n } catch (error) {\n throw tagAppPageMetadataError(error);\n }\n}\n\ntype AppPageSearchParams = Record<string, string | string[]>;\n\ntype AppPageHeadModule = Record<string, unknown>;\n\ntype AppPageHeadSource = {\n metadata: Metadata | null;\n routeSegments: readonly string[];\n};\n\ntype AppPageHeadLayout<TModule extends AppPageHeadModule> = {\n module: TModule;\n treePosition: number;\n};\n\ntype AppPageHeadParallelRoute<TModule extends AppPageHeadModule = AppPageHeadModule> = {\n layoutModule?: TModule | null;\n layoutModules?: readonly (TModule | null | undefined)[] | null;\n pageModule?: TModule | null;\n params?: AppPageParams | null;\n routeSegments?: readonly string[] | null;\n};\n\ntype AppPageHeadSlot<TModule extends AppPageHeadModule = AppPageHeadModule> = {\n layout?: TModule | null;\n page?: TModule | null;\n};\n\ntype ResolveActiveParallelRouteHeadInputsOptions<\n TModule extends AppPageHeadModule = AppPageHeadModule,\n> = {\n interceptLayouts?: readonly (TModule | null | undefined)[] | null;\n interceptPage?: TModule | null;\n interceptParams?: AppPageParams | null;\n interceptSlotKey?: string | null;\n params: AppPageParams;\n routeSegments: readonly string[];\n slots?: Record<string, AppPageHeadSlot<TModule>> | null;\n};\n\ntype ResolveAppPageHeadOptions<TModule extends AppPageHeadModule = AppPageHeadModule> = {\n /**\n * Configured next.config `basePath`. Threaded into `applyFileBasedMetadata`\n * so file-based metadata route URLs (icon, opengraph-image, manifest, ...)\n * emitted in <head> are prefixed with the basePath. Empty string when no\n * basePath is configured.\n */\n basePath?: string;\n fallbackOnFileMetadataError?: boolean;\n layoutModules: readonly (TModule | null | undefined)[];\n layoutTreePositions?: readonly number[] | null;\n metadataRoutes: readonly MetadataFileRoute[];\n pageModule?: TModule | null;\n parallelRoutes?: readonly AppPageHeadParallelRoute<TModule>[] | null;\n params: AppPageParams;\n routePath: string;\n routeSegments?: readonly string[] | null;\n searchParams?: URLSearchParams | null;\n};\n\ntype ResolveAppPageHeadResult = {\n hasDynamicMetadata: boolean;\n hasSearchParams: boolean;\n metadata: Metadata | null;\n pageSearchParams: AppPageSearchParams;\n viewport: Viewport;\n};\n\ntype AppPageSearchParamsCollection = {\n hasSearchParams: boolean;\n pageSearchParams: AppPageSearchParams;\n};\n\ntype ResolvedParallelRouteHead = {\n hasDynamicMetadata: boolean;\n metadataResults: (Metadata | null)[];\n metadataSources: AppPageHeadSource[];\n viewportResults: (Viewport | null)[];\n};\n\nexport function resolveActiveParallelRouteHeadInputs<TModule extends AppPageHeadModule>(\n options: ResolveActiveParallelRouteHeadInputsOptions<TModule>,\n): AppPageHeadParallelRoute<TModule>[] {\n return Object.entries(options.slots ?? {}).map(([slotKey, slot]) => {\n if (options.interceptSlotKey === slotKey && options.interceptPage) {\n return {\n layoutModules: options.interceptLayouts ?? [],\n pageModule: options.interceptPage,\n params: options.interceptParams ?? options.params,\n routeSegments: options.routeSegments,\n };\n }\n\n return {\n layoutModules: slot.layout ? [slot.layout] : [],\n pageModule: slot.page,\n params: options.params,\n routeSegments: options.routeSegments,\n };\n });\n}\n\nfunction isPresent<T>(value: T | null | undefined): value is T {\n return value !== null && value !== undefined;\n}\n\nfunction hasGenerateMetadata(module: AppPageHeadModule | null | undefined): boolean {\n return typeof module?.generateMetadata === \"function\";\n}\n\nexport function collectAppPageSearchParams(\n searchParams: URLSearchParams | null | undefined,\n): AppPageSearchParamsCollection {\n const pageSearchParams: AppPageSearchParams = Object.create(null);\n let hasSearchParams = false;\n\n searchParams?.forEach((value, key) => {\n hasSearchParams = true;\n const currentValue = pageSearchParams[key];\n if (Array.isArray(currentValue)) {\n pageSearchParams[key] = [...currentValue, value];\n return;\n }\n if (currentValue !== undefined) {\n pageSearchParams[key] = [currentValue, value];\n return;\n }\n pageSearchParams[key] = value;\n });\n\n return { hasSearchParams, pageSearchParams };\n}\n\nfunction createMetadataSources(\n metadataResults: readonly (Metadata | null)[],\n routeSegments: readonly string[],\n layoutTreePositions: readonly number[],\n pageMetadata: Metadata | null,\n includePageSource: boolean,\n): AppPageHeadSource[] {\n const metadataSources: AppPageHeadSource[] = metadataResults.map((metadata, index) => ({\n routeSegments: routeSegments.slice(0, layoutTreePositions[index] ?? 0),\n metadata,\n }));\n\n if (includePageSource) {\n metadataSources.push({\n routeSegments,\n metadata: pageMetadata,\n });\n }\n\n return metadataSources;\n}\n\nfunction createLayoutInputs<TModule extends AppPageHeadModule>(\n layoutModules: readonly (TModule | null | undefined)[],\n layoutTreePositions: readonly number[],\n): AppPageHeadLayout<TModule>[] {\n const layoutInputs: AppPageHeadLayout<TModule>[] = [];\n\n for (let index = 0; index < layoutModules.length; index++) {\n const layoutModule = layoutModules[index];\n if (!isPresent(layoutModule)) {\n continue;\n }\n layoutInputs.push({\n module: layoutModule,\n treePosition: layoutTreePositions[index] ?? 0,\n });\n }\n\n return layoutInputs;\n}\n\nasync function resolveLayoutMetadata<TModule extends AppPageHeadModule>(\n layoutInputs: readonly AppPageHeadLayout<TModule>[],\n params: AppPageParams,\n routeSegments: readonly string[],\n): Promise<(Metadata | null)[]> {\n const layoutMetadataPromises: Promise<Metadata | null>[] = [];\n let accumulatedMetadata = Promise.resolve<Metadata>({});\n\n for (const layoutInput of layoutInputs) {\n const parentForLayout = accumulatedMetadata;\n const layoutParams = resolveAppPageSegmentParams(\n routeSegments,\n layoutInput.treePosition,\n params,\n );\n const metadataPromise = resolveModuleMetadata(\n layoutInput.module,\n layoutParams,\n undefined,\n parentForLayout,\n );\n layoutMetadataPromises.push(metadataPromise);\n void metadataPromise.catch(() => null);\n\n accumulatedMetadata = metadataPromise.then(async (metadataResult) => {\n if (metadataResult) {\n return mergeMetadataEntries([\n { metadata: await parentForLayout },\n { metadata: metadataResult },\n ]);\n }\n return parentForLayout;\n });\n void accumulatedMetadata.catch(() => null);\n }\n\n return Promise.all(layoutMetadataPromises);\n}\n\nasync function resolveLayoutViewport<TModule extends AppPageHeadModule>(\n layoutInputs: readonly AppPageHeadLayout<TModule>[],\n params: AppPageParams,\n routeSegments: readonly string[],\n): Promise<(Viewport | null)[]> {\n return Promise.all(\n layoutInputs.map((layoutInput) => {\n const layoutParams = resolveAppPageSegmentParams(\n routeSegments,\n layoutInput.treePosition,\n params,\n );\n return resolveModuleViewport(layoutInput.module, layoutParams);\n }),\n );\n}\n\nasync function resolveParallelRouteHead<TModule extends AppPageHeadModule>(\n parallelRoute: AppPageHeadParallelRoute<TModule>,\n fallbackParams: AppPageParams,\n fallbackRouteSegments: readonly string[],\n pageSearchParams: AppPageSearchParams,\n parent: Promise<Metadata>,\n): Promise<ResolvedParallelRouteHead> {\n const params = parallelRoute.params ?? fallbackParams;\n const routeSegments = parallelRoute.routeSegments ?? fallbackRouteSegments;\n const metadataResults: (Metadata | null)[] = [];\n const viewportResults: (Viewport | null)[] = [];\n const metadataSources: AppPageHeadSource[] = [];\n let accumulatedMetadata = parent;\n const layoutModules = [...(parallelRoute.layoutModules ?? []), parallelRoute.layoutModule].filter(\n isPresent,\n );\n const hasDynamicMetadata =\n layoutModules.some(hasGenerateMetadata) || hasGenerateMetadata(parallelRoute.pageModule);\n const layoutViewportPromises = layoutModules.map((layoutModule) =>\n resolveModuleViewport(layoutModule, params),\n );\n const pageViewportPromise = parallelRoute.pageModule\n ? resolveModuleViewport(parallelRoute.pageModule, params)\n : Promise.resolve(null);\n for (const layoutViewportPromise of layoutViewportPromises) {\n void layoutViewportPromise.catch(() => null);\n }\n void pageViewportPromise.catch(() => null);\n\n for (const layoutModule of layoutModules) {\n const layoutMetadata = await resolveModuleMetadata(\n layoutModule,\n params,\n undefined,\n accumulatedMetadata,\n );\n metadataResults.push(layoutMetadata);\n // Parallel route metadata sources are scoped to the active slot branch because\n // the route tree input does not carry per-layout segment positions inside that branch.\n metadataSources.push({ metadata: layoutMetadata, routeSegments });\n if (layoutMetadata) {\n const parentForLayout = accumulatedMetadata;\n accumulatedMetadata = parentForLayout.then(async (parentMetadata) =>\n mergeMetadataEntries([{ metadata: parentMetadata }, { metadata: layoutMetadata }]),\n );\n void accumulatedMetadata.catch(() => null);\n }\n }\n\n if (parallelRoute.pageModule) {\n const pageMetadata = await resolveModuleMetadata(\n parallelRoute.pageModule,\n params,\n pageSearchParams,\n accumulatedMetadata,\n );\n metadataResults.push(pageMetadata);\n // Keep the page source scoped to the same active slot branch as its layouts.\n metadataSources.push({ metadata: pageMetadata, routeSegments });\n }\n\n viewportResults.push(...(await Promise.all(layoutViewportPromises)));\n const pageViewport = await pageViewportPromise;\n if (parallelRoute.pageModule) {\n viewportResults.push(pageViewport);\n }\n\n return { hasDynamicMetadata, metadataResults, metadataSources, viewportResults };\n}\n\nexport async function resolveAppPageHead<TModule extends AppPageHeadModule>(\n options: ResolveAppPageHeadOptions<TModule>,\n): Promise<ResolveAppPageHeadResult> {\n return await runWithFetchDedupe(() => resolveAppPageHeadInner(options));\n}\n\nasync function resolveAppPageHeadInner<TModule extends AppPageHeadModule>(\n options: ResolveAppPageHeadOptions<TModule>,\n): Promise<ResolveAppPageHeadResult> {\n const routeSegments = options.routeSegments ?? [];\n const layoutTreePositions = options.layoutTreePositions ?? [];\n const layoutInputs = createLayoutInputs(options.layoutModules, layoutTreePositions);\n const layoutSourcePositions = layoutInputs.map((input) => input.treePosition);\n const primaryHasDynamicMetadata =\n layoutInputs.some((input) => hasGenerateMetadata(input.module)) ||\n hasGenerateMetadata(options.pageModule);\n const { hasSearchParams, pageSearchParams } = collectAppPageSearchParams(options.searchParams);\n const layoutMetadataPromise = resolveLayoutMetadata(layoutInputs, options.params, routeSegments);\n const layoutViewportPromise = resolveLayoutViewport(layoutInputs, options.params, routeSegments);\n\n const layoutMetadataResultsForParent = layoutMetadataPromise.then((metadataResults) =>\n metadataResults.filter(isPresent),\n );\n void layoutMetadataResultsForParent.catch(() => null);\n const pageParentPromise = layoutMetadataResultsForParent.then((metadataResults) =>\n metadataResults.length > 0\n ? mergeMetadataEntries(metadataResults.map((metadata) => ({ metadata })))\n : {},\n );\n void pageParentPromise.catch(() => null);\n const pageMetadataPromise = options.pageModule\n ? resolveModuleMetadata(options.pageModule, options.params, pageSearchParams, pageParentPromise)\n : Promise.resolve(null);\n const pageViewportPromise = options.pageModule\n ? resolveModuleViewport(options.pageModule, options.params)\n : Promise.resolve(null);\n const parallelRouteHeadPromise = Promise.all(\n (options.parallelRoutes ?? []).map((parallelRoute) =>\n resolveParallelRouteHead(\n parallelRoute,\n options.params,\n routeSegments,\n pageSearchParams,\n pageParentPromise,\n ),\n ),\n );\n\n const [\n layoutMetadataResults,\n layoutViewportResults,\n pageMetadata,\n pageViewport,\n parallelRouteHeads,\n ] = await Promise.all([\n layoutMetadataPromise,\n layoutViewportPromise,\n pageMetadataPromise,\n pageViewportPromise,\n parallelRouteHeadPromise,\n ]);\n const parallelMetadataResults = parallelRouteHeads.flatMap((head) => head.metadataResults);\n const parallelViewportResults = parallelRouteHeads.flatMap((head) => head.viewportResults);\n const parallelMetadataSources = parallelRouteHeads.flatMap((head) => head.metadataSources);\n const hasDynamicMetadata =\n primaryHasDynamicMetadata || parallelRouteHeads.some((head) => head.hasDynamicMetadata);\n\n // Active parallel slot metadata is suppressed from contributing the primary\n // <title> when the matched page already provides one. This preserves Next.js\n // behavior where slot pages (typically modals/sidebars rendered alongside the\n // main page) don't clobber the page title. When the route has no children\n // page providing a title (e.g. a parallel layout that doesn't render\n // `{children}`, or a parent that only has `default.tsx`), the slot page's\n // title is the most specific signal and is allowed to contribute — matching\n // Next.js's loader-tree walk which appends slot metadata items in tree order\n // with no title suppression.\n // Reference: https://github.com/vercel/next.js/blob/canary/packages/next/src/lib/metadata/resolve-metadata.ts\n const primaryPageHasTitle = pageMetadata != null && pageMetadata.title !== undefined;\n const metadataEntries: MetadataMergeEntry[] = [\n ...layoutMetadataResults.filter(isPresent).map((metadata) => ({ metadata })),\n ...(pageMetadata ? [{ isPage: true, metadata: pageMetadata }] : []),\n ...parallelMetadataResults\n .filter(isPresent)\n .map((metadata) => ({ contributesTitle: !primaryPageHasTitle, metadata })),\n ];\n const viewportList = [\n ...layoutViewportResults.filter(isPresent),\n ...(pageViewport ? [pageViewport] : []),\n ...parallelViewportResults.filter(isPresent),\n ];\n\n const resolvedMetadataBase =\n metadataEntries.length > 0 ? mergeMetadataEntries(metadataEntries) : null;\n const metadataSources = createMetadataSources(\n layoutMetadataResults,\n routeSegments,\n layoutSourcePositions,\n pageMetadata,\n Boolean(options.pageModule),\n );\n metadataSources.push(...parallelMetadataSources);\n let metadata = resolvedMetadataBase;\n\n try {\n metadata = await applyFileBasedMetadata(\n resolvedMetadataBase,\n options.routePath,\n options.params,\n options.metadataRoutes,\n {\n routeSegments,\n metadataSources,\n basePath: options.basePath ?? \"\",\n },\n );\n } catch (error) {\n if (!options.fallbackOnFileMetadataError) {\n throw error;\n }\n console.error(\n `[vinext] File-based metadata resolution failed while rendering error boundary for ${options.routePath}:`,\n error,\n );\n }\n\n if (metadata) {\n metadata = postProcessMetadata(metadata);\n }\n\n return {\n hasDynamicMetadata,\n hasSearchParams,\n metadata,\n pageSearchParams,\n viewport: mergeViewport(viewportList),\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import { AppElementsInterception } from "./app-elements-wire.js";
2
+ //#region src/server/app-page-render-identity.d.ts
3
+ type AppPageRenderIdentityInput = {
4
+ displayPathname: string;
5
+ interceptionContext?: string | null;
6
+ interceptSourceMatchedUrl?: string | null;
7
+ interceptSlotId?: string | null;
8
+ };
9
+ type AppPageRenderIdentity = {
10
+ displayPathname: string;
11
+ interception: AppElementsInterception | null;
12
+ interceptionContext: string | null;
13
+ matchedRoutePathname: string;
14
+ pageId: string;
15
+ routeId: string;
16
+ targetMatchedPathname: string;
17
+ };
18
+ declare function normalizeAppPageInterceptionProofPathname(pathname: string | null): string | null;
19
+ declare function createAppPageRenderIdentity(input: AppPageRenderIdentityInput): AppPageRenderIdentity;
20
+ //#endregion
21
+ export { AppPageRenderIdentity, createAppPageRenderIdentity, normalizeAppPageInterceptionProofPathname };
22
+ //# sourceMappingURL=app-page-render-identity.d.ts.map
@@ -0,0 +1,42 @@
1
+ import { normalizePathnameForRouteMatch } from "../routing/utils.js";
2
+ import { isInterceptionMatchedUrlPath, normalizePath } from "./normalize-path.js";
3
+ import { AppElementsWire } from "./app-elements-wire.js";
4
+ import "./app-elements.js";
5
+ //#region src/server/app-page-render-identity.ts
6
+ function normalizeAppPageRenderMatchedPathname(pathname) {
7
+ if (!pathname.startsWith("/")) throw new Error(`[vinext] App Router render pathname must be absolute: ${pathname}`);
8
+ return normalizePath(normalizePathnameForRouteMatch(pathname));
9
+ }
10
+ function normalizeAppPageInterceptionProofPathname(pathname) {
11
+ if (pathname === null || !isInterceptionMatchedUrlPath(pathname)) return null;
12
+ return normalizeAppPageRenderMatchedPathname(pathname);
13
+ }
14
+ function createAppPageRenderIdentity(input) {
15
+ const interceptionContext = input.interceptionContext ?? null;
16
+ const targetMatchedPathname = normalizeAppPageRenderMatchedPathname(input.displayPathname);
17
+ const sourceMatchedPathname = normalizeAppPageInterceptionProofPathname(input.interceptSourceMatchedUrl ?? null);
18
+ const slotId = input.interceptSlotId ?? null;
19
+ const matchedRoutePathname = sourceMatchedPathname ?? targetMatchedPathname;
20
+ const routeId = AppElementsWire.encodeRouteId(matchedRoutePathname, null);
21
+ const pageId = AppElementsWire.encodePageId(matchedRoutePathname, null);
22
+ const interception = sourceMatchedPathname === null || slotId === null ? null : {
23
+ sourceMatchedUrl: sourceMatchedPathname,
24
+ sourceRouteId: AppElementsWire.encodeRouteId(sourceMatchedPathname, null),
25
+ slotId,
26
+ targetMatchedUrl: targetMatchedPathname,
27
+ targetRouteId: AppElementsWire.encodeRouteId(targetMatchedPathname, null)
28
+ };
29
+ return {
30
+ displayPathname: input.displayPathname,
31
+ interception,
32
+ interceptionContext,
33
+ matchedRoutePathname,
34
+ pageId,
35
+ routeId,
36
+ targetMatchedPathname
37
+ };
38
+ }
39
+ //#endregion
40
+ export { createAppPageRenderIdentity, normalizeAppPageInterceptionProofPathname };
41
+
42
+ //# sourceMappingURL=app-page-render-identity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-page-render-identity.js","names":[],"sources":["../../src/server/app-page-render-identity.ts"],"sourcesContent":["import { normalizePathnameForRouteMatch } from \"../routing/utils.js\";\nimport { AppElementsWire, type AppElementsInterception } from \"./app-elements.js\";\nimport { isInterceptionMatchedUrlPath, normalizePath } from \"./normalize-path.js\";\n\ntype AppPageRenderIdentityInput = {\n displayPathname: string;\n interceptionContext?: string | null;\n interceptSourceMatchedUrl?: string | null;\n interceptSlotId?: string | null;\n};\n\nexport type AppPageRenderIdentity = {\n displayPathname: string;\n interception: AppElementsInterception | null;\n interceptionContext: string | null;\n matchedRoutePathname: string;\n pageId: string;\n routeId: string;\n targetMatchedPathname: string;\n};\n\nfunction normalizeAppPageRenderMatchedPathname(pathname: string): string {\n if (!pathname.startsWith(\"/\")) {\n throw new Error(`[vinext] App Router render pathname must be absolute: ${pathname}`);\n }\n return normalizePath(normalizePathnameForRouteMatch(pathname));\n}\n\nexport function normalizeAppPageInterceptionProofPathname(pathname: string | null): string | null {\n if (pathname === null || !isInterceptionMatchedUrlPath(pathname)) return null;\n return normalizeAppPageRenderMatchedPathname(pathname);\n}\n\nexport function createAppPageRenderIdentity(\n input: AppPageRenderIdentityInput,\n): AppPageRenderIdentity {\n const interceptionContext = input.interceptionContext ?? null;\n const targetMatchedPathname = normalizeAppPageRenderMatchedPathname(input.displayPathname);\n const sourceMatchedPathname = normalizeAppPageInterceptionProofPathname(\n input.interceptSourceMatchedUrl ?? null,\n );\n const slotId = input.interceptSlotId ?? null;\n const matchedRoutePathname = sourceMatchedPathname ?? targetMatchedPathname;\n const routeId = AppElementsWire.encodeRouteId(matchedRoutePathname, null);\n const pageId = AppElementsWire.encodePageId(matchedRoutePathname, null);\n const interception =\n sourceMatchedPathname === null || slotId === null\n ? null\n : {\n sourceMatchedUrl: sourceMatchedPathname,\n sourceRouteId: AppElementsWire.encodeRouteId(sourceMatchedPathname, null),\n slotId,\n targetMatchedUrl: targetMatchedPathname,\n targetRouteId: AppElementsWire.encodeRouteId(targetMatchedPathname, null),\n };\n\n return {\n displayPathname: input.displayPathname,\n interception,\n interceptionContext,\n matchedRoutePathname,\n pageId,\n routeId,\n targetMatchedPathname,\n };\n}\n"],"mappings":";;;;;AAqBA,SAAS,sCAAsC,UAA0B;CACvE,IAAI,CAAC,SAAS,WAAW,IAAI,EAC3B,MAAM,IAAI,MAAM,yDAAyD,WAAW;CAEtF,OAAO,cAAc,+BAA+B,SAAS,CAAC;;AAGhE,SAAgB,0CAA0C,UAAwC;CAChG,IAAI,aAAa,QAAQ,CAAC,6BAA6B,SAAS,EAAE,OAAO;CACzE,OAAO,sCAAsC,SAAS;;AAGxD,SAAgB,4BACd,OACuB;CACvB,MAAM,sBAAsB,MAAM,uBAAuB;CACzD,MAAM,wBAAwB,sCAAsC,MAAM,gBAAgB;CAC1F,MAAM,wBAAwB,0CAC5B,MAAM,6BAA6B,KACpC;CACD,MAAM,SAAS,MAAM,mBAAmB;CACxC,MAAM,uBAAuB,yBAAyB;CACtD,MAAM,UAAU,gBAAgB,cAAc,sBAAsB,KAAK;CACzE,MAAM,SAAS,gBAAgB,aAAa,sBAAsB,KAAK;CACvE,MAAM,eACJ,0BAA0B,QAAQ,WAAW,OACzC,OACA;EACE,kBAAkB;EAClB,eAAe,gBAAgB,cAAc,uBAAuB,KAAK;EACzE;EACA,kBAAkB;EAClB,eAAe,gBAAgB,cAAc,uBAAuB,KAAK;EAC1E;CAEP,OAAO;EACL,iBAAiB,MAAM;EACvB;EACA;EACA;EACA;EACA;EACA;EACD"}
@@ -1,6 +1,6 @@
1
1
  import { fnv1a64 } from "../utils/hash.js";
2
- import { normalizeMountedSlotsHeader } from "./app-mounted-slots-header.js";
3
2
  import { AppElementsWire, isAppElementsRecord } from "./app-elements-wire.js";
3
+ import { normalizeMountedSlotsHeader } from "./app-mounted-slots-header.js";
4
4
  import "./app-elements.js";
5
5
  import { buildRenderObservation, buildRenderRequestApiObservations } from "./cache-proof.js";
6
6
  //#region src/server/app-page-render-observation.ts