@timber-js/app 0.1.0

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 (310) hide show
  1. package/bin/timber.mjs +5 -0
  2. package/dist/_chunks/error-boundary-dj-WO5uq.js +121 -0
  3. package/dist/_chunks/error-boundary-dj-WO5uq.js.map +1 -0
  4. package/dist/_chunks/format-DNt20Kt8.js +163 -0
  5. package/dist/_chunks/format-DNt20Kt8.js.map +1 -0
  6. package/dist/_chunks/interception-DIaZN1bF.js +669 -0
  7. package/dist/_chunks/interception-DIaZN1bF.js.map +1 -0
  8. package/dist/_chunks/metadata-routes-BDnswgRO.js +141 -0
  9. package/dist/_chunks/metadata-routes-BDnswgRO.js.map +1 -0
  10. package/dist/_chunks/registry-DUIpYD_x.js +20 -0
  11. package/dist/_chunks/registry-DUIpYD_x.js.map +1 -0
  12. package/dist/_chunks/request-context-D6XHINkR.js +330 -0
  13. package/dist/_chunks/request-context-D6XHINkR.js.map +1 -0
  14. package/dist/_chunks/tracing-BtOwb8O6.js +174 -0
  15. package/dist/_chunks/tracing-BtOwb8O6.js.map +1 -0
  16. package/dist/_chunks/use-cookie-8ZlA0rr3.js +125 -0
  17. package/dist/_chunks/use-cookie-8ZlA0rr3.js.map +1 -0
  18. package/dist/adapters/cloudflare.d.ts +92 -0
  19. package/dist/adapters/cloudflare.d.ts.map +1 -0
  20. package/dist/adapters/cloudflare.js +188 -0
  21. package/dist/adapters/cloudflare.js.map +1 -0
  22. package/dist/adapters/nitro.d.ts +72 -0
  23. package/dist/adapters/nitro.d.ts.map +1 -0
  24. package/dist/adapters/nitro.js +217 -0
  25. package/dist/adapters/nitro.js.map +1 -0
  26. package/dist/adapters/types.d.ts +53 -0
  27. package/dist/adapters/types.d.ts.map +1 -0
  28. package/dist/cache/index.d.ts +52 -0
  29. package/dist/cache/index.d.ts.map +1 -0
  30. package/dist/cache/index.js +283 -0
  31. package/dist/cache/index.js.map +1 -0
  32. package/dist/cache/redis-handler.d.ts +45 -0
  33. package/dist/cache/redis-handler.d.ts.map +1 -0
  34. package/dist/cache/register-cached-function.d.ts +17 -0
  35. package/dist/cache/register-cached-function.d.ts.map +1 -0
  36. package/dist/cache/singleflight.d.ts +11 -0
  37. package/dist/cache/singleflight.d.ts.map +1 -0
  38. package/dist/cache/stable-stringify.d.ts +7 -0
  39. package/dist/cache/stable-stringify.d.ts.map +1 -0
  40. package/dist/cache/timber-cache.d.ts +21 -0
  41. package/dist/cache/timber-cache.d.ts.map +1 -0
  42. package/dist/cli.d.ts +44 -0
  43. package/dist/cli.d.ts.map +1 -0
  44. package/dist/cli.js +135 -0
  45. package/dist/cli.js.map +1 -0
  46. package/dist/client/browser-entry.d.ts +22 -0
  47. package/dist/client/browser-entry.d.ts.map +1 -0
  48. package/dist/client/error-boundary.d.ts +42 -0
  49. package/dist/client/error-boundary.d.ts.map +1 -0
  50. package/dist/client/form.d.ts +115 -0
  51. package/dist/client/form.d.ts.map +1 -0
  52. package/dist/client/head.d.ts +16 -0
  53. package/dist/client/head.d.ts.map +1 -0
  54. package/dist/client/history.d.ts +29 -0
  55. package/dist/client/history.d.ts.map +1 -0
  56. package/dist/client/index.d.ts +32 -0
  57. package/dist/client/index.d.ts.map +1 -0
  58. package/dist/client/index.js +1218 -0
  59. package/dist/client/index.js.map +1 -0
  60. package/dist/client/link-navigate-interceptor.d.ts +28 -0
  61. package/dist/client/link-navigate-interceptor.d.ts.map +1 -0
  62. package/dist/client/link-status-provider.d.ts +11 -0
  63. package/dist/client/link-status-provider.d.ts.map +1 -0
  64. package/dist/client/link.d.ts +119 -0
  65. package/dist/client/link.d.ts.map +1 -0
  66. package/dist/client/nuqs-adapter.d.ts +11 -0
  67. package/dist/client/nuqs-adapter.d.ts.map +1 -0
  68. package/dist/client/router-ref.d.ts +11 -0
  69. package/dist/client/router-ref.d.ts.map +1 -0
  70. package/dist/client/router.d.ts +85 -0
  71. package/dist/client/router.d.ts.map +1 -0
  72. package/dist/client/segment-cache.d.ts +88 -0
  73. package/dist/client/segment-cache.d.ts.map +1 -0
  74. package/dist/client/segment-context.d.ts +32 -0
  75. package/dist/client/segment-context.d.ts.map +1 -0
  76. package/dist/client/ssr-data.d.ts +64 -0
  77. package/dist/client/ssr-data.d.ts.map +1 -0
  78. package/dist/client/types.d.ts +5 -0
  79. package/dist/client/types.d.ts.map +1 -0
  80. package/dist/client/unload-guard.d.ts +18 -0
  81. package/dist/client/unload-guard.d.ts.map +1 -0
  82. package/dist/client/use-cookie.d.ts +37 -0
  83. package/dist/client/use-cookie.d.ts.map +1 -0
  84. package/dist/client/use-link-status.d.ts +35 -0
  85. package/dist/client/use-link-status.d.ts.map +1 -0
  86. package/dist/client/use-navigation-pending.d.ts +26 -0
  87. package/dist/client/use-navigation-pending.d.ts.map +1 -0
  88. package/dist/client/use-params.d.ts +50 -0
  89. package/dist/client/use-params.d.ts.map +1 -0
  90. package/dist/client/use-pathname.d.ts +20 -0
  91. package/dist/client/use-pathname.d.ts.map +1 -0
  92. package/dist/client/use-query-states.d.ts +36 -0
  93. package/dist/client/use-query-states.d.ts.map +1 -0
  94. package/dist/client/use-router.d.ts +39 -0
  95. package/dist/client/use-router.d.ts.map +1 -0
  96. package/dist/client/use-search-params.d.ts +24 -0
  97. package/dist/client/use-search-params.d.ts.map +1 -0
  98. package/dist/client/use-selected-layout-segment.d.ts +68 -0
  99. package/dist/client/use-selected-layout-segment.d.ts.map +1 -0
  100. package/dist/content/index.d.ts +11 -0
  101. package/dist/content/index.d.ts.map +1 -0
  102. package/dist/content/index.js +2 -0
  103. package/dist/cookies/define-cookie.d.ts +61 -0
  104. package/dist/cookies/define-cookie.d.ts.map +1 -0
  105. package/dist/cookies/index.d.ts +3 -0
  106. package/dist/cookies/index.d.ts.map +1 -0
  107. package/dist/cookies/index.js +82 -0
  108. package/dist/cookies/index.js.map +1 -0
  109. package/dist/fonts/ast.d.ts +38 -0
  110. package/dist/fonts/ast.d.ts.map +1 -0
  111. package/dist/fonts/css.d.ts +43 -0
  112. package/dist/fonts/css.d.ts.map +1 -0
  113. package/dist/fonts/fallbacks.d.ts +36 -0
  114. package/dist/fonts/fallbacks.d.ts.map +1 -0
  115. package/dist/fonts/google.d.ts +122 -0
  116. package/dist/fonts/google.d.ts.map +1 -0
  117. package/dist/fonts/local.d.ts +76 -0
  118. package/dist/fonts/local.d.ts.map +1 -0
  119. package/dist/fonts/types.d.ts +85 -0
  120. package/dist/fonts/types.d.ts.map +1 -0
  121. package/dist/index.d.ts +150 -0
  122. package/dist/index.d.ts.map +1 -0
  123. package/dist/index.js +14701 -0
  124. package/dist/index.js.map +1 -0
  125. package/dist/plugins/adapter-build.d.ts +18 -0
  126. package/dist/plugins/adapter-build.d.ts.map +1 -0
  127. package/dist/plugins/build-manifest.d.ts +79 -0
  128. package/dist/plugins/build-manifest.d.ts.map +1 -0
  129. package/dist/plugins/build-report.d.ts +63 -0
  130. package/dist/plugins/build-report.d.ts.map +1 -0
  131. package/dist/plugins/cache-transform.d.ts +36 -0
  132. package/dist/plugins/cache-transform.d.ts.map +1 -0
  133. package/dist/plugins/chunks.d.ts +45 -0
  134. package/dist/plugins/chunks.d.ts.map +1 -0
  135. package/dist/plugins/content.d.ts +19 -0
  136. package/dist/plugins/content.d.ts.map +1 -0
  137. package/dist/plugins/dev-error-overlay.d.ts +60 -0
  138. package/dist/plugins/dev-error-overlay.d.ts.map +1 -0
  139. package/dist/plugins/dev-logs.d.ts +46 -0
  140. package/dist/plugins/dev-logs.d.ts.map +1 -0
  141. package/dist/plugins/dev-server.d.ts +22 -0
  142. package/dist/plugins/dev-server.d.ts.map +1 -0
  143. package/dist/plugins/dynamic-transform.d.ts +72 -0
  144. package/dist/plugins/dynamic-transform.d.ts.map +1 -0
  145. package/dist/plugins/entries.d.ts +21 -0
  146. package/dist/plugins/entries.d.ts.map +1 -0
  147. package/dist/plugins/fonts.d.ts +77 -0
  148. package/dist/plugins/fonts.d.ts.map +1 -0
  149. package/dist/plugins/mdx.d.ts +21 -0
  150. package/dist/plugins/mdx.d.ts.map +1 -0
  151. package/dist/plugins/react-prod.d.ts +18 -0
  152. package/dist/plugins/react-prod.d.ts.map +1 -0
  153. package/dist/plugins/routing.d.ts +13 -0
  154. package/dist/plugins/routing.d.ts.map +1 -0
  155. package/dist/plugins/server-action-exports.d.ts +26 -0
  156. package/dist/plugins/server-action-exports.d.ts.map +1 -0
  157. package/dist/plugins/server-bundle.d.ts +15 -0
  158. package/dist/plugins/server-bundle.d.ts.map +1 -0
  159. package/dist/plugins/shims.d.ts +18 -0
  160. package/dist/plugins/shims.d.ts.map +1 -0
  161. package/dist/plugins/static-build.d.ts +55 -0
  162. package/dist/plugins/static-build.d.ts.map +1 -0
  163. package/dist/routing/codegen.d.ts +29 -0
  164. package/dist/routing/codegen.d.ts.map +1 -0
  165. package/dist/routing/index.d.ts +8 -0
  166. package/dist/routing/index.d.ts.map +1 -0
  167. package/dist/routing/index.js +2 -0
  168. package/dist/routing/interception.d.ts +46 -0
  169. package/dist/routing/interception.d.ts.map +1 -0
  170. package/dist/routing/scanner.d.ts +28 -0
  171. package/dist/routing/scanner.d.ts.map +1 -0
  172. package/dist/routing/status-file-lint.d.ts +33 -0
  173. package/dist/routing/status-file-lint.d.ts.map +1 -0
  174. package/dist/routing/types.d.ts +81 -0
  175. package/dist/routing/types.d.ts.map +1 -0
  176. package/dist/search-params/analyze.d.ts +54 -0
  177. package/dist/search-params/analyze.d.ts.map +1 -0
  178. package/dist/search-params/codecs.d.ts +53 -0
  179. package/dist/search-params/codecs.d.ts.map +1 -0
  180. package/dist/search-params/create.d.ts +106 -0
  181. package/dist/search-params/create.d.ts.map +1 -0
  182. package/dist/search-params/index.d.ts +7 -0
  183. package/dist/search-params/index.d.ts.map +1 -0
  184. package/dist/search-params/index.js +300 -0
  185. package/dist/search-params/index.js.map +1 -0
  186. package/dist/search-params/registry.d.ts +20 -0
  187. package/dist/search-params/registry.d.ts.map +1 -0
  188. package/dist/server/access-gate.d.ts +42 -0
  189. package/dist/server/access-gate.d.ts.map +1 -0
  190. package/dist/server/action-client.d.ts +190 -0
  191. package/dist/server/action-client.d.ts.map +1 -0
  192. package/dist/server/action-handler.d.ts +48 -0
  193. package/dist/server/action-handler.d.ts.map +1 -0
  194. package/dist/server/actions.d.ts +108 -0
  195. package/dist/server/actions.d.ts.map +1 -0
  196. package/dist/server/asset-headers.d.ts +42 -0
  197. package/dist/server/asset-headers.d.ts.map +1 -0
  198. package/dist/server/body-limits.d.ts +30 -0
  199. package/dist/server/body-limits.d.ts.map +1 -0
  200. package/dist/server/build-manifest.d.ts +120 -0
  201. package/dist/server/build-manifest.d.ts.map +1 -0
  202. package/dist/server/canonicalize.d.ts +30 -0
  203. package/dist/server/canonicalize.d.ts.map +1 -0
  204. package/dist/server/client-module-map.d.ts +47 -0
  205. package/dist/server/client-module-map.d.ts.map +1 -0
  206. package/dist/server/csrf.d.ts +34 -0
  207. package/dist/server/csrf.d.ts.map +1 -0
  208. package/dist/server/deny-renderer.d.ts +49 -0
  209. package/dist/server/deny-renderer.d.ts.map +1 -0
  210. package/dist/server/dev-logger.d.ts +44 -0
  211. package/dist/server/dev-logger.d.ts.map +1 -0
  212. package/dist/server/dev-span-processor.d.ts +29 -0
  213. package/dist/server/dev-span-processor.d.ts.map +1 -0
  214. package/dist/server/dev-warnings.d.ts +129 -0
  215. package/dist/server/dev-warnings.d.ts.map +1 -0
  216. package/dist/server/early-hints-sender.d.ts +38 -0
  217. package/dist/server/early-hints-sender.d.ts.map +1 -0
  218. package/dist/server/early-hints.d.ts +83 -0
  219. package/dist/server/early-hints.d.ts.map +1 -0
  220. package/dist/server/error-boundary-wrapper.d.ts +17 -0
  221. package/dist/server/error-boundary-wrapper.d.ts.map +1 -0
  222. package/dist/server/error-formatter.d.ts +17 -0
  223. package/dist/server/error-formatter.d.ts.map +1 -0
  224. package/dist/server/flush.d.ts +74 -0
  225. package/dist/server/flush.d.ts.map +1 -0
  226. package/dist/server/form-data.d.ts +60 -0
  227. package/dist/server/form-data.d.ts.map +1 -0
  228. package/dist/server/form-flash.d.ts +78 -0
  229. package/dist/server/form-flash.d.ts.map +1 -0
  230. package/dist/server/html-injectors.d.ts +101 -0
  231. package/dist/server/html-injectors.d.ts.map +1 -0
  232. package/dist/server/index.d.ts +54 -0
  233. package/dist/server/index.d.ts.map +1 -0
  234. package/dist/server/index.js +2925 -0
  235. package/dist/server/index.js.map +1 -0
  236. package/dist/server/instrumentation.d.ts +61 -0
  237. package/dist/server/instrumentation.d.ts.map +1 -0
  238. package/dist/server/logger.d.ts +83 -0
  239. package/dist/server/logger.d.ts.map +1 -0
  240. package/dist/server/manifest-status-resolver.d.ts +58 -0
  241. package/dist/server/manifest-status-resolver.d.ts.map +1 -0
  242. package/dist/server/metadata-render.d.ts +20 -0
  243. package/dist/server/metadata-render.d.ts.map +1 -0
  244. package/dist/server/metadata-routes.d.ts +67 -0
  245. package/dist/server/metadata-routes.d.ts.map +1 -0
  246. package/dist/server/metadata.d.ts +67 -0
  247. package/dist/server/metadata.d.ts.map +1 -0
  248. package/dist/server/middleware-runner.d.ts +21 -0
  249. package/dist/server/middleware-runner.d.ts.map +1 -0
  250. package/dist/server/nuqs-ssr-provider.d.ts +28 -0
  251. package/dist/server/nuqs-ssr-provider.d.ts.map +1 -0
  252. package/dist/server/pipeline.d.ts +81 -0
  253. package/dist/server/pipeline.d.ts.map +1 -0
  254. package/dist/server/prerender.d.ts +77 -0
  255. package/dist/server/prerender.d.ts.map +1 -0
  256. package/dist/server/primitives.d.ts +131 -0
  257. package/dist/server/primitives.d.ts.map +1 -0
  258. package/dist/server/proxy.d.ts +23 -0
  259. package/dist/server/proxy.d.ts.map +1 -0
  260. package/dist/server/request-context.d.ts +175 -0
  261. package/dist/server/request-context.d.ts.map +1 -0
  262. package/dist/server/route-element-builder.d.ts +66 -0
  263. package/dist/server/route-element-builder.d.ts.map +1 -0
  264. package/dist/server/route-handler.d.ts +35 -0
  265. package/dist/server/route-handler.d.ts.map +1 -0
  266. package/dist/server/route-matcher.d.ts +78 -0
  267. package/dist/server/route-matcher.d.ts.map +1 -0
  268. package/dist/server/rsc-entry/api-handler.d.ts +11 -0
  269. package/dist/server/rsc-entry/api-handler.d.ts.map +1 -0
  270. package/dist/server/rsc-entry/error-renderer.d.ts +30 -0
  271. package/dist/server/rsc-entry/error-renderer.d.ts.map +1 -0
  272. package/dist/server/rsc-entry/helpers.d.ts +73 -0
  273. package/dist/server/rsc-entry/helpers.d.ts.map +1 -0
  274. package/dist/server/rsc-entry/index.d.ts +11 -0
  275. package/dist/server/rsc-entry/index.d.ts.map +1 -0
  276. package/dist/server/rsc-entry/ssr-bridge.d.ts +6 -0
  277. package/dist/server/rsc-entry/ssr-bridge.d.ts.map +1 -0
  278. package/dist/server/slot-resolver.d.ts +34 -0
  279. package/dist/server/slot-resolver.d.ts.map +1 -0
  280. package/dist/server/ssr-entry.d.ts +73 -0
  281. package/dist/server/ssr-entry.d.ts.map +1 -0
  282. package/dist/server/ssr-render.d.ts +67 -0
  283. package/dist/server/ssr-render.d.ts.map +1 -0
  284. package/dist/server/status-code-resolver.d.ts +77 -0
  285. package/dist/server/status-code-resolver.d.ts.map +1 -0
  286. package/dist/server/tracing.d.ts +99 -0
  287. package/dist/server/tracing.d.ts.map +1 -0
  288. package/dist/server/tree-builder.d.ts +116 -0
  289. package/dist/server/tree-builder.d.ts.map +1 -0
  290. package/dist/server/types.d.ts +231 -0
  291. package/dist/server/types.d.ts.map +1 -0
  292. package/dist/shims/font-google.d.ts +41 -0
  293. package/dist/shims/font-google.d.ts.map +1 -0
  294. package/dist/shims/headers.d.ts +11 -0
  295. package/dist/shims/headers.d.ts.map +1 -0
  296. package/dist/shims/image.d.ts +328 -0
  297. package/dist/shims/image.d.ts.map +1 -0
  298. package/dist/shims/link.d.ts +9 -0
  299. package/dist/shims/link.d.ts.map +1 -0
  300. package/dist/shims/navigation-client.d.ts +25 -0
  301. package/dist/shims/navigation-client.d.ts.map +1 -0
  302. package/dist/shims/navigation.d.ts +25 -0
  303. package/dist/shims/navigation.d.ts.map +1 -0
  304. package/dist/utils/directive-parser.d.ts +70 -0
  305. package/dist/utils/directive-parser.d.ts.map +1 -0
  306. package/dist/utils/format.d.ts +6 -0
  307. package/dist/utils/format.d.ts.map +1 -0
  308. package/dist/utils/startup-timer.d.ts +34 -0
  309. package/dist/utils/startup-timer.d.ts.map +1 -0
  310. package/package.json +140 -0
