@timber-js/app 0.2.0-alpha.186 → 0.2.0-alpha.187

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 (314) hide show
  1. package/dist/_chunks/{actions-C-Rw9vPc.js → actions-35jnMdeJ.js} +4 -3
  2. package/dist/_chunks/{actions-C-Rw9vPc.js.map → actions-35jnMdeJ.js.map} +1 -1
  3. package/dist/_chunks/als-registry-C6kcfprT.js +41 -0
  4. package/dist/_chunks/als-registry-C6kcfprT.js.map +1 -0
  5. package/dist/_chunks/{als-slots-mFweg276.js → als-slots-BEEIPKYm.js} +3 -4
  6. package/dist/_chunks/{als-slots-mFweg276.js.map → als-slots-BEEIPKYm.js.map} +1 -1
  7. package/dist/_chunks/{cache-api-Cd0VZ_Pd.js → cache-api-DjNrIWRR.js} +7 -13
  8. package/dist/_chunks/cache-api-DjNrIWRR.js.map +1 -0
  9. package/dist/_chunks/cli-check-CpmN7Nh-.js +256 -0
  10. package/dist/_chunks/cli-check-CpmN7Nh-.js.map +1 -0
  11. package/dist/_chunks/cli-schema-sync-CGMp_Psg.js +298 -0
  12. package/dist/_chunks/cli-schema-sync-CGMp_Psg.js.map +1 -0
  13. package/dist/_chunks/{cloudflare-Cs0uZXea.js → cloudflare-CGP6BZKO.js} +4 -3
  14. package/dist/_chunks/{cloudflare-Cs0uZXea.js.map → cloudflare-CGP6BZKO.js.map} +1 -1
  15. package/dist/_chunks/convention-lint-kXsgc_-7.js +784 -0
  16. package/dist/_chunks/convention-lint-kXsgc_-7.js.map +1 -0
  17. package/dist/_chunks/{error-boundary-DpYRI_I1.js → error-boundary-D-ODYX41.js} +25 -2
  18. package/dist/_chunks/error-boundary-D-ODYX41.js.map +1 -0
  19. package/dist/_chunks/file-cache-DmX7OqZP.js +454 -0
  20. package/dist/_chunks/file-cache-DmX7OqZP.js.map +1 -0
  21. package/dist/_chunks/{logger-N7e5auP0.js → logger-D8xJZXIN.js} +27 -40
  22. package/dist/_chunks/logger-D8xJZXIN.js.map +1 -0
  23. package/dist/_chunks/mdx-file-C005ay-P.js +25 -0
  24. package/dist/_chunks/mdx-file-C005ay-P.js.map +1 -0
  25. package/dist/_chunks/{plugin-context-DEGLSJs3.js → plugin-context-rCinWLiE.js} +7 -2
  26. package/dist/_chunks/{plugin-context-DEGLSJs3.js.map → plugin-context-rCinWLiE.js.map} +1 -1
  27. package/dist/_chunks/purge-store-Byr8XOjU.js +14 -0
  28. package/dist/_chunks/purge-store-Byr8XOjU.js.map +1 -0
  29. package/dist/_chunks/{resolve-schema-Dz3fcFUo.js → resolve-schema-5ma5pp1b.js} +2 -2
  30. package/dist/_chunks/{resolve-schema-Dz3fcFUo.js.map → resolve-schema-5ma5pp1b.js.map} +1 -1
  31. package/dist/_chunks/rsc-media-type-DRqE_lD_.js +46 -0
  32. package/dist/_chunks/rsc-media-type-DRqE_lD_.js.map +1 -0
  33. package/dist/_chunks/{cli-schema-sync-CKgHC2MB.js → scanner-C8b0Gcw3.js} +5 -298
  34. package/dist/_chunks/scanner-C8b0Gcw3.js.map +1 -0
  35. package/dist/_chunks/schema-bridge-Cc2Gngu1.js +199 -0
  36. package/dist/_chunks/schema-bridge-Cc2Gngu1.js.map +1 -0
  37. package/dist/_chunks/segment-classify-C539Pa2O.js.map +1 -1
  38. package/dist/_chunks/{param-value-C8TNYchQ.js → segment-context-CjOlyB8Y.js} +33 -2
  39. package/dist/_chunks/segment-context-CjOlyB8Y.js.map +1 -0
  40. package/dist/_chunks/{use-query-states-DFvWd-EA.js → use-query-states-BbU5Ge1V.js} +74 -26
  41. package/dist/_chunks/use-query-states-BbU5Ge1V.js.map +1 -0
  42. package/dist/_chunks/{navigation-root-B29qg0_T.js → use-segment-params-C4r4BD9T.js} +129 -5
  43. package/dist/_chunks/use-segment-params-C4r4BD9T.js.map +1 -0
  44. package/dist/_chunks/walkers-RzN6AFjr.js +141 -0
  45. package/dist/_chunks/walkers-RzN6AFjr.js.map +1 -0
  46. package/dist/adapters/cloudflare-dev.js +1 -1
  47. package/dist/adapters/cloudflare-kv-cache.js +1 -1
  48. package/dist/adapters/cloudflare.d.ts.map +1 -1
  49. package/dist/adapters/cloudflare.js +1 -1
  50. package/dist/adapters/compress-module.d.ts +12 -0
  51. package/dist/adapters/compress-module.d.ts.map +1 -1
  52. package/dist/adapters/nitro.js +54 -2
  53. package/dist/adapters/nitro.js.map +1 -1
  54. package/dist/cache/index.js +1 -1
  55. package/dist/cdn/cloudflare-purge.js +30 -0
  56. package/dist/cdn/cloudflare-purge.js.map +1 -0
  57. package/dist/cdn/fastly-purge.js +33 -0
  58. package/dist/cdn/fastly-purge.js.map +1 -0
  59. package/dist/cdn/index.js +94 -0
  60. package/dist/cdn/index.js.map +1 -0
  61. package/dist/cdn/workers-cache-purge.js +35 -0
  62. package/dist/cdn/workers-cache-purge.js.map +1 -0
  63. package/dist/cli-check.d.ts +153 -0
  64. package/dist/cli-check.d.ts.map +1 -0
  65. package/dist/cli.d.ts +34 -8
  66. package/dist/cli.d.ts.map +1 -1
  67. package/dist/cli.js +46 -21
  68. package/dist/cli.js.map +1 -1
  69. package/dist/client/browser-entry/action-dispatch.d.ts.map +1 -1
  70. package/dist/client/browser-entry/index.d.ts +1 -1
  71. package/dist/client/browser-entry/index.d.ts.map +1 -1
  72. package/dist/client/error-boundary.d.ts +6 -0
  73. package/dist/client/error-boundary.d.ts.map +1 -1
  74. package/dist/client/error-boundary.js +1 -1
  75. package/dist/client/index.d.ts +1 -0
  76. package/dist/client/index.d.ts.map +1 -1
  77. package/dist/client/index.js +27 -33
  78. package/dist/client/index.js.map +1 -1
  79. package/dist/client/internal.js +21 -14
  80. package/dist/client/internal.js.map +1 -1
  81. package/dist/client/link.d.ts +1 -7
  82. package/dist/client/link.d.ts.map +1 -1
  83. package/dist/client/navigation-commit.d.ts +16 -0
  84. package/dist/client/navigation-commit.d.ts.map +1 -1
  85. package/dist/client/router-pipeline.d.ts.map +1 -1
  86. package/dist/client/router.d.ts.map +1 -1
  87. package/dist/client/rsc-fetch.d.ts +9 -1
  88. package/dist/client/rsc-fetch.d.ts.map +1 -1
  89. package/dist/client/segment-cache.d.ts +8 -0
  90. package/dist/client/segment-cache.d.ts.map +1 -1
  91. package/dist/client/use-query-states.d.ts +9 -3
  92. package/dist/client/use-query-states.d.ts.map +1 -1
  93. package/dist/codec.js +1 -1
  94. package/dist/cookies/define-cookie.d.ts.map +1 -1
  95. package/dist/cookies/index.js +2 -2
  96. package/dist/cookies/index.js.map +1 -1
  97. package/dist/index.d.ts +4 -1
  98. package/dist/index.d.ts.map +1 -1
  99. package/dist/index.js +142 -477
  100. package/dist/index.js.map +1 -1
  101. package/dist/params/index.js +1 -1
  102. package/dist/plugin-context.d.ts +15 -0
  103. package/dist/plugin-context.d.ts.map +1 -1
  104. package/dist/plugins/routing.d.ts +0 -9
  105. package/dist/plugins/routing.d.ts.map +1 -1
  106. package/dist/plugins/shims.d.ts.map +1 -1
  107. package/dist/plugins/static-build.d.ts +24 -0
  108. package/dist/plugins/static-build.d.ts.map +1 -1
  109. package/dist/routing/codegen-shared.d.ts +3 -44
  110. package/dist/routing/codegen-shared.d.ts.map +1 -1
  111. package/dist/routing/codegen-types.d.ts +10 -31
  112. package/dist/routing/codegen-types.d.ts.map +1 -1
  113. package/dist/routing/codegen-write.d.ts +51 -0
  114. package/dist/routing/codegen-write.d.ts.map +1 -0
  115. package/dist/routing/codegen.d.ts.map +1 -1
  116. package/dist/routing/convention-lint.d.ts +18 -4
  117. package/dist/routing/convention-lint.d.ts.map +1 -1
  118. package/dist/routing/export-detect.d.ts +16 -0
  119. package/dist/routing/export-detect.d.ts.map +1 -1
  120. package/dist/routing/index.js +3 -2
  121. package/dist/routing/link-codegen.d.ts +19 -4
  122. package/dist/routing/link-codegen.d.ts.map +1 -1
  123. package/dist/routing/manifest-codegen.d.ts +1 -7
  124. package/dist/routing/manifest-codegen.d.ts.map +1 -1
  125. package/dist/routing/types.d.ts +0 -6
  126. package/dist/routing/types.d.ts.map +1 -1
  127. package/dist/schema-bridge.d.ts +60 -9
  128. package/dist/schema-bridge.d.ts.map +1 -1
  129. package/dist/search-params/define.d.ts +62 -8
  130. package/dist/search-params/define.d.ts.map +1 -1
  131. package/dist/search-params/index.d.ts +0 -1
  132. package/dist/search-params/index.d.ts.map +1 -1
  133. package/dist/search-params/index.js +66 -29
  134. package/dist/search-params/index.js.map +1 -1
  135. package/dist/search-params/parse-total.d.ts +70 -0
  136. package/dist/search-params/parse-total.d.ts.map +1 -0
  137. package/dist/search-params/wrappers.d.ts +26 -3
  138. package/dist/search-params/wrappers.d.ts.map +1 -1
  139. package/dist/server/access-gate.d.ts +19 -8
  140. package/dist/server/access-gate.d.ts.map +1 -1
  141. package/dist/server/action-handler.d.ts.map +1 -1
  142. package/dist/server/als-registry.d.ts +16 -0
  143. package/dist/server/als-registry.d.ts.map +1 -1
  144. package/dist/server/compress.d.ts.map +1 -1
  145. package/dist/server/deny-boundary.d.ts +148 -15
  146. package/dist/server/deny-boundary.d.ts.map +1 -1
  147. package/dist/server/deny-renderer.d.ts +2 -2
  148. package/dist/server/deny-renderer.d.ts.map +1 -1
  149. package/dist/server/error-boundary-wrapper.d.ts +85 -15
  150. package/dist/server/error-boundary-wrapper.d.ts.map +1 -1
  151. package/dist/server/index.d.ts +0 -1
  152. package/dist/server/index.d.ts.map +1 -1
  153. package/dist/server/index.js +3 -2
  154. package/dist/server/index.js.map +1 -1
  155. package/dist/server/internal.d.ts +3 -1
  156. package/dist/server/internal.d.ts.map +1 -1
  157. package/dist/server/internal.js +343 -230
  158. package/dist/server/internal.js.map +1 -1
  159. package/dist/server/metadata-collector.d.ts +52 -0
  160. package/dist/server/metadata-collector.d.ts.map +1 -0
  161. package/dist/server/param-coercion.d.ts +12 -5
  162. package/dist/server/param-coercion.d.ts.map +1 -1
  163. package/dist/server/pipeline-helpers.d.ts.map +1 -1
  164. package/dist/server/pipeline-outcome.d.ts.map +1 -1
  165. package/dist/server/pipeline-phases.d.ts.map +1 -1
  166. package/dist/server/primitives.d.ts +23 -0
  167. package/dist/server/primitives.d.ts.map +1 -1
  168. package/dist/server/route-element-builder.d.ts +1 -12
  169. package/dist/server/route-element-builder.d.ts.map +1 -1
  170. package/dist/server/rsc-cache-key-guard.d.ts.map +1 -1
  171. package/dist/server/rsc-entry/deny-fallback.d.ts.map +1 -1
  172. package/dist/server/rsc-entry/error-renderer.d.ts +1 -1
  173. package/dist/server/rsc-entry/error-renderer.d.ts.map +1 -1
  174. package/dist/server/rsc-entry/helpers.d.ts +0 -7
  175. package/dist/server/rsc-entry/helpers.d.ts.map +1 -1
  176. package/dist/server/rsc-entry/index.d.ts +0 -1
  177. package/dist/server/rsc-entry/index.d.ts.map +1 -1
  178. package/dist/server/rsc-entry/render-route.d.ts.map +1 -1
  179. package/dist/server/rsc-entry/revalidate-renderer.d.ts.map +1 -1
  180. package/dist/server/rsc-entry/rsc-payload.d.ts +1 -3
  181. package/dist/server/rsc-entry/rsc-payload.d.ts.map +1 -1
  182. package/dist/server/rsc-entry/rsc-stream.d.ts +12 -0
  183. package/dist/server/rsc-entry/rsc-stream.d.ts.map +1 -1
  184. package/dist/server/rsc-entry/ssr-renderer.d.ts +0 -2
  185. package/dist/server/rsc-entry/ssr-renderer.d.ts.map +1 -1
  186. package/dist/server/slot-resolver.d.ts.map +1 -1
  187. package/dist/server/ssr-bridge-types.d.ts +11 -0
  188. package/dist/server/ssr-bridge-types.d.ts.map +1 -1
  189. package/dist/server/ssr-entry.d.ts +0 -1
  190. package/dist/server/ssr-entry.d.ts.map +1 -1
  191. package/dist/server/static-generator.d.ts.map +1 -1
  192. package/dist/server/status-code-resolver.d.ts +8 -1
  193. package/dist/server/status-code-resolver.d.ts.map +1 -1
  194. package/dist/server/tree-builder.d.ts +28 -36
  195. package/dist/server/tree-builder.d.ts.map +1 -1
  196. package/dist/server/types.d.ts +12 -7
  197. package/dist/server/types.d.ts.map +1 -1
  198. package/dist/server/utils/element-type.d.ts +40 -0
  199. package/dist/server/utils/element-type.d.ts.map +1 -0
  200. package/dist/shared/rsc-media-type.d.ts +40 -0
  201. package/dist/shared/rsc-media-type.d.ts.map +1 -0
  202. package/docs/api/30-api-server.mdx +1 -1
  203. package/docs/api/33-api-search-params.mdx +38 -16
  204. package/docs/api/35-api-typescript.mdx +3 -3
  205. package/docs/api/36-cli.mdx +34 -7
  206. package/docs/learn/00-introduction.mdx +1 -1
  207. package/docs/learn/02-pages-and-layouts.mdx +1 -1
  208. package/docs/learn/05-typed-params.mdx +8 -8
  209. package/docs/learn/07-typed-routes.mdx +12 -7
  210. package/docs/learn/11-error-handling.mdx +16 -0
  211. package/docs/more/01-advanced-routing.mdx +1 -1
  212. package/docs/more/03-coming-from-nextjs.mdx +2 -2
  213. package/docs/more/50-ai-agent-instructions.mdx +6 -4
  214. package/package.json +8 -5
  215. package/src/adapters/cloudflare.ts +4 -1
  216. package/src/adapters/compress-module.ts +79 -1
  217. package/src/cli-check.ts +458 -0
  218. package/src/cli.ts +59 -24
  219. package/src/client/browser-entry/action-dispatch.ts +2 -1
  220. package/src/client/browser-entry/index.ts +0 -5
  221. package/src/client/error-boundary.tsx +65 -1
  222. package/src/client/index.ts +14 -3
  223. package/src/client/link.tsx +65 -64
  224. package/src/client/navigation-commit.ts +27 -4
  225. package/src/client/params-context.ts +4 -4
  226. package/src/client/router-pipeline.ts +4 -0
  227. package/src/client/router.ts +1 -0
  228. package/src/client/rsc-fetch.ts +14 -4
  229. package/src/client/segment-cache.ts +8 -0
  230. package/src/client/use-query-states.ts +102 -39
  231. package/src/cookies/define-cookie.ts +6 -1
  232. package/src/index.ts +20 -3
  233. package/src/plugin-context.ts +26 -0
  234. package/src/plugins/routing.ts +84 -146
  235. package/src/plugins/shims.ts +0 -1
  236. package/src/plugins/static-build.ts +78 -24
  237. package/src/routing/codegen-shared.ts +3 -79
  238. package/src/routing/codegen-types.ts +10 -31
  239. package/src/routing/codegen-write.ts +139 -0
  240. package/src/routing/codegen.ts +56 -182
  241. package/src/routing/convention-lint.ts +139 -40
  242. package/src/routing/export-detect.ts +151 -7
  243. package/src/routing/link-codegen.ts +32 -65
  244. package/src/routing/manifest-codegen.ts +1 -59
  245. package/src/routing/scanner.ts +3 -3
  246. package/src/routing/types.ts +0 -6
  247. package/src/schema-bridge.ts +180 -58
  248. package/src/search-params/define.ts +102 -37
  249. package/src/search-params/index.ts +0 -1
  250. package/src/search-params/parse-total.ts +78 -0
  251. package/src/search-params/wrappers.ts +60 -11
  252. package/src/server/access-gate.tsx +60 -40
  253. package/src/server/action-handler.ts +1 -4
  254. package/src/server/als-registry.ts +16 -0
  255. package/src/server/compress.ts +9 -1
  256. package/src/server/deny-boundary.ts +269 -41
  257. package/src/server/deny-renderer.ts +32 -21
  258. package/src/server/error-boundary-wrapper.ts +166 -79
  259. package/src/server/index.ts +1 -3
  260. package/src/server/internal.ts +2 -2
  261. package/src/server/metadata-collector.ts +115 -0
  262. package/src/server/param-coercion.ts +13 -61
  263. package/src/server/pipeline-helpers.ts +2 -2
  264. package/src/server/pipeline-outcome.ts +2 -1
  265. package/src/server/pipeline-phases.ts +9 -9
  266. package/src/server/primitives.ts +25 -0
  267. package/src/server/route-element-builder.ts +167 -170
  268. package/src/server/rsc-cache-key-guard.ts +2 -8
  269. package/src/server/rsc-entry/deny-fallback.ts +3 -2
  270. package/src/server/rsc-entry/error-renderer.ts +31 -11
  271. package/src/server/rsc-entry/helpers.ts +2 -12
  272. package/src/server/rsc-entry/index.ts +0 -5
  273. package/src/server/rsc-entry/render-route.ts +14 -11
  274. package/src/server/rsc-entry/revalidate-renderer.ts +2 -1
  275. package/src/server/rsc-entry/rsc-payload.ts +104 -20
  276. package/src/server/rsc-entry/rsc-stream.ts +25 -2
  277. package/src/server/rsc-entry/ssr-renderer.ts +28 -16
  278. package/src/server/slot-resolver.ts +10 -2
  279. package/src/server/ssr-bridge-types.ts +12 -0
  280. package/src/server/ssr-entry.ts +8 -6
  281. package/src/server/static-generator.ts +3 -2
  282. package/src/server/status-code-resolver.ts +28 -11
  283. package/src/server/tree-builder.ts +35 -218
  284. package/src/server/types.ts +12 -7
  285. package/src/server/utils/element-type.ts +72 -0
  286. package/src/shared/rsc-media-type.ts +43 -0
  287. package/dist/_chunks/cache-api-Cd0VZ_Pd.js.map +0 -1
  288. package/dist/_chunks/cli-schema-sync-CKgHC2MB.js.map +0 -1
  289. package/dist/_chunks/error-boundary-DpYRI_I1.js.map +0 -1
  290. package/dist/_chunks/logger-N7e5auP0.js.map +0 -1
  291. package/dist/_chunks/navigation-root-B29qg0_T.js.map +0 -1
  292. package/dist/_chunks/param-value-C8TNYchQ.js.map +0 -1
  293. package/dist/_chunks/registry-DbJPKoBp.js +0 -20
  294. package/dist/_chunks/registry-DbJPKoBp.js.map +0 -1
  295. package/dist/_chunks/schema-bridge-DT_Tn0Xf.js +0 -119
  296. package/dist/_chunks/schema-bridge-DT_Tn0Xf.js.map +0 -1
  297. package/dist/_chunks/segment-context-ZDnXDkbz.js +0 -34
  298. package/dist/_chunks/segment-context-ZDnXDkbz.js.map +0 -1
  299. package/dist/_chunks/use-query-states-DFvWd-EA.js.map +0 -1
  300. package/dist/_chunks/use-segment-params-ClyUNq4d.js +0 -128
  301. package/dist/_chunks/use-segment-params-ClyUNq4d.js.map +0 -1
  302. package/dist/_chunks/walkers-BhhwI9TD.js +0 -936
  303. package/dist/_chunks/walkers-BhhwI9TD.js.map +0 -1
  304. package/dist/search-params/registry.d.ts +0 -20
  305. package/dist/search-params/registry.d.ts.map +0 -1
  306. package/dist/segment-params/define.d.ts +0 -83
  307. package/dist/segment-params/define.d.ts.map +0 -1
  308. package/dist/segment-params/index.d.ts +0 -3
  309. package/dist/segment-params/index.d.ts.map +0 -1
  310. package/dist/segment-params/index.js +0 -70
  311. package/dist/segment-params/index.js.map +0 -1
  312. package/src/search-params/registry.ts +0 -31
  313. package/src/segment-params/define.ts +0 -226
  314. package/src/segment-params/index.ts +0 -9
