@timber-js/app 0.2.0-alpha.97 → 0.2.0-alpha.99

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 (372) hide show
  1. package/dist/_chunks/actions-CQ8Z8VGL.js +1061 -0
  2. package/dist/_chunks/actions-CQ8Z8VGL.js.map +1 -0
  3. package/dist/_chunks/build-output-helper-DXnW0qjz.js +61 -0
  4. package/dist/_chunks/build-output-helper-DXnW0qjz.js.map +1 -0
  5. package/dist/_chunks/{define-Itxvcd7F.js → define-B-Q_UMOD.js} +19 -23
  6. package/dist/_chunks/define-B-Q_UMOD.js.map +1 -0
  7. package/dist/_chunks/{define-C77ScO0m.js → define-CfBPoJb0.js} +24 -7
  8. package/dist/_chunks/define-CfBPoJb0.js.map +1 -0
  9. package/dist/_chunks/define-cookie-BjpIt4UC.js +194 -0
  10. package/dist/_chunks/define-cookie-BjpIt4UC.js.map +1 -0
  11. package/dist/_chunks/{format-CYBGxKtc.js → format-Bcn-Iv1x.js} +1 -1
  12. package/dist/_chunks/{format-CYBGxKtc.js.map → format-Bcn-Iv1x.js.map} +1 -1
  13. package/dist/_chunks/handler-store-B-lqaGyh.js +54 -0
  14. package/dist/_chunks/handler-store-B-lqaGyh.js.map +1 -0
  15. package/dist/_chunks/logger-0m8MsKdc.js +291 -0
  16. package/dist/_chunks/logger-0m8MsKdc.js.map +1 -0
  17. package/dist/_chunks/merge-search-params-BphMdht_.js +122 -0
  18. package/dist/_chunks/merge-search-params-BphMdht_.js.map +1 -0
  19. package/dist/_chunks/{metadata-routes-DS3eKNmf.js → metadata-routes-BU684ls2.js} +1 -1
  20. package/dist/_chunks/{metadata-routes-DS3eKNmf.js.map → metadata-routes-BU684ls2.js.map} +1 -1
  21. package/dist/_chunks/navigation-root-BCYczjml.js +96 -0
  22. package/dist/_chunks/navigation-root-BCYczjml.js.map +1 -0
  23. package/dist/_chunks/registry-I2ss-lvy.js +20 -0
  24. package/dist/_chunks/registry-I2ss-lvy.js.map +1 -0
  25. package/dist/_chunks/router-ref-h3-UaCQv.js +28 -0
  26. package/dist/_chunks/router-ref-h3-UaCQv.js.map +1 -0
  27. package/dist/_chunks/{schema-bridge-C3xl_vfb.js → schema-bridge-Cxu4l-7p.js} +1 -1
  28. package/dist/_chunks/{schema-bridge-C3xl_vfb.js.map → schema-bridge-Cxu4l-7p.js.map} +1 -1
  29. package/dist/_chunks/segment-classify-BjfuctV2.js +137 -0
  30. package/dist/_chunks/segment-classify-BjfuctV2.js.map +1 -0
  31. package/dist/_chunks/{segment-context-fHFLF1PE.js → segment-context-Dx_OizxD.js} +1 -1
  32. package/dist/_chunks/{segment-context-fHFLF1PE.js.map → segment-context-Dx_OizxD.js.map} +1 -1
  33. package/dist/_chunks/{router-ref-C8OCm7g7.js → ssr-data-B4CdH7rE.js} +2 -26
  34. package/dist/_chunks/ssr-data-B4CdH7rE.js.map +1 -0
  35. package/dist/_chunks/{stale-reload-BX5gL1r-.js → stale-reload-Bab885FO.js} +1 -1
  36. package/dist/_chunks/{stale-reload-BX5gL1r-.js.map → stale-reload-Bab885FO.js.map} +1 -1
  37. package/dist/_chunks/tracing-C8V-YGsP.js +329 -0
  38. package/dist/_chunks/tracing-C8V-YGsP.js.map +1 -0
  39. package/dist/_chunks/{use-query-states-BiV5GJgm.js → use-query-states-B2XTqxDR.js} +3 -19
  40. package/dist/_chunks/use-query-states-B2XTqxDR.js.map +1 -0
  41. package/dist/_chunks/{use-params-IOPu7E8t.js → use-segment-params-BkpKAQ7D.js} +9 -95
  42. package/dist/_chunks/use-segment-params-BkpKAQ7D.js.map +1 -0
  43. package/dist/_chunks/{interception-BbqMCVXa.js → walkers-Tg0Alwcg.js} +66 -87
  44. package/dist/_chunks/walkers-Tg0Alwcg.js.map +1 -0
  45. package/dist/_chunks/{dev-warnings-DpGRGoDi.js → warnings-Cg47l5sk.js} +3 -3
  46. package/dist/_chunks/warnings-Cg47l5sk.js.map +1 -0
  47. package/dist/adapters/build-output-helper.d.ts +28 -0
  48. package/dist/adapters/build-output-helper.d.ts.map +1 -0
  49. package/dist/adapters/cloudflare.d.ts.map +1 -1
  50. package/dist/adapters/cloudflare.js +8 -28
  51. package/dist/adapters/cloudflare.js.map +1 -1
  52. package/dist/adapters/nitro.d.ts.map +1 -1
  53. package/dist/adapters/nitro.js +63 -31
  54. package/dist/adapters/nitro.js.map +1 -1
  55. package/dist/adapters/shared.d.ts +16 -0
  56. package/dist/adapters/shared.d.ts.map +1 -0
  57. package/dist/cache/index.js +9 -2
  58. package/dist/cache/index.js.map +1 -1
  59. package/dist/cache/timber-cache.d.ts.map +1 -1
  60. package/dist/client/error-boundary.js +2 -1
  61. package/dist/client/error-boundary.js.map +1 -1
  62. package/dist/client/form.d.ts +10 -24
  63. package/dist/client/form.d.ts.map +1 -1
  64. package/dist/client/index.d.ts +1 -5
  65. package/dist/client/index.d.ts.map +1 -1
  66. package/dist/client/index.js +41 -91
  67. package/dist/client/index.js.map +1 -1
  68. package/dist/client/internal.d.ts +2 -1
  69. package/dist/client/internal.d.ts.map +1 -1
  70. package/dist/client/internal.js +81 -7
  71. package/dist/client/internal.js.map +1 -1
  72. package/dist/client/rsc-fetch.d.ts.map +1 -1
  73. package/dist/client/state.d.ts +1 -1
  74. package/dist/client/use-cookie.d.ts +8 -0
  75. package/dist/client/use-cookie.d.ts.map +1 -1
  76. package/dist/client/{use-params.d.ts → use-segment-params.d.ts} +1 -1
  77. package/dist/client/use-segment-params.d.ts.map +1 -0
  78. package/dist/codec.d.ts +1 -1
  79. package/dist/codec.d.ts.map +1 -1
  80. package/dist/codec.js +2 -2
  81. package/dist/config-types.d.ts +28 -0
  82. package/dist/config-types.d.ts.map +1 -1
  83. package/dist/cookies/define-cookie.d.ts +87 -35
  84. package/dist/cookies/define-cookie.d.ts.map +1 -1
  85. package/dist/cookies/index.d.ts +2 -1
  86. package/dist/cookies/index.d.ts.map +1 -1
  87. package/dist/cookies/index.js +48 -2
  88. package/dist/cookies/index.js.map +1 -0
  89. package/dist/cookies/json-cookie.d.ts +64 -0
  90. package/dist/cookies/json-cookie.d.ts.map +1 -0
  91. package/dist/cookies/validation.d.ts +46 -0
  92. package/dist/cookies/validation.d.ts.map +1 -0
  93. package/dist/{plugins/dev-404-page.d.ts → dev-tools/404-page.d.ts} +9 -19
  94. package/dist/dev-tools/404-page.d.ts.map +1 -0
  95. package/dist/{plugins/dev-browser-logs.d.ts → dev-tools/browser-logs.d.ts} +1 -1
  96. package/dist/dev-tools/browser-logs.d.ts.map +1 -0
  97. package/dist/{plugins/dev-error-page.d.ts → dev-tools/error-page.d.ts} +2 -2
  98. package/dist/dev-tools/error-page.d.ts.map +1 -0
  99. package/dist/{server/dev-holding-server.d.ts → dev-tools/holding-server.d.ts} +5 -3
  100. package/dist/dev-tools/holding-server.d.ts.map +1 -0
  101. package/dist/dev-tools/index.d.ts +31 -0
  102. package/dist/dev-tools/index.d.ts.map +1 -0
  103. package/dist/{server/dev-span-processor.d.ts → dev-tools/instrumentation.d.ts} +26 -6
  104. package/dist/dev-tools/instrumentation.d.ts.map +1 -0
  105. package/dist/{server/dev-logger.d.ts → dev-tools/logger.d.ts} +1 -1
  106. package/dist/dev-tools/logger.d.ts.map +1 -0
  107. package/dist/{plugins/dev-logs.d.ts → dev-tools/logs.d.ts} +1 -1
  108. package/dist/dev-tools/logs.d.ts.map +1 -0
  109. package/dist/{plugins/dev-error-overlay.d.ts → dev-tools/overlay.d.ts} +3 -12
  110. package/dist/dev-tools/overlay.d.ts.map +1 -0
  111. package/dist/dev-tools/stack-classifier.d.ts +34 -0
  112. package/dist/dev-tools/stack-classifier.d.ts.map +1 -0
  113. package/dist/{plugins/dev-terminal-error.d.ts → dev-tools/terminal.d.ts} +2 -2
  114. package/dist/dev-tools/terminal.d.ts.map +1 -0
  115. package/dist/{server/dev-warnings.d.ts → dev-tools/warnings.d.ts} +1 -1
  116. package/dist/dev-tools/warnings.d.ts.map +1 -0
  117. package/dist/index.d.ts +1 -0
  118. package/dist/index.d.ts.map +1 -1
  119. package/dist/index.js +285 -133
  120. package/dist/index.js.map +1 -1
  121. package/dist/plugin-context.d.ts +1 -1
  122. package/dist/plugin-context.d.ts.map +1 -1
  123. package/dist/plugins/adapter-build.d.ts.map +1 -1
  124. package/dist/plugins/build-report.d.ts +6 -4
  125. package/dist/plugins/build-report.d.ts.map +1 -1
  126. package/dist/routing/convention-lint.d.ts.map +1 -1
  127. package/dist/routing/index.d.ts +5 -3
  128. package/dist/routing/index.d.ts.map +1 -1
  129. package/dist/routing/index.js +3 -3
  130. package/dist/routing/scanner.d.ts +1 -10
  131. package/dist/routing/scanner.d.ts.map +1 -1
  132. package/dist/routing/segment-classify.d.ts +37 -8
  133. package/dist/routing/segment-classify.d.ts.map +1 -1
  134. package/dist/routing/status-file-lint.d.ts.map +1 -1
  135. package/dist/routing/types.d.ts +63 -23
  136. package/dist/routing/types.d.ts.map +1 -1
  137. package/dist/routing/walkers.d.ts +51 -0
  138. package/dist/routing/walkers.d.ts.map +1 -0
  139. package/dist/search-params/define.d.ts +25 -7
  140. package/dist/search-params/define.d.ts.map +1 -1
  141. package/dist/search-params/index.js +5 -3
  142. package/dist/search-params/index.js.map +1 -1
  143. package/dist/search-params/wrappers.d.ts +2 -2
  144. package/dist/search-params/wrappers.d.ts.map +1 -1
  145. package/dist/segment-params/define.d.ts +23 -6
  146. package/dist/segment-params/define.d.ts.map +1 -1
  147. package/dist/segment-params/index.js +1 -1
  148. package/dist/server/access-gate.d.ts +4 -3
  149. package/dist/server/access-gate.d.ts.map +1 -1
  150. package/dist/server/action-handler.d.ts +15 -6
  151. package/dist/server/action-handler.d.ts.map +1 -1
  152. package/dist/server/als-registry.d.ts +5 -5
  153. package/dist/server/als-registry.d.ts.map +1 -1
  154. package/dist/server/asset-headers.d.ts +1 -15
  155. package/dist/server/asset-headers.d.ts.map +1 -1
  156. package/dist/server/cookie-context.d.ts +170 -0
  157. package/dist/server/cookie-context.d.ts.map +1 -0
  158. package/dist/server/cookie-parsing.d.ts +51 -0
  159. package/dist/server/cookie-parsing.d.ts.map +1 -0
  160. package/dist/server/deny-boundary.d.ts +90 -0
  161. package/dist/server/deny-boundary.d.ts.map +1 -0
  162. package/dist/server/deny-renderer.d.ts.map +1 -1
  163. package/dist/server/early-hints-sender.d.ts.map +1 -1
  164. package/dist/server/html-injector-core.d.ts +212 -0
  165. package/dist/server/html-injector-core.d.ts.map +1 -0
  166. package/dist/server/html-injectors.d.ts +59 -59
  167. package/dist/server/html-injectors.d.ts.map +1 -1
  168. package/dist/server/index.d.ts +5 -4
  169. package/dist/server/index.d.ts.map +1 -1
  170. package/dist/server/index.js +4 -149
  171. package/dist/server/index.js.map +1 -1
  172. package/dist/server/internal.d.ts +6 -4
  173. package/dist/server/internal.d.ts.map +1 -1
  174. package/dist/server/internal.js +852 -852
  175. package/dist/server/internal.js.map +1 -1
  176. package/dist/server/logger.d.ts +14 -0
  177. package/dist/server/logger.d.ts.map +1 -1
  178. package/dist/server/middleware-runner.d.ts +17 -0
  179. package/dist/server/middleware-runner.d.ts.map +1 -1
  180. package/dist/server/node-stream-transforms.d.ts +46 -49
  181. package/dist/server/node-stream-transforms.d.ts.map +1 -1
  182. package/dist/server/param-coercion.d.ts +26 -0
  183. package/dist/server/param-coercion.d.ts.map +1 -0
  184. package/dist/server/pipeline-helpers.d.ts +95 -0
  185. package/dist/server/pipeline-helpers.d.ts.map +1 -0
  186. package/dist/server/pipeline-outcome.d.ts +49 -0
  187. package/dist/server/pipeline-outcome.d.ts.map +1 -0
  188. package/dist/server/pipeline-phases.d.ts +52 -0
  189. package/dist/server/pipeline-phases.d.ts.map +1 -0
  190. package/dist/server/pipeline.d.ts +51 -32
  191. package/dist/server/pipeline.d.ts.map +1 -1
  192. package/dist/server/port-resolution.d.ts +117 -0
  193. package/dist/server/port-resolution.d.ts.map +1 -0
  194. package/dist/server/request-context.d.ts +22 -159
  195. package/dist/server/request-context.d.ts.map +1 -1
  196. package/dist/server/route-element-builder.d.ts.map +1 -1
  197. package/dist/server/route-matcher.d.ts +20 -47
  198. package/dist/server/route-matcher.d.ts.map +1 -1
  199. package/dist/server/rsc-entry/action-middleware-runner.d.ts +66 -0
  200. package/dist/server/rsc-entry/action-middleware-runner.d.ts.map +1 -0
  201. package/dist/server/rsc-entry/helpers.d.ts +1 -1
  202. package/dist/server/rsc-entry/helpers.d.ts.map +1 -1
  203. package/dist/server/rsc-entry/index.d.ts.map +1 -1
  204. package/dist/server/rsc-entry/render-route.d.ts +50 -0
  205. package/dist/server/rsc-entry/render-route.d.ts.map +1 -0
  206. package/dist/server/rsc-entry/wrap-action-dispatch.d.ts +119 -0
  207. package/dist/server/rsc-entry/wrap-action-dispatch.d.ts.map +1 -0
  208. package/dist/server/state-tree-diff.d.ts.map +1 -1
  209. package/dist/server/status-code-resolver.d.ts +16 -11
  210. package/dist/server/status-code-resolver.d.ts.map +1 -1
  211. package/dist/server/tracing.d.ts +1 -1
  212. package/dist/server/tracing.d.ts.map +1 -1
  213. package/dist/server/tree-builder.d.ts +45 -16
  214. package/dist/server/tree-builder.d.ts.map +1 -1
  215. package/dist/server/types.d.ts +48 -0
  216. package/dist/server/types.d.ts.map +1 -1
  217. package/dist/server/utils/escape-html.d.ts +14 -0
  218. package/dist/server/utils/escape-html.d.ts.map +1 -0
  219. package/dist/shims/headers.d.ts +2 -2
  220. package/dist/shims/headers.d.ts.map +1 -1
  221. package/dist/shims/navigation-client.d.ts +3 -1
  222. package/dist/shims/navigation-client.d.ts.map +1 -1
  223. package/dist/shims/navigation.d.ts +9 -4
  224. package/dist/shims/navigation.d.ts.map +1 -1
  225. package/dist/utils/directive-parser.d.ts +0 -45
  226. package/dist/utils/directive-parser.d.ts.map +1 -1
  227. package/package.json +1 -1
  228. package/src/adapters/build-output-helper.ts +77 -0
  229. package/src/adapters/cloudflare.ts +10 -50
  230. package/src/adapters/nitro.ts +66 -50
  231. package/src/adapters/shared.ts +40 -0
  232. package/src/cache/timber-cache.ts +3 -2
  233. package/src/client/form.tsx +17 -25
  234. package/src/client/index.ts +16 -9
  235. package/src/client/internal.ts +3 -2
  236. package/src/client/router.ts +1 -1
  237. package/src/client/rsc-fetch.ts +15 -0
  238. package/src/client/state.ts +2 -2
  239. package/src/client/use-cookie.ts +29 -0
  240. package/src/codec.ts +3 -7
  241. package/src/config-types.ts +28 -0
  242. package/src/cookies/define-cookie.ts +271 -78
  243. package/src/cookies/index.ts +11 -8
  244. package/src/cookies/json-cookie.ts +105 -0
  245. package/src/cookies/validation.ts +134 -0
  246. package/src/{plugins/dev-404-page.ts → dev-tools/404-page.ts} +17 -48
  247. package/src/{plugins/dev-error-page.ts → dev-tools/error-page.ts} +5 -32
  248. package/src/{server/dev-holding-server.ts → dev-tools/holding-server.ts} +4 -2
  249. package/src/dev-tools/index.ts +90 -0
  250. package/src/dev-tools/instrumentation.ts +176 -0
  251. package/src/{plugins/dev-logs.ts → dev-tools/logs.ts} +2 -2
  252. package/src/{plugins/dev-error-overlay.ts → dev-tools/overlay.ts} +5 -23
  253. package/src/dev-tools/stack-classifier.ts +75 -0
  254. package/src/{plugins/dev-terminal-error.ts → dev-tools/terminal.ts} +4 -38
  255. package/src/{server/dev-warnings.ts → dev-tools/warnings.ts} +1 -1
  256. package/src/index.ts +95 -34
  257. package/src/plugin-context.ts +1 -1
  258. package/src/plugins/adapter-build.ts +3 -1
  259. package/src/plugins/build-report.ts +13 -22
  260. package/src/plugins/dev-server.ts +3 -3
  261. package/src/plugins/routing.ts +14 -12
  262. package/src/plugins/shims.ts +1 -1
  263. package/src/plugins/static-build.ts +1 -1
  264. package/src/routing/codegen.ts +1 -1
  265. package/src/routing/convention-lint.ts +9 -8
  266. package/src/routing/index.ts +5 -3
  267. package/src/routing/interception.ts +1 -1
  268. package/src/routing/scanner.ts +22 -95
  269. package/src/routing/segment-classify.ts +107 -8
  270. package/src/routing/status-file-lint.ts +7 -5
  271. package/src/routing/types.ts +63 -23
  272. package/src/routing/walkers.ts +90 -0
  273. package/src/search-params/define.ts +71 -15
  274. package/src/search-params/wrappers.ts +9 -2
  275. package/src/segment-params/define.ts +66 -13
  276. package/src/server/access-gate.tsx +9 -8
  277. package/src/server/action-handler.ts +34 -38
  278. package/src/server/als-registry.ts +5 -5
  279. package/src/server/asset-headers.ts +8 -34
  280. package/src/server/cookie-context.ts +468 -0
  281. package/src/server/cookie-parsing.ts +135 -0
  282. package/src/server/{deny-page-resolver.ts → deny-boundary.ts} +78 -14
  283. package/src/server/deny-renderer.ts +7 -12
  284. package/src/server/early-hints-sender.ts +3 -2
  285. package/src/server/fallback-error.ts +2 -2
  286. package/src/server/html-injector-core.ts +403 -0
  287. package/src/server/html-injectors.ts +158 -297
  288. package/src/server/index.ts +13 -14
  289. package/src/server/internal.ts +10 -3
  290. package/src/server/logger.ts +23 -0
  291. package/src/server/middleware-runner.ts +44 -0
  292. package/src/server/node-stream-transforms.ts +108 -248
  293. package/src/server/param-coercion.ts +76 -0
  294. package/src/server/pipeline-helpers.ts +204 -0
  295. package/src/server/pipeline-outcome.ts +167 -0
  296. package/src/server/pipeline-phases.ts +409 -0
  297. package/src/server/pipeline.ts +70 -540
  298. package/src/server/port-resolution.ts +215 -0
  299. package/src/server/request-context.ts +46 -451
  300. package/src/server/route-element-builder.ts +8 -4
  301. package/src/server/route-matcher.ts +28 -60
  302. package/src/server/rsc-entry/action-middleware-runner.ts +167 -0
  303. package/src/server/rsc-entry/api-handler.ts +2 -2
  304. package/src/server/rsc-entry/error-renderer.ts +2 -2
  305. package/src/server/rsc-entry/helpers.ts +2 -7
  306. package/src/server/rsc-entry/index.ts +81 -366
  307. package/src/server/rsc-entry/render-route.ts +304 -0
  308. package/src/server/rsc-entry/rsc-payload.ts +1 -1
  309. package/src/server/rsc-entry/ssr-renderer.ts +2 -2
  310. package/src/server/rsc-entry/wrap-action-dispatch.ts +449 -0
  311. package/src/server/sitemap-generator.ts +1 -1
  312. package/src/server/slot-resolver.ts +1 -1
  313. package/src/server/ssr-entry.ts +1 -1
  314. package/src/server/state-tree-diff.ts +4 -1
  315. package/src/server/status-code-resolver.ts +112 -128
  316. package/src/server/tracing.ts +3 -3
  317. package/src/server/tree-builder.ts +134 -56
  318. package/src/server/types.ts +52 -0
  319. package/src/server/utils/escape-html.ts +20 -0
  320. package/src/shims/headers.ts +3 -3
  321. package/src/shims/navigation-client.ts +4 -3
  322. package/src/shims/navigation.ts +9 -7
  323. package/src/utils/directive-parser.ts +0 -392
  324. package/dist/_chunks/actions-DLnUaR65.js +0 -421
  325. package/dist/_chunks/actions-DLnUaR65.js.map +0 -1
  326. package/dist/_chunks/als-registry-HS0LGUl2.js +0 -41
  327. package/dist/_chunks/als-registry-HS0LGUl2.js.map +0 -1
  328. package/dist/_chunks/debug-ECi_61pb.js +0 -108
  329. package/dist/_chunks/debug-ECi_61pb.js.map +0 -1
  330. package/dist/_chunks/define-C77ScO0m.js.map +0 -1
  331. package/dist/_chunks/define-Itxvcd7F.js.map +0 -1
  332. package/dist/_chunks/define-cookie-BowvzoP0.js +0 -94
  333. package/dist/_chunks/define-cookie-BowvzoP0.js.map +0 -1
  334. package/dist/_chunks/dev-warnings-DpGRGoDi.js.map +0 -1
  335. package/dist/_chunks/interception-BbqMCVXa.js.map +0 -1
  336. package/dist/_chunks/merge-search-params-Cm_KIWDX.js +0 -41
  337. package/dist/_chunks/merge-search-params-Cm_KIWDX.js.map +0 -1
  338. package/dist/_chunks/request-context-CK5tZqIP.js +0 -478
  339. package/dist/_chunks/request-context-CK5tZqIP.js.map +0 -1
  340. package/dist/_chunks/router-ref-C8OCm7g7.js.map +0 -1
  341. package/dist/_chunks/segment-classify-BDNn6EzD.js +0 -65
  342. package/dist/_chunks/segment-classify-BDNn6EzD.js.map +0 -1
  343. package/dist/_chunks/tracing-CCYbKn5n.js +0 -238
  344. package/dist/_chunks/tracing-CCYbKn5n.js.map +0 -1
  345. package/dist/_chunks/use-params-IOPu7E8t.js.map +0 -1
  346. package/dist/_chunks/use-query-states-BiV5GJgm.js.map +0 -1
  347. package/dist/client/use-params.d.ts.map +0 -1
  348. package/dist/plugins/dev-404-page.d.ts.map +0 -1
  349. package/dist/plugins/dev-browser-logs.d.ts.map +0 -1
  350. package/dist/plugins/dev-error-overlay.d.ts.map +0 -1
  351. package/dist/plugins/dev-error-page.d.ts.map +0 -1
  352. package/dist/plugins/dev-logs.d.ts.map +0 -1
  353. package/dist/plugins/dev-terminal-error.d.ts.map +0 -1
  354. package/dist/server/deny-page-resolver.d.ts +0 -52
  355. package/dist/server/deny-page-resolver.d.ts.map +0 -1
  356. package/dist/server/dev-fetch-instrumentation.d.ts +0 -22
  357. package/dist/server/dev-fetch-instrumentation.d.ts.map +0 -1
  358. package/dist/server/dev-holding-server.d.ts.map +0 -1
  359. package/dist/server/dev-logger.d.ts.map +0 -1
  360. package/dist/server/dev-span-processor.d.ts.map +0 -1
  361. package/dist/server/dev-warnings.d.ts.map +0 -1
  362. package/dist/server/manifest-status-resolver.d.ts +0 -58
  363. package/dist/server/manifest-status-resolver.d.ts.map +0 -1
  364. package/dist/server/page-deny-boundary.d.ts +0 -31
  365. package/dist/server/page-deny-boundary.d.ts.map +0 -1
  366. package/src/server/dev-fetch-instrumentation.ts +0 -96
  367. package/src/server/dev-span-processor.ts +0 -78
  368. package/src/server/manifest-status-resolver.ts +0 -215
  369. package/src/server/page-deny-boundary.tsx +0 -56
  370. /package/src/client/{use-params.ts → use-segment-params.ts} +0 -0
  371. /package/src/{plugins/dev-browser-logs.ts → dev-tools/browser-logs.ts} +0 -0
  372. /package/src/{server/dev-logger.ts → dev-tools/logger.ts} +0 -0
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Stack trace frame classifier — categorizes stack frames by origin.
3
+ *
4
+ * Provides `classifyFrame` (single frame) and `classifyStack` (full stack
5
+ * string) as the shared primitives used by the error page, terminal
6
+ * formatter, and overlay.
7
+ *
8
+ * Dev-only: this module is only imported by other dev-tools modules.
9
+ */
10
+
11
+ // ─── Types ──────────────────────────────────────────────────────────────────
12
+
13
+ export type FrameType = 'app' | 'framework' | 'internal';
14
+
15
+ export interface ClassifiedFrame {
16
+ raw: string;
17
+ type: FrameType;
18
+ file?: string;
19
+ line?: number;
20
+ col?: number;
21
+ }
22
+
23
+ // ─── Frame Parsing ──────────────────────────────────────────────────────────
24
+
25
+ /** Parse file/line/col from a stack frame line. */
26
+ function parseFrame(frameLine: string): { file?: string; line?: number; col?: number } {
27
+ const parenMatch = /\(([^)]+):(\d+):(\d+)\)/.exec(frameLine);
28
+ if (parenMatch) {
29
+ return { file: parenMatch[1], line: Number(parenMatch[2]), col: Number(parenMatch[3]) };
30
+ }
31
+ const bareMatch = /at (\/[^:]+):(\d+):(\d+)/.exec(frameLine);
32
+ if (bareMatch) {
33
+ return { file: bareMatch[1], line: Number(bareMatch[2]), col: Number(bareMatch[3]) };
34
+ }
35
+ return {};
36
+ }
37
+
38
+ // ─── Classification ─────────────────────────────────────────────────────────
39
+
40
+ /**
41
+ * Classify a single stack frame line by origin.
42
+ *
43
+ * - 'app': user application code (in project root, not node_modules)
44
+ * - 'framework': timber-app internal code
45
+ * - 'internal': node_modules, Node.js internals
46
+ */
47
+ export function classifyFrame(frameLine: string, projectRoot: string): FrameType {
48
+ const trimmed = frameLine.trim();
49
+
50
+ if (trimmed.includes('packages/timber-app/')) return 'framework';
51
+ if (trimmed.includes('node_modules/')) return 'internal';
52
+ if (trimmed.startsWith('at node:') || trimmed.includes('(node:')) return 'internal';
53
+ if (trimmed.includes(projectRoot)) return 'app';
54
+
55
+ return 'internal';
56
+ }
57
+
58
+ /**
59
+ * Classify all frames in a full stack trace string.
60
+ *
61
+ * Parses the stack, skips the first line (error message), filters to
62
+ * lines starting with "at ", and returns classified frames with optional
63
+ * file/line/col metadata.
64
+ */
65
+ export function classifyStack(stack: string, projectRoot: string): ClassifiedFrame[] {
66
+ return stack
67
+ .split('\n')
68
+ .slice(1) // skip first line (error message)
69
+ .filter((line) => line.trim().startsWith('at '))
70
+ .map((raw) => {
71
+ const type = classifyFrame(raw, projectRoot);
72
+ const { file, line, col } = parseFrame(raw);
73
+ return { raw, type, file, line, col };
74
+ });
75
+ }
@@ -16,13 +16,12 @@
16
16
 
