vinext 1.0.0-beta.0 → 1.0.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (305) hide show
  1. package/README.md +21 -2
  2. package/dist/build/assets-ignore.js +1 -1
  3. package/dist/build/clean-output.js +1 -1
  4. package/dist/build/client-build-config.js +7 -4
  5. package/dist/build/css-url-assets.js +3 -3
  6. package/dist/build/google-fonts/fallback-metrics.js +1 -1
  7. package/dist/build/google-fonts/font-metadata.js +1 -1
  8. package/dist/build/inject-pregenerated-paths.js +1 -1
  9. package/dist/build/inline-css.js +2 -5
  10. package/dist/build/module-dependency-cache.d.ts +4 -0
  11. package/dist/build/module-dependency-cache.js +13 -0
  12. package/dist/build/next-client-runtime-manifests.js +1 -1
  13. package/dist/build/pages-client-assets-module.js +1 -1
  14. package/dist/build/precompress.js +7 -7
  15. package/dist/build/prerender-paths.d.ts +2 -2
  16. package/dist/build/prerender-paths.js +10 -15
  17. package/dist/build/prerender-server-pool.js +2 -2
  18. package/dist/build/prerender.js +1 -1
  19. package/dist/build/preview-credentials.d.ts +11 -0
  20. package/dist/build/preview-credentials.js +19 -0
  21. package/dist/build/report.d.ts +8 -4
  22. package/dist/build/report.js +14 -6
  23. package/dist/build/run-prerender.d.ts +2 -11
  24. package/dist/build/run-prerender.js +2 -10
  25. package/dist/build/server-manifest.js +1 -1
  26. package/dist/build/ssr-manifest.d.ts +4 -0
  27. package/dist/build/ssr-manifest.js +10 -6
  28. package/dist/build/standalone.js +37 -37
  29. package/dist/cache/cache-adapters-virtual.d.ts +1 -17
  30. package/dist/cache/cache-adapters-virtual.js +20 -11
  31. package/dist/check.d.ts +0 -16
  32. package/dist/check.js +27 -36
  33. package/dist/cli.js +61 -55
  34. package/dist/client/instrumentation-client-inject.js +1 -1
  35. package/dist/client/pages-router-link-navigation.js +1 -1
  36. package/dist/config/config-matchers.d.ts +13 -37
  37. package/dist/config/config-matchers.js +33 -123
  38. package/dist/config/dotenv.js +1 -1
  39. package/dist/config/next-config.d.ts +7 -3
  40. package/dist/config/next-config.js +84 -15
  41. package/dist/config/prerender.d.ts +2 -2
  42. package/dist/config/prerender.js +7 -15
  43. package/dist/config/request-context.d.ts +14 -0
  44. package/dist/config/request-context.js +26 -0
  45. package/dist/config/server-external-packages.js +1 -1
  46. package/dist/config/tsconfig-paths.js +2 -2
  47. package/dist/{node_modules/.pnpm/am-i-vibing@0.5.0/node_modules → deps/.pnpm/am-i-vibing@0.5.0/deps}/am-i-vibing/dist/detector-1yx2Hoe0.js +2 -2
  48. package/dist/deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js +50 -0
  49. package/dist/entries/app-rsc-entry.js +39 -14
  50. package/dist/entries/app-rsc-manifest.js +21 -4
  51. package/dist/entries/pages-client-entry.js +6 -5
  52. package/dist/entries/pages-server-entry.js +18 -28
  53. package/dist/entries/runtime-entry-module.js +2 -2
  54. package/dist/index.js +314 -230
  55. package/dist/init-cloudflare.js +1 -1
  56. package/dist/init-platform.js +1 -1
  57. package/dist/init.d.ts +6 -4
  58. package/dist/init.js +25 -27
  59. package/dist/plugins/ast-utils.js +1 -1
  60. package/dist/plugins/dynamic-preload-metadata.js +7 -7
  61. package/dist/plugins/extensionless-dynamic-import.js +1 -1
  62. package/dist/plugins/fonts.js +3 -3
  63. package/dist/plugins/ignore-dynamic-requests.js +13 -8
  64. package/dist/plugins/import-meta-url.js +16 -17
  65. package/dist/plugins/middleware-export-validation.js +1 -1
  66. package/dist/plugins/middleware-server-only.js +2 -2
  67. package/dist/plugins/og-asset-ownership.js +7 -6
  68. package/dist/plugins/og-assets.js +5 -6
  69. package/dist/plugins/optimize-imports.js +12 -13
  70. package/dist/plugins/postcss.js +1 -1
  71. package/dist/plugins/remove-console.js +1 -1
  72. package/dist/plugins/require-context.js +1 -1
  73. package/dist/plugins/sass.js +2 -2
  74. package/dist/plugins/server-externals-manifest.js +1 -1
  75. package/dist/plugins/strip-server-exports.js +1 -1
  76. package/dist/plugins/styled-jsx.js +3 -2
  77. package/dist/plugins/typeof-window.js +1 -1
  78. package/dist/routing/app-route-graph.d.ts +16 -15
  79. package/dist/routing/app-route-graph.js +86 -88
  80. package/dist/routing/app-router.d.ts +0 -5
  81. package/dist/routing/app-router.js +12 -11
  82. package/dist/routing/file-matcher.d.ts +13 -28
  83. package/dist/routing/file-matcher.js +31 -44
  84. package/dist/routing/pages-router.js +26 -32
  85. package/dist/routing/route-matching.d.ts +14 -1
  86. package/dist/routing/route-matching.js +13 -1
  87. package/dist/routing/route-pattern.d.ts +2 -1
  88. package/dist/routing/route-pattern.js +7 -4
  89. package/dist/routing/route-trie.d.ts +5 -1
  90. package/dist/routing/route-trie.js +5 -2
  91. package/dist/server/api-handler.js +41 -33
  92. package/dist/server/app-browser-entry.js +18 -11
  93. package/dist/server/app-browser-history-controller.d.ts +1 -1
  94. package/dist/server/app-browser-history-controller.js +3 -3
  95. package/dist/server/app-browser-navigation-controller.js +4 -2
  96. package/dist/server/app-browser-server-action-client.js +1 -1
  97. package/dist/server/app-elements-wire.d.ts +2 -1
  98. package/dist/server/app-fallback-renderer.d.ts +11 -0
  99. package/dist/server/app-fallback-renderer.js +16 -2
  100. package/dist/server/app-middleware.js +2 -1
  101. package/dist/server/app-page-boundary-render.d.ts +26 -2
  102. package/dist/server/app-page-boundary-render.js +190 -55
  103. package/dist/server/app-page-cache-finalizer.d.ts +1 -1
  104. package/dist/server/app-page-cache.d.ts +1 -1
  105. package/dist/server/app-page-dispatch.d.ts +12 -2
  106. package/dist/server/app-page-dispatch.js +37 -51
  107. package/dist/server/app-page-element-builder.d.ts +8 -2
  108. package/dist/server/app-page-element-builder.js +112 -27
  109. package/dist/server/app-page-execution.d.ts +3 -1
  110. package/dist/server/app-page-execution.js +31 -39
  111. package/dist/server/app-page-head.d.ts +54 -2
  112. package/dist/server/app-page-head.js +164 -78
  113. package/dist/server/app-page-http-access-fallback-metadata.d.ts +29 -0
  114. package/dist/server/app-page-http-access-fallback-metadata.js +106 -0
  115. package/dist/server/app-page-render.d.ts +2 -2
  116. package/dist/server/app-page-render.js +1 -1
  117. package/dist/server/app-page-request.d.ts +12 -0
  118. package/dist/server/app-page-request.js +134 -14
  119. package/dist/server/app-page-route-wiring.d.ts +9 -0
  120. package/dist/server/app-page-route-wiring.js +44 -4
  121. package/dist/server/app-post-middleware-context.d.ts +1 -1
  122. package/dist/server/app-post-middleware-context.js +1 -1
  123. package/dist/server/app-route-handler-dispatch.js +31 -17
  124. package/dist/server/app-route-handler-execution.d.ts +6 -1
  125. package/dist/server/app-route-handler-execution.js +41 -15
  126. package/dist/server/app-route-handler-policy.d.ts +2 -0
  127. package/dist/server/app-route-handler-policy.js +3 -3
  128. package/dist/server/app-route-module-loader.d.ts +3 -1
  129. package/dist/server/app-route-module-loader.js +1 -0
  130. package/dist/server/app-route-tree-prefetch.js +1 -1
  131. package/dist/server/app-rsc-cache-busting.d.ts +1 -1
  132. package/dist/server/app-rsc-cache-busting.js +1 -1
  133. package/dist/server/app-rsc-errors.d.ts +1 -1
  134. package/dist/server/app-rsc-errors.js +12 -2
  135. package/dist/server/app-rsc-handler.d.ts +10 -5
  136. package/dist/server/app-rsc-handler.js +51 -23
  137. package/dist/server/app-rsc-redirect-flight.d.ts +66 -0
  138. package/dist/server/app-rsc-redirect-flight.js +71 -0
  139. package/dist/server/app-rsc-request-normalization.d.ts +3 -2
  140. package/dist/server/app-rsc-request-normalization.js +8 -2
  141. package/dist/server/app-rsc-response-finalizer.d.ts +2 -2
  142. package/dist/server/app-rsc-response-finalizer.js +5 -11
  143. package/dist/server/app-rsc-route-matching.d.ts +20 -0
  144. package/dist/server/app-rsc-route-matching.js +87 -11
  145. package/dist/server/app-segment-config.js +15 -5
  146. package/dist/server/app-server-action-execution.d.ts +3 -1
  147. package/dist/server/app-server-action-execution.js +8 -6
  148. package/dist/server/app-ssr-entry.js +4 -1
  149. package/dist/server/app-ssr-stream.d.ts +2 -7
  150. package/dist/server/app-ssr-stream.js +5 -10
  151. package/dist/server/cache-proof.js +1 -1
  152. package/dist/server/config-headers.d.ts +24 -0
  153. package/dist/server/config-headers.js +52 -0
  154. package/dist/server/default-global-error-module.d.ts +3 -1
  155. package/dist/server/default-not-found-module.d.ts +3 -1
  156. package/dist/server/dev-initial-server-error.d.ts +1 -1
  157. package/dist/server/dev-lockfile.js +2 -3
  158. package/dist/server/dev-route-files.js +1 -1
  159. package/dist/server/dev-server.js +281 -173
  160. package/dist/server/dev-stack-sourcemap.js +4 -3
  161. package/dist/server/headers.d.ts +3 -1
  162. package/dist/server/headers.js +3 -1
  163. package/dist/server/image-optimization.js +2 -2
  164. package/dist/server/implicit-tags.js +2 -1
  165. package/dist/server/instrumentation.js +2 -2
  166. package/dist/server/isr-cache.d.ts +1 -1
  167. package/dist/server/isr-cache.js +1 -1
  168. package/dist/server/metadata-routes.js +4 -4
  169. package/dist/server/middleware-matcher-pattern.d.ts +22 -0
  170. package/dist/server/middleware-matcher-pattern.js +219 -0
  171. package/dist/server/middleware-matcher.d.ts +2 -7
  172. package/dist/server/middleware-matcher.js +13 -60
  173. package/dist/server/middleware-path-to-regexp.d.ts +14 -0
  174. package/dist/server/middleware-path-to-regexp.js +228 -0
  175. package/dist/server/middleware-response-headers.js +1 -1
  176. package/dist/server/middleware-runtime.js +5 -4
  177. package/dist/server/middleware.d.ts +2 -1
  178. package/dist/server/middleware.js +3 -2
  179. package/dist/server/navigation-planner.d.ts +2 -0
  180. package/dist/server/navigation-planner.js +8 -2
  181. package/dist/server/next-error-digest.d.ts +9 -24
  182. package/dist/server/next-error-digest.js +23 -15
  183. package/dist/server/operation-token.js +1 -1
  184. package/dist/server/pages-body-parser-config.js +2 -1
  185. package/dist/server/pages-data-route.d.ts +11 -1
  186. package/dist/server/pages-data-route.js +16 -1
  187. package/dist/server/pages-dev-hydration.d.ts +14 -0
  188. package/dist/server/pages-dev-hydration.js +83 -0
  189. package/dist/server/pages-dev-module-url.js +2 -2
  190. package/dist/server/pages-get-initial-props.d.ts +15 -2
  191. package/dist/server/pages-get-initial-props.js +16 -6
  192. package/dist/server/pages-node-compat.d.ts +13 -6
  193. package/dist/server/pages-node-compat.js +44 -45
  194. package/dist/server/pages-page-data.d.ts +4 -2
  195. package/dist/server/pages-page-data.js +20 -10
  196. package/dist/server/pages-page-handler.d.ts +4 -2
  197. package/dist/server/pages-page-handler.js +74 -43
  198. package/dist/server/pages-page-response.d.ts +2 -2
  199. package/dist/server/pages-page-response.js +1 -1
  200. package/dist/server/pages-preview.d.ts +26 -0
  201. package/dist/server/pages-preview.js +165 -0
  202. package/dist/server/pages-readiness.d.ts +5 -4
  203. package/dist/server/pages-readiness.js +4 -2
  204. package/dist/server/pages-request-pipeline.d.ts +1 -0
  205. package/dist/server/pages-request-pipeline.js +25 -11
  206. package/dist/server/pages-router-entry.js +6 -0
  207. package/dist/server/prerender-route-params.js +2 -2
  208. package/dist/server/prod-server.d.ts +1 -4
  209. package/dist/server/prod-server.js +44 -30
  210. package/dist/server/request-pipeline.d.ts +15 -29
  211. package/dist/server/request-pipeline.js +25 -60
  212. package/dist/server/rsc-stream-hints.js +122 -18
  213. package/dist/server/seed-cache.js +1 -1
  214. package/dist/server/socket-error-backstop.js +1 -1
  215. package/dist/server/static-file-cache.js +7 -8
  216. package/dist/server/worker-utils.js +1 -1
  217. package/dist/shims/app.d.ts +1 -1
  218. package/dist/shims/cache-request-state.d.ts +27 -1
  219. package/dist/shims/cache-request-state.js +60 -1
  220. package/dist/shims/cache-runtime.d.ts +12 -1
  221. package/dist/shims/cache-runtime.js +22 -7
  222. package/dist/shims/cache.d.ts +5 -5
  223. package/dist/shims/cache.js +28 -11
  224. package/dist/shims/constants.js +1 -1
  225. package/dist/shims/document.d.ts +42 -83
  226. package/dist/shims/document.js +62 -53
  227. package/dist/shims/dynamic.d.ts +5 -25
  228. package/dist/shims/dynamic.js +29 -8
  229. package/dist/shims/error-boundary.d.ts +4 -4
  230. package/dist/shims/error.d.ts +22 -6
  231. package/dist/shims/error.js +53 -15
  232. package/dist/shims/fetch-cache.js +13 -4
  233. package/dist/shims/font-google-base.d.ts +11 -4
  234. package/dist/shims/font-local.d.ts +10 -3
  235. package/dist/shims/font-utils.js +1 -1
  236. package/dist/shims/form.d.ts +1 -1
  237. package/dist/shims/head.d.ts +13 -2
  238. package/dist/shims/head.js +6 -6
  239. package/dist/shims/headers.d.ts +28 -31
  240. package/dist/shims/headers.js +115 -53
  241. package/dist/shims/image.d.ts +27 -37
  242. package/dist/shims/image.js +21 -14
  243. package/dist/shims/internal/cookie-serialize.d.ts +3 -1
  244. package/dist/shims/internal/cookie-serialize.js +3 -1
  245. package/dist/shims/internal/hybrid-client-route-owner.js +3 -1
  246. package/dist/shims/internal/pages-data-fetch-dedup.js +5 -3
  247. package/dist/shims/internal/utils.d.ts +3 -3
  248. package/dist/shims/layout-segment-context.d.ts +1 -1
  249. package/dist/shims/legacy-image.d.ts +3 -37
  250. package/dist/shims/legacy-image.js +6 -16
  251. package/dist/shims/link.d.ts +16 -21
  252. package/dist/shims/link.js +25 -13
  253. package/dist/shims/navigation-context-state.d.ts +1 -1
  254. package/dist/shims/navigation-errors.d.ts +2 -1
  255. package/dist/shims/navigation-errors.js +9 -13
  256. package/dist/shims/navigation.d.ts +8 -4
  257. package/dist/shims/navigation.js +29 -18
  258. package/dist/shims/next-shims-augmentations.d.ts +1 -0
  259. package/dist/shims/next-shims-public.d.ts +1 -0
  260. package/dist/shims/public-shim-map.json.js +103 -0
  261. package/dist/shims/readonly-url-search-params.d.ts +3 -3
  262. package/dist/shims/readonly-url-search-params.js +3 -3
  263. package/dist/shims/request-state-types.d.ts +1 -1
  264. package/dist/shims/router-state.d.ts +1 -0
  265. package/dist/shims/router.d.ts +57 -22
  266. package/dist/shims/router.js +178 -46
  267. package/dist/shims/script.d.ts +6 -10
  268. package/dist/shims/script.js +7 -4
  269. package/dist/shims/server.d.ts +45 -22
  270. package/dist/shims/server.js +46 -7
  271. package/dist/shims/slot.d.ts +1 -1
  272. package/dist/shims/thenable-params.js +1 -1
  273. package/dist/shims/unified-request-context.d.ts +1 -1
  274. package/dist/shims/unified-request-context.js +2 -0
  275. package/dist/shims/url-safety.d.ts +1 -1
  276. package/dist/shims/url-safety.js +2 -2
  277. package/dist/typegen.d.ts +8 -2
  278. package/dist/typegen.js +84 -25
  279. package/dist/utils/client-build-manifest.js +2 -2
  280. package/dist/utils/client-entry-manifest.js +1 -1
  281. package/dist/utils/client-runtime-metadata.js +1 -1
  282. package/dist/utils/commonjs-loader.js +3 -3
  283. package/dist/utils/domain-locale.d.ts +2 -2
  284. package/dist/utils/external-url.d.ts +5 -0
  285. package/dist/utils/external-url.js +7 -0
  286. package/dist/utils/mdx-scan.js +1 -1
  287. package/dist/utils/path.d.ts +1 -13
  288. package/dist/utils/path.js +1 -15
  289. package/dist/utils/plugin-options.d.ts +4 -0
  290. package/dist/utils/plugin-options.js +8 -0
  291. package/dist/utils/project.d.ts +0 -6
  292. package/dist/utils/project.js +2 -8
  293. package/dist/utils/public-routes.js +2 -2
  294. package/dist/utils/react-version.js +1 -1
  295. package/dist/utils/redirect-digest.d.ts +9 -0
  296. package/dist/utils/redirect-digest.js +25 -0
  297. package/dist/utils/regex-safety.d.ts +8 -0
  298. package/dist/utils/regex-safety.js +737 -0
  299. package/dist/utils/vinext-root.js +1 -1
  300. package/dist/utils/vite-version.d.ts +5 -2
  301. package/dist/utils/vite-version.js +43 -17
  302. package/package.json +16 -4
  303. /package/dist/build/google-fonts/{fallback-metrics-data.js → fallback-metrics-data.json.js} +0 -0
  304. /package/dist/build/google-fonts/{font-data.js → font-data.json.js} +0 -0
  305. /package/dist/{node_modules/.pnpm/process-ancestry@0.1.0/node_modules → deps/.pnpm/process-ancestry@0.1.0/deps}/process-ancestry/dist/index.js +0 -0
