vinext 0.0.46 → 0.0.48

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 (351) hide show
  1. package/README.md +8 -6
  2. package/dist/build/layout-classification.js +3 -1
  3. package/dist/build/layout-classification.js.map +1 -1
  4. package/dist/build/prerender.d.ts +2 -1
  5. package/dist/build/prerender.js +80 -24
  6. package/dist/build/prerender.js.map +1 -1
  7. package/dist/build/report.d.ts +9 -5
  8. package/dist/build/report.js +17 -7
  9. package/dist/build/report.js.map +1 -1
  10. package/dist/build/route-classification-injector.d.ts +35 -0
  11. package/dist/build/route-classification-injector.js +61 -0
  12. package/dist/build/route-classification-injector.js.map +1 -0
  13. package/dist/build/route-classification-manifest.d.ts +1 -1
  14. package/dist/build/run-prerender.d.ts +5 -0
  15. package/dist/build/run-prerender.js +4 -1
  16. package/dist/build/run-prerender.js.map +1 -1
  17. package/dist/build/server-manifest.js +2 -7
  18. package/dist/build/server-manifest.js.map +1 -1
  19. package/dist/build/standalone.js +3 -5
  20. package/dist/build/standalone.js.map +1 -1
  21. package/dist/build/static-export.d.ts +1 -1
  22. package/dist/check.js +45 -29
  23. package/dist/check.js.map +1 -1
  24. package/dist/cli-args.d.ts +33 -0
  25. package/dist/cli-args.js +121 -0
  26. package/dist/cli-args.js.map +1 -0
  27. package/dist/cli.js +11 -20
  28. package/dist/cli.js.map +1 -1
  29. package/dist/cloudflare/kv-cache-handler.js +29 -9
  30. package/dist/cloudflare/kv-cache-handler.js.map +1 -1
  31. package/dist/config/config-matchers.js +46 -37
  32. package/dist/config/config-matchers.js.map +1 -1
  33. package/dist/config/next-config.d.ts +4 -2
  34. package/dist/config/next-config.js +3 -0
  35. package/dist/config/next-config.js.map +1 -1
  36. package/dist/deploy.d.ts +18 -2
  37. package/dist/deploy.js +47 -4
  38. package/dist/deploy.js.map +1 -1
  39. package/dist/entries/app-rsc-entry.d.ts +4 -3
  40. package/dist/entries/app-rsc-entry.js +379 -858
  41. package/dist/entries/app-rsc-entry.js.map +1 -1
  42. package/dist/entries/app-rsc-manifest.d.ts +1 -1
  43. package/dist/entries/app-rsc-manifest.js +6 -1
  44. package/dist/entries/app-rsc-manifest.js.map +1 -1
  45. package/dist/entries/pages-client-entry.js +3 -2
  46. package/dist/entries/pages-client-entry.js.map +1 -1
  47. package/dist/entries/pages-server-entry.js +19 -61
  48. package/dist/entries/pages-server-entry.js.map +1 -1
  49. package/dist/entries/runtime-entry-module.d.ts +12 -3
  50. package/dist/entries/runtime-entry-module.js +15 -4
  51. package/dist/entries/runtime-entry-module.js.map +1 -1
  52. package/dist/index.js +40 -58
  53. package/dist/index.js.map +1 -1
  54. package/dist/plugins/fonts.js +54 -32
  55. package/dist/plugins/fonts.js.map +1 -1
  56. package/dist/plugins/og-assets.js +15 -16
  57. package/dist/plugins/og-assets.js.map +1 -1
  58. package/dist/plugins/rsc-client-shim-excludes.d.ts +2 -1
  59. package/dist/plugins/rsc-client-shim-excludes.js +11 -1
  60. package/dist/plugins/rsc-client-shim-excludes.js.map +1 -1
  61. package/dist/routing/app-route-graph.d.ts +195 -0
  62. package/dist/routing/app-route-graph.js +1022 -0
  63. package/dist/routing/app-route-graph.js.map +1 -0
  64. package/dist/routing/app-router.d.ts +14 -88
  65. package/dist/routing/app-router.js +21 -712
  66. package/dist/routing/app-router.js.map +1 -1
  67. package/dist/routing/file-matcher.d.ts +3 -1
  68. package/dist/routing/file-matcher.js +6 -1
  69. package/dist/routing/file-matcher.js.map +1 -1
  70. package/dist/routing/pages-router.js +10 -19
  71. package/dist/routing/pages-router.js.map +1 -1
  72. package/dist/routing/route-matching.d.ts +28 -0
  73. package/dist/routing/route-matching.js +44 -0
  74. package/dist/routing/route-matching.js.map +1 -0
  75. package/dist/routing/route-pattern.js +4 -1
  76. package/dist/routing/route-pattern.js.map +1 -1
  77. package/dist/routing/route-trie.d.ts +8 -0
  78. package/dist/routing/route-trie.js +12 -1
  79. package/dist/routing/route-trie.js.map +1 -1
  80. package/dist/routing/route-validation.js +3 -4
  81. package/dist/routing/route-validation.js.map +1 -1
  82. package/dist/routing/utils.d.ts +8 -1
  83. package/dist/routing/utils.js +25 -2
  84. package/dist/routing/utils.js.map +1 -1
  85. package/dist/server/app-browser-entry.js +145 -294
  86. package/dist/server/app-browser-entry.js.map +1 -1
  87. package/dist/server/app-browser-error.d.ts +3 -4
  88. package/dist/server/app-browser-error.js +8 -4
  89. package/dist/server/app-browser-error.js.map +1 -1
  90. package/dist/server/app-browser-navigation-controller.d.ts +75 -0
  91. package/dist/server/app-browser-navigation-controller.js +290 -0
  92. package/dist/server/app-browser-navigation-controller.js.map +1 -0
  93. package/dist/server/app-browser-state.d.ts +33 -15
  94. package/dist/server/app-browser-state.js +52 -59
  95. package/dist/server/app-browser-state.js.map +1 -1
  96. package/dist/server/app-browser-visible-commit.d.ts +68 -0
  97. package/dist/server/app-browser-visible-commit.js +182 -0
  98. package/dist/server/app-browser-visible-commit.js.map +1 -0
  99. package/dist/server/app-client-reference-preloader.d.ts +15 -0
  100. package/dist/server/app-client-reference-preloader.js +46 -0
  101. package/dist/server/app-client-reference-preloader.js.map +1 -0
  102. package/dist/server/app-elements-wire.d.ts +130 -0
  103. package/dist/server/app-elements-wire.js +205 -0
  104. package/dist/server/app-elements-wire.js.map +1 -0
  105. package/dist/server/app-elements.d.ts +2 -84
  106. package/dist/server/app-elements.js +4 -107
  107. package/dist/server/app-elements.js.map +1 -1
  108. package/dist/server/app-fallback-renderer.d.ts +57 -0
  109. package/dist/server/app-fallback-renderer.js +79 -0
  110. package/dist/server/app-fallback-renderer.js.map +1 -0
  111. package/dist/server/app-hook-warning-suppression.d.ts +7 -0
  112. package/dist/server/app-hook-warning-suppression.js +12 -0
  113. package/dist/server/app-hook-warning-suppression.js.map +1 -0
  114. package/dist/server/app-middleware.d.ts +2 -1
  115. package/dist/server/app-middleware.js +34 -11
  116. package/dist/server/app-middleware.js.map +1 -1
  117. package/dist/server/app-mounted-slots-header.d.ts +17 -0
  118. package/dist/server/app-mounted-slots-header.js +21 -0
  119. package/dist/server/app-mounted-slots-header.js.map +1 -0
  120. package/dist/server/app-page-boundary-render.d.ts +3 -3
  121. package/dist/server/app-page-boundary-render.js +8 -5
  122. package/dist/server/app-page-boundary-render.js.map +1 -1
  123. package/dist/server/app-page-boundary.js +2 -1
  124. package/dist/server/app-page-boundary.js.map +1 -1
  125. package/dist/server/app-page-cache.d.ts +19 -4
  126. package/dist/server/app-page-cache.js +60 -22
  127. package/dist/server/app-page-cache.js.map +1 -1
  128. package/dist/server/app-page-dispatch.d.ts +9 -5
  129. package/dist/server/app-page-dispatch.js +41 -17
  130. package/dist/server/app-page-dispatch.js.map +1 -1
  131. package/dist/server/app-page-element-builder.d.ts +61 -0
  132. package/dist/server/app-page-element-builder.js +142 -0
  133. package/dist/server/app-page-element-builder.js.map +1 -0
  134. package/dist/server/app-page-execution.d.ts +23 -5
  135. package/dist/server/app-page-execution.js +39 -24
  136. package/dist/server/app-page-execution.js.map +1 -1
  137. package/dist/server/app-page-head.js +2 -1
  138. package/dist/server/app-page-head.js.map +1 -1
  139. package/dist/server/app-page-method.js +2 -5
  140. package/dist/server/app-page-method.js.map +1 -1
  141. package/dist/server/app-page-params.d.ts +2 -1
  142. package/dist/server/app-page-params.js +3 -3
  143. package/dist/server/app-page-params.js.map +1 -1
  144. package/dist/server/app-page-probe.d.ts +1 -1
  145. package/dist/server/app-page-probe.js +5 -1
  146. package/dist/server/app-page-probe.js.map +1 -1
  147. package/dist/server/app-page-render.d.ts +6 -2
  148. package/dist/server/app-page-render.js +118 -30
  149. package/dist/server/app-page-render.js.map +1 -1
  150. package/dist/server/app-page-request.d.ts +19 -5
  151. package/dist/server/app-page-request.js +49 -7
  152. package/dist/server/app-page-request.js.map +1 -1
  153. package/dist/server/app-page-response.d.ts +1 -0
  154. package/dist/server/app-page-response.js +6 -9
  155. package/dist/server/app-page-response.js.map +1 -1
  156. package/dist/server/app-page-route-wiring.d.ts +20 -4
  157. package/dist/server/app-page-route-wiring.js +15 -12
  158. package/dist/server/app-page-route-wiring.js.map +1 -1
  159. package/dist/server/app-page-stream.d.ts +7 -0
  160. package/dist/server/app-page-stream.js +9 -2
  161. package/dist/server/app-page-stream.js.map +1 -1
  162. package/dist/server/app-post-middleware-context.d.ts +16 -0
  163. package/dist/server/app-post-middleware-context.js +28 -0
  164. package/dist/server/app-post-middleware-context.js.map +1 -0
  165. package/dist/server/app-prerender-endpoints.js +3 -2
  166. package/dist/server/app-prerender-endpoints.js.map +1 -1
  167. package/dist/server/app-request-context.d.ts +22 -0
  168. package/dist/server/app-request-context.js +30 -0
  169. package/dist/server/app-request-context.js.map +1 -0
  170. package/dist/server/app-route-handler-cache.d.ts +1 -0
  171. package/dist/server/app-route-handler-cache.js +7 -2
  172. package/dist/server/app-route-handler-cache.js.map +1 -1
  173. package/dist/server/app-route-handler-dispatch.d.ts +1 -0
  174. package/dist/server/app-route-handler-dispatch.js +8 -5
  175. package/dist/server/app-route-handler-dispatch.js.map +1 -1
  176. package/dist/server/app-route-handler-execution.d.ts +2 -1
  177. package/dist/server/app-route-handler-execution.js +2 -2
  178. package/dist/server/app-route-handler-execution.js.map +1 -1
  179. package/dist/server/app-route-handler-policy.js +13 -13
  180. package/dist/server/app-route-handler-policy.js.map +1 -1
  181. package/dist/server/app-route-handler-response.d.ts +4 -2
  182. package/dist/server/app-route-handler-response.js +9 -7
  183. package/dist/server/app-route-handler-response.js.map +1 -1
  184. package/dist/server/app-route-handler-runtime.d.ts +9 -1
  185. package/dist/server/app-route-handler-runtime.js +11 -1
  186. package/dist/server/app-route-handler-runtime.js.map +1 -1
  187. package/dist/server/app-router-entry.js +9 -4
  188. package/dist/server/app-router-entry.js.map +1 -1
  189. package/dist/server/app-rsc-cache-busting.d.ts +34 -0
  190. package/dist/server/app-rsc-cache-busting.js +137 -0
  191. package/dist/server/app-rsc-cache-busting.js.map +1 -0
  192. package/dist/server/app-rsc-error-handler.d.ts +21 -0
  193. package/dist/server/app-rsc-error-handler.js +30 -0
  194. package/dist/server/app-rsc-error-handler.js.map +1 -0
  195. package/dist/server/app-rsc-handler.d.ts +117 -0
  196. package/dist/server/app-rsc-handler.js +271 -0
  197. package/dist/server/app-rsc-handler.js.map +1 -0
  198. package/dist/server/app-rsc-request-normalization.d.ts +42 -0
  199. package/dist/server/app-rsc-request-normalization.js +67 -0
  200. package/dist/server/app-rsc-request-normalization.js.map +1 -0
  201. package/dist/server/app-rsc-response-finalizer.d.ts +30 -0
  202. package/dist/server/app-rsc-response-finalizer.js +38 -0
  203. package/dist/server/app-rsc-response-finalizer.js.map +1 -0
  204. package/dist/server/app-rsc-route-matching.js +8 -4
  205. package/dist/server/app-rsc-route-matching.js.map +1 -1
  206. package/dist/server/app-segment-config.d.ts +33 -0
  207. package/dist/server/app-segment-config.js +90 -0
  208. package/dist/server/app-segment-config.js.map +1 -0
  209. package/dist/server/app-server-action-execution.d.ts +2 -0
  210. package/dist/server/app-server-action-execution.js +45 -51
  211. package/dist/server/app-server-action-execution.js.map +1 -1
  212. package/dist/server/app-ssr-entry.js +21 -20
  213. package/dist/server/app-ssr-entry.js.map +1 -1
  214. package/dist/server/artifact-compatibility.d.ts +44 -0
  215. package/dist/server/artifact-compatibility.js +82 -0
  216. package/dist/server/artifact-compatibility.js.map +1 -0
  217. package/dist/server/cache-control.d.ts +24 -0
  218. package/dist/server/cache-control.js +33 -0
  219. package/dist/server/cache-control.js.map +1 -0
  220. package/dist/server/cache-proof.d.ts +200 -0
  221. package/dist/server/cache-proof.js +342 -0
  222. package/dist/server/cache-proof.js.map +1 -0
  223. package/dist/server/dev-error-overlay-store.d.ts +23 -0
  224. package/dist/server/dev-error-overlay-store.js +67 -0
  225. package/dist/server/dev-error-overlay-store.js.map +1 -0
  226. package/dist/server/dev-error-overlay.d.ts +15 -0
  227. package/dist/server/dev-error-overlay.js +548 -0
  228. package/dist/server/dev-error-overlay.js.map +1 -0
  229. package/dist/server/dev-origin-check.js +8 -4
  230. package/dist/server/dev-origin-check.js.map +1 -1
  231. package/dist/server/dev-server.js +1 -6
  232. package/dist/server/dev-server.js.map +1 -1
  233. package/dist/server/http-error-responses.d.ts +67 -0
  234. package/dist/server/http-error-responses.js +77 -0
  235. package/dist/server/http-error-responses.js.map +1 -0
  236. package/dist/server/image-optimization.js +2 -1
  237. package/dist/server/image-optimization.js.map +1 -1
  238. package/dist/server/instrumentation-runtime.d.ts +44 -0
  239. package/dist/server/instrumentation-runtime.js +29 -0
  240. package/dist/server/instrumentation-runtime.js.map +1 -0
  241. package/dist/server/isr-cache.d.ts +2 -7
  242. package/dist/server/isr-cache.js +7 -10
  243. package/dist/server/isr-cache.js.map +1 -1
  244. package/dist/server/metadata-route-response.js +6 -5
  245. package/dist/server/metadata-route-response.js.map +1 -1
  246. package/dist/server/metadata-routes.d.ts +1 -0
  247. package/dist/server/metadata-routes.js +6 -0
  248. package/dist/server/metadata-routes.js.map +1 -1
  249. package/dist/server/middleware-matcher.js +2 -2
  250. package/dist/server/middleware-matcher.js.map +1 -1
  251. package/dist/server/middleware-response-headers.js +21 -0
  252. package/dist/server/middleware-response-headers.js.map +1 -1
  253. package/dist/server/middleware-runtime.js +3 -3
  254. package/dist/server/middleware-runtime.js.map +1 -1
  255. package/dist/server/navigation-trace.d.ts +33 -0
  256. package/dist/server/navigation-trace.js +35 -0
  257. package/dist/server/navigation-trace.js.map +1 -0
  258. package/dist/server/next-error-digest.d.ts +44 -0
  259. package/dist/server/next-error-digest.js +40 -0
  260. package/dist/server/next-error-digest.js.map +1 -0
  261. package/dist/server/pages-api-route.js +2 -1
  262. package/dist/server/pages-api-route.js.map +1 -1
  263. package/dist/server/pages-node-compat.js +4 -16
  264. package/dist/server/pages-node-compat.js.map +1 -1
  265. package/dist/server/pages-page-data.d.ts +2 -1
  266. package/dist/server/pages-page-data.js +6 -5
  267. package/dist/server/pages-page-data.js.map +1 -1
  268. package/dist/server/pages-page-response.d.ts +3 -8
  269. package/dist/server/pages-page-response.js +46 -15
  270. package/dist/server/pages-page-response.js.map +1 -1
  271. package/dist/server/prod-server.d.ts +6 -0
  272. package/dist/server/prod-server.js +28 -21
  273. package/dist/server/prod-server.js.map +1 -1
  274. package/dist/server/request-pipeline.d.ts +42 -1
  275. package/dist/server/request-pipeline.js +97 -17
  276. package/dist/server/request-pipeline.js.map +1 -1
  277. package/dist/server/rsc-stream-hints.d.ts +3 -1
  278. package/dist/server/rsc-stream-hints.js +4 -1
  279. package/dist/server/rsc-stream-hints.js.map +1 -1
  280. package/dist/server/seed-cache.js +19 -8
  281. package/dist/server/seed-cache.js.map +1 -1
  282. package/dist/shims/cache-runtime.d.ts +2 -2
  283. package/dist/shims/cache-runtime.js +31 -17
  284. package/dist/shims/cache-runtime.js.map +1 -1
  285. package/dist/shims/cache.d.ts +15 -3
  286. package/dist/shims/cache.js +45 -20
  287. package/dist/shims/cache.js.map +1 -1
  288. package/dist/shims/error-boundary.d.ts +17 -1
  289. package/dist/shims/error-boundary.js +31 -1
  290. package/dist/shims/error-boundary.js.map +1 -1
  291. package/dist/shims/fetch-cache.d.ts +4 -1
  292. package/dist/shims/fetch-cache.js +57 -16
  293. package/dist/shims/fetch-cache.js.map +1 -1
  294. package/dist/shims/head-state.js +2 -3
  295. package/dist/shims/head-state.js.map +1 -1
  296. package/dist/shims/headers.js +4 -44
  297. package/dist/shims/headers.js.map +1 -1
  298. package/dist/shims/i18n-state.js +2 -3
  299. package/dist/shims/i18n-state.js.map +1 -1
  300. package/dist/shims/image.js +93 -5
  301. package/dist/shims/image.js.map +1 -1
  302. package/dist/shims/internal/als-registry.d.ts +15 -0
  303. package/dist/shims/internal/als-registry.js +55 -0
  304. package/dist/shims/internal/als-registry.js.map +1 -0
  305. package/dist/shims/internal/cookie-serialize.d.ts +46 -0
  306. package/dist/shims/internal/cookie-serialize.js +51 -0
  307. package/dist/shims/internal/cookie-serialize.js.map +1 -0
  308. package/dist/shims/link.js +31 -26
  309. package/dist/shims/link.js.map +1 -1
  310. package/dist/shims/metadata.d.ts +26 -1
  311. package/dist/shims/metadata.js +94 -4
  312. package/dist/shims/metadata.js.map +1 -1
  313. package/dist/shims/navigation-state.js +2 -3
  314. package/dist/shims/navigation-state.js.map +1 -1
  315. package/dist/shims/navigation.d.ts +2 -7
  316. package/dist/shims/navigation.js +44 -36
  317. package/dist/shims/navigation.js.map +1 -1
  318. package/dist/shims/request-context.js +2 -4
  319. package/dist/shims/request-context.js.map +1 -1
  320. package/dist/shims/request-state-types.d.ts +1 -1
  321. package/dist/shims/router-state.js +2 -3
  322. package/dist/shims/router-state.js.map +1 -1
  323. package/dist/shims/router.js +2 -2
  324. package/dist/shims/router.js.map +1 -1
  325. package/dist/shims/server.js +5 -30
  326. package/dist/shims/server.js.map +1 -1
  327. package/dist/shims/slot.d.ts +1 -1
  328. package/dist/shims/slot.js +5 -4
  329. package/dist/shims/slot.js.map +1 -1
  330. package/dist/shims/thenable-params.d.ts +5 -2
  331. package/dist/shims/thenable-params.js +26 -6
  332. package/dist/shims/thenable-params.js.map +1 -1
  333. package/dist/shims/unified-request-context.d.ts +1 -1
  334. package/dist/shims/unified-request-context.js +3 -14
  335. package/dist/shims/unified-request-context.js.map +1 -1
  336. package/dist/shims/use-merged-ref.d.ts +7 -0
  337. package/dist/shims/use-merged-ref.js +40 -0
  338. package/dist/shims/use-merged-ref.js.map +1 -0
  339. package/dist/utils/base-path.d.ts +7 -1
  340. package/dist/utils/base-path.js +12 -1
  341. package/dist/utils/base-path.js.map +1 -1
  342. package/dist/utils/cache-control-metadata.d.ts +6 -0
  343. package/dist/utils/cache-control-metadata.js +16 -0
  344. package/dist/utils/cache-control-metadata.js.map +1 -0
  345. package/dist/utils/safe-json-file.d.ts +18 -0
  346. package/dist/utils/safe-json-file.js +25 -0
  347. package/dist/utils/safe-json-file.js.map +1 -0
  348. package/dist/utils/text-stream.d.ts +29 -0
  349. package/dist/utils/text-stream.js +66 -0
  350. package/dist/utils/text-stream.js.map +1 -0
  351. package/package.json +5 -5