package/dist/index.js CHANGED
@@ -1,9 +1,11 @@
1
1
  import { a as __esmMin, o as __exportAll, s as __toCommonJS } from "./_chunks/dist-BA3u1z3W.js";
2
+ import { t as RSC_CONTENT_TYPE } from "./_chunks/rsc-media-type-DRqE_lD_.js";
2
3
  import { a as canonicalize, d as isMetadataRouteServePath, u as isDynamicMetadataExtension } from "./_chunks/segment-keys-BawYuNFO.js";
3
- import { a as collectInterceptionRewrites, i as scanRoutes, o as matchUrlParts } from "./_chunks/cli-schema-sync-CKgHC2MB.js";
4
- import { h as swallow } from "./_chunks/logger-N7e5auP0.js";
5
- import { a as fileHasAnyExport, c as fileHasExport, i as generateRouteMap, l as fileHasStarExport, o as fileHasDefaultExport, r as validateSchemaAgainstRoutes, s as fileHasDirective, t as collectLeafRoutes, u as getPrerenderExport } from "./_chunks/walkers-BhhwI9TD.js";
6
- import { a as resolveAppDir, c as createNoopTimer, n as loadTimberConfigFile, o as resolveBuildDir, r as mergeFileConfig, s as resolveClientJavascript, t as createPluginContext } from "./_chunks/plugin-context-DEGLSJs3.js";
4
+ import { n as collectInterceptionRewrites, r as matchUrlParts, t as scanRoutes } from "./_chunks/scanner-C8b0Gcw3.js";
5
+ import { h as swallow } from "./_chunks/logger-D8xJZXIN.js";
6
+ import { c as writeRouteCodegen, i as getPrerenderExport, l as buildDirToRouteMap, n as formatConventionWarnings, r as lintConventions, s as flushCodegenWrites, t as checkAppDirExists } from "./_chunks/convention-lint-kXsgc_-7.js";
7
+ import { c as resolveClientJavascript, i as mergeFileConfig, l as createNoopTimer, o as resolveAppDir, r as loadTimberConfigFile, s as resolveBuildDir, t as createPluginContext } from "./_chunks/plugin-context-rCinWLiE.js";
8
+ import { r as validateSchemaAgainstRoutes, t as collectLeafRoutes } from "./_chunks/walkers-RzN6AFjr.js";
7
9
  import { i as withBasePath, n as normalizeBasePath, t as isRelativeBase } from "./_chunks/base-path-DaQrzbez.js";