17
17
  import { pathToFileURL } from 'node:url';
18
18
  import {
19
- classifyFrame,
20
19
  extractComponentStack,
21
20
  parseFirstAppFrame,
22
21
  PHASE_LABELS,
23
22
  type ErrorPhase,
24
- type FrameType,
25
- } from './dev-error-overlay.js';
23
+ } from './overlay.js';
24
+ import { classifyStack } from './stack-classifier.js';
26
25
 
27
26
  // ─── ANSI Codes ─────────────────────────────────────────────────────────────
28
27
 
@@ -93,40 +92,7 @@ function box(lines: string[], borderColor: string, width = 80): string {
93
92
  return output.join('\n');
94
93
  }
95
94
 
96
- // ─── Frame Extraction ───────────────────────────────────────────────────────
97
-
98
- interface ClassifiedFrame {
99
- raw: string;
100
- type: FrameType;
101
- file?: string;
102
- line?: number;
103
- col?: number;
104
- }
105
-
106
- /** Parse file/line/col from a stack frame line. */
107
- function parseFrame(frameLine: string): { file?: string; line?: number; col?: number } {
108
- const parenMatch = /\(([^)]+):(\d+):(\d+)\)/.exec(frameLine);
109
- if (parenMatch) {
110
- return { file: parenMatch[1], line: Number(parenMatch[2]), col: Number(parenMatch[3]) };
111
- }
112
- const bareMatch = /at (\/[^:]+):(\d+):(\d+)/.exec(frameLine);
113
- if (bareMatch) {
114
- return { file: bareMatch[1], line: Number(bareMatch[2]), col: Number(bareMatch[3]) };
115
- }
116
- return {};
117
- }
118
-
119
- function classifyFrames(stack: string, projectRoot: string): ClassifiedFrame[] {
120
- return stack
121
- .split('\n')
122
- .slice(1)
123
- .filter((l) => l.trim().startsWith('at '))
124
- .map((raw) => {
125
- const type = classifyFrame(raw, projectRoot);
126
- const { file, line, col } = parseFrame(raw);
127
- return { raw, type, file, line, col };
128
- });
129
- }
95
+ // Frame classification and parsing provided by stack-classifier.ts
130
96
 