@@ -1,7 +1,7 @@
1
+ import path, { toSlash } from "../deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
1
2
  import { markCssUrlAssetReferences, rebaseCssUrlAssetReferences } from "../build/css-url-assets.js";
2
3
  import { createRequire } from "node:module";
3
4
  import fs from "node:fs";
4
- import path from "node:path";
5
5
  import { preprocessCSS } from "vite";
6
6
  import { pathToFileURL } from "node:url";
7
7
  //#region src/plugins/sass.ts
@@ -98,7 +98,7 @@ function sassStylesheetCandidates(importPath) {
98
98
  return candidates;
99
99
  }
100
100
  function deriveSassNamespace(importUrl) {
101
- const parts = importUrl.replaceAll("\\", "/").replace(/\/$/, "").split("/");
101
+ const parts = toSlash(importUrl).replace(/\/$/, "").split("/");
102
102
  let basename = parts.pop() ?? "stylesheet";
103
103
  basename = basename.replace(/^_/, "").replace(/\..*$/, "");
104
104
  if (basename === "index" && parts.length > 0) basename = (parts.pop() ?? basename).replace(/^_/, "").replace(/\..*$/, "");
@@ -1,6 +1,6 @@
1
+ import path from "../deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
1
2
  import { builtinModules } from "node:module";
2
3
  import fs from "node:fs";
3
- import path from "node:path";
4
4
  //#region src/plugins/server-externals-manifest.ts
5
5
  const BUILTIN_MODULES = new Set(builtinModules.flatMap((name) => name.startsWith("node:") ? [name, name.slice(5)] : [name, `node:${name}`]));
6
6
  /**
@@ -1,7 +1,7 @@
1
1
  import { parseAst } from "vite";
2
2
  import MagicString from "magic-string";
3
3
  //#region src/plugins/strip-server-exports.ts
4
- const SERVER_EXPORTS = new Set([
4
+ const SERVER_EXPORTS = /* @__PURE__ */ new Set([
5
5
  "getServerSideProps",
6
6
  "getStaticProps",
7
7
  "getStaticPaths",
@@ -1,5 +1,5 @@
1
+ import path from "../deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
1
2
  import { createRequire } from "node:module";
2
- import path from "node:path";
3
3
  import { parseAst } from "vite";
4
4
  import { pathToFileURL } from "node:url";
5
5
  //#region src/plugins/styled-jsx.ts
@@ -75,7 +75,8 @@ function createStyledJsxPlugin(initialProjectRoot, options = {}) {
75
75
  if (!compilerPromise) {
76
76
  const requireFromNext = getNextRequire();
77
77
  if (!requireFromNext) throw new Error("[vinext] styled-jsx requires an installed next package so vinext can use its matching compiler.");
78
- compilerPromise = importModule(pathToFileURL(requireFromNext.resolve("next/dist/build/swc")).href).then(async (compiler) => {
78
+ const compilerPath = requireFromNext.resolve("next/dist/build/swc");
79
+ compilerPromise = importModule(pathToFileURL(compilerPath).href).then(async (compiler) => {
79
80
  await compiler.loadBindings();
80
81
  return compiler;
81
82
  });
@@ -1,6 +1,6 @@
1
+ import path from "../deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
1
2
  import { collectBindingNames, forEachAstChild, hasRange, isAstRecord, isIdentifierNamed, nodeArray } from "./ast-utils.js";
2
3
  import { collectDirectScopeBindings, collectLoopScopeBindings, collectSwitchScopeBindings, collectVarScopeBindings, createAstScope, hasAstBinding, isFunctionNode } from "./ast-scope.js";
3
- import path from "node:path";
4
4
  import { parseAst } from "vite";
5
5
  import MagicString from "magic-string";
6
6
  //#region src/plugins/typeof-window.ts
@@ -25,7 +25,10 @@ type InterceptingRoute = {
25
25
  sourcePageSegments?: string[]; /** Absolute layout paths inside the intercepting route tree, outermost to innermost */
26
26
  layoutPaths: string[]; /** Normalized branch segments accumulated at each intercept layout. */
27
27
  layoutSegments?: string[][]; /** Full normalized interception branch segments through the page. */
28
- branchSegments?: string[]; /** Parameter names for dynamic segments */
28
+ branchSegments?: string[]; /** Nearest not-found convention inside the interception branch. */
29
+ notFoundPath?: string | null; /** Slot-root-relative branch segments used to scope not-found params. */
30
+ notFoundBranchSegments?: string[]; /** Tree position relative to the normalized interception branch. */
31
+ notFoundTreePosition?: number | null; /** Parameter names for dynamic segments */
29
32
  params: string[];
30
33
  /**
31
34
  * Synthetic page-carrier slot id for sibling (slot-less) interception.
@@ -46,7 +49,9 @@ type ParallelSlot = {
46
49
  configLayoutPaths?: string[]; /** Tree positions of configLayoutPaths relative to the slot root. */
47
50
  configLayoutTreePositions?: number[]; /** Absolute path to the slot's loading component */
48
51
  loadingPath: string | null; /** Absolute path to the slot's error component */
49
- errorPath: string | null; /** Intercepting routes within this slot */
52
+ errorPath: string | null; /** Nearest not-found convention for the slot's active branch. */
53
+ notFoundPath?: string | null; /** Tree position of that convention relative to the slot root. */
54
+ notFoundTreePosition?: number | null; /** Intercepting routes within this slot */
50
55
  interceptingRoutes: InterceptingRoute[];
51
56
  /**
52
57
  * The layout index (0-based, in route.layouts[]) that this slot belongs to.
@@ -105,7 +110,8 @@ type AppRoute = {
105
110
  layoutErrorPaths: (string | null)[]; /** Per-segment error boundary paths, aligned with errorTreePositions. */
106
111
  errorPaths?: string[]; /** Tree position (directory depth from app/ root) for each error boundary. */
107
112
  errorTreePositions?: number[]; /** Not-found component path (nearest, walking up from page dir) */
108
- notFoundPath: string | null;
113
+ notFoundPath: string | null; /** Tree position of the nearest not-found component's owning segment. */
114
+ notFoundTreePosition?: number | null;
109
115
  /**
110
116
  * Not-found component paths per layout level (aligned with layouts array).
111
117
  * Each entry is the not-found.tsx at that layout's directory, or null.
@@ -119,8 +125,10 @@ type AppRoute = {
119
125
  * Used to create per-layout ForbiddenBoundary.
120
126
  */
121
127
  forbiddenPaths: (string | null)[]; /** Forbidden component path (403) at the route's directory level */
122
- forbiddenPath: string | null; /** Unauthorized component path (401) at the route's directory level */
123
- unauthorizedPath: string | null; /** Unauthorized component paths per layout level (aligned with layouts array). */
128
+ forbiddenPath: string | null; /** Tree position of the nearest forbidden component's owning segment. */
129
+ forbiddenTreePosition?: number | null; /** Unauthorized component path (401) at the route's directory level */
130
+ unauthorizedPath: string | null; /** Tree position of the nearest unauthorized component's owning segment. */
131
+ unauthorizedTreePosition?: number | null; /** Unauthorized component paths per layout level (aligned with layouts array). */
124
132
  unauthorizedPaths: (string | null)[];
125
133
  /**
126
134
  * Filesystem segments from app/ root to the route's directory.
@@ -292,10 +300,6 @@ type RouteManifest = {
292
300
  };
293
301
  /**
294
302
  * Build the App Router route graph by scanning `appDir`.
295
- *
296
- * `appDir` must be forward-slash. Every path in the graph is derived from it
297
- * with `path.posix.*` and `findFile`, so a native appDir would produce mixed
298
- * separators on Windows. Production callers normalize it at their entry.
299
303
  */
300
304
  declare function buildAppRouteGraph(appDir: string, matcher: ValidFileMatcher): Promise<{
301
305
  routes: AppRouteGraphRoute[];
@@ -314,12 +318,9 @@ declare function computeRootParamNames(routeSegments: readonly string[], layoutT
314
318
  * no sibling pages at all (e.g. `deep/path/(...)target` with no
315
319
  * `deep/path/page.tsx`).
316
320
  *
317
- * All comparisons happen in forward-slash space: `appDir` is forward-slash
318
- * (normalized once in the config hook), but `dir` and route file paths
319
- * descend through native `path.join`/`path.dirname`, which reintroduce
320
- * backslashes on Windows. Without normalizing, the `current === appDir`
321
- * termination never fires there and the walk overshoots the app root.
322
- * `routesByDir` keys must be forward-slash dirnames of the route file paths.
321
+ * Comparisons happen in forward-slash space — inputs go through `toSlash` so
322
+ * callers still holding native-separator paths hit the `current === appDir`
323
+ * termination instead of overshooting the app root.
323
324
  *
324
325
  * Exported for tests.
325
326
  */
@@ -1,10 +1,9 @@
1
- import { normalizePathSeparators } from "../utils/path.js";
1
+ import path, { toSlash } from "../deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
2
2
  import { decodeRouteSegment, isInvisibleSegment, sortRoutes } from "./utils.js";
3
3
  import { findFileWithExts, scanWithExtensions } from "./file-matcher.js";
4
4
  import { validateRoutePatterns } from "./route-validation.js";
5
5
  import { compareStrings } from "../utils/compare.js";
6
6
  import fs from "node:fs";
7
- import path from "node:path";
8
7
  import { createHash } from "node:crypto";
9
8
  //#region src/routing/app-route-graph.ts
10
9
  /**
@@ -363,10 +362,6 @@ function createRouteManifestGraphVersion(segmentGraph) {
363
362
  }
364
363
  /**
365
364
  * Build the App Router route graph by scanning `appDir`.
366
- *
367
- * `appDir` must be forward-slash. Every path in the graph is derived from it
368
- * with `path.posix.*` and `findFile`, so a native appDir would produce mixed
369
- * separators on Windows. Production callers normalize it at their entry.
370
365
  */
371
366
  async function buildAppRouteGraph(appDir, matcher) {
372
367
  const routes = [];
@@ -384,8 +379,8 @@ async function buildAppRouteGraph(appDir, matcher) {
384
379
  const routePatterns = new Set(routes.map((route) => route.pattern));
385
380
  const ghostParentRoutes = [];
386
381
  for await (const file of scanWithExtensions("**/layout", appDir, scanMatcher.extensions, excludeDir)) {
387
- const dir = path.posix.dirname(file);
388
- const routeDir = dir === "." ? appDir : path.posix.join(appDir, dir);
382
+ const dir = path.dirname(file);
383
+ const routeDir = dir === "." ? appDir : path.join(appDir, dir);
389
384
  if (!hasParallelSlotDirectory(routeDir)) continue;
390
385
  if (discoverParallelSlots(routeDir, appDir, scanMatcher, true).length === 0) continue;
391
386
  const route = directoryToAppRoute(dir, appDir, scanMatcher, null, null, true);
@@ -440,9 +435,9 @@ function validatePageRouteConflicts(routes, appDir) {
440
435
  }
441
436
  }
442
437
  function formatAppFilePath(filePath, appDir) {
443
- const relativePath = normalizePathSeparators(path.relative(appDir, filePath));
438
+ const relativePath = path.relative(appDir, filePath);
444
439
  const parsedPath = path.parse(relativePath);
445
- const withoutExtension = normalizePathSeparators(path.join(parsedPath.dir, parsedPath.name));
440
+ const withoutExtension = path.join(parsedPath.dir, parsedPath.name);
446
441
  return withoutExtension.startsWith("/") ? withoutExtension : `/${withoutExtension}`;
447
442
  }
448
443
  /**
@@ -464,11 +459,14 @@ function discoverSlotSubRoutes(routes, matcher, ghostParents = []) {
464
459
  const subPage = slotPages.get(slot.key);
465
460
  if (subPage !== void 0) {
466
461
  const configLayoutPaths = findSlotConfigLayoutPaths(slot.ownerDir, subPage, matcher);
462
+ const notFoundBoundary = findSlotNotFoundBoundary(slot.ownerDir, subPage, matcher);
467
463
  return {
468
464
  ...slot,
469
465
  pagePath: subPage,
470
466
  configLayoutPaths,
471
467
  configLayoutTreePositions: findSlotConfigLayoutTreePositions(slot.ownerDir, configLayoutPaths),
468
+ notFoundPath: notFoundBoundary.path,
469
+ notFoundTreePosition: notFoundBoundary.treePosition,
472
470
  routeSegments: rawSegments
473
471
  };
474
472
  }
@@ -550,11 +548,14 @@ function discoverSlotSubRoutes(routes, matcher, ghostParents = []) {
550
548
  const subSlots = parentRoute.parallelSlots.map((slot) => {
551
549
  const subPage = slotPages.get(slot.key);
552
550
  const configLayoutPaths = findSlotConfigLayoutPaths(slot.ownerDir, subPage ?? null, matcher);
551
+ const notFoundBoundary = findSlotNotFoundBoundary(slot.ownerDir, subPage ?? null, matcher);
553
552
  return {
554
553
  ...slot,
555
554
  pagePath: subPage || null,
556
555
  configLayoutPaths,
557
556
  configLayoutTreePositions: findSlotConfigLayoutTreePositions(slot.ownerDir, configLayoutPaths),
557
+ notFoundPath: notFoundBoundary.path,
558
+ notFoundTreePosition: notFoundBoundary.treePosition,
558
559
  routeSegments: subPage ? rawSegments : null
559
560
  };
560
561
  });
@@ -583,10 +584,13 @@ function discoverSlotSubRoutes(routes, matcher, ghostParents = []) {
583
584
  errorPath: parentRoute.errorPath,
584
585
  layoutErrorPaths: parentRoute.layoutErrorPaths,
585
586
  notFoundPath: parentRoute.notFoundPath,
587
+ notFoundTreePosition: parentRoute.notFoundTreePosition,
586
588
  notFoundPaths: parentRoute.notFoundPaths,
587
589
  forbiddenPaths: parentRoute.forbiddenPaths,
588
590
  forbiddenPath: parentRoute.forbiddenPath,
591
+ forbiddenTreePosition: parentRoute.forbiddenTreePosition,
589
592
  unauthorizedPath: parentRoute.unauthorizedPath,
593
+ unauthorizedTreePosition: parentRoute.unauthorizedTreePosition,
590
594
  unauthorizedPaths: parentRoute.unauthorizedPaths,
591
595
  routeSegments: [...parentRoute.routeSegments, ...rawSegments],
592
596
  childrenRouteSegments: childrenDefault ? parentRoute.routeSegments : void 0,
@@ -674,6 +678,25 @@ function findSlotConfigLayoutTreePositions(slotDir, layoutPaths) {
674
678
  return relativeDir ? relativeDir.split(path.sep).filter(Boolean).length : 0;
675
679
  });
676
680
  }
681
+ function findSlotNotFoundBoundary(slotDir, pagePath, matcher) {
682
+ let dir = pagePath ? path.dirname(pagePath) : slotDir;
683
+ while (dir === slotDir || dir.startsWith(`${slotDir}${path.sep}`)) {
684
+ const boundaryPath = findFile(dir, "not-found", matcher);
685
+ if (boundaryPath) {
686
+ const relativeDir = path.relative(slotDir, dir);
687
+ return {
688
+ path: boundaryPath,
689
+ treePosition: relativeDir ? relativeDir.split(path.sep).filter(Boolean).length : 0
690
+ };
691
+ }
692
+ if (dir === slotDir) break;
693
+ dir = path.dirname(dir);
694
+ }
695
+ return {
696
+ path: null,
697
+ treePosition: null
698
+ };
699
+ }
677
700
  /**
678
701
  * Find a sibling catch-all page directly under `dir`, i.e. a `[...slug]` or
679
702
  * `[[...slug]]` directory that contains a `page` file. Returns the absolute
@@ -704,27 +727,15 @@ function findCatchAllPage(dir, matcher) {
704
727
  }
705
728
  /**
706
729
  * Convert a file path relative to app/ into an AppRoute.
707
- *
708
- * `file` and `appDir` must be forward-slash. `file` comes from
709
- * `scanWithExtensions` (already forward-slash) and is joined onto `appDir` with
710
- * `path.posix.join` to form the page/route path, so a native input would
711
- * produce a mixed separator on Windows.
712
730
  */
713
731
  function fileToAppRoute(file, appDir, type, matcher) {
714
- let dir = path.posix.dirname(file);
715
- if (type === "page" && dir !== "." && path.posix.basename(dir) === "@children") {
716
- const parent = path.posix.dirname(dir);
732
+ let dir = path.dirname(file);
733
+ if (type === "page" && dir !== "." && path.basename(dir) === "@children") {
734
+ const parent = path.dirname(dir);
717
735
  dir = parent === "" || parent === "." ? "." : parent;
718
736
  }
719
- return directoryToAppRoute(dir, appDir, matcher, type === "page" ? path.posix.join(appDir, file) : null, type === "route" ? path.posix.join(appDir, file) : null);
737
+ return directoryToAppRoute(dir, appDir, matcher, type === "page" ? path.join(appDir, file) : null, type === "route" ? path.join(appDir, file) : null);
720
738
  }
721
- /**
722
- * `dir`, `appDir`, `pagePath`, and `routePath` must all be forward-slash. `dir`
723
- * is split on `path.posix.sep` and joined onto `appDir` with `path.posix.join`.
724
- * `appDir` is threaded to the layout/slot/boundary discovery below, which builds
725
- * paths the same way. `pagePath` and `routePath` are stored on the route node as
726
- * canonical ids that get compared and re-joined downstream.
727
- */
728
739
  function directoryToAppRoute(dir, appDir, matcher, pagePath, routePath, includeNestedOnlySlots = false) {
729
740
  const segments = dir === "." ? [] : dir.split("/");
730
741
  const params = [];
@@ -743,13 +754,16 @@ function directoryToAppRoute(dir, appDir, matcher, pagePath, routePath, includeN
743
754
  const errorEntries = discoverSegmentErrors(segments, appDir, matcher);
744
755
  const errorPaths = errorEntries.map((entry) => entry.path);
745
756
  const errorTreePositions = errorEntries.map((entry) => entry.treePosition);
746
- const routeDir = dir === "." ? appDir : path.posix.join(appDir, dir);
757
+ const routeDir = dir === "." ? appDir : path.join(appDir, dir);
747
758
  const effectivePagePath = pagePath ?? (routePath ? null : findFile(routeDir, "default", matcher));
748
759
  const loadingPath = findFile(routeDir, "loading", matcher);
749
760
  const errorPath = findFile(routeDir, "error", matcher);
750
- const notFoundPath = discoverBoundaryFile(segments, appDir, "not-found", matcher);
751
- const forbiddenPath = discoverBoundaryFile(segments, appDir, "forbidden", matcher);
752
- const unauthorizedPath = discoverBoundaryFile(segments, appDir, "unauthorized", matcher);
761
+ const notFoundEntry = discoverBoundaryFileEntry(segments, appDir, "not-found", matcher);
762
+ const notFoundPath = notFoundEntry?.path ?? null;
763
+ const forbiddenEntry = discoverBoundaryFileEntry(segments, appDir, "forbidden", matcher);
764
+ const forbiddenPath = forbiddenEntry?.path ?? null;
765
+ const unauthorizedEntry = discoverBoundaryFileEntry(segments, appDir, "unauthorized", matcher);
766
+ const unauthorizedPath = unauthorizedEntry?.path ?? null;
753
767
  const notFoundPaths = discoverBoundaryFilePerLayout(layouts, "not-found", matcher);
754
768
  const forbiddenPaths = discoverBoundaryFilePerLayout(layouts, "forbidden", matcher);
755
769
  const unauthorizedPaths = discoverBoundaryFilePerLayout(layouts, "unauthorized", matcher);
@@ -776,10 +790,13 @@ function directoryToAppRoute(dir, appDir, matcher, pagePath, routePath, includeN
776
790
  errorPaths,
777
791
  errorTreePositions,
778
792
  notFoundPath,
793
+ notFoundTreePosition: notFoundEntry?.treePosition ?? null,
779
794
  notFoundPaths,
780
795
  forbiddenPaths,
781
796
  forbiddenPath,
797
+ forbiddenTreePosition: forbiddenEntry?.treePosition ?? null,
782
798
  unauthorizedPath,
799
+ unauthorizedTreePosition: unauthorizedEntry?.treePosition ?? null,
783
800
  unauthorizedPaths,
784
801
  routeSegments: segments,
785
802
  templateTreePositions,
@@ -850,8 +867,9 @@ function convertTreePathToRouteParts(treePath) {
850
867
  function computeLayoutTreePositions(appDir, layouts) {
851
868
  return layouts.map((layoutPath) => {
852
869
  const layoutDir = path.dirname(layoutPath);
853
- if (layoutDir === appDir) return 0;
854
- return path.relative(appDir, layoutDir).split(path.sep).length;
870
+ const relative = path.relative(appDir, layoutDir);
871
+ if (relative === "") return 0;
872
+ return relative.split(path.sep).length;
855
873
  });
856
874
  }
857
875
  /**
@@ -937,7 +955,7 @@ function discoverLayoutAlignedErrors(segments, appDir, matcher) {
937
955
  * by walking from the route's directory up to the app root.
938
956
  * Returns the first (closest) file found, or null.
939
957
  */
940
- function discoverBoundaryFile(segments, appDir, fileName, matcher) {
958
+ function discoverBoundaryFileEntry(segments, appDir, fileName, matcher) {
941
959
  const dirs = [];
942
960
  let dir = appDir;
943
961
  dirs.push(dir);
@@ -947,7 +965,10 @@ function discoverBoundaryFile(segments, appDir, fileName, matcher) {
947
965
  }
948
966
  for (let i = dirs.length - 1; i >= 0; i--) {
949
967
  const f = findFile(dirs[i], fileName, matcher);
950
- if (f) return f;
968
+ if (f) return {
969
+ path: f,
970
+ treePosition: i
971
+ };
951
972
  }
952
973
  return null;
953
974
  }
@@ -976,10 +997,6 @@ function discoverBoundaryFilePerLayout(layouts, fileName, matcher) {
976
997
  * Walk from appDir through each segment to the route's directory. At each level
977
998
  * that has @slot dirs, collect them. Slots at the route's own directory level
978
999
  * use page.tsx; slots at ancestor levels use default.tsx only.
979
- *
980
- * `appDir` and `routeDir` must be forward-slash — `currentDir` descends from
981
- * `appDir` via `path.posix.join`, and the `dir === routeDir` active-level test
982
- * below only matches when both share the canonical separator.
983
1000
  */
984
1001
  function discoverInheritedParallelSlots(segments, appDir, routeDir, matcher, includeNestedOnlySlots = false) {
985
1002
  const slotMap = /* @__PURE__ */ new Map();
@@ -992,7 +1009,7 @@ function discoverInheritedParallelSlots(segments, appDir, routeDir, matcher, inc
992
1009
  segmentIndex: 0
993
1010
  });
994
1011
  for (let i = 0; i < segments.length; i++) {
995
- currentDir = path.posix.join(currentDir, segments[i]);
1012
+ currentDir = path.join(currentDir, segments[i]);
996
1013
  if (findFile(currentDir, "layout", matcher)) layoutIdx++;
997
1014
  dirsToCheck.push({
998
1015
  dir: currentDir,
@@ -1155,9 +1172,6 @@ function patternsStructurallyEquivalent(a, b) {
1155
1172
  *
1156
1173
  * Returns the absolute page path, or null if no root-level page is found.
1157
1174
  *
1158
- * `slotDir` must be forward-slash: the `path.posix.join` descent stays a
1159
- * canonical id only when the base already is.
1160
- *
1161
1175
  * Only descends into route-group directories (those whose name starts with `(`
1162
1176
  * and ends with `)`). Dynamic segments, regular named dirs, and `@slot` dirs
1163
1177
  * are not transparent and are therefore not searched.
@@ -1169,7 +1183,7 @@ function findSlotRootPage(slotDir, matcher) {
1169
1183
  for (const entry of entries) {
1170
1184
  if (!entry.isDirectory()) continue;
1171
1185
  if (!entry.name.startsWith("(") || !entry.name.endsWith(")")) continue;
1172
- const found = findSlotRootPage(path.posix.join(slotDir, entry.name), matcher);
1186
+ const found = findSlotRootPage(path.join(slotDir, entry.name), matcher);
1173
1187
  if (found) return found;
1174
1188
  }
1175
1189
  return null;
@@ -1178,9 +1192,6 @@ function findSlotRootPage(slotDir, matcher) {
1178
1192
  * Discover parallel route slots (@team, @analytics, etc.) in a directory.
1179
1193
  * Returns a ParallelSlot for each @-prefixed subdirectory that has a page,
1180
1194
  * default component, intercepting route, or nested page-backed sub-route.
1181
- *
1182
- * `dir` and `appDir` must be forward-slash. The slot directory is built from
1183
- * `dir` with `path.posix.join`.
1184
1195
  */
1185
1196
  function discoverParallelSlots(dir, appDir, matcher, includeNestedOnlySlots = false) {
1186
1197
  const entries = readDirEntriesCached(dir, matcher);
@@ -1189,7 +1200,7 @@ function discoverParallelSlots(dir, appDir, matcher, includeNestedOnlySlots = fa
1189
1200
  if (!entry.isDirectory() || !entry.name.startsWith("@")) continue;
1190
1201
  if (entry.name === "@children") continue;
1191
1202
  const slotName = entry.name.slice(1);
1192
- const slotDir = path.posix.join(dir, entry.name);
1203
+ const slotDir = path.join(dir, entry.name);
1193
1204
  const pagePath = findSlotRootPage(slotDir, matcher);
1194
1205
  const defaultPath = findFile(slotDir, "default", matcher);
1195
1206
  const interceptingRoutes = discoverInterceptingRoutes(slotDir, dir, appDir, matcher);
@@ -1198,9 +1209,10 @@ function discoverParallelSlots(dir, appDir, matcher, includeNestedOnlySlots = fa
1198
1209
  const ownerSegments = path.relative(appDir, dir).split(path.sep).filter((segment) => segment.length > 0);
1199
1210
  const ownerTreePath = createAppRouteGraphTreePath(ownerSegments, ownerSegments.length);
1200
1211
  const configLayoutPaths = findSlotConfigLayoutPaths(slotDir, pagePath, matcher);
1212
+ const notFoundBoundary = findSlotNotFoundBoundary(slotDir, pagePath, matcher);
1201
1213
  slots.push({
1202
1214
  id: createAppRouteGraphSlotId(slotName, ownerTreePath),
1203
- key: `${slotName}@${normalizePathSeparators(path.relative(appDir, slotDir))}`,
1215
+ key: `${slotName}@${path.relative(appDir, slotDir)}`,
1204
1216
  name: slotName,
1205
1217
  ownerDir: slotDir,
1206
1218
  ownerTreePath,
@@ -1212,6 +1224,8 @@ function discoverParallelSlots(dir, appDir, matcher, includeNestedOnlySlots = fa
1212
1224
  configLayoutTreePositions: findSlotConfigLayoutTreePositions(slotDir, configLayoutPaths),
1213
1225
  loadingPath: findFile(slotDir, "loading", matcher),
1214
1226
  errorPath: findFile(slotDir, "error", matcher),
1227
+ notFoundPath: notFoundBoundary.path,
1228
+ notFoundTreePosition: notFoundBoundary.treePosition,
1215
1229
  interceptingRoutes,
1216
1230
  layoutIndex: -1,
1217
1231
  routeSegments: pagePath ? [] : null
@@ -1259,9 +1273,6 @@ function isInterceptionMarkerDir(name) {
1259
1273
  * Intercepting routes use conventions like (.)photo, (..)feed, (...), etc.
1260
1274
  * They intercept navigation to another route and render within the slot instead.
1261
1275
  *
1262
- * `slotDir`, `routeDir`, and `appDir` must be forward-slash. They are passed
1263
- * down to `path.posix.join` when building the intercept page paths.
1264
- *
1265
1276
  * @param slotDir - The parallel slot directory (e.g. app/feed/@modal)
1266
1277
  * @param routeDir - The directory of the route that owns this slot (e.g. app/feed)
1267
1278
  * @param appDir - The root app directory
@@ -1269,7 +1280,7 @@ function isInterceptionMarkerDir(name) {
1269
1280
  function discoverInterceptingRoutes(slotDir, routeDir, appDir, matcher) {
1270
1281
  if (!fs.existsSync(slotDir)) return [];
1271
1282
  const results = [];
1272
- scanForInterceptingPages(slotDir, routeDir, appDir, results, matcher);
1283
+ scanForInterceptingPages(slotDir, slotDir, routeDir, appDir, results, matcher);
1273
1284
  return results;
1274
1285
  }
1275
1286
  /**
@@ -1280,17 +1291,13 @@ function discoverInterceptingRoutes(slotDir, routeDir, appDir, matcher) {
1280
1291
  * Sibling intercepts use the same conventions and target-computation logic as
1281
1292
  * slot intercepts, but their intercepting page replaces the full page response
1282
1293
  * (not a slot) during soft navigation.
1283
- *
1284
- * `appDir` and each route's `pagePath` / `routePath` must be forward-slash. The
1285
- * owner directories are derived from `pagePath` / `routePath` and matched
1286
- * against `appDir`-relative paths built with `path.posix.*`.
1287
1294
  */
1288
1295
  function discoverSiblingInterceptingRoutes(routes, appDir, matcher) {
1289
1296
  const routesByDir = /* @__PURE__ */ new Map();
1290
1297
  for (const route of routes) {
1291
1298
  const filePath = route.pagePath ?? route.routePath;
1292
1299
  if (!filePath) continue;
1293
- const routeDir = path.posix.dirname(filePath);
1300
+ const routeDir = path.dirname(filePath);
1294
1301
  if (!routesByDir.has(routeDir)) routesByDir.set(routeDir, route);
1295
1302
  }
1296
1303
  function walk(dir) {
@@ -1304,13 +1311,13 @@ function discoverSiblingInterceptingRoutes(routes, appDir, matcher) {
1304
1311
  if (!entry.isDirectory()) continue;
1305
1312
  if (entry.name.startsWith("_")) continue;
1306
1313
  if (entry.name.startsWith("@")) continue;
1307
- const childDir = path.posix.join(dir, entry.name);
1314
+ const childDir = path.join(dir, entry.name);
1308
1315
  const marker = matchInterceptConvention(entry.name);
1309
1316
  if (marker) {
1310
1317
  const restOfName = entry.name.slice(marker.prefix.length);
1311
1318
  const parentDir = dir;
1312
1319
  const results = [];
1313
- collectInterceptingPages(childDir, childDir, marker.convention, restOfName, parentDir, appDir, parentDir, results, matcher);
1320
+ collectInterceptingPages(childDir, childDir, marker.convention, restOfName, parentDir, appDir, parentDir, null, results, matcher);
1314
1321
  for (const ir of results) {
1315
1322
  ir.slotId = createAppRouteGraphSiblingInterceptSlotId(ir.sourceMatchPattern);
1316
1323
  const owner = findOwnerRouteForDir(parentDir, appDir, routes, routesByDir);
@@ -1335,18 +1342,15 @@ function discoverSiblingInterceptingRoutes(routes, appDir, matcher) {
1335
1342
  * no sibling pages at all (e.g. `deep/path/(...)target` with no
1336
1343
  * `deep/path/page.tsx`).
1337
1344
  *
1338
- * All comparisons happen in forward-slash space: `appDir` is forward-slash
1339
- * (normalized once in the config hook), but `dir` and route file paths
1340
- * descend through native `path.join`/`path.dirname`, which reintroduce
1341
- * backslashes on Windows. Without normalizing, the `current === appDir`
1342
- * termination never fires there and the walk overshoots the app root.
1343
- * `routesByDir` keys must be forward-slash dirnames of the route file paths.
1345
+ * Comparisons happen in forward-slash space — inputs go through `toSlash` so
1346
+ * callers still holding native-separator paths hit the `current === appDir`
1347
+ * termination instead of overshooting the app root.
1344
1348
  *
1345
1349
  * Exported for tests.
1346
1350
  */
1347
1351
  function findOwnerRouteForDir(dir, appDir, routes, routesByDir) {
1348
- const appRoot = normalizePathSeparators(appDir);
1349
- let current = normalizePathSeparators(dir);
1352
+ const appRoot = toSlash(appDir);
1353
+ let current = toSlash(dir);
1350
1354
  while (true) {
1351
1355
  const exact = routesByDir.get(current);
1352
1356
  if (exact) return exact;
@@ -1355,7 +1359,7 @@ function findOwnerRouteForDir(dir, appDir, routes, routesByDir) {
1355
1359
  for (const route of routes) {
1356
1360
  const filePath = route.pagePath ?? route.routePath;
1357
1361
  if (!filePath) continue;
1358
- if (!normalizePathSeparators(filePath).startsWith(currentWithSep)) continue;
1362
+ if (!toSlash(filePath).startsWith(currentWithSep)) continue;
1359
1363
  if (!best || route.patternParts.length < best.patternParts.length) best = route;
1360
1364
  }
1361
1365
  if (best) return best;
@@ -1369,22 +1373,19 @@ function findOwnerRouteForDir(dir, appDir, routes, routesByDir) {
1369
1373
  /**
1370
1374
  * Recursively scan a directory tree for page.tsx files that are inside
1371
1375
  * intercepting route directories.
1372
- *
1373
- * `currentDir`, `routeDir`, and `appDir` must be forward-slash. `currentDir`
1374
- * descends with `path.posix.join` when building the intercept page paths.
1375
1376
  */
1376
- function scanForInterceptingPages(currentDir, routeDir, appDir, results, matcher) {
1377
+ function scanForInterceptingPages(currentDir, slotRootDir, routeDir, appDir, results, matcher) {
1377
1378
  if (!fs.existsSync(currentDir)) return;
1378
1379
  const entries = fs.readdirSync(currentDir, { withFileTypes: true });
1379
1380
  for (const entry of entries) {
1380
1381
  if (!entry.isDirectory()) continue;
1381
1382
  if (entry.name.startsWith("_")) continue;
1382
1383
  const interceptMatch = matchInterceptConvention(entry.name);
1383
- const interceptDir = path.posix.join(currentDir, entry.name);
1384
+ const interceptDir = path.join(currentDir, entry.name);
1384
1385
  if (interceptMatch) {
1385
1386
  const restOfName = entry.name.slice(interceptMatch.prefix.length);
1386
- collectInterceptingPages(interceptDir, interceptDir, interceptMatch.convention, restOfName, routeDir, appDir, currentDir, results, matcher);
1387
- } else scanForInterceptingPages(interceptDir, routeDir, appDir, results, matcher);
1387
+ collectInterceptingPages(interceptDir, interceptDir, interceptMatch.convention, restOfName, routeDir, appDir, currentDir, slotRootDir, results, matcher);
1388
+ } else scanForInterceptingPages(interceptDir, slotRootDir, routeDir, appDir, results, matcher);
1388
1389
  }
1389
1390
  }
1390
1391
  /**
@@ -1397,14 +1398,8 @@ function matchInterceptConvention(name) {
1397
1398
  /**
1398
1399
  * Collect page.tsx files inside an intercepting route directory tree
1399
1400
  * and compute their target URL patterns.
1400
- *
1401
- * `currentDir`, `interceptRoot`, `routeDir`, `appDir`, and `interceptParentDir`
1402
- * must all be forward-slash. `currentDir` descends with `path.posix.join` and
1403
- * its `findFile` results become the stored layout/page paths. The others are
1404
- * relativized against `appDir` (and each other) to derive the intercept page
1405
- * segments and URL patterns.
1406
1401
  */
1407
- function collectInterceptingPages(currentDir, interceptRoot, convention, interceptSegment, routeDir, appDir, interceptParentDir, results, matcher, parentLayoutPaths = []) {
1402
+ function collectInterceptingPages(currentDir, interceptRoot, convention, interceptSegment, routeDir, appDir, interceptParentDir, slotRootDir, results, matcher, parentLayoutPaths = []) {
1408
1403
  const currentLayoutPath = findFile(currentDir, "layout", matcher);
1409
1404
  const layoutPaths = currentLayoutPath ? [...parentLayoutPaths, currentLayoutPath] : parentLayoutPaths;
1410
1405
  const page = findFile(currentDir, "page", matcher);
@@ -1412,17 +1407,23 @@ function collectInterceptingPages(currentDir, interceptRoot, convention, interce
1412
1407
  const targetPattern = computeInterceptTarget(convention, interceptSegment, currentDir, interceptRoot, routeDir, appDir);
1413
1408
  if (targetPattern) {
1414
1409
  const sourceMatchPattern = computeInterceptSourceMatchPattern(interceptParentDir, appDir);
1410
+ const notFoundBoundary = findSlotNotFoundBoundary(slotRootDir ?? interceptRoot, page, matcher);
1411
+ const branchSegments = [interceptSegment, ...path.relative(interceptRoot, path.dirname(page)).split(path.sep).filter(Boolean)];
1412
+ const slotParentSegments = slotRootDir ? path.relative(slotRootDir, interceptParentDir).split(path.sep).filter(Boolean) : [];
1415
1413
  results.push({
1416
- branchSegments: [interceptSegment, ...path.relative(interceptRoot, path.dirname(page)).split(path.sep).filter(Boolean)],
1414
+ branchSegments,
1417
1415
  convention,
1418
1416
  layoutPaths: [...layoutPaths],
1419
1417
  layoutSegments: layoutPaths.map((layoutPath) => {
1420
1418
  return [interceptSegment, ...path.relative(interceptRoot, path.dirname(layoutPath)).split(path.sep).filter(Boolean)];
1421
1419
  }),
1420
+ notFoundBranchSegments: [...slotParentSegments, ...branchSegments],
1421
+ notFoundPath: notFoundBoundary.path,
1422
+ notFoundTreePosition: notFoundBoundary.treePosition === null ? null : slotRootDir ? notFoundBoundary.treePosition : notFoundBoundary.treePosition + 1,
1422
1423
  targetPattern: targetPattern.pattern,
1423
1424
  sourceMatchPattern,
1424
1425
  pagePath: page,
1425
- sourcePageSegments: normalizePathSeparators(path.relative(appDir, path.dirname(page))).split("/").filter(Boolean),
1426
+ sourcePageSegments: path.relative(appDir, path.dirname(page)).split("/").filter(Boolean),
1426
1427
  params: targetPattern.params
1427
1428
  });
1428
1429
  }
@@ -1432,7 +1433,7 @@ function collectInterceptingPages(currentDir, interceptRoot, convention, interce
1432
1433
  for (const entry of entries) {
1433
1434
  if (!entry.isDirectory()) continue;
1434
1435
  if (entry.name.startsWith("_")) continue;
1435
- collectInterceptingPages(path.posix.join(currentDir, entry.name), interceptRoot, convention, interceptSegment, routeDir, appDir, interceptParentDir, results, matcher, layoutPaths);
1436
+ collectInterceptingPages(path.join(currentDir, entry.name), interceptRoot, convention, interceptSegment, routeDir, appDir, interceptParentDir, slotRootDir, results, matcher, layoutPaths);
1436
1437
  }
1437
1438
  }
1438
1439
  /**
@@ -1552,9 +1553,6 @@ const findFileProbeCache = /* @__PURE__ */ new WeakMap();
1552
1553
  * registered per-scan cache; otherwise falls back to a direct probe (identical
1553
1554
  * result). The `null` "not found" outcome is cached too, so repeated misses on
1554
1555
  * shared ancestors cost a single set of `existsSync` calls per scan.
1555
- *
1556
- * `dir` must be forward-slash. The returned path comes from `findFileWithExts`,
1557
- * so it is forward-slash too.
1558
1556
  */
1559
1557
  function findFile(dir, name, matcher) {
1560
1558
  const cache = findFileProbeCache.get(matcher);
@@ -12,16 +12,11 @@ declare function invalidateAppRouteCache(): void;
12
12
  * TODO(#726): Layer 4 should consume this read model directly once the
13
13
  * navigation planner owns route graph facts.
14
14
  *
15
- * `appDir` must be forward-slash — callers normalize it at their entry, and it
16
- * flows into `buildAppRouteGraph`, which builds every path with `path.posix.*`.
17
- *
18
15
  * @internal
19
16
  */
20
17
  declare function appRouteGraph(appDir: string, pageExtensions?: readonly string[], matcher?: ValidFileMatcher): Promise<AppRouteGraph>;
21
18
  /**
22
19
  * Scan the app/ directory and return a list of routes.
23
- *
24
- * `appDir` must be forward-slash — it is forwarded to `appRouteGraph`.
25
20
  */
26
21
  declare function appRouter(appDir: string, pageExtensions?: readonly string[], matcher?: ValidFileMatcher): Promise<AppRouteGraphRoute[]>;
27
22
  /**