8
10
  import { t as rscPayloadPath } from "./_chunks/rsc-payload-path-B_LBodc2.js";
9
11
  import { a as REDIRECTS_FILE, n as detectFsIdentity, o as RESERVED_OUTPUT_PATHS, r as fsKey, t as EXACT_FS_IDENTITY } from "./_chunks/fs-identity-D7vb6GdI.js";
@@ -286,8 +288,8 @@ function classifyStack(stack, projectRoot) {
286
288
  *
287
289
  * Design doc: 21-dev-server.md §"Error Overlay"
288
290
  */
289
- var noValidate$1 = { validateStream: false };
290
- var style$1 = (fmt, text) => styleText(fmt, text, noValidate$1);
291
+ var noValidate = { validateStream: false };
292
+ var style = (fmt, text) => styleText(fmt, text, noValidate);
291
293
  /**
292
294
  * Wrap text in an OSC 8 hyperlink escape sequence.
293
295
  *
@@ -309,7 +311,7 @@ function hyperlink(text, url) {
309
311
  function fileLink(filePath, line, col) {
310
312
  const display = line ? `${filePath}:${line}${col ? `:${col}` : ""}` : filePath;
311
313
  const base = pathToFileURL(filePath).href;
312
- return style$1(["cyan", "underline"], hyperlink(display, line ? `${base}:${line}${col ? `:${col}` : ""}` : base));
314
+ return style(["cyan", "underline"], hyperlink(display, line ? `${base}:${line}${col ? `:${col}` : ""}` : base));
313
315
  }
314
316
  var BOX = {
315
317
  topLeft: "╭",
@@ -328,9 +330,9 @@ var BOX = {
328
330
  function box(lines, borderFormat, width = 80) {
329
331
  const bar = BOX.horizontal.repeat(width - 2);
330
332
  const output = [];
331
- output.push(style$1(borderFormat, `${BOX.topLeft}${bar}${BOX.topRight}`));
332
- for (const line of lines) output.push(`${style$1(borderFormat, BOX.vertical)} ${line}`);
333
- output.push(style$1(borderFormat, `${BOX.bottomLeft}${bar}${BOX.bottomRight}`));
333
+ output.push(style(borderFormat, `${BOX.topLeft}${bar}${BOX.topRight}`));
334
+ for (const line of lines) output.push(`${style(borderFormat, BOX.vertical)} ${line}`);
335
+ output.push(style(borderFormat, `${BOX.bottomLeft}${bar}${BOX.bottomRight}`));
334
336
  return output.join("\n");
335
337
  }
336
338
  /**
@@ -351,32 +353,32 @@ function formatTerminalError$1(error, phase, projectRoot) {
351
353
  const appFrames = frames.filter((f) => f.type === "app");
352
354
  const internalCount = frames.filter((f) => f.type !== "app").length;
353
355
  const boxLines = [];
354
- boxLines.push(style$1(["red", "bold"], `${PHASE_LABELS$1[phase]} Error`));
356
+ boxLines.push(style(["red", "bold"], `${PHASE_LABELS$1[phase]} Error`));
355
357
  boxLines.push("");
356
- for (const msgLine of error.message.split("\n")) boxLines.push(style$1("red", msgLine));
358
+ for (const msgLine of error.message.split("\n")) boxLines.push(style("red", msgLine));
357
359
  if (loc) {
358
360
  boxLines.push("");
359
361
  const relPath = loc.file.startsWith(projectRoot) ? loc.file.slice(projectRoot.length + 1) : loc.file;
360
- boxLines.push(`${style$1("bold", "→")} ${fileLink(loc.file, loc.line, loc.column)} ${style$1("dim", `(${relPath})`)}`);
362
+ boxLines.push(`${style("bold", "→")} ${fileLink(loc.file, loc.line, loc.column)} ${style("dim", `(${relPath})`)}`);
361
363
  }
362
364
  sections.push(box(boxLines, "red"));
363
365
  if (componentStack) {
364
366
  sections.push("");
365
- sections.push(` ${style$1("bold", "Component Stack:")}`);
366
- for (const csLine of componentStack.trim().split("\n")) sections.push(` ${style$1("dim", csLine.trim())}`);
367
+ sections.push(` ${style("bold", "Component Stack:")}`);
368
+ for (const csLine of componentStack.trim().split("\n")) sections.push(` ${style("dim", csLine.trim())}`);
367
369
  }
368
370
  if (appFrames.length > 0) {
369
371
  sections.push("");
370
- sections.push(` ${style$1("bold", "Application Frames:")}`);
372
+ sections.push(` ${style("bold", "Application Frames:")}`);
371
373
  for (let i = 0; i < appFrames.length; i++) {
372
374
  const f = appFrames[i];
373
375
  if (f.file && f.line) {
374
- const prefix = i === 0 ? style$1("bold", "▸") : " ";
375
- sections.push(` ${prefix} ${fileLink(f.file, f.line, f.col)} ${style$1("dim", extractFnName(f.raw))}`);
376
+ const prefix = i === 0 ? style("bold", "▸") : " ";
377
+ sections.push(` ${prefix} ${fileLink(f.file, f.line, f.col)} ${style("dim", extractFnName(f.raw))}`);
376
378
  } else sections.push(` ${f.raw}`);
377
379
  }
378
380
  }
379
- if (internalCount > 0) sections.push(` ${style$1("dim", `… ${internalCount} internal frame${internalCount !== 1 ? "s" : ""} hidden`)}`);
381
+ if (internalCount > 0) sections.push(` ${style("dim", `… ${internalCount} internal frame${internalCount !== 1 ? "s" : ""} hidden`)}`);
380
382
  sections.push("");
381
383
  return sections.join("\n");
382
384
  }
@@ -1378,7 +1380,7 @@ var COMPRESSIBLE_TYPES = /* @__PURE__ */ new Set([
1378
1380
  "text/plain",
1379
1381
  "text/xml",
1380
1382
  "text/javascript",
1381
- "text/x-component",
1383
+ RSC_CONTENT_TYPE,
1382
1384
  "application/json",
1383
1385
  "application/javascript",
1384
1386
  "application/xml",
@@ -2204,69 +2206,10 @@ function timberMdx(ctx) {
2204
2206
  };
2205
2207
  }
2206
2208
  //#endregion
2207
- //#region src/routing/segment-codegen.ts
2208
- /**
2209
- * $segment type generation and directory-to-route mapping.
2210
- *
2211
- * Generates per-route $segment.d.ts files in .timber/types/ that mirror
2212
- * the app/ directory structure. TypeScript's rootDirs feature makes these
2213
- * importable as './$segment' from route files.
2214
- *
2215
- * Design doc: design/41-global-params.md §The $segment Module
2216
- */
2217
- /**
2218
- * Build a map of absolute filesystem directory → segment info.
2219
- * Used by resolveId to map a $segment import's importer to the correct route,
2220
- * and by writeSegmentTypes to generate per-route $segment.d.ts files.
2221
- *
2222
- * Walks every segment (not just leaves) so layouts and middleware
2223
- * at any depth can import $segment.
2224
- */
2225
- function buildDirToRouteMap(tree, appDir) {
2226
- const map = /* @__PURE__ */ new Map();
2227
- function walk(node, fsPath, treePath) {
2228
- map.set(fsPath, {
2229
- route: node.urlPath || "/",
2230
- segmentPath: treePath || "/"
2231
- });
2232
- for (const child of node.children) walk(child, join(fsPath, child.segmentName), `${treePath}/${child.segmentName}`);
2233
- for (const [slotName, slotNode] of Object.entries(node.slots)) walk(slotNode, join(fsPath, `@${slotName}`), `${treePath}/@${slotName}`);
2234
- }
2235
- walk(tree.root, appDir, "");
2236
- return map;
2237
- }
2238
- /**
2239
- * Generate the full $segment.d.ts content for a segment.
2240
- */
2241
- function generateSegmentDts(info) {
2242
- return [
2243
- `export const ROUTE: '${info.route}';`,
2244
- `export const SEGMENT_PATH: '${info.segmentPath}';`,
2245
- ""
2246
- ].join("\n");
2247
- }
2248
- /**
2249
- * Generate per-route $segment.d.ts files in .timber/types/ mirroring
2250
- * the app/ directory structure. These files are importable via TypeScript's
2251
- * rootDirs feature.
2252
- */
2253
- async function writeSegmentTypes(routeTree, appDir, root) {
2254
- const typesBase = join(root, ".timber", "types", appDir.slice(root.length + 1));
2255
- const map = buildDirToRouteMap(routeTree, appDir);
2256
- const writes = [];
2257
- for (const [fsDir, info] of map) {
2258
- const segmentDir = join(typesBase, fsDir.slice(appDir.length));
2259
- const segmentFile = join(segmentDir, "$segment.d.ts");
2260
- const content = generateSegmentDts(info);
2261
- writes.push(mkdir(segmentDir, { recursive: true }).then(() => writeFile(segmentFile, content, "utf-8")));
2262
- }
2263
- await Promise.all(writes);
2264
- }
2265
- //#endregion
2266
2209
  //#region src/routing/manifest-codegen.ts
2267
2210
  /**
2268
2211
  * Route manifest codegen — generates virtual module source for the route
2269
- * manifest, search-params registry, and global schema.
2212
+ * manifest and the global schema loader.
2270
2213
  *
2271
2214
  * Extracted from plugins/routing.ts to keep it under the 500-line budget.
2272
2215
  * See design/18-build-system.md §"Virtual Modules".
@@ -2281,8 +2224,7 @@ var SINGLE_FILE_KEYS = [
2281
2224
  "route",
2282
2225
  "error",
2283
2226
  "default",
2284
- "denied",
2285
- "params"
2227
+ "denied"
2286
2228
  ];
2287
2229
  var RECORD_FILE_KEYS = [
2288
2230
  "statusFiles",
@@ -2382,50 +2324,6 @@ function generateManifestModule(tree, viteRoot) {
2382
2324
  ].filter((line) => line !== void 0 && line !== "").join("\n");
2383
2325
  }
2384
2326
  /**
2385
- * Walk the route tree and collect every page that exports a
2386
- * `searchParams` definition, then emit a module that statically imports
2387
- * each export and registers it into the shared search-params registry.
2388
- */
2389
- function generateSearchParamsRegistryModule(tree) {
2390
- const entries = [];
2391
- function walk(node) {
2392
- if (node.page) {
2393
- if (node.params && fileHasSearchParamsExport(node.params.filePath)) entries.push({
2394
- urlPath: node.urlPath,
2395
- filePath: node.params.filePath
2396
- });
2397
- else if (fileHasSearchParamsExport(node.page.filePath)) entries.push({
2398
- urlPath: node.urlPath,
2399
- filePath: node.page.filePath
2400
- });
2401
- }
2402
- for (const child of node.children) walk(child);
2403
- for (const slot of Object.values(node.slots)) walk(slot);
2404
- }
2405
- walk(tree.root);
2406
- entries.sort((a, b) => a.urlPath.localeCompare(b.urlPath));
2407
- const lines = [];
2408
- lines.push("// Auto-generated search-params registry — do not edit.");
2409
- lines.push("// Generated by timber-routing plugin (TIM-830).");
2410
- lines.push("");
2411
- lines.push("import { registerSearchParams } from '@timber-js/app/search-params';");
2412
- lines.push("");
2413
- entries.forEach((entry, i) => {
2414
- lines.push(`import { searchParams as r${i} } from ${JSON.stringify(entry.filePath)};`);
2415
- });
2416
- lines.push("");
2417
- entries.forEach((entry, i) => {
2418
- lines.push(`registerSearchParams(${JSON.stringify(entry.urlPath)}, r${i});`);
2419
- });
2420
- lines.push("");
2421
- lines.push("export {};");
2422
- lines.push("");
2423
- return lines.join("\n");
2424
- }
2425
- function fileHasSearchParamsExport(filePath) {
2426
- return fileHasExport(filePath, "searchParams");
2427
- }
2428
- /**
2429
2327
  * Generate the virtual:timber-schema module.
2430
2328
  *
2431
2329
  * When app/schema.{ts,tsx,js,jsx} exists, generates a module that
@@ -2457,348 +2355,34 @@ function generateSchemaModule(appDir) {
2457
2355
  ].join("\n");
2458
2356
  }
2459
2357
  //#endregion
2460
- //#region src/routing/convention-lint.ts
2461
- /**
2462
- * Convention linter — validates common misconfigurations in the route tree.
2463
- *
2464
- * Runs at scan time (build and dev startup). Each check produces a warning
2465
- * with the file path, what's wrong, and what to do about it.
2466
- *
2467
- * These are warnings, not errors — they don't block the build. The goal is
2468
- * to catch issues that would otherwise produce cryptic runtime behavior
2469
- * (silent 404s, empty pages, confusing React errors).
2470
- *
2471
- * Design doc: 07-routing.md, 10-error-handling.md
2472
- */
2473
- /**
2474
- * Run all convention lint checks on a route tree.
2475
- *
2476
- * Returns an array of warnings. Empty array means everything looks good.
2477
- */
2478
- function lintConventions(tree, appDir) {
2479
- const warnings = [];
2480
- checkEmptyApp(tree.root, appDir, warnings);
2481
- checkRouteExports(tree.root, warnings);
2482
- checkRootLayout(tree.root, warnings);
2483
- checkDefaultExports(tree.root, warnings);
2484
- checkErrorDirectives(tree.root, warnings);
2485
- if (tree.globalError && isScriptExtension(tree.globalError.extension)) checkFileUseClientDirective(tree.globalError.filePath, "global-error", warnings);
2486
- return warnings;
2487
- }
2488
- /**
2489
- * Warn when the app/ directory has no routable files at all.
2490
- *
2491
- * This catches the "I created app/ but didn't add any pages" case where
2492
- * every request silently 404s with no guidance.
2493
- */
2494
- function checkEmptyApp(root, appDir, warnings) {
2495
- if (hasAnyRoutable(root)) return;
2496
- warnings.push({
2497
- id: "EMPTY_APP",
2498
- summary: "No pages or route handlers found in app/",
2499
- details: ` Directory: ${appDir}\n\n Your app/ directory has no page.tsx or route.ts files.
2500
- Every request will return 404.
2501
-
2502
- To fix: Create app/page.tsx with a default export:
2503
-
2504
- export default function Home() {
2505
- return <h1>Hello</h1>;
2506
- }
2507
- `,
2508
- level: "warn"
2509
- });
2510
- }
2511
- /**
2512
- * Check if a segment tree has any routable files (page or route) anywhere.
2513
- */
2514
- function hasAnyRoutable(node) {
2515
- if (node.page || node.route) return true;
2516
- for (const child of node.children) if (hasAnyRoutable(child)) return true;
2517
- for (const slot of Object.values(node.slots)) if (hasAnyRoutable(slot)) return true;
2518
- return false;
2519
- }
2520
- /**
2521
- * Check if a segment tree has any page files (not just route handlers).
2522
- * Used by checkRootLayout to avoid false positives for API-only apps (TIM-794).
2523
- * API-only apps (just route.ts handlers) don't need a root layout.
2524
- */
2525
- function hasAnyPage(node) {
2526
- if (node.page) return true;
2527
- for (const child of node.children) if (hasAnyPage(child)) return true;
2528
- for (const slot of Object.values(node.slots)) if (hasAnyPage(slot)) return true;
2529
- return false;
2530
- }
2531
- /** HTTP methods that route.ts can export. */
2532
- var HTTP_METHODS = [
2533
- "GET",
2534
- "POST",
2535
- "PUT",
2536
- "PATCH",
2537
- "DELETE",
2538
- "HEAD",
2539
- "OPTIONS"
2540
- ];
2541
- /**
2542
- * Warn when a route.ts file doesn't appear to export any recognized HTTP methods.
2543
- *
2544
- * Uses AST-based detection via parseAst for precise matching.
2545
- * Falls back silently when the file can't be parsed (syntax errors produce
2546
- * no spurious warning — the TS compiler will catch those).
2547
- */
2548
- function checkRouteExports(node, warnings) {
2549
- if (node.route) {
2550
- const filePath = node.route.filePath;
2551
- try {
2552
- if (fileHasAnyExport(filePath, HTTP_METHODS).size === 0 && !fileHasStarExport(filePath)) {
2553
- const hint = fileHasDefaultExport(filePath) ? " It looks like you have a default export. route.ts uses named exports\n for HTTP methods (GET, POST, etc.), not a default export.\n" : "";
2554
- warnings.push({
2555
- id: "ROUTE_NO_METHODS",
2556
- summary: `route.ts has no HTTP method exports: ${filePath}`,
2557
- details: ` File: ${filePath}\n\n route.ts files must export named HTTP method handlers.
2558
- Without them, the route matches but returns 405 Method Not Allowed.
2559
-
2560
- ` + hint + " Example:\n\n export function GET(ctx: RouteContext) {\n return Response.json({ hello: 'world' });\n }\n",
2561
- level: "warn"
2562
- });
2563
- }
2564
- } catch (err) {
2565
- swallow(err, `convention-lint: unreadable route.ts ${filePath}`);
2566
- }
2567
- }
2568
- for (const child of node.children) checkRouteExports(child, warnings);
2569
- for (const slot of Object.values(node.slots)) checkRouteExports(slot, warnings);
2570
- }
2571
- /**
2572
- * Warn when the root segment has no layout.tsx.
2573
- *
2574
- * Without a root layout, there's no HTML shell (<html>, <body>).
2575
- * The page will render but may have hydration issues or no proper document structure.
2576
- */
2577
- function checkRootLayout(root, warnings) {
2578
- if (root.layout) return;
2579
- if (!hasAnyPage(root)) return;
2580
- warnings.push({
2581
- id: "NO_ROOT_LAYOUT",
2582
- summary: "No root layout.tsx found",
2583
- details: " Your app has pages but no root layout.\n Without app/layout.tsx, pages have no <html> or <body> wrapper.\n\n To fix: Create app/layout.tsx:\n\n export default function RootLayout({ children }: { children: React.ReactNode }) {\n return (\n <html lang=\"en\">\n <body>{children}</body>\n </html>\n );\n }\n",
2584
- level: "warn"
2585
- });
2586
- }
2587
- /**
2588
- * Warn when page.tsx or layout.tsx files don't have a default export.
2589
- *
2590
- * Without a default export:
2591
- * - page.tsx: the page renders nothing (empty content)
2592
- * - layout.tsx: the layout module loads but has no component to render
2593
- *
2594
- * Uses AST-based detection via parseAst for precise matching. Falls back
2595
- * silently when the file can't be parsed (no spurious warning on syntax errors).
2596
- */
2597
- function checkDefaultExports(node, warnings) {
2598
- if (node.page && isScriptExtension(node.page.extension)) checkFileDefaultExport(node.page.filePath, "page", warnings);
2599
- if (node.layout && isScriptExtension(node.layout.extension)) checkFileDefaultExport(node.layout.filePath, "layout", warnings);
2600
- for (const child of node.children) checkDefaultExports(child, warnings);
2601
- for (const slot of Object.values(node.slots)) checkDefaultExports(slot, warnings);
2602
- }
2603
- function isScriptExtension(ext) {
2604
- return ext === "tsx" || ext === "ts" || ext === "jsx" || ext === "js";
2605
- }
2606
- function checkFileDefaultExport(filePath, fileType, warnings) {
2607
- try {
2608
- if (!fileHasDefaultExport(filePath)) warnings.push({
2609
- id: `NO_DEFAULT_EXPORT:${filePath}`,
2610
- summary: `${fileType}.tsx has no default export: ${filePath}`,
2611
- details: ` File: ${filePath}\n\n ${fileType}.tsx files must export a default React component.\n Without a default export, the ${fileType === "page" ? "page renders nothing" : "layout has no component to wrap children"}.\n\n To fix: Add a default export:\n\n export default function My${fileType === "page" ? "Page" : "Layout"}(${fileType === "layout" ? "{ children }" : ""}) {\n return ${fileType === "layout" ? "<div>{children}</div>" : "<h1>Hello</h1>"};\n }\n`,
2612
- level: "warn"
2613
- });
2614
- } catch (err) {
2615
- swallow(err, `convention-lint: unreadable ${fileType} file ${filePath}`);
2616
- }
2617
- }
2618
- /**
2619
- * Warn when error.tsx or global-error.tsx is missing 'use client'.
2620
- *
2621
- * React's error boundary API requires client components. Without the directive,
2622
- * the error boundary silently fails — the component renders as a server component
2623
- * with no error catching. Users discover this only via cryptic runtime errors.
2624
- */
2625
- function checkErrorDirectives(node, warnings) {
2626
- if (node.error && isScriptExtension(node.error.extension)) checkFileUseClientDirective(node.error.filePath, "error", warnings);
2627
- for (const child of node.children) checkErrorDirectives(child, warnings);
2628
- for (const slot of Object.values(node.slots)) checkErrorDirectives(slot, warnings);
2629
- }
2630
- function checkFileUseClientDirective(filePath, fileType, warnings) {
2631
- try {
2632
- if (!fileHasDirective(filePath, "use client")) warnings.push({
2633
- id: `ERROR_NO_USE_CLIENT:${filePath}`,
2634
- summary: `${fileType}.tsx is missing 'use client': ${filePath}`,
2635
- details: ` File: ${filePath}\n\n ${fileType}.tsx must start with 'use client' because React error\n boundaries require client components. Without it, the error boundary
2636
- silently fails and errors are uncaught.
2637
-
2638
- To fix: Add 'use client' as the first line of the file:
2639
-
2640
- 'use client';
2641
-
2642
- export default function ${fileType === "global-error" ? "GlobalError" : "ErrorBoundary"}({ error, reset }) {\n return <div><h2>Something went wrong</h2><button onClick={reset}>Try again</button></div>;
2643
- }
2644
- `,
2645
- level: "warn"
2646
- });
2647
- } catch (err) {
2648
- swallow(err, `convention-lint: unreadable ${fileType} file ${filePath}`);
2649
- }
2650
- }
2651
- /**
2652
- * Check if the app/ directory exists. Called before scanning.
2653
- * Returns a warning if missing, or null if the directory exists.
2654
- */
2655
- function checkAppDirExists(appDir) {
2656
- if (existsSync(appDir)) return null;
2657
- return {
2658
- id: "NO_APP_DIR",
2659
- summary: "No app/ directory found",
2660
- details: ` Expected: ${appDir}\n\n timber.js requires an app/ directory for file-system routing.
2661
- Every request will return 404 until you create it.
2662
-
2663
- To fix: Create the app/ directory with a page:
2664
-
2665
- mkdir -p app
2666
- # Create app/layout.tsx and app/page.tsx
2667
- `,
2668
- level: "error"
2669
- };
2670
- }
2671
- var noValidate = { validateStream: false };
2672
- var style = (fmt, text) => styleText(fmt, text, noValidate);
2673
- /**
2674
- * Format warnings for terminal output.
2675
- *
2676
- * Groups by severity, uses colors, and includes fix suggestions.
2677
- */
2678
- function formatConventionWarnings(warnings) {
2679
- if (warnings.length === 0) return "";
2680
- const errors = warnings.filter((w) => w.level === "error");
2681
- const warns = warnings.filter((w) => w.level === "warn");
2682
- const lines = [];
2683
- if (errors.length > 0) {
2684
- lines.push(`${style(["red", "bold"], "[timber]")} ${style("red", `${errors.length} configuration error${errors.length !== 1 ? "s" : ""}:`)}`);
2685
- for (const e of errors) {
2686
- lines.push("");
2687
- lines.push(` ${style("red", "✗")} ${e.summary}`);
2688
- lines.push(style("dim", e.details));
2689
- }
2690
- }
2691
- if (warns.length > 0) {
2692
- if (errors.length > 0) lines.push("");
2693
- lines.push(`${style(["yellow", "bold"], "[timber]")} ${style("yellow", `${warns.length} configuration warning${warns.length !== 1 ? "s" : ""}:`)}`);
2694
- for (const w of warns) {
2695
- lines.push("");
2696
- lines.push(` ${style("yellow", "⚠")} ${w.summary}`);
2697
- lines.push(style("dim", w.details));
2698
- }
2699
- }
2700
- return lines.join("\n");
2701
- }
2702
- //#endregion
2703
2358
  //#region src/plugins/routing.ts
2704
2359
  var VIRTUAL_MODULE_ID$1 = "virtual:timber-route-manifest";
2705
2360
  var RESOLVED_VIRTUAL_ID$1 = `\0${VIRTUAL_MODULE_ID$1}`;
2706
- var SEARCH_PARAMS_REGISTRY_ID = "virtual:timber-search-params-registry";
2707
- var RESOLVED_SEARCH_PARAMS_REGISTRY_ID = `\0${SEARCH_PARAMS_REGISTRY_ID}`;
2708
2361
  var SCHEMA_ID = "virtual:timber-schema";
2709
2362
  var RESOLVED_SCHEMA_ID = `\0${SCHEMA_ID}`;
2710
2363
  var SEGMENT_PREFIX = "\0$segment:";
2711
2364
  /**
2712
2365
  * File convention names we track for changes that require manifest regeneration.
2713
2366
  */
2714
- var ROUTE_FILE_PATTERNS = /\/(page|layout|middleware|access|route|error|global-error|default|denied|params|schema|\d{3}|[45]xx|sitemap|robots|manifest|favicon|icon|opengraph-image|apple-icon)\./;
2367
+ var ROUTE_FILE_PATTERNS = /\/(page|layout|middleware|access|route|error|global-error|default|denied|schema|\d{3}|[45]xx|sitemap|robots|manifest|favicon|icon|opengraph-image|apple-icon)\./;
2715
2368
  /**
2716
2369
  * Create the timber-routing Vite plugin.
2717
2370
  *
2718
2371
  * Hooks: resolveId, load, buildStart, configureServer
2719
2372
  */
2720
- /** Absolute path to the generated route map declaration file. */
2721
- var CODEGEN_OUTPUT = ".timber/timber-routes.d.ts";
2722
- /**
2723
- * Content of the timber-env.d.ts file written to the project root.
2724
- *
2725
- * Uses a triple-slash reference so TypeScript auto-discovers the generated
2726
- * route map without the user needing to edit tsconfig.json — same pattern
2727
- * as Next.js's next-env.d.ts.
2728
- *
2729
- * Also references `vite/client` so side-effect imports like
2730
- * `import './globals.css'` type-check without ts(2882), and so
2731
- * `import.meta.env`, `import.meta.hot`, `import.meta.glob`, asset
2732
- * imports (`*.png`, `*.svg`, …), CSS modules, and `?url`/`?raw`/
2733
- * `?inline` query variants all resolve without the user editing
2734
- * tsconfig.json themselves. Next.js's next-env.d.ts plays the same
2735
- * role via `next/types/global`.
2736
- *
2737
- * `vite` is a peer dep of `@timber-js/app`, so the reference always
2738
- * resolves in a correctly-installed project.
2739
- */
2740
- var TIMBER_ENV_DTS = [
2741
- "// This file is auto-generated by timber.js. Do not edit.",
2742
- "// It ensures TypeScript picks up the generated route types and",
2743
- "// Vite-provided ambient types (CSS, assets, import.meta.env, HMR).",
2744
- "/* eslint-disable @typescript-eslint/triple-slash-reference */",
2745
- "/// <reference path=\".timber/timber-routes.d.ts\" />",
2746
- "/// <reference types=\"vite/client\" />",
2747
- ""
2748
- ].join("\n");
2749
- /**
2750
- * Codegen writes that have been started but have not yet landed on disk.
2751
- *
2752
- * `writeCodegen` is deliberately unawaited on the hot path, which leaves a
2753
- * caller no way to know the files exist yet. Anything that tears down the
2754
- * project root — a build process exiting, a test removing its temp root —
2755
- * then races the writes: `rmSync` empties a directory that a pending `mkdir`
2756
- * immediately recreates, and the removal fails with ENOTEMPTY.
2757
- *
2758
- * Every started write registers here and deregisters when it settles, so
2759
- * `flushCodegenWrites()` can await them instead of guessing at a delay.
2760
- */
2761
- var pendingCodegenWrites = /* @__PURE__ */ new Set();
2762
- /**
2763
- * Resolve once every codegen write started so far has landed on disk.
2764
- *
2765
- * Safe to call at any time and from anywhere — writes are tracked per process,
2766
- * not per plugin instance, so a caller does not need a handle on the plugin
2767
- * that started them. Never rejects: individual write failures are already
2768
- * swallowed and logged by `writeCodegen`.
2769
- */
2770
- async function flushCodegenWrites() {
2771
- while (pendingCodegenWrites.size > 0) {
2772
- const inFlight = [...pendingCodegenWrites];
2773
- await Promise.all(inFlight);
2774
- for (const write of inFlight) pendingCodegenWrites.delete(write);
2775
- }
2776
- }
2777
2373
  /**
2778
2374
  * Write the generated route map and the timber-env.d.ts reference file.
2779
2375
  *
2780
- * Not awaited by callers, to avoid blocking the hot path — use
2781
- * `flushCodegenWrites()` to wait for the writes to land.
2782
- * Errors are swallowed; a missing .d.ts is a developer UX issue, not a runtime failure.
2376
+ * Not awaited, to avoid blocking the hot path — use `flushCodegenWrites()` to
2377
+ * wait for the writes to land. Failures are logged, not thrown: a missing .d.ts
2378
+ * is a developer UX issue in dev/build, not a runtime failure. (`timber check`
2379
+ * calls `writeRouteCodegen` directly and does treat a failure as fatal.)
2783
2380
  */
2784
2381
  function writeCodegen(ctx) {
2785
2382
  if (!ctx.routeTree) return;
2786
- const timberDir = join(ctx.root, ".timber");
2787
- const content = generateRouteMap(ctx.routeTree, {
2788
- appDir: ctx.appDir,
2789
- outputDir: timberDir
2790
- });
2791
- const routesPath = join(ctx.root, CODEGEN_OUTPUT);
2792
- const envPath = join(ctx.root, "timber-env.d.ts");
2793
- const done = mkdir(timberDir, { recursive: true }).then(() => Promise.all([
2794
- writeFile(routesPath, content, "utf-8"),
2795
- writeFile(envPath, TIMBER_ENV_DTS, "utf-8"),
2796
- writeSegmentTypes(ctx.routeTree, ctx.appDir, ctx.root)
2797
- ])).then(() => void 0).catch((err) => {
2383
+ writeRouteCodegen(ctx.routeTree, ctx.appDir, ctx.root).catch((err) => {
2798
2384
  console.warn("[timber] Failed to write codegen output:", err);
2799
2385
  });
2800
- pendingCodegenWrites.add(done);
2801
- done.finally(() => pendingCodegenWrites.delete(done));
2802
2386
  }
2803
2387
  function timberRouting(ctx) {
2804
2388
  /** Track warned files to avoid repeating warnings on rescan. */
@@ -2820,12 +2404,16 @@ function timberRouting(ctx) {
2820
2404
  return;
2821
2405
  }
2822
2406
  ctx.timer.start("route-scan");
2823
- ctx.routeTree = scanRoutes(ctx.appDir, { pageExtensions: ctx.config.pageExtensions });
2407
+ const tree = scanRoutes(ctx.appDir, { pageExtensions: ctx.config.pageExtensions });
2824
2408
  ctx.timer.end("route-scan");
2409
+ const conventionWarnings = lintConventions(tree, ctx.appDir);
2410
+ const fatal = conventionWarnings.filter((w) => w.level === "error");
2411
+ if (fatal.length > 0) throw new Error(formatConventionWarnings(fatal, { color: false }));
2412
+ ctx.routeTree = tree;
2825
2413
  dirToRoute = buildDirToRouteMap(ctx.routeTree, ctx.appDir);
2826
2414
  writeCodegen(ctx);
2827
2415
  warnSchemaValidation(ctx);
2828
- const newConventionWarnings = lintConventions(ctx.routeTree, ctx.appDir).filter((w) => !warnedFiles.has(w.id));
2416
+ const newConventionWarnings = conventionWarnings.filter((w) => !warnedFiles.has(w.id));
2829
2417
  if (newConventionWarnings.length > 0) {
2830
2418
  for (const w of newConventionWarnings) warnedFiles.add(w.id);
2831
2419
  const formatted = formatConventionWarnings(newConventionWarnings);
@@ -2846,7 +2434,6 @@ function timberRouting(ctx) {
2846
2434
  const cleanId = id.startsWith("\0") ? id.slice(1) : id;
2847
2435
  if (cleanId === VIRTUAL_MODULE_ID$1) return RESOLVED_VIRTUAL_ID$1;
2848
2436
  if (cleanId.endsWith(`/${VIRTUAL_MODULE_ID$1}`)) return RESOLVED_VIRTUAL_ID$1;
2849
- if (cleanId === SEARCH_PARAMS_REGISTRY_ID || cleanId.endsWith(`/${SEARCH_PARAMS_REGISTRY_ID}`)) return RESOLVED_SEARCH_PARAMS_REGISTRY_ID;
2850
2437
  if (cleanId === SCHEMA_ID || cleanId.endsWith(`/${SCHEMA_ID}`)) return RESOLVED_SCHEMA_ID;
2851
2438
  if (importer && (cleanId === "./$segment" || cleanId === "./$segment.js" || cleanId === "./$segment.ts")) {
2852
2439
  const cleanImporter = importer.startsWith("\0") ? importer.slice(1) : importer;
@@ -2867,10 +2454,6 @@ function timberRouting(ctx) {
2867
2454
  if (!ctx.routeTree) rescan();
2868
2455
  return generateManifestModule(ctx.routeTree, ctx.root);
2869
2456
  }
2870
- if (id === RESOLVED_SEARCH_PARAMS_REGISTRY_ID) {
2871
- if (!ctx.routeTree) rescan();
2872
- return generateSearchParamsRegistryModule(ctx.routeTree);
2873
- }
2874
2457
  if (id === RESOLVED_SCHEMA_ID) return generateSchemaModule(ctx.appDir);
2875
2458
  if (id.startsWith(SEGMENT_PREFIX)) {
2876
2459
  const info = JSON.parse(id.slice(10));
@@ -2917,13 +2500,59 @@ function timberRouting(ctx) {
2917
2500
  /** Snapshot of the last generated manifest, used to detect structural changes. */
2918
2501
  let lastManifest = ctx.routeTree ? generateManifestModule(ctx.routeTree, ctx.root) : "";
2919
2502
  /**
2503
+ * Rescan without letting a rejected route tree kill the dev server.
2504
+ *
2505
+ * `rescan()` throws on hard misconfigurations — duplicate conventions,
2506
+ * route collisions, a status file that is not a client component. Those
2507
+ * throws are correct at startup and in `vite build`, but here they run
2508
+ * inside a chokidar listener, where an exception is an unhandled
2509
+ * rejection that takes the process down mid-edit. Report and keep the
2510
+ * last good tree so the server stays up and the fix is one save away.
2511
+ *
2512
+ * Returns false when the scan failed, so callers skip the manifest
2513
+ * regeneration that would otherwise publish a stale tree as fresh.
2514
+ */
2515
+ /**
2516
+ * True while the last rescan is still failing.
2517
+ *
2518
+ * Recovery has to force a reload even when the manifest is byte-identical.
2519
+ * Adding a missing `'use client'` back to a status file changes no route
2520
+ * metadata, so the content-change path would send nothing and leave the
2521
+ * error overlay on screen over a page that is now fine.
2522
+ */
2523
+ let scanFailed = false;
2524
+ const rescanSafely = () => {
2525
+ try {
2526
+ rescan();
2527
+ if (scanFailed) {
2528
+ scanFailed = false;
2529
+ lastManifest = ctx.routeTree ? generateManifestModule(ctx.routeTree, ctx.root) : "";
2530
+ invalidateManifest(devServer);
2531
+ }
2532
+ return true;
2533
+ } catch (err) {
2534
+ const message = err instanceof Error ? err.message : String(err);
2535
+ process.stderr.write(`[timber] Route scan failed — keeping previous routes.\n${message}\n`);
2536
+ scanFailed = true;
2537
+ devServer.hot.send({
2538
+ type: "error",
2539
+ err: {
2540
+ message,
2541
+ stack: "",
2542
+ plugin: "timber-routing"
2543
+ }
2544
+ });
2545
+ return false;
2546
+ }
2547
+ };
2548
+ /**
2920
2549
  * Handle a route-significant file being added or removed.
2921
2550
  * Always triggers a full-reload since the route tree structure changed.
2922
2551
  */
2923
2552
  const handleStructuralChange = (filePath) => {
2924
2553
  if (!filePath.startsWith(ctx.appDir)) return;
2925
2554
  if (!ROUTE_FILE_PATTERNS.test(filePath)) return;
2926
- rescan();
2555
+ if (!rescanSafely()) return;
2927
2556
  lastManifest = ctx.routeTree ? generateManifestModule(ctx.routeTree, ctx.root) : "";
2928
2557
  invalidateManifest(devServer);
2929
2558
  };
@@ -2938,7 +2567,7 @@ function timberRouting(ctx) {
2938
2567
  const handleContentChange = (filePath) => {
2939
2568
  if (!filePath.startsWith(ctx.appDir)) return;
2940
2569
  if (!ROUTE_FILE_PATTERNS.test(filePath)) return;
2941
- rescan();
2570
+ if (!rescanSafely()) return;
2942
2571
  const newManifest = ctx.routeTree ? generateManifestModule(ctx.routeTree, ctx.root) : "";
2943
2572
  if (newManifest !== lastManifest) {
2944
2573
  lastManifest = newManifest;
@@ -2988,11 +2617,7 @@ function invalidateManifest(server) {
2988
2617
  for (const envName of Object.keys(server.environments)) {
2989
2618
  const env = server.environments[envName];
2990
2619
  if (!env?.moduleGraph) continue;
2991
- for (const virtualId of [
2992
- RESOLVED_VIRTUAL_ID$1,
2993
- RESOLVED_SEARCH_PARAMS_REGISTRY_ID,
2994
- RESOLVED_SCHEMA_ID
2995
- ]) {
2620
+ for (const virtualId of [RESOLVED_VIRTUAL_ID$1, RESOLVED_SCHEMA_ID]) {
2996
2621
  const mod = env.moduleGraph.getModuleById(virtualId);
2997
2622
  if (mod) env.moduleGraph.invalidateModule(mod);
2998
2623
  }
@@ -3030,7 +2655,6 @@ var SUBPATH_SRC_MAP = {
3030
2655
  "cookies": "cookies/index.ts",
3031
2656
  "params": "params/index.ts",
3032
2657
  "search-params": "search-params/index.ts",
3033
- "segment-params": "segment-params/index.ts",
3034
2658
  "cache/stores/memory": "cache/stores/memory.ts",
3035
2659
  "cache/stores/redis": "cache/stores/redis.ts",
3036
2660
  "cache/stores/cloudflare-kv": "cache/stores/cloudflare-kv.ts",
@@ -6769,6 +6393,45 @@ async function cleanConditionalArtifacts(staticDir) {
6769
6393
  await rm(join(staticDir, REDIRECTS_FILE), { force: true }).catch(() => {});
6770
6394
  }
6771
6395
  /**
6396
+ * Run `fn` with `MessageChannel` instrumented, then stop every channel opened
6397
+ * while it ran from holding the process open.
6398
+ *
6399
+ * Static generation is the only build pass that renders **HTML** in the build
6400
+ * process: it imports the built app bundle and drives the full SSR pipeline
6401
+ * through it. React's scheduler opens a `MessageChannel` at module scope in
6402
+ * that bundle (`scheduler`'s DOM build) and never closes it, and a ref'd
6403
+ * `MessagePort` keeps Node's event loop alive on its own — so `timber build`
6404
+ * finished the site, printed its summary, and then hung forever. Server-mode
6405
+ * builds are unaffected because nothing there renders through react-dom.
6406
+ *
6407
+ * `unref()` is the whole fix: it leaves the port working and only drops the
6408
+ * "keep the process alive" property. It is applied **after** `fn` resolves, so
6409
+ * a render in flight can never be cut short by the process exiting under it —
6410
+ * an unref'd port would let Node exit while the scheduler still had a
6411
+ * continuation queued, which is a silently truncated page rather than a hang.
6412
+ *
6413
+ * Nothing renders after this pass. If that ever changes, the release has to
6414
+ * move to the end of whatever renders last.
6415
+ *
6416
+ * @internal Exported for testing.
6417
+ */
6418
+ async function withSchedulerPortsReleased(fn) {
6419
+ const opened = [];
6420
+ const OriginalMessageChannel = globalThis.MessageChannel;
6421
+ globalThis.MessageChannel = class extends OriginalMessageChannel {
6422
+ constructor() {
6423
+ super();
6424
+ opened.push(this.port1, this.port2);
6425
+ }
6426
+ };
6427
+ try {
6428
+ return await fn();
6429
+ } finally {
6430
+ globalThis.MessageChannel = OriginalMessageChannel;
6431
+ for (const port of opened) port.unref?.();
6432
+ }
6433
+ }
6434
+ /**
6772
6435
  * Load the built RSC entry and run the static generation pass.
6773
6436
  *
6774
6437
  * Unlike prebuilt capture, this needs the FULL pipeline handler, so
@@ -6783,27 +6446,29 @@ async function runStaticGeneration(ctx) {
6783
6446
  g.__TIMBER_CAPTURE_MODE__ = false;
6784
6447
  if (ctx.buildManifest) await mergeRscAssetsManifest(ctx.buildDir, ctx.root, ctx.buildManifest, ctx.routeClientRefKeys);
6785
6448
  console.log("[timber] Static generation: building site…");
6786
- let mod;
6787
- try {
6788
- mod = await import(
6789
- /* @vite-ignore */
6790
- `${pathToFileURL(entryPath).href}?t=${Date.now()}`
6791
- );
6792
- } catch (error) {
6793
- throw new Error(`[timber] Static generation: failed to load the built RSC entry (${entryPath}). The full request handler must initialize for static rendering.`, { cause: error });
6794
- }
6795
- const generate = mod.__timber_generateStaticSite;
6796
- if (typeof generate !== "function") throw new Error("[timber] Static generation: built RSC entry does not export __timber_generateStaticSite — rebuild from a clean build directory");
6797
6449
  const staticDir = join(ctx.buildDir, "static-pages");
6798
6450
  await cleanConditionalArtifacts(staticDir);
6799
6451
  await mkdir(staticDir, { recursive: true });
6800
6452
  const sitemapConfig = ctx.config.sitemap;
6801
6453
  const autoSitemapEnabled = Boolean(sitemapConfig?.enabled && sitemapConfig?.baseUrl);
6802
6454
  const writer = new StaticSiteWriter(staticDir, await detectFsIdentity(staticDir));
6803
- const summary = await generate({
6804
- autoSitemapEnabled,
6805
- renderTimeoutMs: ctx.config.renderTimeoutMs,
6806
- onEntry: (entry) => writer.write(entry)
6455
+ const summary = await withSchedulerPortsReleased(async () => {
6456
+ let mod;
6457
+ try {
6458
+ mod = await import(
6459
+ /* @vite-ignore */
6460
+ `${pathToFileURL(entryPath).href}?t=${Date.now()}`
6461
+ );
6462
+ } catch (error) {
6463
+ throw new Error(`[timber] Static generation: failed to load the built RSC entry (${entryPath}). The full request handler must initialize for static rendering.`, { cause: error });
6464
+ }
6465
+ const generate = mod.__timber_generateStaticSite;
6466
+ if (typeof generate !== "function") throw new Error("[timber] Static generation: built RSC entry does not export __timber_generateStaticSite — rebuild from a clean build directory");
6467
+ return generate({
6468
+ autoSitemapEnabled,
6469
+ renderTimeoutMs: ctx.config.renderTimeoutMs,
6470
+ onEntry: (entry) => writer.write(entry)
6471
+ });
6807
6472
  });
6808
6473
  const totalUrls = summary.rendered + summary.errors.length;
6809
6474
  console.log(`[timber] Static generation: rendered ${summary.rendered}/${totalUrls} entries.` + (summary.errors.length > 0 ? ` ${summary.errors.length} failed.` : "") + (summary.redirects.length > 0 ? ` ${summary.redirects.length} redirect(s).` : ""));
@@ -10935,6 +10600,7 @@ function timber(config) {
10935
10600
  const earlyFileConfig = loadTimberConfigFile(process.cwd());
10936
10601
  const rootSync = {
10937
10602
  name: "timber-root-sync",
10603
+ api: { getContext: () => ctx },
10938
10604
  async config(userConfig, { command, isPreview }) {
10939
10605
  const viteRoot = resolve(userConfig.root ?? process.cwd());
10940
10606
  ctx.timer.start("config-load");
@@ -10991,7 +10657,6 @@ function timber(config) {
10991
10657
  "@timber-js/app/cookies",
10992
10658
  "@timber-js/app/cache",
10993
10659
  "@timber-js/app/search-params",
10994
- "@timber-js/app/segment-params",
10995
10660
  "@timber-js/app/params",
10996
10661
  "@timber-js/app/codec"
10997
10662
  ];