131
97
  // ─── Public API ─────────────────────────────────────────────────────────────
132
98
 
@@ -144,7 +110,7 @@ export function formatTerminalError(error: Error, phase: ErrorPhase, projectRoot
144
110
  const sections: string[] = [];
145
111
  const componentStack = extractComponentStack(error);
146
112
  const loc = parseFirstAppFrame(error.stack ?? '', projectRoot);
147
- const frames = error.stack ? classifyFrames(error.stack, projectRoot) : [];
113
+ const frames = error.stack ? classifyStack(error.stack, projectRoot) : [];
148
114
  const appFrames = frames.filter((f) => f.type === 'app');
149
115
  const internalCount = frames.filter((f) => f.type !== 'app').length;
150
116
 
@@ -15,7 +15,7 @@
15
15
  */
16
16
 
17
17
  import type { ViteDevServer } from 'vite';
18
- import { isDebug } from './debug.js';
18
+ import { isDebug } from '../server/debug.js';
19
19
 
20
20
  // ─── Warning IDs ───────────────────────────────────────────────────────────
21
21
 
package/src/index.ts CHANGED
@@ -24,8 +24,8 @@ import { timberFonts } from './plugins/fonts';
24
24
  import { timberStaticBuild } from './plugins/static-build';
25
25
  import { timberServerActionExports } from './plugins/server-action-exports';
26
26
  import { timberBuildManifest } from './plugins/build-manifest';
27
- import { timberDevLogs } from './plugins/dev-logs';
28
- import { timberDevBrowserLogs } from './plugins/dev-browser-logs';
27
+ import { timberDevLogs } from './dev-tools/logs';
28
+ import { timberDevBrowserLogs } from './dev-tools/browser-logs';
29
29
  import { timberReactProd } from './plugins/react-prod';
30
30
  import { timberChunks } from './plugins/chunks';
31
31
  import { clientChunkGroup } from './plugins/client-chunks';
@@ -34,7 +34,8 @@ import { timberAdapterBuild } from './plugins/adapter-build';
34
34
  import { timberBuildReport } from './plugins/build-report';
35
35
  import { createNoopTimer } from './utils/startup-timer';
36
36
  import { resolveEncryptionKeyExpression, shouldEnableEncryption } from './server/action-encryption';
37
- import { createHoldingServer } from './server/dev-holding-server.js';
37
+ import { createHoldingServer } from './dev-tools/holding-server.js';
38
+ import { resolveStartPort, startDevServerPort } from './server/port-resolution.js';
38
39
  import type { TimberUserConfig } from './config-types.js';
39
40
  import type { PluginContext } from './plugin-context.js';
40
41
  import {
@@ -51,6 +52,14 @@ import {
51
52
 
52
53
  export type { TimberUserConfig } from './config-types.js';
53
54
 
55
+ // Metadata route handler types — re-exported so user-authored metadata
56
+ // route files (sitemap.ts, robots.ts, manifest.ts, icon.tsx, etc.) can
57
+ // import them from the package root without reaching into ./server.
58
+ // The canonical location for these types is '@timber-js/app/server'; this
59
+ // root re-export is a convenience for typed metadata route handlers.
60
+ // See design/16-metadata.md §"Metadata Routes".
61
+ export type { Metadata, MetadataRoute, MetadataHandler, MetadataResult } from './server/types.js';
62
+
54
63
  /**
55
64
  * Route map interface — augmented by the generated timber-routes.d.ts.
56
65
  *
@@ -232,7 +241,7 @@ export function timber(config?: TimberUserConfig): PluginOption[] {
232
241
  // config() hook must NOT return a `plugins` field (Vite ignores it).
233
242
  const rootSync: Plugin = {
234
243
  name: 'timber-root-sync',
235
- config(userConfig, { command }) {
244
+ async config(userConfig, { command, isPreview }) {
236
245
  // ── Load timber.config.ts from the correct root ───────────────
237
246
  // Vite's `config` hook fires before `configResolved`. The user's
238
247
  // `root` option (if set) tells us where the project lives.
@@ -287,36 +296,60 @@ export function timber(config?: TimberUserConfig): PluginOption[] {
287
296
  // We explicitly set `oxc.jsx.development: false` for builds so
288
297
  // the client bundle always uses jsx/jsxs from react/jsx-runtime,
289
298
  // regardless of the ambient NODE_ENV value.
290
- // ── Start holding server for dev mode ───────────────────────
291
- // Bind the port immediately so browsers see a loading page
292
- // instead of ERR_CONNECTION_REFUSED during the ~6-8s startup.
293
- // The holding server is closed in timber-dev-server's
294
- // configureServer hook (the last plugin), right before Vite
295
- // calls server.listen().
296
- // See design/21-dev-server.md §"Startup Holding Server", TIM-665.
297
- if (command === 'serve') {
298
- const port = (userConfig.server?.port as number) ?? 5173;
299
- try {
300
- ctx.holdingServer = createHoldingServer();
301
- // listen() is async but we fire-and-forget the server
302
- // starts binding immediately and will be ready well before
303
- // any browser request arrives. We can't await here because
304
- // config() must return synchronously.
305
- ctx.holdingServer.listen(port).then(
306
- (boundPort) => {
307
- const url = `http://localhost:${boundPort}`;
308
- console.log(
309
- `\n \x1b[2m🪵 timber.js dev server starting at\x1b[0m \x1b[36m${url}\x1b[0m\n`
310
- );
311
- },
312
- () => {
313
- // Port already in use — skip gracefully.
314
- ctx.holdingServer = null;
315
- }
316
- );
317
- } catch {
299
+ // ── Resolve dev/preview port (TIM-842) ───────────────────────
300
+ // Default port is 3000 with auto-bump on conflict. Explicit user
301
+ // overrides (`--port`, `PORT` env, `vite.config.ts` server.port)
302
+ // are honored as-is and fail loudly via `strictPort: true`.
303
+ //
304
+ // For dev (`command === 'serve' && !isPreview`) we start the
305
+ // holding server and use its `listen()` as the probe, pairing
306
+ // the probe with the real bind to eliminate TOCTOU races.
307
+ //
308
+ // For `vite preview` (`command === 'serve' && isPreview`) we
309
+ // resolve the config/env port but delegate the actual bind +
310
+ // auto-bump to Vite's own preview server. Starting the holding
311
+ // server in preview mode would self-conflict because the dev
312
+ // plugin's `configureServer` handoff does not run for preview;
313
+ // the holding server would keep the port occupied while Vite's
314
+ // preview tried to bind the same port.
315
+ //
316
+ // See design/21-dev-server.md §"Default Port and Auto-Bump".
317
+ let resolvedDevPort: number | null = null;
318
+ let resolvedDevPortExplicit = false;
319
+ if (command === 'serve' && !isPreview) {
320
+ ctx.holdingServer = createHoldingServer();
321
+ const holdingRef = ctx.holdingServer;
322
+ const result = await startDevServerPort({
323
+ configPort:
324
+ typeof userConfig.server?.port === 'number' ? userConfig.server.port : undefined,
325
+ envPort: process.env.PORT,
326
+ listen: (p) => holdingRef.listen(p),
327
+ });
328
+ resolvedDevPort = result.port;
329
+ resolvedDevPortExplicit = result.explicit;
330
+ if (!result.bound) {
331
+ // Holding server failed to bind. Drop the reference so the
332
+ // dev-server plugin doesn't try to close a server that was
333
+ // never listening, and let Vite surface the conflict.
318
334
  ctx.holdingServer = null;
319
335
  }
336
+ } else if (command === 'serve' && isPreview) {
337
+ // Preview mode: resolve the starting port from config/env/
338
+ // default but do NOT probe. Vite's preview server handles the
339
+ // bind itself, with `preview.strictPort` controlling whether
340
+ // an in-use port auto-bumps (implicit) or errors (explicit).
341
+ const previewConfigPort =
342
+ typeof userConfig.preview?.port === 'number'
343
+ ? userConfig.preview.port
344
+ : typeof userConfig.server?.port === 'number'
345
+ ? userConfig.server.port
346
+ : undefined;
347
+ const start = resolveStartPort({
348
+ configPort: previewConfigPort,
349
+ envPort: process.env.PORT,
350
+ });
351
+ resolvedDevPort = start.port;
352
+ resolvedDevPortExplicit = start.explicit;
320
353
  }
321
354
 
322
355
  // Return the resolved buildDir as Vite's build.outDir so the RSC
@@ -349,8 +382,36 @@ export function timber(config?: TimberUserConfig): PluginOption[] {
349
382
  };
350
383
  }
351
384
 
352
- // Dev mode: set outDir so dev-time references are consistent
353
- return { build: { outDir: buildOutDir }, environments: envOutDirs };
385
+ // Dev/preview mode: set outDir so dev-time references are
386
+ // consistent, and surface the resolved port to Vite.
387
+ //
388
+ // For dev, we set strictPort: true so Vite uses exactly the port
389
+ // the holding server probed for — without strictPort, Vite would
390
+ // run its own auto-bump starting from `port`, which would skip
391
+ // straight past whatever holding-server-bumped port we picked.
392
+ //
393
+ // For `vite preview`, we set `preview.strictPort` based on
394
+ // whether the user explicitly set the port. Implicit (default)
395
+ // preview uses strictPort: false so Vite's preview server can
396
+ // auto-bump from 3000. Explicit preview uses strictPort: true
397
+ // so conflicts fail loudly, matching the dev behavior and the
398
+ // TIM-842 spec.
399
+ const serverConfig: { port?: number; strictPort?: boolean } = {};
400
+ const previewConfig: { port?: number; strictPort?: boolean } = {};
401
+ if (resolvedDevPort != null) {
402
+ if (!isPreview) {
403
+ serverConfig.port = resolvedDevPort;
404
+ serverConfig.strictPort = true;
405
+ }
406
+ previewConfig.port = resolvedDevPort;
407
+ previewConfig.strictPort = resolvedDevPortExplicit;
408
+ }
409
+ return {
410
+ build: { outDir: buildOutDir },
411
+ environments: envOutDirs,
412
+ server: serverConfig,
413
+ preview: previewConfig,
414
+ };
354
415
  },
355
416
  configResolved(resolved) {
356
417
  ctx.root = resolved.root;
@@ -16,7 +16,7 @@ import type { BuildManifest } from './server/build-manifest';
16
16
  import type { StartupTimer } from './utils/startup-timer';
17
17
  import { createStartupTimer } from './utils/startup-timer';
18
18
  import type { TimberUserConfig, ClientJavascriptConfig } from './config-types.js';
19
- import type { HoldingServer } from './server/dev-holding-server.js';
19
+ import type { HoldingServer } from './dev-tools/holding-server.js';
20
20
 
21
21
  // Re-export for sub-plugin convenience — they import from plugin-context.ts
22
22
  export type { TimberUserConfig, ClientJavascriptConfig } from './config-types.js';
@@ -18,6 +18,7 @@ import { join } from 'node:path';
18
18
  import { readFile, writeFile } from 'node:fs/promises';
19
19
  import type { PluginContext } from '../plugin-context.js';
20
20
  import type { TimberPlatformAdapter, TimberConfig } from '../adapters/types.js';
21
+ import { swallow } from '../server/logger.js';
21
22
 
22
23
  export function timberAdapterBuild(ctx: PluginContext): Plugin {
23
24
  return {
@@ -103,7 +104,8 @@ async function stripJsFromRscAssetsManifests(buildDir: string): Promise<void> {
103
104
  let manifest: Record<string, unknown>;
104
105
  try {
105
106
  manifest = JSON.parse(jsonStr);
106
- } catch {
107
+ } catch (err) {
108
+ swallow(err, `corrupted RSC assets manifest: ${path}`, { level: 'warn' });
107
109
  continue;
108
110
  }
109
111
 
@@ -17,6 +17,7 @@ import { gzipSync } from 'node:zlib';
17
17
  import type { Plugin, Logger } from 'vite';
18
18
  import type { PluginContext } from '../plugin-context.js';
19
19
  import type { SegmentNode, RouteTree } from '../routing/types.js';
20
+ import { collectLeafRoutes } from '../routing/walkers.js';
20
21
  import { formatSize } from '../utils/format.js';
21
22
 
22
23
  // ─── Public types ─────────────────────────────────────────────────────────
@@ -83,33 +84,23 @@ interface RouteInfo {
83
84
  /**
84
85
  * Walk the route tree and collect all leaf routes (pages + API endpoints).
85
86
  *
86
- * Parallel slots (`@artists`, `@shows`, etc.) are intentionally skipped —
87
- * they render alongside the parent page at the same URL and are not
88
- * separately URL-addressable. Their JS is captured in shared/layout chunks.
87
+ * Wraps the shared `collectLeafRoutes` walker (TIM-848). Parallel slots
88
+ * (`@artists`, `@shows`, etc.) are intentionally skipped they render
89
+ * alongside the parent page at the same URL and are not separately
90
+ * URL-addressable. Their JS is captured in shared/layout chunks.
89
91
  *
90
92
  * After collection, entries are deduplicated by URL path so that overlapping
91
93
  * route groups (e.g. `(browse)` and `(marketing)` both producing `/`) only
92
- * appear once. The entry with the largest route-specific size wins.
94
+ * appear once. The entry with the longest segment chain (most specific
95
+ * match) wins.
93
96
  */
94
97
  export function collectRoutes(tree: RouteTree): RouteInfo[] {
95
- const routes: RouteInfo[] = [];
96
-
97
- function walk(node: SegmentNode, chain: SegmentNode[]): void {
98
- const currentChain = [...chain, node];
99
- const path = node.urlPath || '/';
100
-
101
- if (node.page) {
102
- routes.push({ path, segments: currentChain, entryFilePath: node.page.filePath });
103
- }
104
- if (node.route) {
105
- routes.push({ path, segments: currentChain, entryFilePath: node.route.filePath });
106
- }
107
-
108
- // Recurse into child segments only — skip parallel slots (node.slots)
109
- for (const child of node.children) walk(child, currentChain);
110
- }
111
-
112
- walk(tree.root, []);
98
+ const leaves = collectLeafRoutes(tree.root);
99
+ const routes: RouteInfo[] = leaves.map((leaf) => ({
100
+ path: leaf.urlPath,
101
+ segments: leaf.segments,
102
+ entryFilePath: leaf.page?.filePath ?? leaf.route?.filePath ?? null,
103
+ }));
113
104
 
114
105
  // Deduplicate entries with the same URL path (e.g. from overlapping route groups).
115
106
  // Keep the entry with the longest segment chain (most specific match).
@@ -16,19 +16,19 @@ import type { Plugin, ViteDevServer, DevEnvironment } from 'vite';
16
16
  import type { IncomingMessage, ServerResponse } from 'node:http';
17
17
  import { join } from 'node:path';
18
18
  import type { PluginContext } from '../plugin-context.js';
19
- import { setViteServer } from '../server/dev-warnings.js';
19
+ import { setViteServer } from '../dev-tools/warnings.js';
20
20
  import {
21
21
  sendErrorToOverlay,
22
22
  classifyErrorPhase,
23
23
  fixErrorStacktrace,
24
24
  parseFirstAppFrame,
25
25
  type ErrorPhase,
26
- } from './dev-error-overlay.js';
26
+ } from '../dev-tools/overlay.js';
27
27
  import {
28
28
  generateDevErrorPage,
29
29
  extractHmrOptions,
30
30
  type DevErrorHmrOptions,
31
- } from './dev-error-page.js';
31
+ } from '../dev-tools/error-page.js';
32
32
  import { addVirtualModuleContext } from '../config-validation.js';
33
33
  import { compressResponse } from '../server/compress.js';
34
34
 
@@ -116,7 +116,7 @@ export function timberRouting(ctx: PluginContext): Plugin {
116
116
  segmentType: 'static',
117
117
  urlPath: '/',
118
118
  children: [],
119
- slots: new Map(),
119
+ slots: {},
120
120
  },
121
121
  };
122
122
  return;
@@ -326,7 +326,7 @@ function generateSearchParamsRegistryModule(tree: RouteTree): string {
326
326
  }
327
327
  }