@@ -0,0 +1,1022 @@
1
+ import { compareRoutes, decodeRouteSegment } from "./utils.js";
2
+ import { scanWithExtensions } from "./file-matcher.js";
3
+ import { validateRoutePatterns } from "./route-validation.js";
4
+ import fs from "node:fs";
5
+ import path from "node:path";
6
+ import { createHash } from "node:crypto";
7
+ //#region src/routing/app-route-graph.ts
8
+ /**
9
+ * App Router route graph construction.
10
+ *
11
+ * Scans app/ directories and materializes route metadata before the request-time
12
+ * matcher consumes it. Keep request matching and cache ownership in app-router.ts.
13
+ */
14
+ function createAppRouteGraphRouteId(pattern) {
15
+ return `route:${pattern}`;
16
+ }
17
+ function createAppRouteGraphPageId(pattern) {
18
+ return `page:${pattern}`;
19
+ }
20
+ function createAppRouteGraphRouteHandlerId(pattern) {
21
+ return `route-handler:${pattern}`;
22
+ }
23
+ function createAppRouteGraphLayoutId(treePath) {
24
+ return `layout:${treePath}`;
25
+ }
26
+ function createAppRouteGraphTemplateId(treePath) {
27
+ return `template:${treePath}`;
28
+ }
29
+ function createAppRouteGraphSlotId(slotName, ownerTreePath) {
30
+ return `slot:${slotName}:${ownerTreePath}`;
31
+ }
32
+ function createAppRouteGraphRootBoundaryId(treePath) {
33
+ return `root-boundary:${treePath}`;
34
+ }
35
+ function compareStableStrings(left, right) {
36
+ if (left < right) return -1;
37
+ if (left > right) return 1;
38
+ return 0;
39
+ }
40
+ function sortedMapValues(map) {
41
+ return Array.from(map.entries()).sort(([left], [right]) => compareStableStrings(left, right)).map(([, value]) => value);
42
+ }
43
+ function createRouteManifest(routes) {
44
+ const segmentGraph = createStaticSegmentGraph(routes);
45
+ return {
46
+ graphVersion: createRouteManifestGraphVersion(segmentGraph),
47
+ segmentGraph
48
+ };
49
+ }
50
+ function createStaticSegmentGraph(routes) {
51
+ const routeEntries = /* @__PURE__ */ new Map();
52
+ const pages = /* @__PURE__ */ new Map();
53
+ const routeHandlers = /* @__PURE__ */ new Map();
54
+ const layouts = /* @__PURE__ */ new Map();
55
+ const templates = /* @__PURE__ */ new Map();
56
+ const slots = /* @__PURE__ */ new Map();
57
+ const rootBoundaries = /* @__PURE__ */ new Map();
58
+ for (const route of routes) {
59
+ routeEntries.set(route.ids.route, {
60
+ id: route.ids.route,
61
+ pattern: route.pattern,
62
+ patternParts: [...route.patternParts],
63
+ isDynamic: route.isDynamic,
64
+ paramNames: [...route.params],
65
+ rootParamNames: [...route.rootParamNames],
66
+ rootBoundaryId: route.ids.rootBoundary,
67
+ pageId: route.ids.page,
68
+ routeHandlerId: route.ids.routeHandler,
69
+ layoutIds: [...route.ids.layouts],
70
+ templateIds: [...route.ids.templates],
71
+ slotIds: route.parallelSlots.map((slot) => slot.id).sort(compareStableStrings)
72
+ });
73
+ if (route.ids.page) pages.set(route.ids.page, {
74
+ id: route.ids.page,
75
+ routeId: route.ids.route,
76
+ pattern: route.pattern
77
+ });
78
+ if (route.ids.routeHandler) routeHandlers.set(route.ids.routeHandler, {
79
+ id: route.ids.routeHandler,
80
+ routeId: route.ids.route,
81
+ pattern: route.pattern
82
+ });
83
+ for (const [index, layoutId] of route.ids.layouts.entries()) {
84
+ const treePosition = route.layoutTreePositions[index];
85
+ assertRouteManifestTreePosition("layout", route, layoutId, treePosition);
86
+ const treePath = createAppRouteGraphTreePath(route.routeSegments, treePosition);
87
+ const existingLayout = layouts.get(layoutId);
88
+ if (existingLayout) assertRouteManifestRootBoundary("layout", route, layoutId, existingLayout.rootBoundaryId);
89
+ layouts.set(layoutId, {
90
+ id: layoutId,
91
+ treePath,
92
+ rootBoundaryId: route.ids.rootBoundary
93
+ });
94
+ if (index === 0 && route.ids.rootBoundary) rootBoundaries.set(route.ids.rootBoundary, {
95
+ id: route.ids.rootBoundary,
96
+ layoutId,
97
+ treePath
98
+ });
99
+ }
100
+ for (const [index, templateId] of route.ids.templates.entries()) {
101
+ const treePosition = route.templateTreePositions?.[index];
102
+ assertRouteManifestTreePosition("template", route, templateId, treePosition);
103
+ const existingTemplate = templates.get(templateId);
104
+ if (existingTemplate) assertRouteManifestRootBoundary("template", route, templateId, existingTemplate.rootBoundaryId);
105
+ templates.set(templateId, {
106
+ id: templateId,
107
+ treePath: createAppRouteGraphTreePath(route.routeSegments, treePosition),
108
+ rootBoundaryId: route.ids.rootBoundary
109
+ });
110
+ }
111
+ for (const slot of route.parallelSlots) slots.set(slot.id, {
112
+ id: slot.id,
113
+ key: slot.key,
114
+ name: slot.name
115
+ });
116
+ }
117
+ return {
118
+ routes: routeEntries,
119
+ pages,
120
+ routeHandlers,
121
+ layouts,
122
+ templates,
123
+ slots,
124
+ rootBoundaries
125
+ };
126
+ }
127
+ function assertRouteManifestTreePosition(kind, route, id, treePosition) {
128
+ if (treePosition !== void 0) return;
129
+ throw new Error(`[vinext] App route graph invariant violated: missing ${kind} tree position for ${id} on ${route.pattern}`);
130
+ }
131
+ function assertRouteManifestRootBoundary(kind, route, id, existingRootBoundaryId) {
132
+ if (existingRootBoundaryId === route.ids.rootBoundary) return;
133
+ throw new Error(`[vinext] App route graph invariant violated: ${kind} ${id} is shared across root boundaries (${existingRootBoundaryId ?? "none"} and ${route.ids.rootBoundary ?? "none"}) on ${route.pattern}`);
134
+ }
135
+ function createRouteManifestGraphVersion(segmentGraph) {
136
+ const stableShape = {
137
+ routes: sortedMapValues(segmentGraph.routes),
138
+ pages: sortedMapValues(segmentGraph.pages),
139
+ routeHandlers: sortedMapValues(segmentGraph.routeHandlers),
140
+ layouts: sortedMapValues(segmentGraph.layouts),
141
+ templates: sortedMapValues(segmentGraph.templates),
142
+ slots: sortedMapValues(segmentGraph.slots),
143
+ rootBoundaries: sortedMapValues(segmentGraph.rootBoundaries)
144
+ };
145
+ return `graph:${createHash("sha256").update(JSON.stringify(stableShape)).digest("hex")}`;
146
+ }
147
+ async function buildAppRouteGraph(appDir, matcher) {
148
+ const routes = [];
149
+ const excludeDir = (name) => name.startsWith("@") || name.startsWith("_");
150
+ for await (const file of scanWithExtensions("**/page", appDir, matcher.extensions, excludeDir)) {
151
+ const route = fileToAppRoute(file, appDir, "page", matcher);
152
+ if (route) routes.push(route);
153
+ }
154
+ for await (const file of scanWithExtensions("**/route", appDir, matcher.extensions, excludeDir)) {
155
+ const route = fileToAppRoute(file, appDir, "route", matcher);
156
+ if (route) routes.push(route);
157
+ }
158
+ const routePatterns = new Set(routes.map((route) => route.pattern));
159
+ for await (const file of scanWithExtensions("**/layout", appDir, matcher.extensions, excludeDir)) {
160
+ const dir = path.dirname(file);
161
+ const routeDir = dir === "." ? appDir : path.join(appDir, dir);
162
+ if (!hasParallelSlotDirectory(routeDir)) continue;
163
+ if (discoverParallelSlots(routeDir, appDir, matcher).length === 0) continue;
164
+ const route = directoryToAppRoute(dir, appDir, matcher, null, null);
165
+ if (!route || routePatterns.has(route.pattern)) continue;
166
+ routes.push(route);
167
+ routePatterns.add(route.pattern);
168
+ }
169
+ const slotSubRoutes = discoverSlotSubRoutes(routes, matcher);
170
+ routes.push(...slotSubRoutes);
171
+ validatePageRouteConflicts(routes, appDir);
172
+ validateRoutePatterns(routes.map((route) => route.pattern));
173
+ validateRoutePatterns([...new Set(routes.flatMap((route) => route.parallelSlots.flatMap((slot) => slot.interceptingRoutes.map((intercept) => intercept.targetPattern))))]);
174
+ routes.sort(compareRoutes);
175
+ return {
176
+ routes,
177
+ routeManifest: createRouteManifest(routes)
178
+ };
179
+ }
180
+ function hasParallelSlotDirectory(dir) {
181
+ try {
182
+ return fs.readdirSync(dir, { withFileTypes: true }).some((entry) => entry.isDirectory() && entry.name.startsWith("@"));
183
+ } catch {
184
+ return false;
185
+ }
186
+ }
187
+ function validatePageRouteConflicts(routes, appDir) {
188
+ const byPattern = /* @__PURE__ */ new Map();
189
+ for (const route of routes) {
190
+ const entry = byPattern.get(route.pattern);
191
+ if (!entry) {
192
+ byPattern.set(route.pattern, {
193
+ pagePath: route.pagePath,
194
+ routePath: route.routePath
195
+ });
196
+ continue;
197
+ }
198
+ if (!entry.pagePath && route.pagePath) entry.pagePath = route.pagePath;
199
+ if (!entry.routePath && route.routePath) entry.routePath = route.routePath;
200
+ }
201
+ for (const [pattern, entry] of byPattern) {
202
+ if (!entry.pagePath || !entry.routePath) continue;
203
+ throw new Error(`Conflicting route and page at ${pattern}: route at ${formatAppFilePath(entry.routePath, appDir)} and page at ${formatAppFilePath(entry.pagePath, appDir)}`);
204
+ }
205
+ }
206
+ function formatAppFilePath(filePath, appDir) {
207
+ const relativePath = path.relative(appDir, filePath).replace(/\\/g, "/");
208
+ const parsedPath = path.parse(relativePath);
209
+ const withoutExtension = path.join(parsedPath.dir, parsedPath.name).replace(/\\/g, "/");
210
+ return withoutExtension.startsWith("/") ? withoutExtension : `/${withoutExtension}`;
211
+ }
212
+ /**
213
+ * Discover sub-routes created by nested pages within parallel slots.
214
+ *
215
+ * In Next.js, pages nested inside @slot directories create additional URL routes.
216
+ * For example, given:
217
+ * app/parallel-routes/@audience/demographics/page.tsx
218
+ * This creates a route at /parallel-routes/demographics where:
219
+ * - children slot → parent's default.tsx
220
+ * - @audience slot → @audience/demographics/page.tsx (matched)
221
+ * - other slots → their default.tsx (fallback)
222
+ */
223
+ function discoverSlotSubRoutes(routes, matcher) {
224
+ const syntheticRoutes = [];
225
+ const routesByPattern = new Map(routes.map((r) => [r.pattern, r]));
226
+ const applySlotSubPages = (route, slotPages, rawSegments) => {
227
+ route.parallelSlots = route.parallelSlots.map((slot) => {
228
+ const subPage = slotPages.get(slot.key);
229
+ if (subPage !== void 0) return {
230
+ ...slot,
231
+ pagePath: subPage,
232
+ routeSegments: rawSegments
233
+ };
234
+ return slot;
235
+ });
236
+ };
237
+ for (const parentRoute of routes) {
238
+ if (parentRoute.parallelSlots.length === 0) continue;
239
+ const isLayoutOnlyUiRoute = !parentRoute.pagePath && !parentRoute.routePath && parentRoute.layouts.length > 0;
240
+ if (!parentRoute.pagePath && !isLayoutOnlyUiRoute) continue;
241
+ const parentPageDir = parentRoute.pagePath ? path.dirname(parentRoute.pagePath) : path.dirname(parentRoute.layouts[parentRoute.layouts.length - 1]);
242
+ const subPathMap = /* @__PURE__ */ new Map();
243
+ for (const slot of parentRoute.parallelSlots) {
244
+ if (path.dirname(slot.ownerDir) !== parentPageDir) continue;
245
+ const slotDir = slot.ownerDir;
246
+ if (!fs.existsSync(slotDir)) continue;
247
+ const subPages = findSlotSubPages(slotDir, matcher);
248
+ for (const { relativePath, pagePath } of subPages) {
249
+ const subSegments = relativePath.split(path.sep);
250
+ const convertedSubRoute = convertSegmentsToRouteParts(subSegments);
251
+ if (!convertedSubRoute) continue;
252
+ const { urlSegments } = convertedSubRoute;
253
+ const normalizedSubPath = urlSegments.join("/");
254
+ let subPathEntry = subPathMap.get(normalizedSubPath);
255
+ if (!subPathEntry) {
256
+ subPathEntry = {
257
+ rawSegments: subSegments,
258
+ converted: convertedSubRoute,
259
+ slotPages: /* @__PURE__ */ new Map()
260
+ };
261
+ subPathMap.set(normalizedSubPath, subPathEntry);
262
+ }
263
+ if (subPathEntry.slotPages.get(slot.key)) {
264
+ const pattern = joinRoutePattern(parentRoute.pattern, normalizedSubPath);
265
+ throw new Error(`You cannot have two routes that resolve to the same path ("${pattern}").`);
266
+ }
267
+ subPathEntry.slotPages.set(slot.key, pagePath);
268
+ }
269
+ }
270
+ if (subPathMap.size === 0) continue;
271
+ const childrenDefault = findFile(parentPageDir, "default", matcher);
272
+ if (parentRoute.pagePath && !childrenDefault) continue;
273
+ for (const { rawSegments, converted: convertedSubRoute, slotPages } of subPathMap.values()) {
274
+ const { urlSegments: urlParts, params: subParams, isDynamic: subIsDynamic } = convertedSubRoute;
275
+ const subUrlPath = urlParts.join("/");
276
+ const pattern = joinRoutePattern(parentRoute.pattern, subUrlPath);
277
+ const existingRoute = routesByPattern.get(pattern);
278
+ if (existingRoute) {
279
+ if (existingRoute.routePath && !existingRoute.pagePath) throw new Error(`You cannot have two routes that resolve to the same path ("${pattern}").`);
280
+ applySlotSubPages(existingRoute, slotPages, rawSegments);
281
+ continue;
282
+ }
283
+ const syntheticParts = [...parentRoute.patternParts, ...urlParts];
284
+ if (Array.from(routesByPattern.values()).some((r) => patternsStructurallyEquivalent(r.patternParts, syntheticParts))) continue;
285
+ const subSlots = parentRoute.parallelSlots.map((slot) => {
286
+ const subPage = slotPages.get(slot.key);
287
+ return {
288
+ ...slot,
289
+ pagePath: subPage || null,
290
+ routeSegments: subPage ? rawSegments : null
291
+ };
292
+ });
293
+ const newRoute = {
294
+ ids: createAppRouteSemanticIds({
295
+ pattern,
296
+ pagePath: childrenDefault,
297
+ routePath: null,
298
+ routeSegments: [...parentRoute.routeSegments, ...rawSegments],
299
+ layoutTreePositions: parentRoute.layoutTreePositions,
300
+ templateTreePositions: parentRoute.templateTreePositions,
301
+ slots: subSlots
302
+ }),
303
+ pattern,
304
+ pagePath: childrenDefault,
305
+ routePath: null,
306
+ layouts: parentRoute.layouts,
307
+ templates: parentRoute.templates,
308
+ parallelSlots: subSlots,
309
+ loadingPath: parentRoute.loadingPath,
310
+ errorPath: parentRoute.errorPath,
311
+ layoutErrorPaths: parentRoute.layoutErrorPaths,
312
+ notFoundPath: parentRoute.notFoundPath,
313
+ notFoundPaths: parentRoute.notFoundPaths,
314
+ forbiddenPaths: parentRoute.forbiddenPaths,
315
+ forbiddenPath: parentRoute.forbiddenPath,
316
+ unauthorizedPath: parentRoute.unauthorizedPath,
317
+ unauthorizedPaths: parentRoute.unauthorizedPaths,
318
+ routeSegments: [...parentRoute.routeSegments, ...rawSegments],
319
+ templateTreePositions: parentRoute.templateTreePositions,
320
+ layoutTreePositions: parentRoute.layoutTreePositions,
321
+ isDynamic: parentRoute.isDynamic || subIsDynamic,
322
+ params: [...parentRoute.params, ...subParams],
323
+ rootParamNames: parentRoute.rootParamNames,
324
+ patternParts: [...parentRoute.patternParts, ...urlParts]
325
+ };
326
+ syntheticRoutes.push(newRoute);
327
+ routesByPattern.set(pattern, newRoute);
328
+ }
329
+ }
330
+ return syntheticRoutes;
331
+ }
332
+ const findSlotSubPagesCache = /* @__PURE__ */ new WeakMap();
333
+ function findSlotSubPages(slotDir, matcher) {
334
+ let perMatcher = findSlotSubPagesCache.get(matcher);
335
+ if (!perMatcher) {
336
+ perMatcher = /* @__PURE__ */ new Map();
337
+ findSlotSubPagesCache.set(matcher, perMatcher);
338
+ }
339
+ const cached = perMatcher.get(slotDir);
340
+ if (cached) return cached;
341
+ const results = [];
342
+ function scan(dir) {
343
+ if (!fs.existsSync(dir)) return;
344
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
345
+ for (const entry of entries) {
346
+ if (!entry.isDirectory()) continue;
347
+ if (matchInterceptConvention(entry.name)) continue;
348
+ if (entry.name.startsWith("_")) continue;
349
+ const subDir = path.join(dir, entry.name);
350
+ const page = findFile(subDir, "page", matcher);
351
+ if (page) {
352
+ const relativePath = path.relative(slotDir, subDir);
353
+ results.push({
354
+ relativePath,
355
+ pagePath: page
356
+ });
357
+ }
358
+ scan(subDir);
359
+ }
360
+ }
361
+ scan(slotDir);
362
+ perMatcher.set(slotDir, results);
363
+ return results;
364
+ }
365
+ /**
366
+ * Convert a file path relative to app/ into an AppRoute.
367
+ */
368
+ function fileToAppRoute(file, appDir, type, matcher) {
369
+ return directoryToAppRoute(path.dirname(file), appDir, matcher, type === "page" ? path.join(appDir, file) : null, type === "route" ? path.join(appDir, file) : null);
370
+ }
371
+ function directoryToAppRoute(dir, appDir, matcher, pagePath, routePath) {
372
+ const segments = dir === "." ? [] : dir.split(path.sep);
373
+ const params = [];
374
+ let isDynamic = false;
375
+ const convertedRoute = convertSegmentsToRouteParts(segments);
376
+ if (!convertedRoute) return null;
377
+ const { urlSegments, params: routeParams, isDynamic: routeIsDynamic } = convertedRoute;
378
+ params.push(...routeParams);
379
+ isDynamic = routeIsDynamic;
380
+ const pattern = "/" + urlSegments.join("/");
381
+ const layouts = discoverLayouts(segments, appDir, matcher);
382
+ const templates = discoverTemplates(segments, appDir, matcher);
383
+ const templateTreePositions = computeLayoutTreePositions(appDir, templates);
384
+ const layoutTreePositions = computeLayoutTreePositions(appDir, layouts);
385
+ const layoutErrorPaths = discoverLayoutAlignedErrors(segments, appDir, matcher);
386
+ const routeDir = dir === "." ? appDir : path.join(appDir, dir);
387
+ const loadingPath = findFile(routeDir, "loading", matcher);
388
+ const errorPath = findFile(routeDir, "error", matcher);
389
+ const notFoundPath = discoverBoundaryFile(segments, appDir, "not-found", matcher);
390
+ const forbiddenPath = discoverBoundaryFile(segments, appDir, "forbidden", matcher);
391
+ const unauthorizedPath = discoverBoundaryFile(segments, appDir, "unauthorized", matcher);
392
+ const notFoundPaths = discoverBoundaryFilePerLayout(layouts, "not-found", matcher);
393
+ const forbiddenPaths = discoverBoundaryFilePerLayout(layouts, "forbidden", matcher);
394
+ const unauthorizedPaths = discoverBoundaryFilePerLayout(layouts, "unauthorized", matcher);
395
+ const parallelSlots = discoverInheritedParallelSlots(segments, appDir, routeDir, matcher);
396
+ return {
397
+ ids: createAppRouteSemanticIds({
398
+ pattern: pattern === "/" ? "/" : pattern,
399
+ pagePath,
400
+ routePath,
401
+ routeSegments: segments,
402
+ layoutTreePositions,
403
+ templateTreePositions,
404
+ slots: parallelSlots
405
+ }),
406
+ pattern: pattern === "/" ? "/" : pattern,
407
+ pagePath,
408
+ routePath,
409
+ layouts,
410
+ templates,
411
+ parallelSlots,
412
+ loadingPath,
413
+ errorPath,
414
+ layoutErrorPaths,
415
+ notFoundPath,
416
+ notFoundPaths,
417
+ forbiddenPaths,
418
+ forbiddenPath,
419
+ unauthorizedPath,
420
+ unauthorizedPaths,
421
+ routeSegments: segments,
422
+ templateTreePositions,
423
+ layoutTreePositions,
424
+ isDynamic,
425
+ params,
426
+ rootParamNames: computeRootParamNames(segments, layoutTreePositions),
427
+ patternParts: urlSegments
428
+ };
429
+ }
430
+ function dynamicParamNameFromSegment(segment) {
431
+ if (segment.startsWith("[[...") && segment.endsWith("]]")) return segment.slice(5, -2);
432
+ if (segment.startsWith("[...") && segment.endsWith("]")) return segment.slice(4, -1);
433
+ if (segment.startsWith("[") && segment.endsWith("]")) return segment.slice(1, -1);
434
+ return null;
435
+ }
436
+ function computeRootParamNames(routeSegments, layoutTreePositions) {
437
+ const rootLayoutPosition = layoutTreePositions[0];
438
+ if (rootLayoutPosition == null || rootLayoutPosition <= 0) return [];
439
+ const names = [];
440
+ for (const segment of routeSegments.slice(0, rootLayoutPosition)) {
441
+ const name = dynamicParamNameFromSegment(segment);
442
+ if (name && !names.includes(name)) names.push(name);
443
+ }
444
+ return names;
445
+ }
446
+ function resolveRootBoundaryId(routeSegments, layoutTreePositions) {
447
+ const rootLayoutPosition = layoutTreePositions[0];
448
+ if (rootLayoutPosition === void 0) return null;
449
+ return createAppRouteGraphRootBoundaryId(createAppRouteGraphTreePath(routeSegments, rootLayoutPosition));
450
+ }
451
+ function createAppRouteSemanticIds(input) {
452
+ const slots = {};
453
+ for (const slot of input.slots) slots[slot.key] = slot.id;
454
+ return {
455
+ route: createAppRouteGraphRouteId(input.pattern),
456
+ page: input.pagePath ? createAppRouteGraphPageId(input.pattern) : null,
457
+ routeHandler: input.routePath ? createAppRouteGraphRouteHandlerId(input.pattern) : null,
458
+ rootBoundary: resolveRootBoundaryId(input.routeSegments, input.layoutTreePositions),
459
+ layouts: input.layoutTreePositions.map((treePosition) => createAppRouteGraphLayoutId(createAppRouteGraphTreePath(input.routeSegments, treePosition))),
460
+ templates: (input.templateTreePositions ?? []).map((treePosition) => createAppRouteGraphTemplateId(createAppRouteGraphTreePath(input.routeSegments, treePosition))),
461
+ slots
462
+ };
463
+ }
464
+ function createAppRouteGraphTreePath(routeSegments, treePosition) {
465
+ const treePathSegments = routeSegments.slice(0, treePosition);
466
+ if (treePathSegments.length === 0) return "/";
467
+ return `/${treePathSegments.join("/")}`;
468
+ }
469
+ /**
470
+ * Compute the tree position (directory depth from app root) for each layout.
471
+ * Root layout = 0, a layout at app/blog/ = 1, app/blog/(group)/ = 2.
472
+ * Counts ALL directory levels including route groups and parallel slots.
473
+ */
474
+ function computeLayoutTreePositions(appDir, layouts) {
475
+ return layouts.map((layoutPath) => {
476
+ const layoutDir = path.dirname(layoutPath);
477
+ if (layoutDir === appDir) return 0;
478
+ return path.relative(appDir, layoutDir).split(path.sep).length;
479
+ });
480
+ }
481
+ /**
482
+ * Discover all layout files from root to the given directory.
483
+ * Each level of the directory tree may have a layout.tsx.
484
+ */
485
+ function discoverLayouts(segments, appDir, matcher) {
486
+ const layouts = [];
487
+ const rootLayout = findFile(appDir, "layout", matcher);
488
+ if (rootLayout) layouts.push(rootLayout);
489
+ let currentDir = appDir;
490
+ for (const segment of segments) {
491
+ currentDir = path.join(currentDir, segment);
492
+ const layout = findFile(currentDir, "layout", matcher);
493
+ if (layout) layouts.push(layout);
494
+ }
495
+ return layouts;
496
+ }
497
+ /**
498
+ * Discover all template files from root to the given directory.
499
+ * Each level of the directory tree may have a template.tsx.
500
+ * Templates are like layouts but re-mount on navigation.
501
+ */
502
+ function discoverTemplates(segments, appDir, matcher) {
503
+ const templates = [];
504
+ const rootTemplate = findFile(appDir, "template", matcher);
505
+ if (rootTemplate) templates.push(rootTemplate);
506
+ let currentDir = appDir;
507
+ for (const segment of segments) {
508
+ currentDir = path.join(currentDir, segment);
509
+ const template = findFile(currentDir, "template", matcher);
510
+ if (template) templates.push(template);
511
+ }
512
+ return templates;
513
+ }
514
+ /**
515
+ * Discover error.tsx files aligned with the layouts array.
516
+ * Walks the same directory levels as discoverLayouts and, for each level
517
+ * that contributes a layout entry, checks whether error.tsx also exists.
518
+ * Returns an array of the same length as discoverLayouts() would return,
519
+ * with the error path (or null) at each corresponding layout level.
520
+ *
521
+ * This enables interleaving ErrorBoundary components with layouts in the
522
+ * rendering tree, matching Next.js behavior where each segment independently
523
+ * wraps its children with an error boundary.
524
+ */
525
+ function discoverLayoutAlignedErrors(segments, appDir, matcher) {
526
+ const errors = [];
527
+ if (findFile(appDir, "layout", matcher)) errors.push(findFile(appDir, "error", matcher));
528
+ let currentDir = appDir;
529
+ for (const segment of segments) {
530
+ currentDir = path.join(currentDir, segment);
531
+ if (findFile(currentDir, "layout", matcher)) errors.push(findFile(currentDir, "error", matcher));
532
+ }
533
+ return errors;
534
+ }
535
+ /**
536
+ * Discover the nearest boundary file (not-found, forbidden, unauthorized)
537
+ * by walking from the route's directory up to the app root.
538
+ * Returns the first (closest) file found, or null.
539
+ */
540
+ function discoverBoundaryFile(segments, appDir, fileName, matcher) {
541
+ const dirs = [];
542
+ let dir = appDir;
543
+ dirs.push(dir);
544
+ for (const segment of segments) {
545
+ dir = path.join(dir, segment);
546
+ dirs.push(dir);
547
+ }
548
+ for (let i = dirs.length - 1; i >= 0; i--) {
549
+ const f = findFile(dirs[i], fileName, matcher);
550
+ if (f) return f;
551
+ }
552
+ return null;
553
+ }
554
+ /**
555
+ * Discover boundary files (not-found, forbidden, unauthorized) at each layout directory.
556
+ * Returns an array aligned with the layouts array, where each entry is the boundary
557
+ * file at that layout's directory, or null if none exists there.
558
+ *
559
+ * This is used for per-layout error boundaries. In Next.js, each layout level
560
+ * has its own boundary that wraps the layout's children. When notFound() is thrown
561
+ * from a layout, it propagates up to the parent layout's boundary.
562
+ */
563
+ function discoverBoundaryFilePerLayout(layouts, fileName, matcher) {
564
+ return layouts.map((layoutPath) => {
565
+ return findFile(path.dirname(layoutPath), fileName, matcher);
566
+ });
567
+ }
568
+ /**
569
+ * Discover parallel slots inherited from ancestor directories.
570
+ *
571
+ * In Next.js, parallel slots belong to the layout that defines them. When a
572
+ * child route is rendered, its parent layout's slots must still be present.
573
+ * If the child doesn't have matching content in a slot, the slot's default.tsx
574
+ * is rendered instead.
575
+ *
576
+ * Walk from appDir through each segment to the route's directory. At each level
577
+ * that has @slot dirs, collect them. Slots at the route's own directory level
578
+ * use page.tsx; slots at ancestor levels use default.tsx only.
579
+ */
580
+ function discoverInheritedParallelSlots(segments, appDir, routeDir, matcher) {
581
+ const slotMap = /* @__PURE__ */ new Map();
582
+ let currentDir = appDir;
583
+ const dirsToCheck = [];
584
+ let layoutIdx = findFile(appDir, "layout", matcher) ? 0 : -1;
585
+ dirsToCheck.push({
586
+ dir: appDir,
587
+ layoutIdx,
588
+ segmentIndex: 0
589
+ });
590
+ for (let i = 0; i < segments.length; i++) {
591
+ currentDir = path.join(currentDir, segments[i]);
592
+ if (findFile(currentDir, "layout", matcher)) layoutIdx++;
593
+ dirsToCheck.push({
594
+ dir: currentDir,
595
+ layoutIdx,
596
+ segmentIndex: i + 1
597
+ });
598
+ }
599
+ const routeHasLayout = layoutIdx >= 0;
600
+ for (const { dir, layoutIdx: lvlLayoutIdx, segmentIndex } of dirsToCheck) {
601
+ if (lvlLayoutIdx < 0 && routeHasLayout) continue;
602
+ const isOwnDir = dir === routeDir;
603
+ const slotLayoutIdx = Math.max(lvlLayoutIdx, 0);
604
+ const slotsAtLevel = discoverParallelSlots(dir, appDir, matcher);
605
+ const segmentsBelow = segments.slice(segmentIndex);
606
+ for (const slot of slotsAtLevel) if (isOwnDir) {
607
+ slot.layoutIndex = slotLayoutIdx;
608
+ slotMap.set(slot.key, slot);
609
+ } else {
610
+ const mirror = findMirroredSlotPage(slot.ownerDir, segmentsBelow, matcher);
611
+ let slotPatternParts;
612
+ let slotParamNames;
613
+ if (mirror) {
614
+ const ownerUrl = convertSegmentsToRouteParts([...segments.slice(0, segmentIndex)]);
615
+ slotPatternParts = [...ownerUrl?.urlSegments ?? [], ...mirror.slotUrlSegments];
616
+ slotParamNames = [...ownerUrl?.params ?? [], ...mirror.slotParamNames];
617
+ }
618
+ const inheritedSlot = {
619
+ ...slot,
620
+ pagePath: mirror?.pagePath ?? null,
621
+ layoutIndex: slotLayoutIdx,
622
+ routeSegments: mirror?.segments ?? null,
623
+ slotPatternParts,
624
+ slotParamNames
625
+ };
626
+ slotMap.set(slot.key, inheritedSlot);
627
+ }
628
+ }
629
+ return Array.from(slotMap.values());
630
+ }
631
+ /**
632
+ * Look for a page file inside a parallel slot directory that mirrors the
633
+ * route's path below the slot's owner. The match falls through two tiers:
634
+ * 1. Literal filesystem path — fast path when route and slot share shape.
635
+ * 2. Scored pattern compatibility — enumerate sub-pages, accept those
636
+ * whose URL pattern can match the route's URL space (slot dynamic
637
+ * markers may have different names than the route's, and slot
638
+ * catch-alls may subsume the route), and pick the most-specific via
639
+ * `scoreSlotPattern`. Exact URL-parts equality (e.g. through route
640
+ * groups appearing on only one side, like `(marketing)/about` ↔
641
+ * `@breadcrumbs/about`) naturally wins because all literal segments
642
+ * score highest.
643
+ *
644
+ * Returns the slot sub-page's absolute path, its raw filesystem segments
645
+ * (for `routeSegments`), and its URL parts / param names (for
646
+ * `slotPatternParts` / `slotParamNames`). Returns null when no mirror matches.
647
+ */
648
+ function findMirroredSlotPage(slotDir, segmentsBelow, matcher) {
649
+ if (segmentsBelow.length === 0) return null;
650
+ const routeUrl = convertSegmentsToRouteParts([...segmentsBelow]);
651
+ const literalPage = findFile(path.join(slotDir, ...segmentsBelow), "page", matcher);
652
+ if (literalPage) return {
653
+ pagePath: literalPage,
654
+ segments: [...segmentsBelow],
655
+ slotUrlSegments: routeUrl?.urlSegments ?? [],
656
+ slotParamNames: routeUrl?.params ?? []
657
+ };
658
+ if (!routeUrl || routeUrl.urlSegments.length === 0) return null;
659
+ let best = null;
660
+ for (const { relativePath, pagePath } of findSlotSubPages(slotDir, matcher)) {
661
+ const slotSegments = relativePath.split(path.sep);
662
+ const slotUrl = convertSegmentsToRouteParts(slotSegments);
663
+ if (!slotUrl) continue;
664
+ if (!patternsCompatible(slotUrl.urlSegments, routeUrl.urlSegments)) continue;
665
+ const score = scoreSlotPattern(slotUrl.urlSegments);
666
+ if (!best || score > best.score) best = {
667
+ pagePath,
668
+ segments: slotSegments,
669
+ slotUrlSegments: slotUrl.urlSegments,
670
+ slotParamNames: slotUrl.params,
671
+ score
672
+ };
673
+ }
674
+ return best;
675
+ }
676
+ /**
677
+ * Whether a slot pattern can match the same URL space as the route's URL
678
+ * parts (where the route's parts are themselves a pattern, since a route
679
+ * file like `[id]/page.tsx` produces `:id`).
680
+ *
681
+ * - `:name+` (catch-all) consumes one-or-more remaining segments.
682
+ * - `:name*` (optional catch-all) consumes zero-or-more.
683
+ * - `:name` (single dynamic) consumes exactly one segment, matching any
684
+ * route segment (literal or dynamic).
685
+ * - Literal slot segments must equal the route's segment exactly; a literal
686
+ * slot segment paired with a dynamic route segment is rejected because we
687
+ * can't know statically whether the runtime value will equal the literal.
688
+ * This also means a literal slot sub-page never matches a catch-all route
689
+ * (e.g. slot `about/page.tsx` is not bound to a route `[...slug]`) — the
690
+ * catch-all might or might not resolve to "about" at request time.
691
+ */
692
+ function patternsCompatible(slotParts, routeParts) {
693
+ let i = 0;
694
+ let j = 0;
695
+ while (i < slotParts.length) {
696
+ const sp = slotParts[i];
697
+ if (sp.endsWith("+")) return j < routeParts.length;
698
+ if (sp.endsWith("*")) return true;
699
+ if (j >= routeParts.length) return false;
700
+ const rp = routeParts[j];
701
+ if (sp.startsWith(":")) {
702
+ i++;
703
+ j++;
704
+ continue;
705
+ }
706
+ if (rp.startsWith(":")) return false;
707
+ if (sp !== rp) return false;
708
+ i++;
709
+ j++;
710
+ }
711
+ return j === routeParts.length;
712
+ }
713
+ /**
714
+ * Score a slot pattern by specificity so the most-specific match wins:
715
+ * literal > single dynamic > catch-all > optional catch-all.
716
+ *
717
+ * Required catch-all (`:name+`, ≥1 segment) is more constrained than the
718
+ * optional variant (`:name*`, ≥0 segments), so it scores higher.
719
+ */
720
+ function scoreSlotPattern(urlSegments) {
721
+ let score = 0;
722
+ for (const seg of urlSegments) if (seg.endsWith("*")) score += 1;
723
+ else if (seg.endsWith("+")) score += 2;
724
+ else if (seg.startsWith(":")) score += 3;
725
+ else score += 4;
726
+ return score;
727
+ }
728
+ /**
729
+ * Map a pattern segment to the tree-node type used by Next.js' route
730
+ * validator. Two segments are structurally equivalent iff they share the
731
+ * same tree-node type.
732
+ */
733
+ function segmentTreeNodeType(seg) {
734
+ if (!seg.startsWith(":")) return `literal:${seg}`;
735
+ if (seg.endsWith("*")) return "optionalCatchAll";
736
+ if (seg.endsWith("+")) return "catchAll";
737
+ return "dynamic";
738
+ }
739
+ function patternsStructurallyEquivalent(a, b) {
740
+ if (a.length !== b.length) return false;
741
+ for (let i = 0; i < a.length; i++) if (segmentTreeNodeType(a[i]) !== segmentTreeNodeType(b[i])) return false;
742
+ return true;
743
+ }
744
+ /**
745
+ * Discover parallel route slots (@team, @analytics, etc.) in a directory.
746
+ * Returns a ParallelSlot for each @-prefixed subdirectory that has a page or default component.
747
+ */
748
+ function discoverParallelSlots(dir, appDir, matcher) {
749
+ if (!fs.existsSync(dir)) return [];
750
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
751
+ const slots = [];
752
+ for (const entry of entries) {
753
+ if (!entry.isDirectory() || !entry.name.startsWith("@")) continue;
754
+ const slotName = entry.name.slice(1);
755
+ const slotDir = path.join(dir, entry.name);
756
+ const pagePath = findFile(slotDir, "page", matcher);
757
+ const defaultPath = findFile(slotDir, "default", matcher);
758
+ const interceptingRoutes = discoverInterceptingRoutes(slotDir, dir, appDir, matcher);
759
+ if (!pagePath && !defaultPath && interceptingRoutes.length === 0) continue;
760
+ const ownerSegments = path.relative(appDir, dir).split(path.sep).filter((segment) => segment.length > 0);
761
+ const ownerTreePath = createAppRouteGraphTreePath(ownerSegments, ownerSegments.length);
762
+ slots.push({
763
+ id: createAppRouteGraphSlotId(slotName, ownerTreePath),
764
+ key: `${slotName}@${path.relative(appDir, slotDir).replace(/\\/g, "/")}`,
765
+ name: slotName,
766
+ ownerDir: slotDir,
767
+ pagePath,
768
+ defaultPath,
769
+ layoutPath: findFile(slotDir, "layout", matcher),
770
+ loadingPath: findFile(slotDir, "loading", matcher),
771
+ errorPath: findFile(slotDir, "error", matcher),
772
+ interceptingRoutes,
773
+ layoutIndex: -1,
774
+ routeSegments: pagePath ? [] : null
775
+ });
776
+ }
777
+ return slots;
778
+ }
779
+ /**
780
+ * The interception convention prefix patterns.
781
+ * (.) — same level, (..) — one level up, (..)(..)" — two levels up, (...) — root
782
+ */
783
+ const INTERCEPT_PATTERNS = [
784
+ {
785
+ prefix: "(...)",
786
+ convention: "..."
787
+ },
788
+ {
789
+ prefix: "(..)(..)",
790
+ convention: "../.."
791
+ },
792
+ {
793
+ prefix: "(..)",
794
+ convention: ".."
795
+ },
796
+ {
797
+ prefix: "(.)",
798
+ convention: "."
799
+ }
800
+ ];
801
+ /**
802
+ * Discover intercepting routes inside a parallel slot directory.
803
+ *
804
+ * Intercepting routes use conventions like (.)photo, (..)feed, (...), etc.
805
+ * They intercept navigation to another route and render within the slot instead.
806
+ *
807
+ * @param slotDir - The parallel slot directory (e.g. app/feed/@modal)
808
+ * @param routeDir - The directory of the route that owns this slot (e.g. app/feed)
809
+ * @param appDir - The root app directory
810
+ */
811
+ function discoverInterceptingRoutes(slotDir, routeDir, appDir, matcher) {
812
+ if (!fs.existsSync(slotDir)) return [];
813
+ const results = [];
814
+ scanForInterceptingPages(slotDir, routeDir, appDir, results, matcher);
815
+ return results;
816
+ }
817
+ /**
818
+ * Recursively scan a directory tree for page.tsx files that are inside
819
+ * intercepting route directories.
820
+ */
821
+ function scanForInterceptingPages(currentDir, routeDir, appDir, results, matcher) {
822
+ if (!fs.existsSync(currentDir)) return;
823
+ const entries = fs.readdirSync(currentDir, { withFileTypes: true });
824
+ for (const entry of entries) {
825
+ if (!entry.isDirectory()) continue;
826
+ if (entry.name.startsWith("_")) continue;
827
+ const interceptMatch = matchInterceptConvention(entry.name);
828
+ if (interceptMatch) {
829
+ const restOfName = entry.name.slice(interceptMatch.prefix.length);
830
+ const interceptDir = path.join(currentDir, entry.name);
831
+ collectInterceptingPages(interceptDir, interceptDir, interceptMatch.convention, restOfName, routeDir, appDir, results, matcher);
832
+ } else scanForInterceptingPages(path.join(currentDir, entry.name), routeDir, appDir, results, matcher);
833
+ }
834
+ }
835
+ /**
836
+ * Match a directory name against interception convention prefixes.
837
+ */
838
+ function matchInterceptConvention(name) {
839
+ for (const pattern of INTERCEPT_PATTERNS) if (name.startsWith(pattern.prefix)) return pattern;
840
+ return null;
841
+ }
842
+ /**
843
+ * Collect page.tsx files inside an intercepting route directory tree
844
+ * and compute their target URL patterns.
845
+ */
846
+ function collectInterceptingPages(currentDir, interceptRoot, convention, interceptSegment, routeDir, appDir, results, matcher, parentLayoutPaths = []) {
847
+ const currentLayoutPath = findFile(currentDir, "layout", matcher);
848
+ const layoutPaths = currentLayoutPath ? [...parentLayoutPaths, currentLayoutPath] : parentLayoutPaths;
849
+ const page = findFile(currentDir, "page", matcher);
850
+ if (page) {
851
+ const targetPattern = computeInterceptTarget(convention, interceptSegment, currentDir, interceptRoot, routeDir, appDir);
852
+ if (targetPattern) results.push({
853
+ convention,
854
+ layoutPaths: [...layoutPaths],
855
+ targetPattern: targetPattern.pattern,
856
+ pagePath: page,
857
+ params: targetPattern.params
858
+ });
859
+ }
860
+ if (!fs.existsSync(currentDir)) return;
861
+ const entries = fs.readdirSync(currentDir, { withFileTypes: true });
862
+ for (const entry of entries) {
863
+ if (!entry.isDirectory()) continue;
864
+ if (entry.name.startsWith("_")) continue;
865
+ collectInterceptingPages(path.join(currentDir, entry.name), interceptRoot, convention, interceptSegment, routeDir, appDir, results, matcher, layoutPaths);
866
+ }
867
+ }
868
+ /**
869
+ * Check whether a path segment is invisible in the URL (route groups, parallel slots, ".").
870
+ *
871
+ * Used by computeInterceptTarget, convertSegmentsToRouteParts, and
872
+ * hasRemainingVisibleSegments — keep this the single source of truth.
873
+ */
874
+ function isInvisibleSegment(segment) {
875
+ if (segment === ".") return true;
876
+ if (segment.startsWith("(") && segment.endsWith(")")) return true;
877
+ if (segment.startsWith("@")) return true;
878
+ return false;
879
+ }
880
+ /**
881
+ * Compute the target URL pattern for an intercepting route.
882
+ *
883
+ * Interception conventions (..), (..)(..)" climb by *visible route segments*
884
+ * (not filesystem directories). Route groups like (marketing) and parallel
885
+ * slots like @modal are invisible and must be skipped when counting levels.
886
+ *
887
+ * - (.) same level: resolve relative to routeDir
888
+ * - (..) one level up: climb 1 visible segment
889
+ * - (..)(..) two levels up: climb 2 visible segments
890
+ * - (...) root: resolve from appDir
891
+ */
892
+ function computeInterceptTarget(convention, interceptSegment, currentDir, interceptRoot, routeDir, appDir) {
893
+ const routeSegments = path.relative(appDir, routeDir).split(path.sep).filter(Boolean);
894
+ let baseParts;
895
+ switch (convention) {
896
+ case ".":
897
+ baseParts = routeSegments;
898
+ break;
899
+ case "..":
900
+ case "../..": {
901
+ const levelsToClimb = convention === ".." ? 1 : 2;
902
+ let climbed = 0;
903
+ let cutIndex = routeSegments.length;
904
+ while (cutIndex > 0 && climbed < levelsToClimb) {
905
+ cutIndex--;
906
+ if (!isInvisibleSegment(routeSegments[cutIndex])) climbed++;
907
+ }
908
+ if (climbed < levelsToClimb) {
909
+ const interceptionRoute = formatInterceptionRoutePath(routeSegments, convention, interceptSegment, path.relative(interceptRoot, currentDir).split(path.sep).filter(Boolean));
910
+ if (convention === "..") throw new Error(`Invalid interception route: ${interceptionRoute}. Cannot use (..) marker at the root level, use (.) instead.`);
911
+ throw new Error(`Invalid interception route: ${interceptionRoute}. Cannot use (..)(..) marker at the root level or one level up.`);
912
+ }
913
+ baseParts = routeSegments.slice(0, cutIndex);
914
+ break;
915
+ }
916
+ case "...":
917
+ baseParts = [];
918
+ break;
919
+ default: return null;
920
+ }
921
+ const nestedParts = path.relative(interceptRoot, currentDir).split(path.sep).filter(Boolean);
922
+ const convertedTarget = convertSegmentsToRouteParts([
923
+ ...baseParts,
924
+ interceptSegment,
925
+ ...nestedParts
926
+ ]);
927
+ if (!convertedTarget) return null;
928
+ const { urlSegments, params } = convertedTarget;
929
+ const pattern = "/" + urlSegments.join("/");
930
+ return {
931
+ pattern: pattern === "/" ? "/" : pattern,
932
+ params
933
+ };
934
+ }
935
+ function formatInterceptionRoutePath(routeSegments, convention, interceptSegment, nestedParts) {
936
+ const marker = markerForInterceptionConvention(convention);
937
+ const convertedRoute = convertSegmentsToRouteParts(routeSegments);
938
+ const routePath = [
939
+ ...convertedRoute ? convertedRoute.urlSegments : routeSegments.filter((segment) => !isInvisibleSegment(segment)),
940
+ `${marker}${interceptSegment}`,
941
+ ...nestedParts
942
+ ].filter(Boolean).join("/");
943
+ return routePath ? `/${routePath}` : "/";
944
+ }
945
+ function markerForInterceptionConvention(convention) {
946
+ switch (convention) {
947
+ case ".": return "(.)";
948
+ case "..": return "(..)";
949
+ case "../..": return "(..)(..)";
950
+ case "...": return "(...)";
951
+ default: return "";
952
+ }
953
+ }
954
+ /**
955
+ * Find a file by name (without extension) in a directory.
956
+ * Checks configured pageExtensions.
957
+ */
958
+ function findFile(dir, name, matcher) {
959
+ for (const ext of matcher.dottedExtensions) {
960
+ const filePath = path.join(dir, name + ext);
961
+ if (fs.existsSync(filePath)) return filePath;
962
+ }
963
+ return null;
964
+ }
965
+ /**
966
+ * Convert filesystem path segments to URL route parts, skipping invisible segments
967
+ * (route groups, @slots, ".") and converting dynamic segment syntax to Express-style
968
+ * patterns (e.g. "[id]" → ":id", "[...slug]" → ":slug+").
969
+ */
970
+ function convertSegmentsToRouteParts(segments) {
971
+ const urlSegments = [];
972
+ const params = [];
973
+ let isDynamic = false;
974
+ for (let i = 0; i < segments.length; i++) {
975
+ const segment = segments[i];
976
+ if (isInvisibleSegment(segment)) continue;
977
+ const catchAllMatch = segment.match(/^\[\.\.\.([^\]]+)\]$/);
978
+ if (catchAllMatch) {
979
+ if (hasRemainingVisibleSegments(segments, i + 1)) return null;
980
+ if (catchAllMatch[1].endsWith("+") || catchAllMatch[1].endsWith("*")) return null;
981
+ isDynamic = true;
982
+ params.push(catchAllMatch[1]);
983
+ urlSegments.push(`:${catchAllMatch[1]}+`);
984
+ continue;
985
+ }
986
+ const optionalCatchAllMatch = segment.match(/^\[\[\.\.\.([^\]]+)\]\]$/);
987
+ if (optionalCatchAllMatch) {
988
+ if (hasRemainingVisibleSegments(segments, i + 1)) return null;
989
+ if (optionalCatchAllMatch[1].endsWith("+") || optionalCatchAllMatch[1].endsWith("*")) return null;
990
+ isDynamic = true;
991
+ params.push(optionalCatchAllMatch[1]);
992
+ urlSegments.push(`:${optionalCatchAllMatch[1]}*`);
993
+ continue;
994
+ }
995
+ const dynamicMatch = segment.match(/^\[([^\]]+)\]$/);
996
+ if (dynamicMatch) {
997
+ if (dynamicMatch[1].endsWith("+") || dynamicMatch[1].endsWith("*")) return null;
998
+ isDynamic = true;
999
+ params.push(dynamicMatch[1]);
1000
+ urlSegments.push(`:${dynamicMatch[1]}`);
1001
+ continue;
1002
+ }
1003
+ urlSegments.push(decodeRouteSegment(segment));
1004
+ }
1005
+ return {
1006
+ urlSegments,
1007
+ params,
1008
+ isDynamic
1009
+ };
1010
+ }
1011
+ function hasRemainingVisibleSegments(segments, startIndex) {
1012
+ for (let i = startIndex; i < segments.length; i++) if (!isInvisibleSegment(segments[i])) return true;
1013
+ return false;
1014
+ }
1015
+ function joinRoutePattern(basePattern, subPath) {
1016
+ if (!subPath) return basePattern;
1017
+ return basePattern === "/" ? `/${subPath}` : `${basePattern}/${subPath}`;
1018
+ }
1019
+ //#endregion
1020
+ export { buildAppRouteGraph, computeRootParamNames };
1021
+
1022
+ //# sourceMappingURL=app-route-graph.js.map