@@ -0,0 +1,669 @@
1
+ import { n as classifyMetadataRoute } from "./metadata-routes-BDnswgRO.js";
2
+ import { existsSync, readdirSync, statSync } from "node:fs";
3
+ import { basename, extname, join, posix, relative } from "node:path";
4
+ //#region src/routing/types.ts
5
+ /** All recognized interception markers, ordered longest-first for parsing. */
6
+ var INTERCEPTION_MARKERS = [
7
+ "(..)(..)",
8
+ "(.)",
9
+ "(..)",
10
+ "(...)"
11
+ ];
12
+ /** Default page extensions */
13
+ var DEFAULT_PAGE_EXTENSIONS = [
14
+ "tsx",
15
+ "ts",
16
+ "jsx",
17
+ "js"
18
+ ];
19
+ //#endregion
20
+ //#region src/routing/scanner.ts
21
+ /**
22
+ * Route discovery scanner.
23
+ *
24
+ * Pure function: (appDir, config) → RouteTree
25
+ *
26
+ * Scans the app/ directory and builds a segment tree recognizing all
27
+ * timber.js file conventions. Does NOT handle request matching — this
28
+ * is discovery only.
29
+ */
30
+ /**
31
+ * Pattern matching encoded path delimiters that must be rejected during route discovery.
32
+ * %2F / %2f (forward slash) and %5C / %5c (backslash) can cause route collisions
33
+ * when decoded. See design/13-security.md §"Encoded separators rejected".
34
+ */
35
+ var ENCODED_SEPARATOR_PATTERN = /%(?:2[fF]|5[cC])/;
36
+ /**
37
+ * Pattern matching encoded null bytes (%00) that must be rejected.
38
+ * See design/13-security.md §"Null bytes rejected".
39
+ */
40
+ var ENCODED_NULL_PATTERN = /%00/;
41
+ /**
42
+ * File convention names that use pageExtensions (can be .tsx, .ts, .jsx, .js, .mdx, etc.)
43
+ */
44
+ var PAGE_EXT_CONVENTIONS = new Set([
45
+ "page",
46
+ "layout",
47
+ "error",
48
+ "default",
49
+ "denied"
50
+ ]);
51
+ /**
52
+ * Legacy compat status-code files.
53
+ * Maps legacy file name → HTTP status code for the fallback chain.
54
+ * See design/10-error-handling.md §"Fallback Chain".
55
+ */
56
+ var LEGACY_STATUS_FILES = {
57
+ "not-found": 404,
58
+ "forbidden": 403,
59
+ "unauthorized": 401
60
+ };
61
+ /**
62
+ * File convention names that are always .ts/.tsx (never .mdx etc.)
63
+ */
64
+ var FIXED_CONVENTIONS = new Set([
65
+ "middleware",
66
+ "access",
67
+ "route",
68
+ "prerender",
69
+ "search-params"
70
+ ]);
71
+ /**
72
+ * Status-code file patterns:
73
+ * - Exact 3-digit codes: 401.tsx, 429.tsx, 503.tsx
74
+ * - Category catch-alls: 4xx.tsx, 5xx.tsx
75
+ */
76
+ var STATUS_CODE_PATTERN = /^(\d{3}|[45]xx)$/;
77
+ /**
78
+ * Scan the app/ directory and build the route tree.
79
+ *
80
+ * @param appDir - Absolute path to the app/ directory
81
+ * @param config - Scanner configuration
82
+ * @returns The complete route tree
83
+ */
84
+ function scanRoutes(appDir, config = {}) {
85
+ const pageExtensions = config.pageExtensions ?? DEFAULT_PAGE_EXTENSIONS;
86
+ const extSet = new Set(pageExtensions);
87
+ const tree = { root: createSegmentNode("", "static", "/") };
88
+ const proxyFile = findFixedFile(appDir, "proxy");
89
+ if (proxyFile) tree.proxy = proxyFile;
90
+ scanSegmentFiles(appDir, tree.root, extSet);
91
+ scanChildren(appDir, tree.root, extSet);
92
+ validateRouteGroupCollisions(tree.root);
93
+ return tree;
94
+ }
95
+ /**
96
+ * Create an empty segment node.
97
+ */
98
+ function createSegmentNode(segmentName, segmentType, urlPath, paramName, interceptionMarker, interceptedSegmentName) {
99
+ return {
100
+ segmentName,
101
+ segmentType,
102
+ urlPath,
103
+ paramName,
104
+ interceptionMarker,
105
+ interceptedSegmentName,
106
+ children: [],
107
+ slots: /* @__PURE__ */ new Map()
108
+ };
109
+ }
110
+ /**
111
+ * Classify a directory name into its segment type.
112
+ */
113
+ function classifySegment(dirName) {
114
+ if (dirName.startsWith("_")) return { type: "private" };
115
+ if (dirName.startsWith("@")) return { type: "slot" };
116
+ const interception = parseInterceptionMarker(dirName);
117
+ if (interception) return {
118
+ type: "intercepting",
119
+ interceptionMarker: interception.marker,
120
+ interceptedSegmentName: interception.segmentName
121
+ };
122
+ if (dirName.startsWith("(") && dirName.endsWith(")")) return { type: "group" };
123
+ if (dirName.startsWith("[[...") && dirName.endsWith("]]")) return {
124
+ type: "optional-catch-all",
125
+ paramName: dirName.slice(5, -2)
126
+ };
127
+ if (dirName.startsWith("[...") && dirName.endsWith("]")) return {
128
+ type: "catch-all",
129
+ paramName: dirName.slice(4, -1)
130
+ };
131
+ if (dirName.startsWith("[") && dirName.endsWith("]")) return {
132
+ type: "dynamic",
133
+ paramName: dirName.slice(1, -1)
134
+ };
135
+ return { type: "static" };
136
+ }
137
+ /**
138
+ * Parse an interception marker from a directory name.
139
+ *
140
+ * Returns the marker and the remaining segment name, or null if not an
141
+ * intercepting route. Markers are checked longest-first to avoid (..)
142
+ * matching before (..)(..).
143
+ *
144
+ * Examples:
145
+ * "(.)photo" → { marker: "(.)", segmentName: "photo" }
146
+ * "(..)feed" → { marker: "(..)", segmentName: "feed" }
147
+ * "(...)photos" → { marker: "(...)", segmentName: "photos" }
148
+ * "(..)(..)admin" → { marker: "(..)(..)", segmentName: "admin" }
149
+ * "(marketing)" → null (route group, not interception)
150
+ */
151
+ function parseInterceptionMarker(dirName) {
152
+ for (const marker of INTERCEPTION_MARKERS) if (dirName.startsWith(marker)) {
153
+ const rest = dirName.slice(marker.length);
154
+ if (rest.length > 0 && !rest.endsWith(")")) return {
155
+ marker,
156
+ segmentName: rest
157
+ };
158
+ }
159
+ return null;
160
+ }
161
+ /**
162
+ * Compute the URL path for a child segment given its parent's URL path.
163
+ * Route groups, slots, and intercepting routes do NOT add URL depth.
164
+ */
165
+ function computeUrlPath(parentUrlPath, dirName, segmentType) {
166
+ if (segmentType === "group" || segmentType === "slot" || segmentType === "intercepting") return parentUrlPath;
167
+ return `${parentUrlPath === "/" ? "" : parentUrlPath}/${dirName}`;
168
+ }
169
+ /**
170
+ * Scan a directory for file conventions and populate the segment node.
171
+ */
172
+ function scanSegmentFiles(dirPath, node, extSet) {
173
+ let entries;
174
+ try {
175
+ entries = readdirSync(dirPath);
176
+ } catch {
177
+ return;
178
+ }
179
+ for (const entry of entries) {
180
+ const fullPath = join(dirPath, entry);
181
+ try {
182
+ if (statSync(fullPath).isDirectory()) continue;
183
+ } catch {
184
+ continue;
185
+ }
186
+ const ext = extname(entry).slice(1);
187
+ const name = basename(entry, `.${ext}`);
188
+ if (PAGE_EXT_CONVENTIONS.has(name) && extSet.has(ext)) {
189
+ const file = {
190
+ filePath: fullPath,
191
+ extension: ext
192
+ };
193
+ switch (name) {
194
+ case "page":
195
+ node.page = file;
196
+ break;
197
+ case "layout":
198
+ node.layout = file;
199
+ break;
200
+ case "error":
201
+ node.error = file;
202
+ break;
203
+ case "default":
204
+ node.default = file;
205
+ break;
206
+ case "denied":
207
+ node.denied = file;
208
+ break;
209
+ }
210
+ continue;
211
+ }
212
+ if (FIXED_CONVENTIONS.has(name) && /\.?[jt]sx?$/.test(ext)) {
213
+ const file = {
214
+ filePath: fullPath,
215
+ extension: ext
216
+ };
217
+ switch (name) {
218
+ case "middleware":
219
+ node.middleware = file;
220
+ break;
221
+ case "access":
222
+ node.access = file;
223
+ break;
224
+ case "route":
225
+ node.route = file;
226
+ break;
227
+ case "prerender":
228
+ node.prerender = file;
229
+ break;
230
+ case "search-params":
231
+ node.searchParams = file;
232
+ break;
233
+ }
234
+ continue;
235
+ }
236
+ if (STATUS_CODE_PATTERN.test(name) && ext === "json") {
237
+ if (!node.jsonStatusFiles) node.jsonStatusFiles = /* @__PURE__ */ new Map();
238
+ node.jsonStatusFiles.set(name, {
239
+ filePath: fullPath,
240
+ extension: ext
241
+ });
242
+ continue;
243
+ }
244
+ if (STATUS_CODE_PATTERN.test(name) && extSet.has(ext)) {
245
+ if (!node.statusFiles) node.statusFiles = /* @__PURE__ */ new Map();
246
+ node.statusFiles.set(name, {
247
+ filePath: fullPath,
248
+ extension: ext
249
+ });
250
+ continue;
251
+ }
252
+ if (name in LEGACY_STATUS_FILES && extSet.has(ext)) {
253
+ if (!node.legacyStatusFiles) node.legacyStatusFiles = /* @__PURE__ */ new Map();
254
+ node.legacyStatusFiles.set(name, {
255
+ filePath: fullPath,
256
+ extension: ext
257
+ });
258
+ continue;
259
+ }
260
+ if (classifyMetadataRoute(entry)) {
261
+ if (!node.metadataRoutes) node.metadataRoutes = /* @__PURE__ */ new Map();
262
+ node.metadataRoutes.set(name, {
263
+ filePath: fullPath,
264
+ extension: ext
265
+ });
266
+ }
267
+ }
268
+ if (node.route && node.page) throw new Error(`Build error: route.ts and page.* cannot coexist in the same segment.\n route.ts: ${node.route.filePath}\n page: ${node.page.filePath}\nA URL is either an API endpoint or a rendered page, not both.`);
269
+ }
270
+ /**
271
+ * Recursively scan child directories and build the segment tree.
272
+ */
273
+ function scanChildren(dirPath, parentNode, extSet) {
274
+ let entries;
275
+ try {
276
+ entries = readdirSync(dirPath);
277
+ } catch {
278
+ return;
279
+ }
280
+ for (const entry of entries) {
281
+ const fullPath = join(dirPath, entry);
282
+ try {
283
+ if (!statSync(fullPath).isDirectory()) continue;
284
+ } catch {
285
+ continue;
286
+ }
287
+ if (ENCODED_SEPARATOR_PATTERN.test(entry)) throw new Error(`Build error: directory name contains an encoded path delimiter (%%2F or %%5C).\n Directory: ${fullPath}\nEncoded separators in directory names cause route collisions when decoded. Rename the directory to remove the encoded delimiter.`);
288
+ if (ENCODED_NULL_PATTERN.test(entry)) throw new Error(`Build error: directory name contains an encoded null byte (%%00).\n Directory: ${fullPath}\nEncoded null bytes in directory names are not allowed. Rename the directory to remove the null byte encoding.`);
289
+ const { type, paramName, interceptionMarker, interceptedSegmentName } = classifySegment(entry);
290
+ if (type === "private") continue;
291
+ const childNode = createSegmentNode(entry, type, computeUrlPath(parentNode.urlPath, entry, type), paramName, interceptionMarker, interceptedSegmentName);
292
+ scanSegmentFiles(fullPath, childNode, extSet);
293
+ scanChildren(fullPath, childNode, extSet);
294
+ if (type === "slot") {
295
+ const slotName = entry.slice(1);
296
+ parentNode.slots.set(slotName, childNode);
297
+ } else parentNode.children.push(childNode);
298
+ }
299
+ }
300
+ /**
301
+ * Validate that route groups don't produce conflicting pages/routes at the same URL path.
302
+ *
303
+ * Two route groups like (auth)/login/page.tsx and (marketing)/login/page.tsx both claim
304
+ * /login — the scanner must detect and reject this at build time.
305
+ *
306
+ * Parallel slots are excluded from collision detection — they intentionally coexist at
307
+ * the same URL path as their parent (that's the whole point of parallel routes).
308
+ */
309
+ function validateRouteGroupCollisions(root) {
310
+ collectRoutableLeaves(root, /* @__PURE__ */ new Map(), "", false);
311
+ }
312
+ /**
313
+ * Walk the segment tree and collect all routable leaves (page or route files),
314
+ * throwing on collision. Slots are tracked in their own collision space since
315
+ * they are parallel routes that intentionally share URL paths with their parent.
316
+ */
317
+ function collectRoutableLeaves(node, seen, segmentPath, insideSlot) {
318
+ const currentPath = segmentPath ? `${segmentPath}/${node.segmentName}` : node.segmentName || "(root)";
319
+ if (!insideSlot) {
320
+ const routableFile = node.page ?? node.route;
321
+ if (routableFile) {
322
+ const existing = seen.get(node.urlPath);
323
+ if (existing) throw new Error(`Build error: route collision — multiple route groups produce a page/route at the same URL path.\n URL path: ${node.urlPath}\n File 1: ${existing.filePath} (via ${existing.segmentPath})\n File 2: ${routableFile.filePath} (via ${currentPath})\nEach URL path must map to exactly one page or route handler. Rename or move one of the conflicting files.`);
324
+ seen.set(node.urlPath, {
325
+ filePath: routableFile.filePath,
326
+ segmentPath: currentPath
327
+ });
328
+ }
329
+ }
330
+ for (const child of node.children) collectRoutableLeaves(child, seen, currentPath, insideSlot);
331
+ for (const [, slotNode] of node.slots) collectRoutableLeaves(slotNode, seen, currentPath, true);
332
+ }
333
+ /**
334
+ * Find a fixed-extension file (proxy.ts) in a directory.
335
+ */
336
+ function findFixedFile(dirPath, name) {
337
+ for (const ext of ["ts", "tsx"]) {
338
+ const fullPath = join(dirPath, `${name}.${ext}`);
339
+ try {
340
+ if (statSync(fullPath).isFile()) return {
341
+ filePath: fullPath,
342
+ extension: ext
343
+ };
344
+ } catch {}
345
+ }
346
+ }
347
+ //#endregion
348
+ //#region src/routing/codegen.ts
349
+ /**
350
+ * Route map codegen.
351
+ *
352
+ * Walks the scanned RouteTree and generates a TypeScript declaration file
353
+ * mapping every route to its params and searchParams shapes.
354
+ *
355
+ * This runs at build time and in dev (regenerated on file changes).
356
+ * No runtime overhead — purely static type generation.
357
+ */
358
+ /**
359
+ * Generate a TypeScript declaration file string from a scanned route tree.
360
+ *
361
+ * The output is a `declare module '@timber/app'` block containing the Routes
362
+ * interface that maps every route path to its params and searchParams shape.
363
+ */
364
+ function generateRouteMap(tree, options = {}) {
365
+ const routes = [];
366
+ collectRoutes(tree.root, [], options.appDir, routes);
367
+ routes.sort((a, b) => a.urlPath.localeCompare(b.urlPath));
368
+ return formatDeclarationFile(routes, options.outputDir ?? options.appDir);
369
+ }
370
+ /**
371
+ * Recursively walk the segment tree and collect route entries.
372
+ *
373
+ * A route entry is created for any segment that has a `page` or `route` file.
374
+ * Params accumulate from ancestor dynamic segments.
375
+ */
376
+ function collectRoutes(node, ancestorParams, appDir, routes) {
377
+ const params = [...ancestorParams];
378
+ if (node.paramName) params.push({
379
+ name: node.paramName,
380
+ type: paramTypeForSegment(node.segmentType)
381
+ });
382
+ const isPage = !!node.page;
383
+ const isApiRoute = !!node.route;
384
+ if (isPage || isApiRoute) {
385
+ const entry = {
386
+ urlPath: node.urlPath,
387
+ params: [...params],
388
+ hasSearchParams: false,
389
+ isApiRoute
390
+ };
391
+ if (appDir && isPage) {
392
+ const searchParamsFile = findSearchParamsFile(resolveSegmentDir(appDir, node));
393
+ if (searchParamsFile) {
394
+ entry.hasSearchParams = true;
395
+ entry.searchParamsAbsPath = searchParamsFile;
396
+ }
397
+ }
398
+ routes.push(entry);
399
+ }
400
+ for (const child of node.children) collectRoutes(child, params, appDir, routes);
401
+ for (const [, slot] of node.slots) collectRoutes(slot, params, appDir, routes);
402
+ }
403
+ /**
404
+ * Determine the TypeScript type for a segment's param.
405
+ */
406
+ function paramTypeForSegment(segmentType) {
407
+ switch (segmentType) {
408
+ case "catch-all": return "string[]";
409
+ case "optional-catch-all": return "string[] | undefined";
410
+ default: return "string";
411
+ }
412
+ }
413
+ /**
414
+ * Resolve the absolute directory path for a segment node.
415
+ *
416
+ * Reconstructs the filesystem path by walking from appDir through
417
+ * the segment names encoded in the urlPath, accounting for groups and slots.
418
+ */
419
+ function resolveSegmentDir(appDir, node) {
420
+ const file = node.page ?? node.route;
421
+ if (file) {
422
+ const parts = file.filePath.split("/");
423
+ parts.pop();
424
+ return parts.join("/");
425
+ }
426
+ return appDir;
427
+ }
428
+ /**
429
+ * Find a search-params.ts file in a directory.
430
+ */
431
+ function findSearchParamsFile(dirPath) {
432
+ for (const ext of ["ts", "tsx"]) {
433
+ const candidate = join(dirPath, `search-params.${ext}`);
434
+ if (existsSync(candidate)) return candidate;
435
+ }
436
+ }
437
+ /**
438
+ * Format the collected routes into a TypeScript declaration file.
439
+ */
440
+ function formatDeclarationFile(routes, importBase) {
441
+ const lines = [];
442
+ lines.push("// This file is auto-generated by timber.js route map codegen.");
443
+ lines.push("// Do not edit manually. Regenerated on build and in dev mode.");
444
+ lines.push("");
445
+ lines.push("export {};");
446
+ lines.push("");
447
+ lines.push("declare module '@timber/app' {");
448
+ lines.push(" interface Routes {");
449
+ for (const route of routes) {
450
+ const paramsType = formatParamsType(route.params);
451
+ const searchParamsType = formatSearchParamsType(route, importBase);
452
+ lines.push(` '${route.urlPath}': {`);
453
+ lines.push(` params: ${paramsType}`);
454
+ lines.push(` searchParams: ${searchParamsType}`);
455
+ lines.push(` }`);
456
+ }
457
+ lines.push(" }");
458
+ lines.push("}");
459
+ lines.push("");
460
+ const pageRoutes = routes.filter((r) => !r.isApiRoute);
461
+ if (pageRoutes.length > 0) {
462
+ lines.push("declare module '@timber/app/server' {");
463
+ lines.push(" import type { Routes } from '@timber/app'");
464
+ lines.push(" export function searchParams<R extends keyof Routes>(): Promise<Routes[R]['searchParams']>");
465
+ lines.push("}");
466
+ lines.push("");
467
+ }
468
+ const dynamicRoutes = routes.filter((r) => r.params.length > 0);
469
+ if (dynamicRoutes.length > 0 || pageRoutes.length > 0) {
470
+ lines.push("declare module '@timber/app/client' {");
471
+ lines.push(" import type { SearchParamsDefinition, SetParams, QueryStatesOptions, SearchParamCodec } from '@timber/app/search-params'");
472
+ lines.push("");
473
+ if (dynamicRoutes.length > 0) {
474
+ for (const route of dynamicRoutes) {
475
+ const paramsType = formatParamsType(route.params);
476
+ lines.push(` export function useParams(route: '${route.urlPath}'): ${paramsType}`);
477
+ }
478
+ lines.push(" export function useParams(): Record<string, string | string[]>");
479
+ lines.push("");
480
+ }
481
+ if (pageRoutes.length > 0) {
482
+ lines.push(...formatUseQueryStatesOverloads(pageRoutes, importBase));
483
+ lines.push("");
484
+ }
485
+ if (pageRoutes.length > 0) {
486
+ lines.push(" // Typed Link props per route");
487
+ lines.push(...formatTypedLinkOverloads(pageRoutes, importBase));
488
+ }
489
+ lines.push("}");
490
+ lines.push("");
491
+ }
492
+ return lines.join("\n");
493
+ }
494
+ /**
495
+ * Format the params type for a route entry.
496
+ */
497
+ function formatParamsType(params) {
498
+ if (params.length === 0) return "{}";
499
+ return `{ ${params.map((p) => `${p.name}: ${p.type}`).join("; ")} }`;
500
+ }
501
+ /**
502
+ * Format the params type for Link props.
503
+ *
504
+ * Link params accept `string | number` for single dynamic segments
505
+ * (convenience — values are stringified at runtime). Catch-all and
506
+ * optional catch-all remain `string[]` / `string[] | undefined`.
507
+ *
508
+ * See design/07-routing.md §"Typed params and searchParams on <Link>"
509
+ */
510
+ function formatLinkParamsType(params) {
511
+ if (params.length === 0) return "{}";
512
+ return `{ ${params.map((p) => {
513
+ const type = p.type === "string" ? "string | number" : p.type;
514
+ return `${p.name}: ${type}`;
515
+ }).join("; ")} }`;
516
+ }
517
+ /**
518
+ * Format the searchParams type for a route entry.
519
+ *
520
+ * When a search-params.ts exists, we reference its inferred type via an import type.
521
+ * The import path is relative to `importBase` (the directory where the .d.ts will be
522
+ * written). When importBase is undefined, falls back to a bare relative path.
523
+ */
524
+ function formatSearchParamsType(route, importBase) {
525
+ if (route.hasSearchParams && route.searchParamsAbsPath) {
526
+ const absPath = route.searchParamsAbsPath.replace(/\.(ts|tsx)$/, "");
527
+ let importPath;
528
+ if (importBase) importPath = "./" + relative(importBase, absPath).replace(/\\/g, "/");
529
+ else importPath = "./" + posix.basename(absPath);
530
+ return `(typeof import('${importPath}'))['default'] extends import('@timber/app/search-params').SearchParamsDefinition<infer T> ? T : never`;
531
+ }
532
+ return "{}";
533
+ }
534
+ /**
535
+ * Generate useQueryStates overloads.
536
+ *
537
+ * For each page route:
538
+ * - Routes with search-params.ts get a typed overload returning the inferred T
539
+ * - Routes without search-params.ts get an overload returning [{}, SetParams<{}>]
540
+ *
541
+ * A fallback overload for standalone codecs (existing API) is emitted last.
542
+ */
543
+ function formatUseQueryStatesOverloads(routes, importBase) {
544
+ const lines = [];
545
+ for (const route of routes) {
546
+ const searchParamsType = route.hasSearchParams ? formatSearchParamsType(route, importBase) : "{}";
547
+ lines.push(` export function useQueryStates<R extends '${route.urlPath}'>(route: R, options?: QueryStatesOptions): [${searchParamsType}, SetParams<${searchParamsType}>]`);
548
+ }
549
+ lines.push(" export function useQueryStates<T extends Record<string, unknown>>(codecs: { [K in keyof T]: SearchParamCodec<T[K]> }, options?: QueryStatesOptions): [T, SetParams<T>]");
550
+ return lines;
551
+ }
552
+ /**
553
+ * Generate typed Link overloads.
554
+ *
555
+ * For each page route, we generate a Link function overload that:
556
+ * - Constrains href to the route pattern
557
+ * - Types the params prop based on dynamic segments
558
+ * - Types the searchParams prop based on search-params.ts (if present)
559
+ *
560
+ * Routes without dynamic segments accept href as a literal string with no params.
561
+ * Routes with dynamic segments require a params prop.
562
+ */
563
+ function formatTypedLinkOverloads(routes, importBase) {
564
+ const lines = [];
565
+ for (const route of routes) {
566
+ const hasDynamicParams = route.params.length > 0;
567
+ const paramsType = formatLinkParamsType(route.params);
568
+ const searchParamsType = route.hasSearchParams ? formatSearchParamsType(route, importBase) : null;
569
+ if (hasDynamicParams) {
570
+ const spProp = searchParamsType ? `searchParams?: { definition: SearchParamsDefinition<${searchParamsType}>; values: Partial<${searchParamsType}> }` : `searchParams?: never`;
571
+ lines.push(` export function Link(props: Omit<import('react').AnchorHTMLAttributes<HTMLAnchorElement>, 'href'> & {`);
572
+ lines.push(` href: '${route.urlPath}'`);
573
+ lines.push(` params: ${paramsType}`);
574
+ lines.push(` ${spProp}`);
575
+ lines.push(` prefetch?: boolean; scroll?: boolean; children?: import('react').ReactNode`);
576
+ lines.push(` }): import('react').JSX.Element`);
577
+ } else {
578
+ const spProp = searchParamsType ? `searchParams?: { definition: SearchParamsDefinition<${searchParamsType}>; values: Partial<${searchParamsType}> }` : `searchParams?: never`;
579
+ lines.push(` export function Link(props: Omit<import('react').AnchorHTMLAttributes<HTMLAnchorElement>, 'href'> & {`);
580
+ lines.push(` href: '${route.urlPath}'`);
581
+ lines.push(` params?: never`);
582
+ lines.push(` ${spProp}`);
583
+ lines.push(` prefetch?: boolean; scroll?: boolean; children?: import('react').ReactNode`);
584
+ lines.push(` }): import('react').JSX.Element`);
585
+ }
586
+ }
587
+ lines.push(` export function Link(props: import('./client/link.js').LinkProps): import('react').JSX.Element`);
588
+ return lines;
589
+ }
590
+ //#endregion
591
+ //#region src/routing/interception.ts
592
+ /**
593
+ * Collect all interception rewrite rules from the route tree.
594
+ *
595
+ * Walks the tree recursively. For each intercepting segment, computes the
596
+ * intercepted URL based on the marker and the segment's position.
597
+ */
598
+ function collectInterceptionRewrites(root) {
599
+ const rewrites = [];
600
+ walkForInterceptions(root, [root], rewrites);
601
+ return rewrites;
602
+ }
603
+ /**
604
+ * Recursively walk the segment tree to find intercepting routes.
605
+ */
606
+ function walkForInterceptions(node, ancestors, rewrites) {
607
+ for (const child of node.children) if (child.segmentType === "intercepting" && child.interceptionMarker) collectFromInterceptingNode(child, ancestors, rewrites);
608
+ else walkForInterceptions(child, [...ancestors, child], rewrites);
609
+ for (const [, slot] of node.slots) walkForInterceptions(slot, ancestors, rewrites);
610
+ }
611
+ /**
612
+ * For an intercepting segment, find all leaf pages in its sub-tree and
613
+ * generate rewrite rules for each.
614
+ */
615
+ function collectFromInterceptingNode(interceptingNode, ancestors, rewrites) {
616
+ const marker = interceptingNode.interceptionMarker;
617
+ const segmentName = interceptingNode.interceptedSegmentName;
618
+ const parentUrlPath = ancestors[ancestors.length - 1].urlPath;
619
+ const interceptedBase = computeInterceptedBase(parentUrlPath, marker);
620
+ collectLeavesWithRewrites(interceptingNode, interceptedBase === "/" ? `/${segmentName}` : `${interceptedBase}/${segmentName}`, parentUrlPath, [...ancestors, interceptingNode], rewrites);
621
+ }
622
+ /**
623
+ * Recursively find leaf pages in an intercepting sub-tree and generate
624
+ * rewrite rules for each.
625
+ */
626
+ function collectLeavesWithRewrites(node, interceptedUrlPath, interceptingPrefix, segmentPath, rewrites) {
627
+ if (node.page) rewrites.push({
628
+ interceptedPattern: interceptedUrlPath,
629
+ interceptingPrefix,
630
+ segmentPath: [...segmentPath]
631
+ });
632
+ for (const child of node.children) collectLeavesWithRewrites(child, child.segmentType === "group" ? interceptedUrlPath : `${interceptedUrlPath}/${child.segmentName}`, interceptingPrefix, [...segmentPath, child], rewrites);
633
+ }
634
+ /**
635
+ * Compute the base URL that an intercepting route intercepts, given the
636
+ * parent's URL path and the interception marker.
637
+ *
638
+ * - (.) — same level: parent's URL path
639
+ * - (..) — one level up: parent's parent URL path
640
+ * - (...) — root level: /
641
+ * - (..)(..) — two levels up: parent's grandparent URL path
642
+ *
643
+ * Level counting operates on URL path segments, NOT filesystem directories.
644
+ * Route groups and parallel slots are already excluded from urlPath (they
645
+ * don't add URL depth), so (..) correctly climbs visible segments. This
646
+ * avoids the Vinext bug where path.dirname() on filesystem paths would
647
+ * waste climbs on invisible route groups.
648
+ */
649
+ function computeInterceptedBase(parentUrlPath, marker) {
650
+ switch (marker) {
651
+ case "(.)": return parentUrlPath;
652
+ case "(..)": {
653
+ const parts = parentUrlPath.split("/").filter(Boolean);
654
+ parts.pop();
655
+ return parts.length === 0 ? "/" : `/${parts.join("/")}`;
656
+ }
657
+ case "(...)": return "/";
658
+ case "(..)(..)": {
659
+ const parts = parentUrlPath.split("/").filter(Boolean);
660
+ parts.pop();
661
+ parts.pop();
662
+ return parts.length === 0 ? "/" : `/${parts.join("/")}`;
663
+ }
664
+ }
665
+ }
666
+ //#endregion
667
+ export { DEFAULT_PAGE_EXTENSIONS as a, scanRoutes as i, generateRouteMap as n, INTERCEPTION_MARKERS as o, classifySegment as r, collectInterceptionRewrites as t };
668
+
669
+ //# sourceMappingURL=interception-DIaZN1bF.js.map