328
328
  for (const child of node.children) walk(child);
329
- for (const [, slot] of node.slots) walk(slot);
329
+ for (const slot of Object.values(node.slots)) walk(slot);
330
330
  }
331
331
  walk(tree.root);
332
332
 
@@ -482,9 +482,9 @@ function generateManifestModule(tree: RouteTree, viteRoot: string): string {
482
482
  // Runtime registration happens in the route loader using the page module.
483
483
 
484
484
  // Status-code files
485
- if (node.statusFiles && node.statusFiles.size > 0) {
485
+ if (node.statusFiles && Object.keys(node.statusFiles).length > 0) {
486
486
  const statusEntries: string[] = [];
487
- for (const [code, file] of node.statusFiles) {
487
+ for (const [code, file] of Object.entries(node.statusFiles)) {
488
488
  const v = addImport(file);
489
489
  statusEntries.push(
490
490
  `${nextIndent} ${JSON.stringify(code)}: { load: ${v}, filePath: ${JSON.stringify(file.filePath)} }`
@@ -494,9 +494,9 @@ function generateManifestModule(tree: RouteTree, viteRoot: string): string {
494
494
  }
495
495
 
496
496
  // JSON status-code files
497
- if (node.jsonStatusFiles && node.jsonStatusFiles.size > 0) {
497
+ if (node.jsonStatusFiles && Object.keys(node.jsonStatusFiles).length > 0) {
498
498
  const jsonEntries: string[] = [];
499
- for (const [code, file] of node.jsonStatusFiles) {
499
+ for (const [code, file] of Object.entries(node.jsonStatusFiles)) {
500
500
  const v = addImport(file);
501
501
  jsonEntries.push(
502
502
  `${nextIndent} ${JSON.stringify(code)}: { load: ${v}, filePath: ${JSON.stringify(file.filePath)} }`
@@ -506,9 +506,9 @@ function generateManifestModule(tree: RouteTree, viteRoot: string): string {
506
506
  }
507
507
 
508
508
  // Legacy status files
509
- if (node.legacyStatusFiles && node.legacyStatusFiles.size > 0) {
509
+ if (node.legacyStatusFiles && Object.keys(node.legacyStatusFiles).length > 0) {
510
510
  const legacyEntries: string[] = [];
511
- for (const [name, file] of node.legacyStatusFiles) {
511
+ for (const [name, file] of Object.entries(node.legacyStatusFiles)) {
512
512
  const v = addImport(file);
513
513
  legacyEntries.push(
514
514
  `${nextIndent} ${JSON.stringify(name)}: { load: ${v}, filePath: ${JSON.stringify(file.filePath)} }`
@@ -520,9 +520,9 @@ function generateManifestModule(tree: RouteTree, viteRoot: string): string {
520
520
  }
521
521
 
522
522
  // Metadata route files (sitemap.ts, robots.ts, icon.tsx, etc.)
523
- if (node.metadataRoutes && node.metadataRoutes.size > 0) {
523
+ if (node.metadataRoutes && Object.keys(node.metadataRoutes).length > 0) {
524
524
  const metaEntries: string[] = [];
525
- for (const [name, file] of node.metadataRoutes) {
525
+ for (const [name, file] of Object.entries(node.metadataRoutes)) {
526
526
  const v = addImport(file);
527
527
  metaEntries.push(
528
528
  `${nextIndent} ${JSON.stringify(name)}: { load: ${v}, filePath: ${JSON.stringify(file.filePath)} }`
@@ -540,9 +540,11 @@ function generateManifestModule(tree: RouteTree, viteRoot: string): string {
540
540
  }
541
541
 
542
542
  // Parallel slots
543
- if (node.slots.size > 0) {
543
+ const slotKeys = Object.keys(node.slots);
544
+ if (slotKeys.length > 0) {
544
545
  const slotEntries: string[] = [];
545
- for (const [slotName, slotNode] of node.slots) {
546
+ for (const slotName of slotKeys) {
547
+ const slotNode = node.slots[slotName]!;
546
548
  slotEntries.push(
547
549
  `${nextIndent} ${JSON.stringify(slotName)}: ${serializeNode(slotNode, nextIndent + ' ')}`
548
550
  );
@@ -299,7 +299,7 @@ export const headers = stub;
299
299
  export const cookies = stub;
300
300
  export const getHeaders = stub;
301
301
  export const getHeader = stub;
302
- export const getCookies = stub;
302
+ export const getCookieJar = stub;
303
303
  export const getCookie = stub;
304
304
  export const getSearchParams = stub;
305
305
  export const getSegmentParams = stub;
@@ -39,7 +39,7 @@ export interface StaticOptions {
39
39
  * We detect both import-level and call-level usage.
40
40
  */
41
41
  const DYNAMIC_API_PATTERNS: Array<{ pattern: RegExp; name: string }> = [
42
- { pattern: /\bgetCookies\s*\(/, name: 'getCookies()' },
42
+ { pattern: /\bgetCookieJar\s*\(/, name: 'getCookieJar()' },
43
43
  { pattern: /\bgetHeaders\s*\(/, name: 'getHeaders()' },
44
44
  { pattern: /\bcookies\s*\(/, name: 'cookies()' },
45
45
  { pattern: /\bheaders\s*\(/, name: 'headers()' },
@@ -149,7 +149,7 @@ function collectRoutes(
149
149
  }
150
150
 
151
151
  // Recurse into slots (they share the parent's URL path, but may have their own pages)
152
- for (const [, slot] of node.slots) {
152
+ for (const slot of Object.values(node.slots)) {
153
153
  collectRoutes(slot, params, nextAncestorFiles, routes);
154
154
  }
155
155
  }
@@ -13,6 +13,7 @@
13
13
 
14
14
  import { readFileSync, existsSync } from 'node:fs';
15
15
  import type { RouteTree, SegmentNode } from './types.js';
16
+ import { swallow } from '../server/logger.js';
16
17
 
17
18
  // ─── Types ──────────────────────────────────────────────────────────────────
18
19
 
@@ -89,7 +90,7 @@ function hasAnyRoutable(node: SegmentNode): boolean {
89
90
  for (const child of node.children) {
90
91
  if (hasAnyRoutable(child)) return true;
91
92
  }
92
- for (const [, slot] of node.slots) {
93
+ for (const slot of Object.values(node.slots)) {
93
94
  if (hasAnyRoutable(slot)) return true;
94
95
  }
95
96
  return false;
@@ -105,7 +106,7 @@ function hasAnyPage(node: SegmentNode): boolean {
105
106
  for (const child of node.children) {
106
107
  if (hasAnyPage(child)) return true;
107
108
  }
108
- for (const [, slot] of node.slots) {
109
+ for (const slot of Object.values(node.slots)) {
109
110
  if (hasAnyPage(slot)) return true;
110
111
  }
111
112
  return false;
@@ -164,8 +165,8 @@ function checkRouteExports(node: SegmentNode, warnings: ConventionWarning[]): vo
164
165
  level: 'warn',
165
166
  });
166
167
  }
167
- } catch {
168
- // Can't read the file — skip silently
168
+ } catch (err) {
169
+ swallow(err, `convention-lint: unreadable route.ts ${filePath}`);
169
170
  }
170
171
  }
171
172
 
@@ -173,7 +174,7 @@ function checkRouteExports(node: SegmentNode, warnings: ConventionWarning[]): vo
173
174
  for (const child of node.children) {
174
175
  checkRouteExports(child, warnings);
175
176
  }
176
- for (const [, slot] of node.slots) {
177
+ for (const slot of Object.values(node.slots)) {
177
178
  checkRouteExports(slot, warnings);
178
179
  }
179
180
  }
@@ -249,7 +250,7 @@ function checkDefaultExports(node: SegmentNode, warnings: ConventionWarning[]):
249
250
  for (const child of node.children) {
250
251
  checkDefaultExports(child, warnings);
251
252
  }
252
- for (const [, slot] of node.slots) {
253
+ for (const slot of Object.values(node.slots)) {
253
254
  checkDefaultExports(slot, warnings);
254
255
  }
255
256
  }
@@ -280,8 +281,8 @@ function checkFileDefaultExport(
280
281
  level: 'warn',
281
282
  });
282
283
  }
283
- } catch {
284
- // Can't read file — skip silently
284
+ } catch (err) {
285
+ swallow(err, `convention-lint: unreadable ${fileType} file ${filePath}`);
285
286
  }
286
287
  }
287
288
 
@@ -1,4 +1,4 @@
1
- export { scanRoutes, classifySegment } from './scanner.js';
1
+ export { scanRoutes } from './scanner.js';
2
2
  export { generateRouteMap } from './codegen.js';
3
3
  export type { CodegenOptions } from './codegen.js';
4
4
  export type {
@@ -12,5 +12,7 @@ export type {
12
12
  export { DEFAULT_PAGE_EXTENSIONS, INTERCEPTION_MARKERS } from './types.js';
13
13
  export { collectInterceptionRewrites } from './interception.js';
14
14
  export type { InterceptionRewrite } from './interception.js';
15
- export { classifyUrlSegment } from './segment-classify.js';
16
- export type { UrlSegment } from './segment-classify.js';
15
+ export { classifyUrlSegment, classifySegment } from './segment-classify.js';
16
+ export type { UrlSegment, SegmentClassification } from './segment-classify.js';
17
+ export { collectLeafRoutes } from './walkers.js';
18
+ export type { LeafRoute, CollectLeafRoutesOptions } from './walkers.js';
@@ -70,7 +70,7 @@ function walkForInterceptions(
70
70
  }
71
71
 
72
72
  // Check slots (intercepting routes are typically inside slots like @modal)
73
- for (const [, slot] of node.slots) {
73
+ for (const slot of Object.values(node.slots)) {
74
74
  walkForInterceptions(slot, ancestors, rewrites);
75
75
  }
76
76
  }