@timber-js/app 0.2.0-alpha.191 → 0.2.0-alpha.194

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 (672) hide show
  1. package/agent-skill.md +2 -0
  2. package/dist/_chunks/{actions-BjbbNRFN.js → actions-CWYtq6ii.js} +3 -3
  3. package/dist/_chunks/actions-CWYtq6ii.js.map +1 -0
  4. package/dist/_chunks/als-registry-C6kcfprT.js.map +1 -1
  5. package/dist/_chunks/als-slots-BEEIPKYm.js.map +1 -1
  6. package/dist/_chunks/{build-output-helper-C3DrfzZR.js → build-output-helper-9dqCv1pZ.js} +3 -2
  7. package/dist/_chunks/build-output-helper-9dqCv1pZ.js.map +1 -0
  8. package/dist/_chunks/{cache-api-DllJ-Lyw.js → cache-api-CQeYzA5g.js} +6 -8
  9. package/dist/_chunks/cache-api-CQeYzA5g.js.map +1 -0
  10. package/dist/_chunks/chains-h7EO-u3n.js +412 -0
  11. package/dist/_chunks/chains-h7EO-u3n.js.map +1 -0
  12. package/dist/_chunks/{cli-check-DJZDc22E.js → cli-check-BVthpfLS.js} +43 -6
  13. package/dist/_chunks/cli-check-BVthpfLS.js.map +1 -0
  14. package/dist/_chunks/{cli-schema-sync-D4_AZVUb.js → cli-schema-sync-ZwM9u_ob.js} +6 -5
  15. package/dist/_chunks/{cli-schema-sync-D4_AZVUb.js.map → cli-schema-sync-ZwM9u_ob.js.map} +1 -1
  16. package/dist/_chunks/{cloudflare-CGP6BZKO.js → cloudflare-BKJC3SC_.js} +28 -22
  17. package/dist/_chunks/{cloudflare-CGP6BZKO.js.map → cloudflare-BKJC3SC_.js.map} +1 -1
  18. package/dist/_chunks/codec-registry-oOUxugz3.js.map +1 -1
  19. package/dist/_chunks/{convention-lint-CpteIpTm.js → convention-lint-DO10_pVl.js} +93 -7
  20. package/dist/_chunks/convention-lint-DO10_pVl.js.map +1 -0
  21. package/dist/_chunks/dist-DtrJyBbw.js +386 -0
  22. package/dist/_chunks/dist-DtrJyBbw.js.map +1 -0
  23. package/dist/_chunks/error-boundary-D-ODYX41.js.map +1 -1
  24. package/dist/_chunks/{file-cache-DmX7OqZP.js → file-cache-Dw6BJPG7.js} +5 -1
  25. package/dist/_chunks/{file-cache-DmX7OqZP.js.map → file-cache-Dw6BJPG7.js.map} +1 -1
  26. package/dist/_chunks/{format-CfwjgPz9.js → format-BKclgVbk.js} +3 -3
  27. package/dist/_chunks/{format-CfwjgPz9.js.map → format-BKclgVbk.js.map} +1 -1
  28. package/dist/_chunks/graph-cache-CP4GEmf9.js +304 -0
  29. package/dist/_chunks/graph-cache-CP4GEmf9.js.map +1 -0
  30. package/dist/_chunks/href-validation-CMc5JRls.js.map +1 -1
  31. package/dist/_chunks/live-graph-Bx4HodF1.js +150 -0
  32. package/dist/_chunks/live-graph-Bx4HodF1.js.map +1 -0
  33. package/dist/_chunks/{logger-CH7IcMmg.js → logger-pumCm3Il.js} +25 -7
  34. package/dist/_chunks/{logger-CH7IcMmg.js.map → logger-pumCm3Il.js.map} +1 -1
  35. package/dist/_chunks/{mdx-file-C005ay-P.js → mdx-file-CXyHGUpS.js} +3 -3
  36. package/dist/_chunks/mdx-file-CXyHGUpS.js.map +1 -0
  37. package/dist/_chunks/{plugin-context-rCinWLiE.js → plugin-context-8idfR3UZ.js} +2 -2
  38. package/dist/_chunks/plugin-context-8idfR3UZ.js.map +1 -0
  39. package/dist/_chunks/poison-rules-DoEhbqaY.js +110 -0
  40. package/dist/_chunks/poison-rules-DoEhbqaY.js.map +1 -0
  41. package/dist/_chunks/poison-scan-BAxfTT5L.js +101 -0
  42. package/dist/_chunks/poison-scan-BAxfTT5L.js.map +1 -0
  43. package/dist/_chunks/resolve-schema-CBR6Lm4i.js.map +1 -1
  44. package/dist/_chunks/rolldown-runtime-D7D4PA-g.js +13 -0
  45. package/dist/_chunks/router-ref-DuYuV_0Q.js.map +1 -1
  46. package/dist/_chunks/{rsc-media-type-DRqE_lD_.js → rsc-media-type-DDc7duTD.js} +17 -2
  47. package/dist/_chunks/{rsc-media-type-DRqE_lD_.js.map → rsc-media-type-DDc7duTD.js.map} +1 -1
  48. package/dist/_chunks/scan-BoP17W3w.js +140 -0
  49. package/dist/_chunks/scan-BoP17W3w.js.map +1 -0
  50. package/dist/_chunks/{scanner-CAietmj4.js → scanner-BRIOmHE2.js} +43 -4
  51. package/dist/_chunks/scanner-BRIOmHE2.js.map +1 -0
  52. package/dist/_chunks/schema-bridge-C83xa9lT.js.map +1 -1
  53. package/dist/_chunks/segment-classify-C539Pa2O.js.map +1 -1
  54. package/dist/_chunks/{segment-keys-BawYuNFO.js → segment-keys-BhqoHiLc.js} +14 -2
  55. package/dist/_chunks/{segment-keys-BawYuNFO.js.map → segment-keys-BhqoHiLc.js.map} +1 -1
  56. package/dist/_chunks/slot-params-BCTmZkQB.js.map +1 -1
  57. package/dist/_chunks/ssr-data-14MXm7Pj.js.map +1 -1
  58. package/dist/_chunks/use-query-states-I3JMng6J.js.map +1 -1
  59. package/dist/_chunks/use-segment-params-C4r4BD9T.js.map +1 -1
  60. package/dist/_chunks/{walkers-BsVLmD1S.js → walkers-uCu3WW6_.js} +9 -6
  61. package/dist/_chunks/{walkers-BsVLmD1S.js.map → walkers-uCu3WW6_.js.map} +1 -1
  62. package/dist/adapters/build-output-helper.d.ts +1 -1
  63. package/dist/adapters/build-output-helper.d.ts.map +1 -1
  64. package/dist/adapters/cloudflare-dev.d.ts.map +1 -1
  65. package/dist/adapters/cloudflare-dev.js +2 -1
  66. package/dist/adapters/cloudflare-dev.js.map +1 -1
  67. package/dist/adapters/cloudflare-kv-cache.d.ts +1 -1
  68. package/dist/adapters/cloudflare-kv-cache.d.ts.map +1 -1
  69. package/dist/adapters/cloudflare-kv-cache.js +7 -8
  70. package/dist/adapters/cloudflare-kv-cache.js.map +1 -1
  71. package/dist/adapters/cloudflare.d.ts +1 -1
  72. package/dist/adapters/cloudflare.d.ts.map +1 -1
  73. package/dist/adapters/cloudflare.js +1 -1
  74. package/dist/adapters/nitro-presets.d.ts +37 -0
  75. package/dist/adapters/nitro-presets.d.ts.map +1 -0
  76. package/dist/adapters/nitro-preview.d.ts +32 -0
  77. package/dist/adapters/nitro-preview.d.ts.map +1 -0
  78. package/dist/adapters/nitro.d.ts +28 -51
  79. package/dist/adapters/nitro.d.ts.map +1 -1
  80. package/dist/adapters/nitro.js +224 -171
  81. package/dist/adapters/nitro.js.map +1 -1
  82. package/dist/analyze/chains.d.ts +124 -0
  83. package/dist/analyze/chains.d.ts.map +1 -0
  84. package/dist/analyze/classify.d.ts +150 -0
  85. package/dist/analyze/classify.d.ts.map +1 -0
  86. package/dist/analyze/crawl-entry.d.ts +17 -0
  87. package/dist/analyze/crawl-entry.d.ts.map +1 -0
  88. package/dist/analyze/crawl-entry.js +313 -0
  89. package/dist/analyze/crawl-entry.js.map +1 -0
  90. package/dist/analyze/crawl.d.ts +134 -0
  91. package/dist/analyze/crawl.d.ts.map +1 -0
  92. package/dist/analyze/graph-cache.d.ts +104 -0
  93. package/dist/analyze/graph-cache.d.ts.map +1 -0
  94. package/dist/analyze/graph-command.d.ts +43 -0
  95. package/dist/analyze/graph-command.d.ts.map +1 -0
  96. package/dist/analyze/graph-command.js +472 -0
  97. package/dist/analyze/graph-command.js.map +1 -0
  98. package/dist/analyze/live-graph.d.ts +81 -0
  99. package/dist/analyze/live-graph.d.ts.map +1 -0
  100. package/dist/analyze/poison-rules.d.ts +72 -0
  101. package/dist/analyze/poison-rules.d.ts.map +1 -0
  102. package/dist/analyze/poison-scan.d.ts +48 -0
  103. package/dist/analyze/poison-scan.d.ts.map +1 -0
  104. package/dist/analyze/scan.d.ts +73 -0
  105. package/dist/analyze/scan.d.ts.map +1 -0
  106. package/dist/cache/cache-api.d.ts +2 -2
  107. package/dist/cache/cache-api.d.ts.map +1 -1
  108. package/dist/cache/index.d.ts +8 -8
  109. package/dist/cache/index.d.ts.map +1 -1
  110. package/dist/cache/index.js +1 -1
  111. package/dist/cache/redis-handler.d.ts +1 -1
  112. package/dist/cache/redis-handler.d.ts.map +1 -1
  113. package/dist/cache/stores/cloudflare-kv.d.ts +1 -1
  114. package/dist/cache/stores/cloudflare-kv.d.ts.map +1 -1
  115. package/dist/cache/stores/cloudflare-kv.js.map +1 -1
  116. package/dist/cache/stores/memory.d.ts +1 -1
  117. package/dist/cache/stores/memory.d.ts.map +1 -1
  118. package/dist/cache/stores/memory.js.map +1 -1
  119. package/dist/cache/stores/redis.d.ts +1 -1
  120. package/dist/cache/stores/redis.d.ts.map +1 -1
  121. package/dist/cache/stores/redis.js.map +1 -1
  122. package/dist/cache/stores/vercel.d.ts +1 -1
  123. package/dist/cache/stores/vercel.d.ts.map +1 -1
  124. package/dist/cache/stores/vercel.js.map +1 -1
  125. package/dist/cache/tag-aware-handler.d.ts +2 -2
  126. package/dist/cache/tag-aware-handler.d.ts.map +1 -1
  127. package/dist/cache/timber-cache.d.ts +1 -1
  128. package/dist/cache/timber-cache.d.ts.map +1 -1
  129. package/dist/cdn/cloudflare-purge.d.ts +1 -1
  130. package/dist/cdn/cloudflare-purge.d.ts.map +1 -1
  131. package/dist/cdn/cloudflare-purge.js.map +1 -1
  132. package/dist/cdn/fastly-purge.d.ts +1 -1
  133. package/dist/cdn/fastly-purge.d.ts.map +1 -1
  134. package/dist/cdn/fastly-purge.js.map +1 -1
  135. package/dist/cdn/index.d.ts +4 -4
  136. package/dist/cdn/index.d.ts.map +1 -1
  137. package/dist/cdn/workers-cache-purge.d.ts +5 -1
  138. package/dist/cdn/workers-cache-purge.d.ts.map +1 -1
  139. package/dist/cdn/workers-cache-purge.js.map +1 -1
  140. package/dist/cli-check.d.ts +10 -0
  141. package/dist/cli-check.d.ts.map +1 -1
  142. package/dist/cli.d.ts +12 -4
  143. package/dist/cli.d.ts.map +1 -1
  144. package/dist/cli.js +30 -6
  145. package/dist/cli.js.map +1 -1
  146. package/dist/client/browser-entry/hydrate.d.ts +2 -2
  147. package/dist/client/browser-entry/hydrate.d.ts.map +1 -1
  148. package/dist/client/browser-entry/post-hydration.d.ts +2 -2
  149. package/dist/client/browser-entry/router-init.d.ts +2 -2
  150. package/dist/client/browser-entry/rsc-stream.d.ts +1 -1
  151. package/dist/client/form.d.ts +2 -2
  152. package/dist/client/form.d.ts.map +1 -1
  153. package/dist/client/history.d.ts +2 -2
  154. package/dist/client/index.d.ts +18 -18
  155. package/dist/client/index.d.ts.map +1 -1
  156. package/dist/client/index.js.map +1 -1
  157. package/dist/client/internal.d.ts +19 -19
  158. package/dist/client/internal.d.ts.map +1 -1
  159. package/dist/client/internal.js +11 -15
  160. package/dist/client/internal.js.map +1 -1
  161. package/dist/client/link.d.ts +3 -3
  162. package/dist/client/navigation-commit.d.ts +5 -5
  163. package/dist/client/navigation-root.d.ts +1 -1
  164. package/dist/client/navigation-root.d.ts.map +1 -1
  165. package/dist/client/params-context.d.ts +3 -3
  166. package/dist/client/router-pipeline.d.ts +7 -7
  167. package/dist/client/router-ref.d.ts +1 -1
  168. package/dist/client/router-types.d.ts +6 -6
  169. package/dist/client/router-types.d.ts.map +1 -1
  170. package/dist/client/router.d.ts +1 -1
  171. package/dist/client/rsc-fetch.d.ts +4 -4
  172. package/dist/client/rsc-fetch.d.ts.map +1 -1
  173. package/dist/client/segment-cache.d.ts +2 -2
  174. package/dist/client/slot-provider.d.ts +1 -1
  175. package/dist/client/ssr-data.d.ts +2 -2
  176. package/dist/client/state.d.ts +4 -4
  177. package/dist/client/use-query-states.d.ts +1 -1
  178. package/dist/client/use-segment-params.d.ts +3 -3
  179. package/dist/codec.d.ts +2 -2
  180. package/dist/codec.js.map +1 -1
  181. package/dist/config-types.d.ts +24 -6
  182. package/dist/config-types.d.ts.map +1 -1
  183. package/dist/config-validation.d.ts +1 -1
  184. package/dist/cookies/define-cookie.d.ts +3 -3
  185. package/dist/cookies/index.d.ts +3 -3
  186. package/dist/cookies/index.js +3 -4
  187. package/dist/cookies/index.js.map +1 -1
  188. package/dist/cookies/json-cookie.d.ts +1 -1
  189. package/dist/dev-tools/404-page.d.ts +2 -2
  190. package/dist/dev-tools/error-page.d.ts +2 -2
  191. package/dist/dev-tools/index.d.ts +12 -12
  192. package/dist/dev-tools/instrumentation.d.ts +1 -1
  193. package/dist/dev-tools/logs.d.ts +1 -1
  194. package/dist/dev-tools/overlay.d.ts +2 -2
  195. package/dist/dev-tools/terminal.d.ts +1 -1
  196. package/dist/fonts/ast.d.ts +4 -4
  197. package/dist/fonts/bundle.d.ts +3 -3
  198. package/dist/fonts/css.d.ts +1 -1
  199. package/dist/fonts/dev-middleware.d.ts +1 -1
  200. package/dist/fonts/google.d.ts +1 -1
  201. package/dist/fonts/local.d.ts +1 -1
  202. package/dist/fonts/pipeline.d.ts +3 -3
  203. package/dist/fonts/transform.d.ts +2 -2
  204. package/dist/fonts/types.d.ts +1 -1
  205. package/dist/fonts/virtual-modules.d.ts +1 -1
  206. package/dist/index.d.ts +3 -3
  207. package/dist/index.d.ts.map +1 -1
  208. package/dist/index.js +816 -310
  209. package/dist/index.js.map +1 -1
  210. package/dist/params/codec-registry.d.ts +1 -1
  211. package/dist/params/define-schema.d.ts +2 -2
  212. package/dist/params/index.d.ts +4 -4
  213. package/dist/params/index.js.map +1 -1
  214. package/dist/params/resolve-schema.d.ts +1 -1
  215. package/dist/plugin-context.d.ts +9 -9
  216. package/dist/plugin-context.d.ts.map +1 -1
  217. package/dist/plugins/adapter-build.d.ts +2 -2
  218. package/dist/plugins/build-manifest.d.ts +2 -2
  219. package/dist/plugins/build-report.d.ts +5 -5
  220. package/dist/plugins/cache.d.ts +1 -1
  221. package/dist/plugins/callsite-jsx.d.ts +2 -2
  222. package/dist/plugins/chunks.d.ts +1 -1
  223. package/dist/plugins/chunks.d.ts.map +1 -1
  224. package/dist/plugins/cloudflare-wasm.d.ts +1 -1
  225. package/dist/plugins/content.d.ts +1 -1
  226. package/dist/plugins/content.d.ts.map +1 -1
  227. package/dist/plugins/dev-discovery.d.ts +123 -0
  228. package/dist/plugins/dev-discovery.d.ts.map +1 -0
  229. package/dist/plugins/dev-server.d.ts +1 -1
  230. package/dist/plugins/dev-server.d.ts.map +1 -1
  231. package/dist/plugins/entries.d.ts +1 -1
  232. package/dist/plugins/fonts.d.ts +3 -3
  233. package/dist/plugins/fonts.d.ts.map +1 -1
  234. package/dist/plugins/graph-endpoint.d.ts +112 -0
  235. package/dist/plugins/graph-endpoint.d.ts.map +1 -0
  236. package/dist/plugins/mdx.d.ts +18 -5
  237. package/dist/plugins/mdx.d.ts.map +1 -1
  238. package/dist/plugins/prebuilt-capture.d.ts +2 -2
  239. package/dist/plugins/prebuilt-options-analysis.d.ts +1 -1
  240. package/dist/plugins/prebuilt.d.ts +1 -1
  241. package/dist/plugins/prerender-sugar.d.ts +11 -14
  242. package/dist/plugins/prerender-sugar.d.ts.map +1 -1
  243. package/dist/plugins/request-dep/analysis.d.ts +1 -1
  244. package/dist/plugins/request-dep/index.d.ts +5 -5
  245. package/dist/plugins/request-dep/origin.d.ts +1 -1
  246. package/dist/plugins/routing.d.ts +1 -1
  247. package/dist/plugins/routing.d.ts.map +1 -1
  248. package/dist/plugins/server-bundle.d.ts.map +1 -1
  249. package/dist/plugins/shims.d.ts +1 -1
  250. package/dist/plugins/shims.d.ts.map +1 -1
  251. package/dist/plugins/static-build.d.ts +5 -5
  252. package/dist/routing/codegen-shared.d.ts +1 -1
  253. package/dist/routing/codegen-write.d.ts +1 -1
  254. package/dist/routing/codegen.d.ts +1 -1
  255. package/dist/routing/collision-probe.d.ts +1 -1
  256. package/dist/routing/collision-spaces.d.ts +1 -1
  257. package/dist/routing/convention-lint.d.ts +1 -1
  258. package/dist/routing/convention-lint.d.ts.map +1 -1
  259. package/dist/routing/index.d.ts +13 -13
  260. package/dist/routing/index.js +3 -3
  261. package/dist/routing/interception.d.ts +1 -1
  262. package/dist/routing/interception.d.ts.map +1 -1
  263. package/dist/routing/link-codegen.d.ts +1 -1
  264. package/dist/routing/manifest-codegen.d.ts +1 -1
  265. package/dist/routing/scanner.d.ts +14 -1
  266. package/dist/routing/scanner.d.ts.map +1 -1
  267. package/dist/routing/schema-validation.d.ts +1 -1
  268. package/dist/routing/segment-classify.d.ts +1 -1
  269. package/dist/routing/segment-codegen.d.ts +1 -1
  270. package/dist/routing/segment-keys.d.ts +1 -1
  271. package/dist/routing/slot-placement.d.ts +1 -1
  272. package/dist/routing/walkers.d.ts +1 -1
  273. package/dist/rsc-runtime/transforms.d.ts +13 -0
  274. package/dist/rsc-runtime/transforms.d.ts.map +1 -0
  275. package/dist/schema-bridge.d.ts +1 -1
  276. package/dist/search-params/define.d.ts +3 -3
  277. package/dist/search-params/index.d.ts +3 -3
  278. package/dist/search-params/index.js.map +1 -1
  279. package/dist/search-params/wrappers.d.ts +1 -1
  280. package/dist/server/access-gate.d.ts +1 -1
  281. package/dist/server/action-client.d.ts +1 -1
  282. package/dist/server/action-handler.d.ts +5 -5
  283. package/dist/server/actions.d.ts +2 -2
  284. package/dist/server/als-registry.d.ts +8 -8
  285. package/dist/server/asset-headers.d.ts +1 -1
  286. package/dist/server/chain-url-parts.d.ts +3 -3
  287. package/dist/server/children-interception.d.ts +2 -2
  288. package/dist/server/cookie-context.d.ts +1 -1
  289. package/dist/server/cookie-parsing.d.ts +2 -2
  290. package/dist/server/default-logger.d.ts +1 -1
  291. package/dist/server/deny-boundary.d.ts +2 -2
  292. package/dist/server/deny-renderer.d.ts +5 -5
  293. package/dist/server/early-hints.d.ts +1 -1
  294. package/dist/server/error-boundary-wrapper.d.ts +2 -4
  295. package/dist/server/error-boundary-wrapper.d.ts.map +1 -1
  296. package/dist/server/fallback-error.d.ts +5 -5
  297. package/dist/server/flight-injection-state.d.ts +1 -1
  298. package/dist/server/form-flash.d.ts +1 -1
  299. package/dist/server/html-injector-core.d.ts +2 -2
  300. package/dist/server/html-injectors.d.ts +1 -1
  301. package/dist/server/index.d.ts +18 -18
  302. package/dist/server/index.d.ts.map +1 -1
  303. package/dist/server/index.js +33 -32
  304. package/dist/server/index.js.map +1 -1
  305. package/dist/server/instrumentation.d.ts +1 -1
  306. package/dist/server/internal.d.ts +50 -50
  307. package/dist/server/internal.d.ts.map +1 -1
  308. package/dist/server/internal.js +1023 -1025
  309. package/dist/server/internal.js.map +1 -1
  310. package/dist/server/metadata-collector.d.ts +2 -2
  311. package/dist/server/metadata-platform.d.ts +2 -2
  312. package/dist/server/metadata-render.d.ts +2 -2
  313. package/dist/server/metadata-routes.d.ts +3 -3
  314. package/dist/server/metadata-social.d.ts +2 -2
  315. package/dist/server/metadata.d.ts +2 -2
  316. package/dist/server/middleware-runner.d.ts +1 -1
  317. package/dist/server/node-stream-transforms.d.ts +1 -1
  318. package/dist/server/param-coercion.d.ts +4 -4
  319. package/dist/server/pipeline-helpers.d.ts +3 -3
  320. package/dist/server/pipeline-interception.d.ts +1 -1
  321. package/dist/server/pipeline-metadata.d.ts +1 -1
  322. package/dist/server/pipeline-outcome.d.ts +2 -2
  323. package/dist/server/pipeline-phases.d.ts +3 -3
  324. package/dist/server/pipeline-phases.d.ts.map +1 -1
  325. package/dist/server/pipeline.d.ts +9 -9
  326. package/dist/server/prebuilt/cache-key.d.ts +1 -1
  327. package/dist/server/prebuilt/capture-state.d.ts +1 -1
  328. package/dist/server/prebuilt/key-discipline.d.ts +1 -1
  329. package/dist/server/prebuilt/manifest-tag-index.d.ts +1 -1
  330. package/dist/server/prebuilt/overlay.d.ts +2 -2
  331. package/dist/server/prebuilt/payload-source.d.ts +1 -1
  332. package/dist/server/prebuilt/slots.d.ts +1 -1
  333. package/dist/server/prebuilt/synthetic-store.d.ts +2 -2
  334. package/dist/server/prebuilt-builder.d.ts +2 -2
  335. package/dist/server/primitives.d.ts +2 -2
  336. package/dist/server/publish-params.d.ts +1 -1
  337. package/dist/server/request-context.d.ts +3 -3
  338. package/dist/server/route-element-builder.d.ts +16 -6
  339. package/dist/server/route-element-builder.d.ts.map +1 -1
  340. package/dist/server/route-handler.d.ts +1 -1
  341. package/dist/server/route-matcher.d.ts +4 -4
  342. package/dist/server/rsc-cache-key-guard.d.ts.map +1 -1
  343. package/dist/server/rsc-entry/action-middleware-runner.d.ts +2 -2
  344. package/dist/server/rsc-entry/api-handler.d.ts +1 -1
  345. package/dist/server/rsc-entry/api-handler.d.ts.map +1 -1
  346. package/dist/server/rsc-entry/deny-fallback.d.ts +3 -3
  347. package/dist/server/rsc-entry/error-renderer.d.ts +4 -4
  348. package/dist/server/rsc-entry/helpers.d.ts +6 -6
  349. package/dist/server/rsc-entry/index.d.ts +7 -6
  350. package/dist/server/rsc-entry/index.d.ts.map +1 -1
  351. package/dist/server/rsc-entry/render-route.d.ts +4 -4
  352. package/dist/server/rsc-entry/revalidate-renderer.d.ts +4 -4
  353. package/dist/server/rsc-entry/rsc-payload.d.ts +4 -4
  354. package/dist/server/rsc-entry/rsc-payload.d.ts.map +1 -1
  355. package/dist/server/rsc-entry/rsc-stream.d.ts +3 -3
  356. package/dist/server/rsc-entry/ssr-bridge.d.ts +1 -1
  357. package/dist/server/rsc-entry/ssr-renderer.d.ts +7 -7
  358. package/dist/server/rsc-entry/wrap-action-dispatch.d.ts +5 -5
  359. package/dist/server/sitemap-generator.d.ts +2 -2
  360. package/dist/server/sitemap-handler.d.ts +1 -1
  361. package/dist/server/skippable-prefix.d.ts +3 -3
  362. package/dist/server/slot-interception.d.ts +2 -2
  363. package/dist/server/slot-resolver.d.ts +3 -7
  364. package/dist/server/slot-resolver.d.ts.map +1 -1
  365. package/dist/server/ssr-bridge-types.d.ts +2 -2
  366. package/dist/server/ssr-entry.d.ts +1 -1
  367. package/dist/server/static-generator.d.ts +1 -1
  368. package/dist/server/static-not-found.d.ts +1 -1
  369. package/dist/server/status-code-resolver.d.ts +1 -1
  370. package/dist/server/tracing.d.ts +3 -3
  371. package/dist/server/tree-builder.d.ts +3 -3
  372. package/dist/server/tree-match.d.ts +1 -1
  373. package/dist/server/types.d.ts +2 -2
  374. package/dist/server/utils/mdx-file.d.ts +2 -0
  375. package/dist/server/utils/mdx-file.d.ts.map +1 -1
  376. package/dist/server/workers-cache-bridge.d.ts +1 -1
  377. package/dist/shared/als-slots.d.ts +1 -1
  378. package/dist/shared/payload-root.d.ts +2 -2
  379. package/dist/shared/rsc-media-type.d.ts +12 -0
  380. package/dist/shared/rsc-media-type.d.ts.map +1 -1
  381. package/dist/shared/slot-params.d.ts +1 -1
  382. package/dist/shims/font-google.d.ts +1 -1
  383. package/dist/shims/font-google.js.map +1 -1
  384. package/dist/shims/font-local.d.ts +1 -1
  385. package/dist/shims/font-local.js.map +1 -1
  386. package/dist/shims/navigation-client.d.ts +4 -4
  387. package/dist/shims/navigation.d.ts +2 -2
  388. package/docs/api/34-api-config.mdx +9 -8
  389. package/docs/api/36-cli.mdx +23 -3
  390. package/docs/learn/11-error-handling.mdx +11 -0
  391. package/docs/more/01-advanced-routing.mdx +1 -0
  392. package/docs/more/04b-mdx.mdx +38 -18
  393. package/package.json +23 -18
  394. package/src/adapters/build-output-helper.ts +4 -4
  395. package/src/adapters/cloudflare-dev.ts +7 -1
  396. package/src/adapters/cloudflare-kv-cache.ts +11 -8
  397. package/src/adapters/cloudflare.ts +21 -15
  398. package/src/adapters/compress-module.ts +2 -2
  399. package/src/adapters/nitro-presets.ts +139 -0
  400. package/src/adapters/nitro-preview.ts +340 -0
  401. package/src/adapters/nitro.ts +100 -465
  402. package/src/adapters/shared.ts +1 -1
  403. package/src/analyze/chains.ts +411 -0
  404. package/src/analyze/classify.ts +258 -0
  405. package/src/analyze/crawl-entry.ts +68 -0
  406. package/src/analyze/crawl.ts +500 -0
  407. package/src/analyze/graph-cache.ts +332 -0
  408. package/src/analyze/graph-command.ts +762 -0
  409. package/src/analyze/live-graph.ts +232 -0
  410. package/src/analyze/poison-rules.ts +165 -0
  411. package/src/analyze/poison-scan.ts +162 -0
  412. package/src/analyze/scan.ts +149 -0
  413. package/src/cache/cache-api.ts +7 -7
  414. package/src/cache/index.ts +9 -9
  415. package/src/cache/redis-handler.ts +2 -2
  416. package/src/cache/stores/cloudflare-kv.ts +1 -1
  417. package/src/cache/stores/memory.ts +2 -2
  418. package/src/cache/stores/redis.ts +1 -1
  419. package/src/cache/stores/vercel.ts +1 -1
  420. package/src/cache/tag-aware-handler.ts +2 -2
  421. package/src/cache/timber-cache.ts +8 -8
  422. package/src/cdn/cloudflare-purge.ts +1 -1
  423. package/src/cdn/fastly-purge.ts +1 -1
  424. package/src/cdn/index.ts +10 -4
  425. package/src/cdn/workers-cache-purge.ts +6 -2
  426. package/src/cli-check.ts +53 -4
  427. package/src/cli-schema-sync.ts +3 -3
  428. package/src/cli.ts +50 -11
  429. package/src/client/browser-dev.ts +1 -1
  430. package/src/client/browser-entry/action-dispatch.ts +5 -5
  431. package/src/client/browser-entry/hmr.ts +1 -1
  432. package/src/client/browser-entry/hydrate.ts +9 -9
  433. package/src/client/browser-entry/index.ts +10 -10
  434. package/src/client/browser-entry/post-hydration.ts +5 -5
  435. package/src/client/browser-entry/router-init.ts +9 -9
  436. package/src/client/browser-entry/rsc-stream.ts +3 -3
  437. package/src/client/error-boundary.tsx +4 -4
  438. package/src/client/form.tsx +7 -2
  439. package/src/client/history.ts +2 -2
  440. package/src/client/index.ts +21 -18
  441. package/src/client/internal.ts +19 -19
  442. package/src/client/link.tsx +9 -9
  443. package/src/client/navigation-api.ts +1 -1
  444. package/src/client/navigation-commit.ts +5 -5
  445. package/src/client/navigation-context.ts +1 -1
  446. package/src/client/navigation-root.tsx +1 -1
  447. package/src/client/nuqs-adapter.tsx +2 -2
  448. package/src/client/params-context.ts +4 -4
  449. package/src/client/router-effects.ts +3 -3
  450. package/src/client/router-lifecycle.ts +2 -2
  451. package/src/client/router-pipeline.ts +10 -10
  452. package/src/client/router-ref.ts +2 -2
  453. package/src/client/router-skew.ts +2 -2
  454. package/src/client/router-types.ts +6 -6
  455. package/src/client/router.ts +13 -13
  456. package/src/client/rsc-fetch.ts +11 -16
  457. package/src/client/segment-cache.ts +2 -2
  458. package/src/client/segment-outlet.tsx +1 -1
  459. package/src/client/shallow-url.ts +1 -1
  460. package/src/client/slot-outlet.tsx +1 -1
  461. package/src/client/slot-provider.tsx +1 -1
  462. package/src/client/ssr-data.ts +3 -3
  463. package/src/client/stale-client.ts +1 -1
  464. package/src/client/state.ts +4 -4
  465. package/src/client/top-loader.tsx +1 -1
  466. package/src/client/unload-guard.ts +1 -1
  467. package/src/client/use-cookie.ts +2 -2
  468. package/src/client/use-pathname.ts +2 -2
  469. package/src/client/use-pending-navigation.ts +1 -1
  470. package/src/client/use-query-states.ts +3 -3
  471. package/src/client/use-router.ts +2 -2
  472. package/src/client/use-search-params.ts +3 -3
  473. package/src/client/use-segment-params.ts +6 -6
  474. package/src/client/use-selected-layout-segment.ts +2 -2
  475. package/src/codec.ts +2 -2
  476. package/src/config-types.ts +27 -7
  477. package/src/config-validation.ts +1 -1
  478. package/src/cookies/define-cookie.ts +6 -6
  479. package/src/cookies/index.ts +3 -3
  480. package/src/cookies/json-cookie.ts +1 -1
  481. package/src/dev-tools/404-page.ts +4 -4
  482. package/src/dev-tools/dev-page-shell.ts +1 -1
  483. package/src/dev-tools/error-page.ts +4 -4
  484. package/src/dev-tools/index.ts +12 -12
  485. package/src/dev-tools/instrumentation.ts +1 -1
  486. package/src/dev-tools/logs.ts +1 -1
  487. package/src/dev-tools/overlay.ts +3 -3
  488. package/src/dev-tools/terminal.ts +2 -2
  489. package/src/fonts/ast.ts +5 -5
  490. package/src/fonts/bundle.ts +5 -5
  491. package/src/fonts/css.ts +1 -1
  492. package/src/fonts/dev-middleware.ts +2 -2
  493. package/src/fonts/google.ts +2 -2
  494. package/src/fonts/local.ts +4 -4
  495. package/src/fonts/pipeline.ts +8 -8
  496. package/src/fonts/transform.ts +5 -5
  497. package/src/fonts/types.ts +1 -1
  498. package/src/fonts/virtual-modules.ts +1 -1
  499. package/src/index.ts +46 -37
  500. package/src/params/codec-registry.ts +1 -1
  501. package/src/params/define-schema.ts +2 -2
  502. package/src/params/index.ts +4 -4
  503. package/src/params/resolve-schema.ts +2 -2
  504. package/src/plugin-context.ts +10 -10
  505. package/src/plugins/adapter-build.ts +5 -5
  506. package/src/plugins/build-manifest.ts +4 -4
  507. package/src/plugins/build-report.ts +6 -6
  508. package/src/plugins/cache.ts +3 -3
  509. package/src/plugins/callsite-ast.ts +1 -1
  510. package/src/plugins/callsite-jsx.ts +2 -2
  511. package/src/plugins/chunks.ts +1 -1
  512. package/src/plugins/cloudflare-wasm.ts +1 -1
  513. package/src/plugins/content.ts +5 -1
  514. package/src/plugins/dev-discovery.ts +218 -0
  515. package/src/plugins/dev-server.ts +97 -12
  516. package/src/plugins/entries.ts +1 -1
  517. package/src/plugins/fonts.ts +17 -10
  518. package/src/plugins/graph-endpoint.ts +532 -0
  519. package/src/plugins/mdx.ts +73 -38
  520. package/src/plugins/prebuilt-capture.ts +3 -3
  521. package/src/plugins/prebuilt-options-analysis.ts +1 -1
  522. package/src/plugins/prebuilt.ts +7 -7
  523. package/src/plugins/prerender-sugar.ts +11 -19
  524. package/src/plugins/request-dep/analysis.ts +1 -1
  525. package/src/plugins/request-dep/index.ts +8 -8
  526. package/src/plugins/request-dep/origin.ts +1 -1
  527. package/src/plugins/routing.ts +12 -7
  528. package/src/plugins/server-bundle.ts +11 -3
  529. package/src/plugins/shims.ts +16 -31
  530. package/src/plugins/static-build.ts +10 -10
  531. package/src/routing/codegen-shared.ts +1 -1
  532. package/src/routing/codegen-write.ts +3 -3
  533. package/src/routing/codegen.ts +14 -5
  534. package/src/routing/collision-probe.ts +3 -3
  535. package/src/routing/collision-spaces.ts +3 -3
  536. package/src/routing/convention-lint.ts +136 -6
  537. package/src/routing/export-detect.ts +1 -1
  538. package/src/routing/index.ts +13 -13
  539. package/src/routing/interception.ts +41 -4
  540. package/src/routing/link-codegen.ts +3 -3
  541. package/src/routing/manifest-codegen.ts +2 -2
  542. package/src/routing/scanner.ts +33 -9
  543. package/src/routing/schema-validation.ts +4 -4
  544. package/src/routing/segment-classify.ts +2 -2
  545. package/src/routing/segment-codegen.ts +1 -1
  546. package/src/routing/segment-keys.ts +1 -1
  547. package/src/routing/slot-placement.ts +1 -1
  548. package/src/routing/walkers.ts +1 -1
  549. package/src/rsc-runtime/transforms.ts +13 -0
  550. package/src/rsc-runtime/vendor-types.d.ts +12 -0
  551. package/src/schema-bridge.ts +1 -1
  552. package/src/search-params/define.ts +8 -8
  553. package/src/search-params/index.ts +3 -3
  554. package/src/search-params/wrappers.ts +3 -3
  555. package/src/server/access-gate.tsx +6 -6
  556. package/src/server/action-client.ts +5 -5
  557. package/src/server/action-handler.ts +16 -16
  558. package/src/server/actions.ts +5 -5
  559. package/src/server/als-registry.ts +8 -8
  560. package/src/server/asset-headers.ts +2 -2
  561. package/src/server/chain-url-parts.ts +5 -5
  562. package/src/server/children-interception.ts +4 -4
  563. package/src/server/compress.ts +1 -1
  564. package/src/server/cookie-context.ts +4 -4
  565. package/src/server/cookie-parsing.ts +2 -2
  566. package/src/server/default-logger.ts +3 -3
  567. package/src/server/deny-boundary.ts +10 -10
  568. package/src/server/deny-renderer.ts +38 -25
  569. package/src/server/early-hints-sender.ts +2 -2
  570. package/src/server/early-hints.ts +2 -2
  571. package/src/server/error-boundary-wrapper.ts +4 -12
  572. package/src/server/fallback-error.ts +13 -13
  573. package/src/server/flight-injection-state.ts +1 -1
  574. package/src/server/form-flash.ts +2 -2
  575. package/src/server/html-injector-core.ts +4 -4
  576. package/src/server/html-injectors.ts +5 -5
  577. package/src/server/index.ts +18 -18
  578. package/src/server/instrumentation.ts +1 -1
  579. package/src/server/internal.ts +50 -50
  580. package/src/server/logger.ts +4 -4
  581. package/src/server/metadata-collector.ts +7 -7
  582. package/src/server/metadata-platform.ts +2 -2
  583. package/src/server/metadata-render.ts +4 -4
  584. package/src/server/metadata-routes.ts +3 -3
  585. package/src/server/metadata-social.ts +2 -2
  586. package/src/server/metadata.ts +2 -2
  587. package/src/server/middleware-runner.ts +1 -1
  588. package/src/server/node-stream-transforms.ts +4 -4
  589. package/src/server/param-coercion.ts +8 -8
  590. package/src/server/pipeline-helpers.ts +7 -7
  591. package/src/server/pipeline-interception.ts +2 -2
  592. package/src/server/pipeline-metadata.ts +1 -1
  593. package/src/server/pipeline-outcome.ts +8 -8
  594. package/src/server/pipeline-phases.ts +24 -28
  595. package/src/server/pipeline.ts +22 -22
  596. package/src/server/prebuilt/cache-key.ts +1 -1
  597. package/src/server/prebuilt/capture-state.ts +1 -1
  598. package/src/server/prebuilt/key-discipline.ts +1 -1
  599. package/src/server/prebuilt/manifest-tag-index.ts +1 -1
  600. package/src/server/prebuilt/overlay.ts +6 -6
  601. package/src/server/prebuilt/payload-source.ts +2 -2
  602. package/src/server/prebuilt/slots.ts +5 -5
  603. package/src/server/prebuilt/synthetic-store.ts +2 -2
  604. package/src/server/prebuilt-builder.ts +16 -16
  605. package/src/server/prebuilt-runtime.ts +12 -12
  606. package/src/server/primitives.ts +7 -7
  607. package/src/server/publish-params.ts +3 -3
  608. package/src/server/request-context.ts +7 -7
  609. package/src/server/route-element-builder.ts +108 -50
  610. package/src/server/route-handler.ts +4 -4
  611. package/src/server/route-matcher.ts +5 -5
  612. package/src/server/rsc-cache-key-guard.ts +3 -5
  613. package/src/server/rsc-entry/action-middleware-runner.ts +7 -7
  614. package/src/server/rsc-entry/api-handler.ts +18 -9
  615. package/src/server/rsc-entry/deny-fallback.ts +10 -10
  616. package/src/server/rsc-entry/error-renderer.ts +27 -27
  617. package/src/server/rsc-entry/helpers.ts +10 -10
  618. package/src/server/rsc-entry/index.ts +51 -47
  619. package/src/server/rsc-entry/render-route.ts +20 -20
  620. package/src/server/rsc-entry/revalidate-renderer.ts +12 -12
  621. package/src/server/rsc-entry/rsc-payload.ts +19 -15
  622. package/src/server/rsc-entry/rsc-stream.ts +8 -8
  623. package/src/server/rsc-entry/ssr-bridge.ts +3 -3
  624. package/src/server/rsc-entry/ssr-renderer.ts +23 -23
  625. package/src/server/rsc-entry/wrap-action-dispatch.ts +9 -9
  626. package/src/server/rsc-prop-warnings.ts +1 -1
  627. package/src/server/sensitive-fields.ts +1 -1
  628. package/src/server/server-timing.ts +1 -1
  629. package/src/server/sitemap-generator.ts +3 -3
  630. package/src/server/sitemap-handler.ts +3 -3
  631. package/src/server/skippable-prefix.ts +7 -7
  632. package/src/server/slot-interception.ts +5 -5
  633. package/src/server/slot-resolver.ts +20 -28
  634. package/src/server/ssr-bridge-types.ts +2 -2
  635. package/src/server/ssr-entry.ts +18 -18
  636. package/src/server/ssr-render.ts +4 -4
  637. package/src/server/ssr-wrappers.tsx +1 -1
  638. package/src/server/state-tree-diff.ts +1 -1
  639. package/src/server/static-generator.ts +6 -6
  640. package/src/server/static-not-found.ts +3 -3
  641. package/src/server/status-code-resolver.ts +1 -1
  642. package/src/server/tracing.ts +5 -5
  643. package/src/server/tree-builder.ts +4 -4
  644. package/src/server/tree-match.ts +1 -1
  645. package/src/server/types.ts +2 -2
  646. package/src/server/utils/mdx-file.ts +2 -2
  647. package/src/server/waituntil-bridge.ts +1 -1
  648. package/src/server/workers-cache-bridge.ts +2 -2
  649. package/src/shared/als-slots.ts +1 -1
  650. package/src/shared/payload-root.ts +2 -2
  651. package/src/shared/rsc-media-type.ts +16 -0
  652. package/src/shared/slot-params.ts +1 -1
  653. package/src/shims/font-google.ts +1 -1
  654. package/src/shims/font-local.ts +1 -1
  655. package/src/shims/navigation-client.ts +5 -5
  656. package/src/shims/navigation.ts +2 -2
  657. package/LICENSE +0 -8
  658. package/dist/_chunks/actions-BjbbNRFN.js.map +0 -1
  659. package/dist/_chunks/build-output-helper-C3DrfzZR.js.map +0 -1
  660. package/dist/_chunks/cache-api-DllJ-Lyw.js.map +0 -1
  661. package/dist/_chunks/cli-check-DJZDc22E.js.map +0 -1
  662. package/dist/_chunks/convention-lint-CpteIpTm.js.map +0 -1
  663. package/dist/_chunks/dist-BA3u1z3W.js +0 -423
  664. package/dist/_chunks/dist-BA3u1z3W.js.map +0 -1
  665. package/dist/_chunks/mdx-file-C005ay-P.js.map +0 -1
  666. package/dist/_chunks/plugin-context-rCinWLiE.js.map +0 -1
  667. package/dist/_chunks/scanner-CAietmj4.js.map +0 -1
  668. package/dist/content/index.d.ts +0 -2
  669. package/dist/content/index.d.ts.map +0 -1
  670. package/dist/content/index.js +0 -0
  671. package/src/content/index.ts +0 -5
  672. package/src/shims/server-only-noop.js +0 -6
@@ -1 +1 @@
1
- {"version":3,"file":"file-cache-DmX7OqZP.js","names":[],"sources":["../../src/routing/codegen-shared.ts","../../src/routing/link-codegen.ts","../../src/routing/codegen.ts","../../src/routing/file-cache.ts"],"sourcesContent":["/**\n * Shared codegen helpers — import-path computation and codec chain type\n * builders.\n *\n * Extracted from `codegen.ts` so `link-codegen.ts` can use the same\n * helpers without a cyclic import.\n */\n\nimport { relative, posix } from 'node:path';\nimport type { ParamEntry } from './codegen-types.js';\n\n/**\n * Compute a relative import specifier for a codec/page file, stripping\n * the .ts/.tsx extension and resolving against the codegen output dir.\n */\nexport function codecImportPath(codecFilePath: string, importBase: string | undefined): string {\n const absPath = codecFilePath.replace(/\\.(ts|tsx)$/, '');\n if (importBase) {\n return './' + relative(importBase, absPath).replace(/\\\\/g, '/');\n }\n return './' + posix.basename(absPath);\n}\n\n/**\n * Build a TypeScript type expression for a single param using the global\n * schema's InferCodecType. Falls back to the param's base type when\n * schema doesn't declare this param (schemas are opt-in per key).\n *\n * Uses `infer` to extract the codec value rather than direct indexing\n * because TypeScript doesn't narrow index validity inside a conditional\n * type's true branch — `typeof schema.segmentParams[K]` produces TS2538\n * even when the condition proves K is a valid key. In .d.ts files that\n * error is suppressed and the type silently becomes `any`.\n */\nexport function buildSchemaParamType(p: ParamEntry, fallback?: string): string {\n const key = JSON.stringify(p.bracketName);\n const fb = fallback ?? p.type;\n return `typeof schema['segmentParams'] extends Record<${key}, infer V> ? InferCodecType<V> : ${fb}`;\n}\n","/**\n * Typed `<Link>` codegen — interface augmentation generation.\n *\n * Extracted from `codegen.ts` to keep that file under the project's\n * 500-line cap. This module owns everything that emits the\n * `interface LinkFunction { ... }` augmentation blocks in the generated\n * `.timber/timber-routes.d.ts`.\n *\n * Two augmentation blocks are emitted:\n *\n * 1. **Per-route discriminated union** (`formatTypedLinkOverloads`) —\n * one call signature whose props is a union keyed on `href`. TS\n * narrows by literal href and reports prop errors against the\n * matched variant. See TIM-835 and `design/09-typescript.md`.\n *\n * 2. **Catch-all overloads** (`formatLinkCatchAllOverloads`) — external\n * href literals (`http://`, `mailto:`, etc.) and a computed-string\n * `<H extends string>` signature for runtime-computed paths.\n *\n * Block ordering is critical for error UX: per-route is emitted FIRST\n * so that, after TS's \"later overload set ordered first\" merge rule,\n * the discriminated union ends up LAST in resolution order — the\n * overload TS reports against on failure.\n */\n\nimport type { ParamEntry, RouteEntry } from './codegen-types.js';\nimport { buildSchemaParamType } from './codegen-shared.js';\nimport { classifyUrlSegment } from './segment-classify.js';\n\n/**\n * The `searchParams` prop type, emitted identically on every Link variant\n * — per-route pattern, resolved template, and catch-all.\n *\n * TIM-1343: there is no per-route narrowing any more. `params.ts` is gone,\n * so codegen has no definition to read a `SearchParamsDefinition<T>` from.\n * Type safety moved to the value's construction site instead: callers write\n * `definition.buildSearchParams({ ... })`, which checks `Partial<T>` there\n * and returns a query STRING — the only shape that survives the RSC Flight\n * boundary. A plain record is the uncodec'd escape hatch. See `client/link.tsx` §\"searchParams prop shapes\".\n *\n * Declared once so the three emission sites cannot drift apart.\n */\nexport const SEARCH_PARAMS_PROP_TYPE = 'string | Record<string, unknown>';\n\n/** Shared Link base-props type literal used in every emitted call signature. */\nexport const LINK_BASE_PROPS_TYPE =\n \"Omit<import('react').AnchorHTMLAttributes<HTMLAnchorElement>, 'href'> & { prefetch?: boolean; scroll?: boolean; preserveSearchParams?: true | string[]; onNavigate?: import('./client/link.js').OnNavigateHandler; children?: import('react').ReactNode }\";\n\n/**\n * Build a TypeScript template literal pattern for a dynamic route.\n * e.g. '/products/[id]' → '/products/${string}'\n * '/blog/[...slug]' → '/blog/${string}'\n * '/docs/[[...path]]' → '/docs/${string}' (also matches /docs)\n * '/[org]/[repo]' → '/${string}/${string}'\n */\nexport function buildResolvedPattern(route: RouteEntry): string | null {\n const parts = route.urlPath.split('/');\n const templateParts = parts.map((part) => {\n const seg = classifyUrlSegment(part);\n switch (seg.kind) {\n case 'catch-all':\n case 'optional-catch-all':\n // eslint-disable-next-line no-template-curly-in-string -- codegen output\n return '${string}';\n case 'dynamic': {\n const prefix = seg.prefix ?? '';\n const suffix = seg.suffix ?? '';\n // eslint-disable-next-line no-template-curly-in-string -- codegen output\n return `${prefix}\\${string}${suffix}`;\n }\n default:\n return part;\n }\n });\n return templateParts.join('/');\n}\n\n/**\n * Format the segmentParams type for Link overloads.\n *\n * Link params accept `string | number` for single dynamic segments\n * (convenience — values are stringified at runtime). Catch-all and\n * optional catch-all remain `string[]` / `string[] | undefined`.\n *\n * With a global schema, the schema codec's inferred type wins. Without\n * one, a URL segment is a raw string (widened to string | number for\n * Link convenience) — per-segment codec chains were deleted in TIM-1342.\n */\nexport function formatLinkParamsType(params: ParamEntry[], hasSchema?: boolean): string {\n if (params.length === 0) {\n return '{}';\n }\n\n const fields = params.map((p) => {\n const fallback = p.type === 'string' ? 'string | number' : p.type;\n const codecType = hasSchema ? buildSchemaParamType(p, fallback) : fallback;\n return `${p.name}: ${codecType}`;\n });\n return `{ ${fields.join('; ')} }`;\n}\n\n/**\n * Catch-all call signatures for `<Link>` — external hrefs and computed\n * `string` variables. Emitted from codegen in a SEPARATE\n * `declare module` block (declared AFTER the per-route block) so the TS\n * \"later overload set ordered first\" rule places these catch-all\n * signatures ahead of per-route in resolution order, leaving per-route\n * as the final overload whose error message is reported on failure.\n *\n * The conditional `string extends H ? ... : never` protection preserves\n * TIM-624's guarantee that unknown internal path literals don't match\n * the catch-all — typos like `<Link href=\"/typo\" />` still error.\n */\nexport function formatLinkCatchAllOverloads(): string[] {\n const lines: string[] = [];\n const baseProps = LINK_BASE_PROPS_TYPE;\n\n const catchAllSearchParams = SEARCH_PARAMS_PROP_TYPE;\n\n // ExternalHref inlined here rather than referenced as an exported\n // alias so the generated .d.ts stands alone without source imports.\n /* eslint-disable no-template-curly-in-string -- codegen output */\n const externalHref =\n '`http://${string}` | `https://${string}` | `mailto:${string}` | `tel:${string}` | `ftp://${string}` | `//${string}` | `#${string}` | `?${string}`';\n /* eslint-enable no-template-curly-in-string */\n\n lines.push(' // Typed Link overloads — catch-all (block 2 / emitted second)');\n lines.push(' interface LinkFunction {');\n\n // (1) External/literal-protocol hrefs.\n //\n // TIM-833: `segmentParams` is permissively typed as\n // `Record<string, unknown>` (not `never`) so generic wrappers that\n // forward a `LinkProps`-shaped object can compile when the wrapper\n // happens to bottom out at an external href. There are no dynamic\n // segments to interpolate for an external href, so this prop is\n // ignored at runtime — accepting an open-shape object instead of\n // forbidding it removes a footgun without changing behavior.\n lines.push(` (props: ${baseProps} & {`);\n lines.push(` href: ${externalHref}`);\n lines.push(` segmentParams?: Record<string, unknown>`);\n lines.push(` searchParams?: ${catchAllSearchParams}`);\n lines.push(` }): import('react').JSX.Element`);\n\n // (2) Computed/variable href — non-literal `string` only.\n //\n // `string extends H` is true only when H is the wide `string` type,\n // not a specific literal. For literal hrefs, this overload must NOT\n // be selectable so the per-route discriminated union (block 1) is\n // the resolution target.\n //\n // TIM-833 follow-up: the previous shape used `string extends H ? {...} : never`\n // on the WHOLE props type. For literal H, that collapsed `props` to\n // `never`, and JSX type inference then read `(never).children` as\n // `never` — producing the misleading\n // `'children' prop expects type 'never' which requires multiple\n // children, but only a single child was provided` (TS2745) error,\n // which buried the actual prop-mismatch message under a noise diagnostic.\n //\n // Fix: move the `: never` from the whole props onto just the `href`\n // field. The overload remains uncallable for literal H (since `\"/x\"`\n // is not assignable to `never`), but `children` retains its real\n // `ReactNode` type so JSX inference no longer collapses to never.\n // The diagnostic surface becomes: TS reports the per-route block's\n // error chain (the helpful \"Type 'number' is not assignable to type\n // 'string'\" message) WITHOUT a leading TS2745 noise line.\n lines.push(` <H extends string>(`);\n lines.push(` props: ${baseProps} & {`);\n lines.push(` href: string extends H ? H : never`);\n lines.push(` segmentParams?: Record<string, string | number | string[]>`);\n lines.push(` searchParams?: ${catchAllSearchParams}`);\n lines.push(` }`);\n lines.push(` ): import('react').JSX.Element`);\n\n lines.push(' }');\n return lines;\n}\n\n/**\n * Generate typed per-route Link call signatures via LinkFunction\n * interface merging.\n *\n * TIM-835: This emits a SINGLE call signature whose props is a\n * discriminated union keyed on `href`. TypeScript narrows the union by\n * the literal `href` at the call site, then checks the rest of the\n * props against the matched variant. When `segmentParams` or\n * `searchParams` is wrong, TS reports the error against the matched\n * variant — naming the user's actual `href` and the offending field.\n *\n * Before TIM-835, this function emitted N separate per-route overloads\n * (one per route, sometimes two for dynamic routes). When ALL overloads\n * failed, TS would pick an arbitrary failed overload (heuristically the\n * \"last tried\") to render the diagnostic, often pointing at a route\n * completely unrelated to the one the user wrote. The discriminated\n * union sidesteps overload-resolution heuristics entirely.\n *\n * Open property shapes (`Record<string, unknown>` instead of `never`)\n * for `segmentParams` on routes that don't declare them keep generic\n * `LinkProps`-spreading wrappers compiling. (Aligned with TIM-833.)\n *\n * TIM-832: this function still emits the per-route block FIRST and the\n * catch-all block follows, so per-route remains the LAST overload set in\n * resolution order — the one TS reports against on failure. With a\n * discriminated union there is only one call signature in this block,\n * so the resolved-template-vs-pattern ordering reduces to placing the\n * pattern variant before the resolved-template variant inside the union.\n */\nexport function formatTypedLinkOverloads(routes: RouteEntry[], hasSchema?: boolean): string[] {\n const lines: string[] = [];\n const baseProps = LINK_BASE_PROPS_TYPE;\n\n // Build the union variants. Each route contributes one variant for the\n // pattern href (e.g. '/products/[id]') and, for dynamic routes, an\n // additional variant for the resolved-template href (e.g.\n // `/products/${string}`).\n //\n // The PATTERN variant must be listed BEFORE the resolved-template\n // variant for the same route so that when the user writes the literal\n // pattern href (`<Link href=\"/products/[id]\" />`), TS narrows to the\n // pattern variant (which carries the typed `segmentParams` shape)\n // rather than the looser resolved-template variant. Without this\n // ordering, TS would silently match the resolved-template variant\n // first — swallowing typed-segmentParams type errors.\n const variants: string[] = [];\n for (const route of routes) {\n const hasDynamicParams = route.params.length > 0;\n // For routes with no dynamic params, accept an absent OR open-shape\n // segmentParams. `Record<string, unknown>` keeps generic spreads\n // compiling and gives a readable error if a non-object is passed.\n const paramsProp = hasDynamicParams\n ? `segmentParams: ${formatLinkParamsType(route.params, hasSchema)}`\n : 'segmentParams?: Record<string, unknown>';\n\n // TIM-1343: searchParams is no longer typed per-route. There is no\n // convention file to read a definition from, so codegen has nothing to\n // narrow with — the value is either a URLSearchParams built by\n // `definition.buildSearchParams()` (which type-checks its own\n // `Partial<T>` at that call) or a plain String()-coerced record.\n const searchParamsProp = `searchParams?: ${SEARCH_PARAMS_PROP_TYPE}`;\n\n // Pattern variant FIRST (more specific).\n variants.push(\n `${baseProps} & { href: '${route.urlPath}'; ${paramsProp}; ${searchParamsProp} }`\n );\n\n // Resolved-template variant SECOND (looser, matches interpolated hrefs).\n if (hasDynamicParams) {\n const templatePattern = buildResolvedPattern(route);\n if (templatePattern) {\n // TIM-835: `Record<string, never>` for segmentParams forbids ANY\n // provided keys on the resolved-template variant. Without this,\n // the resolved-template would shadow the pattern variant for\n // literal pattern hrefs (`<Link href=\"/products/[id]\" segmentParams={...} />`)\n // because both variants accept the literal `'/products/[id]'`\n // and the looser variant would silently swallow segmentParams\n // type errors.\n variants.push(\n `${baseProps} & { href: \\`${templatePattern}\\`; segmentParams?: Record<string, never>; ${searchParamsProp} }`\n );\n }\n }\n }\n\n lines.push(' interface LinkFunction {');\n if (variants.length === 0) {\n // No page routes — emit nothing. The catch-all block in the second\n // augmentation still provides external/computed-string signatures.\n lines.push(' }');\n return lines;\n }\n lines.push(' (');\n lines.push(' props:');\n for (const variant of variants) {\n lines.push(` | (${variant})`);\n }\n lines.push(` ): import('react').JSX.Element`);\n lines.push(' }');\n\n return lines;\n}\n","/**\n * Route map codegen.\n *\n * Walks the scanned RouteTree and generates a TypeScript declaration file\n * mapping every route to its params and searchParams shapes.\n *\n * This runs at build time and in dev (regenerated on file changes).\n * No runtime overhead — purely static type generation.\n */\n\nimport { existsSync } from 'node:fs';\nimport { join, relative } from 'node:path';\nimport type { RouteTree, SegmentNode } from './types.js';\nimport type { ParamEntry, RouteEntry } from './codegen-types.js';\nimport { buildSchemaParamType } from './codegen-shared.js';\nimport { effectiveUrlSegment, type UrlSegmentIdentity } from './segment-classify.js';\nimport { formatLinkCatchAllOverloads, formatTypedLinkOverloads } from './link-codegen.js';\n\n/** Options for route map generation. */\nexport interface CodegenOptions {\n /** Absolute path to the app/ directory. Required for page searchParams detection. */\n appDir?: string;\n /**\n * Absolute path to the directory where the .d.ts file will be written.\n * Used to compute correct relative import paths for page files.\n * Defaults to appDir when not provided (preserves backward compat for tests).\n */\n outputDir?: string;\n /**\n * Whether a global schema (app/schema.ts) exists.\n * Auto-detected from appDir when not explicitly set.\n */\n hasSchema?: boolean;\n}\n\n/**\n * Generate a TypeScript declaration file string from a scanned route tree.\n *\n * The output is a `declare module '@timber-js/app'` block containing the Routes\n * interface that maps every route path to its params and searchParams shape.\n */\nexport function generateRouteMap(tree: RouteTree, options: CodegenOptions = {}): string {\n // Auto-detect schema from appDir when not explicitly set\n const hasSchema = options.hasSchema ?? detectSchema(options.appDir);\n\n const routes: RouteEntry[] = [];\n collectRoutes(tree.root, [], '', routes, hasSchema, false);\n\n // Sort routes alphabetically for deterministic output\n routes.sort((a, b) => a.urlPath.localeCompare(b.urlPath));\n\n // When outputDir differs from appDir, import paths must be relative to outputDir\n const importBase = options.outputDir ?? options.appDir;\n\n return formatDeclarationFile(routes, importBase, hasSchema, options.appDir);\n}\n\n/** Schema file extensions to check, in priority order. */\nconst SCHEMA_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx'];\n\n/**\n * Detect whether app/schema.{ts,tsx,js,jsx} exists.\n */\nfunction detectSchema(appDir: string | undefined): boolean {\n if (!appDir) return false;\n return SCHEMA_EXTENSIONS.some((ext) => existsSync(join(appDir, `schema${ext}`)));\n}\n\n/**\n * Recursively walk the segment tree and collect route entries.\n *\n * A route entry is created for any segment that has a `page` or `route` file.\n * Params accumulate from ancestor dynamic segments.\n */\nfunction collectRoutes(\n node: SegmentNode,\n ancestorParams: ParamEntry[],\n parentTreePath: string,\n routes: RouteEntry[],\n hasSchema: boolean,\n insideSlot: boolean,\n insideIntercepting = false\n): void {\n // Build the tree path (includes groups, slots — uniquely identifies this segment).\n // Root node has empty segmentName, producing treePath = ''.\n const treePath = node.segmentName ? `${parentTreePath}/${node.segmentName}` : parentTreePath;\n // Accumulate params from this segment.\n //\n // The segment is read through `effectiveUrlSegment` for the same reason the\n // runtime coercer is: an intercepting node's own `segmentType` is\n // 'intercepting' and its `paramName` is unset — the param it binds lives in\n // the intercepted name (`(.)[albumSlug]` → `[albumSlug]`). Reading the node\n // directly emitted `{}` for a segment the router does bind and coerce, so\n // the types disagreed with the values for every intercepting page with a\n // dynamic intercepted segment, and for its descendants, which lost the\n // ancestor param too.\n const urlSegment = effectiveUrlSegment(node);\n const params = [...ancestorParams];\n if (urlSegment.paramName) {\n params.push({\n name: urlSegment.paramName,\n bracketName: bracketNameForSegment(urlSegment),\n type: paramTypeForSegment(urlSegment.segmentType),\n });\n }\n\n // Check if this segment is a leaf route (has page or route file)\n const isPage = !!node.page;\n const isApiRoute = !!node.route;\n\n // A segment that hosts code but is not itself addressable — a layout,\n // middleware, or access check above the leaf — still gets a `$segment`\n // module, so `getSegmentParams(SEGMENT_PATH)` is callable from it and needs\n // a matching typed overload. Emitting only leaves left those call sites on\n // the broad `Record<string, string | string[]>` signature, which since\n // TIM-1342 (which deleted `defineSegmentParams`) is their ONLY typed path.\n // design/41-global-params.md's Type Summary promises layouts and middleware\n // exact accumulated params.\n //\n // `isRoutable: false` keeps them out of the urlPath-keyed emissions — the\n // `Routes` interface and `<Link>` variants — where they would publish a URL\n // that no request can reach. They belong only to the segmentPath-keyed\n // APIs: `getSegmentParams`, `useSegmentParams`, `TimberSegmentParams`.\n const hostsCode = !!node.layout || !!node.middleware || !!node.access;\n\n if (isPage || isApiRoute || hostsCode) {\n const entry: RouteEntry = {\n urlPath: node.urlPath,\n segmentPath: treePath || '/',\n params,\n isApiRoute,\n isRoutable: isPage || isApiRoute,\n isSlotRoute: insideSlot,\n isInterceptingRoute: insideIntercepting,\n hasSchema,\n };\n\n routes.push(entry);\n }\n\n // Recurse into children. An intercepting subtree is flagged, and the flag is\n // sticky — the descendants are where the false URLs are. See\n // `RouteEntry.isInterceptingRoute` for what the flag drops and why dedupe\n // alone was not enough.\n for (const child of node.children) {\n const childIntercepting = insideIntercepting || child.segmentType === 'intercepting';\n collectRoutes(child, params, treePath, routes, hasSchema, insideSlot, childIntercepting);\n }\n\n // Recurse into slots (they share the parent's URL path, but may have their own pages)\n for (const slot of Object.values(node.slots)) {\n collectRoutes(slot, params, treePath, routes, hasSchema, true, insideIntercepting);\n }\n}\n\n/**\n * Dedupe route entries by urlPath for urlPath-keyed emissions (the Routes\n * interface and typed Link variants).\n *\n * Parallel-slot pages share their parent's urlPath; emitting one entry per\n * slot page produced duplicate interface keys (TS2300/TS2717 under\n * skipLibCheck:false) and duplicate overloads. The non-slot entry is the\n * canonical one; when only slot entries exist (parent has no page), the\n * lexicographically smallest segmentPath wins so output is independent of\n * slot scan order.\n *\n * The loser is discarded outright. It used to donate its `searchParams`\n * definition to the winner, because a slot could carry one the parent page\n * lacked; since TIM-1343 no route entry carries a searchParams definition at\n * all, there is nothing left to merge.\n *\n * segmentPath-keyed emissions (getSegmentParams, useSegmentParams,\n * TimberSegmentParams) must NOT use this — segmentPaths are already unique\n * and slot entries feed those APIs directly.\n */\nfunction dedupeRoutesByUrlPath(routes: RouteEntry[]): RouteEntry[] {\n const byPath = new Map<string, RouteEntry>();\n for (const route of routes) {\n const existing = byPath.get(route.urlPath);\n if (!existing) {\n byPath.set(route.urlPath, route);\n continue;\n }\n\n const routeWins =\n (existing.isSlotRoute && !route.isSlotRoute) ||\n (!!existing.isSlotRoute === !!route.isSlotRoute &&\n route.segmentPath.localeCompare(existing.segmentPath) < 0);\n byPath.set(route.urlPath, routeWins ? route : existing);\n }\n return [...byPath.values()];\n}\n\n/**\n * Determine the TypeScript type for a segment's param.\n */\n/**\n * Derive the bracket key for schema/codegen lookup.\n * For bare segments like `[id]` this is the segmentName itself.\n * For affixed segments like `img-[id].png` this is `[id]` — the bare bracket form.\n */\nfunction bracketNameForSegment(node: UrlSegmentIdentity): string {\n if (node.paramPrefix || node.paramSuffix) {\n return `[${node.paramName}]`;\n }\n return node.segmentName;\n}\n\nfunction paramTypeForSegment(segmentType: string): ParamEntry['type'] {\n switch (segmentType) {\n case 'catch-all':\n return 'string[]';\n case 'optional-catch-all':\n return 'string[] | undefined';\n default:\n return 'string';\n }\n}\n\n/**\n * Emit the schema-based type exports: TimberSegmentParams, TimberRoute,\n * AllSegmentParams, RoutesWithParams.\n *\n * These types use InferCodecType references so they stay in sync with\n * the schema without hardcoding resolved types.\n *\n * Design doc: design/41-global-params.md §Codegen Output\n */\nfunction formatSchemaTypes(routes: RouteEntry[]): string[] {\n const lines: string[] = [];\n\n lines.push('/** Union of all valid route patterns. */');\n lines.push('export type TimberRoute = keyof TimberSegmentParams;');\n lines.push('');\n lines.push('/** Maps each segment path to its accumulated segment params. */');\n lines.push('export interface TimberSegmentParams {');\n\n for (const route of routes) {\n if (route.params.length === 0) {\n lines.push(` '${route.segmentPath}': {};`);\n } else {\n lines.push(` '${route.segmentPath}': {`);\n for (const p of route.params) {\n lines.push(` ${p.name}: ${buildSchemaParamType(p)};`);\n }\n lines.push(' };');\n }\n }\n\n lines.push('}');\n lines.push('');\n\n // AllSegmentParams — flat union of all param types across all routes\n lines.push('/** All segment params (union of all route params). */');\n lines.push('export type AllSegmentParams = {');\n lines.push(\n ' [K in keyof TimberSegmentParams as TimberSegmentParams[K] extends Record<string, never>'\n );\n lines.push(' ? never');\n lines.push(' : K]: TimberSegmentParams[K];');\n lines.push('} extends infer U');\n lines.push(' ? { [K in keyof U[keyof U]]: U[keyof U][K] }');\n lines.push(' : never;');\n lines.push('');\n\n // RoutesWithParams — filter routes by required param keys\n lines.push(\n '/** Filters TimberRoute to routes whose accumulated params include all listed keys. */'\n );\n lines.push('export type RoutesWithParams<K extends keyof AllSegmentParams> = {');\n lines.push(' [R in TimberRoute]: K extends keyof TimberSegmentParams[R] ? R : never;');\n lines.push('}[TimberRoute];');\n\n return lines;\n}\n\n/**\n * Format the collected routes into a TypeScript declaration file.\n */\nfunction formatDeclarationFile(\n routes: RouteEntry[],\n importBase?: string,\n hasSchema?: boolean,\n appDir?: string\n): string {\n const lines: string[] = [];\n\n lines.push('// This file is auto-generated by timber.js route map codegen.');\n lines.push('// Do not edit manually. Regenerated on build and in dev mode.');\n lines.push('');\n // export {} makes this file a module, so all declare module blocks are\n // augmentations rather than ambient replacements. Without this, the\n // declare module blocks would replace the original module types entirely\n // (removing exports like bindUseQueryStates that aren't listed here).\n lines.push('export {};');\n lines.push('');\n\n if (hasSchema) {\n // Schema-based codegen: import schema and InferCodecType\n // Compute the schema import path relative to the output directory\n const schemaImport =\n appDir && importBase\n ? './' + relative(importBase, join(appDir, 'schema')).replace(/\\\\/g, '/')\n : '../app/schema';\n lines.push(`import type schema from '${schemaImport}';`);\n lines.push(\"import type { InferCodecType, SegmentParamCodec } from '@timber-js/app/params';\");\n lines.push('');\n // Collect all unique bracket keys from routes for key validation\n const allBracketKeys = new Set<string>();\n for (const route of routes) {\n for (const p of route.params) {\n allBracketKeys.add(p.bracketName);\n }\n }\n\n // Augment the ValidDynamicSegmentKeysRegistry so defineSchema constrains keys\n // to known filesystem segments. Unknown keys produce inline TS errors.\n if (allBracketKeys.size > 0) {\n lines.push(\"declare module '@timber-js/app/params' {\");\n lines.push(' interface ValidDynamicSegmentKeysRegistry {');\n for (const key of [...allBracketKeys].sort()) {\n lines.push(` ${JSON.stringify(key)}: true;`);\n }\n lines.push(' }');\n lines.push('}');\n lines.push('');\n }\n // Type-level validation: each schema value must be a Codec or Standard Schema.\n // Running `pnpm run typecheck` will surface errors for invalid values.\n lines.push('type _AssertSchemaValues = {');\n lines.push(\n \" [K in keyof typeof schema['segmentParams']]: typeof schema['segmentParams'][K] extends SegmentParamCodec ? true : ['Error: value for', K, 'is not a valid Codec or Standard Schema'];\"\n );\n lines.push('};');\n // Generic constraint enforces that all values are `true` — if any value is\n // an error tuple, TS reports a real diagnostic instead of silently computing it.\n lines.push(\n 'type _EnforceSchemaValues<_T extends Record<string, true> = _AssertSchemaValues> = true;'\n );\n lines.push('');\n }\n\n // urlPath-keyed emissions use the deduped list (slot pages share their\n // parent's urlPath); segmentPath-keyed emissions below keep all entries.\n //\n // Intercepting subtrees are dropped outright rather than deduped: their\n // computed urlPath is not a route any request can reach, so a path that\n // happens NOT to collide with a real one would otherwise be published as a\n // real route. See `RouteEntry.isInterceptingRoute`.\n const uniqueByUrlPath = dedupeRoutesByUrlPath(\n routes.filter((r) => r.isRoutable && !r.isInterceptingRoute)\n );\n\n lines.push(\"declare module '@timber-js/app' {\");\n lines.push(' interface Routes {');\n\n // TIM-1343: no `searchParams` member. With `params.ts` gone there is no\n // convention file to read a definition from, so the framework has nothing\n // to type it with — and an always-`{}` member is a claim that every route\n // has no search params, which is false. Search-param types now come from\n // the definition object itself, wherever the app chooses to keep it.\n for (const route of uniqueByUrlPath) {\n const paramsType = formatParamsType(route.params, hasSchema);\n\n lines.push(` '${route.urlPath}': {`);\n lines.push(` segmentParams: ${paramsType}`);\n lines.push(` }`);\n }\n\n lines.push(' }');\n\n // When a global schema exists, emit segment param types inside the\n // @timber-js/app module augmentation so that declare module blocks\n // for @timber-js/app/server and @timber-js/app/client can reference\n // AllSegmentParams via `import type { AllSegmentParams } from '@timber-js/app'`.\n if (hasSchema) {\n lines.push('');\n lines.push(...formatSchemaTypes(routes).map((l) => ` ${l}`));\n }\n\n lines.push('}');\n lines.push('');\n\n // useQueryStates + typed Link are urlPath-keyed → deduped list.\n const pageRoutes = uniqueByUrlPath.filter((r) => !r.isApiRoute);\n // getSegmentParams / useSegmentParams are segmentPath-keyed → full list,\n // so slot segments keep their own typed overloads.\n const dynamicRoutes = routes.filter((r) => r.params.length > 0);\n\n // Generate @timber-js/app/server augmentation — typed getSegmentParams\n if (dynamicRoutes.length > 0) {\n lines.push(\"declare module '@timber-js/app/server' {\");\n if (hasSchema) {\n lines.push(\" import type { AllSegmentParams } from '@timber-js/app'\");\n }\n for (const route of dynamicRoutes) {\n const paramsType = formatParamsType(route.params, hasSchema);\n lines.push(\n ` export function getSegmentParams(segmentPath: '${route.segmentPath}'): ${paramsType}`\n );\n }\n lines.push(\n hasSchema\n ? ' export function getSegmentParams(): Partial<AllSegmentParams>'\n : ' export function getSegmentParams(): Record<string, string | string[]>'\n );\n lines.push('}');\n lines.push('');\n }\n\n // Generate overloads for @timber-js/app/client\n\n if (dynamicRoutes.length > 0 || pageRoutes.length > 0) {\n lines.push(\"declare module '@timber-js/app/client' {\");\n lines.push(\" import type { SearchParamsDefinition } from '@timber-js/app/search-params'\");\n if (hasSchema) {\n lines.push(\" import type { AllSegmentParams } from '@timber-js/app'\");\n }\n lines.push('');\n\n // useSegmentParams overloads\n if (dynamicRoutes.length > 0) {\n for (const route of dynamicRoutes) {\n const paramsType = formatParamsType(route.params, hasSchema);\n lines.push(\n ` export function useSegmentParams(segmentPath: '${route.segmentPath}'): ${paramsType}`\n );\n }\n lines.push(\n hasSchema\n ? ' export function useSegmentParams(): Partial<AllSegmentParams>'\n : ' export function useSegmentParams(): Record<string, string | string[]>'\n );\n lines.push('');\n }\n\n // Typed Link overloads — per-route with DIRECT types (no conditionals).\n // Direct types preserve TypeScript's excess property checking.\n //\n // TIM-832: per-route and catch-all are emitted as TWO separate\n // augmentation blocks. Per TS's merging rule \"later overload sets\n // ordered first\", the catch-all block (declared SECOND in this file)\n // ends up FIRST in the merged call-signature list at resolution time,\n // which puts the per-route block LAST — so its error message is the\n // one TypeScript reports when no overload matches. This gives users a\n // clear prop-mismatch error (e.g. \"'string | undefined' is not\n // assignable to 'string | number' on id\") instead of the old\n // confusing \"Type 'string' is not assignable to type 'never'\" cascade.\n if (pageRoutes.length > 0) {\n lines.push(' // Typed Link overloads — per-route (block 1 / emitted first)');\n lines.push(...formatTypedLinkOverloads(pageRoutes, hasSchema));\n lines.push('');\n }\n\n lines.push('}');\n lines.push('');\n }\n\n // TIM-832: catch-all block — emitted as a SEPARATE `declare module`\n // augmentation so TS's \"later overload set first\" rule orders it ahead\n // of the per-route block above at resolution time, leaving per-route as\n // the \"last overload\" whose error TypeScript reports.\n lines.push(\"declare module '@timber-js/app/client' {\");\n lines.push(\" import type { SearchParamsDefinition } from '@timber-js/app/search-params'\");\n lines.push('');\n lines.push(...formatLinkCatchAllOverloads());\n lines.push('}');\n lines.push('');\n\n return lines.join('\\n');\n}\n\n/**\n * Format the params type for a route entry.\n */\nfunction formatParamsType(params: ParamEntry[], hasSchema?: boolean): string {\n if (params.length === 0) {\n return '{}';\n }\n\n // Without a schema a URL segment is an uninterpreted string — that IS\n // its type. Per-segment codec chains used to narrow it here; TIM-1342\n // deleted them along with params.ts.\n const fields = params.map((p) => {\n const codecType = hasSchema ? buildSchemaParamType(p) : p.type;\n return `${p.name}: ${codecType}`;\n });\n return `{ ${fields.join('; ')} }`;\n}\n\n// TIM-1341: `useQueryStates` no longer has a route-string form, so the\n// generated `.d.ts` emits no overloads for it. Its source signature —\n// `useQueryStates(codecs, options?)` — is fully generic over the codec map\n// and needs no per-route augmentation. Reaching another route's codecs is\n// done by importing that route's `searchParams` definition from its\n// `params.ts` and calling `definition.useQueryStates()`, which is typed by\n// inference and cannot fail at runtime on an unpopulated registry.\n// See design/23-search-params.md §\"Client Access\".\n\n// Link overload formatters and helpers (`formatTypedLinkOverloads`,\n// `formatLinkCatchAllOverloads`, `formatLinkParamsType`,\n// `buildResolvedPattern`, `LINK_BASE_PROPS_TYPE`) were extracted to\n// `./link-codegen.ts` (TIM-835) to keep this file under the 500-line\n// cap. They are imported at the top of this file.\n","import { readFileSync, statSync } from 'node:fs';\n\nconst cache = new Map<string, { mtimeMs: number; size: number; content: string }>();\n\n/**\n * Read a file's text content, returning a cached copy when the file\n * hasn't been modified since the last read (checked via mtime + size).\n *\n * Falls back to a fresh read on any stat/read error.\n */\nexport function readFileCached(filePath: string): string {\n try {\n const stat = statSync(filePath);\n const entry = cache.get(filePath);\n if (entry && entry.mtimeMs === stat.mtimeMs && entry.size === stat.size) return entry.content;\n const content = readFileSync(filePath, 'utf-8');\n cache.set(filePath, { mtimeMs: stat.mtimeMs, size: stat.size, content });\n return content;\n } catch {\n cache.delete(filePath);\n return readFileSync(filePath, 'utf-8');\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"file-cache-Dw6BJPG7.js","names":[],"sources":["../../src/routing/codegen-shared.ts","../../src/routing/link-codegen.ts","../../src/routing/codegen.ts","../../src/routing/file-cache.ts"],"sourcesContent":["/**\n * Shared codegen helpers — import-path computation and codec chain type\n * builders.\n *\n * Extracted from `codegen.ts` so `link-codegen.ts` can use the same\n * helpers without a cyclic import.\n */\n\nimport { relative, posix } from 'node:path';\nimport type { ParamEntry } from './codegen-types.ts';\n\n/**\n * Compute a relative import specifier for a codec/page file, stripping\n * the .ts/.tsx extension and resolving against the codegen output dir.\n */\nexport function codecImportPath(codecFilePath: string, importBase: string | undefined): string {\n const absPath = codecFilePath.replace(/\\.(ts|tsx)$/, '');\n if (importBase) {\n return './' + relative(importBase, absPath).replace(/\\\\/g, '/');\n }\n return './' + posix.basename(absPath);\n}\n\n/**\n * Build a TypeScript type expression for a single param using the global\n * schema's InferCodecType. Falls back to the param's base type when\n * schema doesn't declare this param (schemas are opt-in per key).\n *\n * Uses `infer` to extract the codec value rather than direct indexing\n * because TypeScript doesn't narrow index validity inside a conditional\n * type's true branch — `typeof schema.segmentParams[K]` produces TS2538\n * even when the condition proves K is a valid key. In .d.ts files that\n * error is suppressed and the type silently becomes `any`.\n */\nexport function buildSchemaParamType(p: ParamEntry, fallback?: string): string {\n const key = JSON.stringify(p.bracketName);\n const fb = fallback ?? p.type;\n return `typeof schema['segmentParams'] extends Record<${key}, infer V> ? InferCodecType<V> : ${fb}`;\n}\n","/**\n * Typed `<Link>` codegen — interface augmentation generation.\n *\n * Extracted from `codegen.ts` to keep that file under the project's\n * 500-line cap. This module owns everything that emits the\n * `interface LinkFunction { ... }` augmentation blocks in the generated\n * `.timber/timber-routes.d.ts`.\n *\n * Two augmentation blocks are emitted:\n *\n * 1. **Per-route discriminated union** (`formatTypedLinkOverloads`) —\n * one call signature whose props is a union keyed on `href`. TS\n * narrows by literal href and reports prop errors against the\n * matched variant. See TIM-835 and `design/09-typescript.md`.\n *\n * 2. **Catch-all overloads** (`formatLinkCatchAllOverloads`) — external\n * href literals (`http://`, `mailto:`, etc.) and a computed-string\n * `<H extends string>` signature for runtime-computed paths.\n *\n * Block ordering is critical for error UX: per-route is emitted FIRST\n * so that, after TS's \"later overload set ordered first\" merge rule,\n * the discriminated union ends up LAST in resolution order — the\n * overload TS reports against on failure.\n */\n\nimport type { ParamEntry, RouteEntry } from './codegen-types.ts';\nimport { buildSchemaParamType } from './codegen-shared.ts';\nimport { classifyUrlSegment } from './segment-classify.ts';\n\n/**\n * The `searchParams` prop type, emitted identically on every Link variant\n * — per-route pattern, resolved template, and catch-all.\n *\n * TIM-1343: there is no per-route narrowing any more. `params.ts` is gone,\n * so codegen has no definition to read a `SearchParamsDefinition<T>` from.\n * Type safety moved to the value's construction site instead: callers write\n * `definition.buildSearchParams({ ... })`, which checks `Partial<T>` there\n * and returns a query STRING — the only shape that survives the RSC Flight\n * boundary. A plain record is the uncodec'd escape hatch. See `client/link.tsx` §\"searchParams prop shapes\".\n *\n * Declared once so the three emission sites cannot drift apart.\n */\nexport const SEARCH_PARAMS_PROP_TYPE = 'string | Record<string, unknown>';\n\n/** Shared Link base-props type literal used in every emitted call signature. */\nexport const LINK_BASE_PROPS_TYPE =\n \"Omit<import('react').AnchorHTMLAttributes<HTMLAnchorElement>, 'href'> & { prefetch?: boolean; scroll?: boolean; preserveSearchParams?: true | string[]; onNavigate?: import('./client/link.js').OnNavigateHandler; children?: import('react').ReactNode }\";\n\n/**\n * Build a TypeScript template literal pattern for a dynamic route.\n * e.g. '/products/[id]' → '/products/${string}'\n * '/blog/[...slug]' → '/blog/${string}'\n * '/docs/[[...path]]' → '/docs/${string}' (also matches /docs)\n * '/[org]/[repo]' → '/${string}/${string}'\n */\nexport function buildResolvedPattern(route: RouteEntry): string | null {\n const parts = route.urlPath.split('/');\n const templateParts = parts.map((part) => {\n const seg = classifyUrlSegment(part);\n switch (seg.kind) {\n case 'catch-all':\n case 'optional-catch-all':\n // eslint-disable-next-line no-template-curly-in-string -- codegen output\n return '${string}';\n case 'dynamic': {\n const prefix = seg.prefix ?? '';\n const suffix = seg.suffix ?? '';\n // eslint-disable-next-line no-template-curly-in-string -- codegen output\n return `${prefix}\\${string}${suffix}`;\n }\n default:\n return part;\n }\n });\n return templateParts.join('/');\n}\n\n/**\n * Format the segmentParams type for Link overloads.\n *\n * Link params accept `string | number` for single dynamic segments\n * (convenience — values are stringified at runtime). Catch-all and\n * optional catch-all remain `string[]` / `string[] | undefined`.\n *\n * With a global schema, the schema codec's inferred type wins. Without\n * one, a URL segment is a raw string (widened to string | number for\n * Link convenience) — per-segment codec chains were deleted in TIM-1342.\n */\nexport function formatLinkParamsType(params: ParamEntry[], hasSchema?: boolean): string {\n if (params.length === 0) {\n return '{}';\n }\n\n const fields = params.map((p) => {\n const fallback = p.type === 'string' ? 'string | number' : p.type;\n const codecType = hasSchema ? buildSchemaParamType(p, fallback) : fallback;\n return `${p.name}: ${codecType}`;\n });\n return `{ ${fields.join('; ')} }`;\n}\n\n/**\n * Catch-all call signatures for `<Link>` — external hrefs and computed\n * `string` variables. Emitted from codegen in a SEPARATE\n * `declare module` block (declared AFTER the per-route block) so the TS\n * \"later overload set ordered first\" rule places these catch-all\n * signatures ahead of per-route in resolution order, leaving per-route\n * as the final overload whose error message is reported on failure.\n *\n * The conditional `string extends H ? ... : never` protection preserves\n * TIM-624's guarantee that unknown internal path literals don't match\n * the catch-all — typos like `<Link href=\"/typo\" />` still error.\n */\nexport function formatLinkCatchAllOverloads(): string[] {\n const lines: string[] = [];\n const baseProps = LINK_BASE_PROPS_TYPE;\n\n const catchAllSearchParams = SEARCH_PARAMS_PROP_TYPE;\n\n // ExternalHref inlined here rather than referenced as an exported\n // alias so the generated .d.ts stands alone without source imports.\n /* eslint-disable no-template-curly-in-string -- codegen output */\n const externalHref =\n '`http://${string}` | `https://${string}` | `mailto:${string}` | `tel:${string}` | `ftp://${string}` | `//${string}` | `#${string}` | `?${string}`';\n /* eslint-enable no-template-curly-in-string */\n\n lines.push(' // Typed Link overloads — catch-all (block 2 / emitted second)');\n lines.push(' interface LinkFunction {');\n\n // (1) External/literal-protocol hrefs.\n //\n // TIM-833: `segmentParams` is permissively typed as\n // `Record<string, unknown>` (not `never`) so generic wrappers that\n // forward a `LinkProps`-shaped object can compile when the wrapper\n // happens to bottom out at an external href. There are no dynamic\n // segments to interpolate for an external href, so this prop is\n // ignored at runtime — accepting an open-shape object instead of\n // forbidding it removes a footgun without changing behavior.\n lines.push(` (props: ${baseProps} & {`);\n lines.push(` href: ${externalHref}`);\n lines.push(` segmentParams?: Record<string, unknown>`);\n lines.push(` searchParams?: ${catchAllSearchParams}`);\n lines.push(` }): import('react').JSX.Element`);\n\n // (2) Computed/variable href — non-literal `string` only.\n //\n // `string extends H` is true only when H is the wide `string` type,\n // not a specific literal. For literal hrefs, this overload must NOT\n // be selectable so the per-route discriminated union (block 1) is\n // the resolution target.\n //\n // TIM-833 follow-up: the previous shape used `string extends H ? {...} : never`\n // on the WHOLE props type. For literal H, that collapsed `props` to\n // `never`, and JSX type inference then read `(never).children` as\n // `never` — producing the misleading\n // `'children' prop expects type 'never' which requires multiple\n // children, but only a single child was provided` (TS2745) error,\n // which buried the actual prop-mismatch message under a noise diagnostic.\n //\n // Fix: move the `: never` from the whole props onto just the `href`\n // field. The overload remains uncallable for literal H (since `\"/x\"`\n // is not assignable to `never`), but `children` retains its real\n // `ReactNode` type so JSX inference no longer collapses to never.\n // The diagnostic surface becomes: TS reports the per-route block's\n // error chain (the helpful \"Type 'number' is not assignable to type\n // 'string'\" message) WITHOUT a leading TS2745 noise line.\n lines.push(` <H extends string>(`);\n lines.push(` props: ${baseProps} & {`);\n lines.push(` href: string extends H ? H : never`);\n lines.push(` segmentParams?: Record<string, string | number | string[]>`);\n lines.push(` searchParams?: ${catchAllSearchParams}`);\n lines.push(` }`);\n lines.push(` ): import('react').JSX.Element`);\n\n lines.push(' }');\n return lines;\n}\n\n/**\n * Generate typed per-route Link call signatures via LinkFunction\n * interface merging.\n *\n * TIM-835: This emits a SINGLE call signature whose props is a\n * discriminated union keyed on `href`. TypeScript narrows the union by\n * the literal `href` at the call site, then checks the rest of the\n * props against the matched variant. When `segmentParams` or\n * `searchParams` is wrong, TS reports the error against the matched\n * variant — naming the user's actual `href` and the offending field.\n *\n * Before TIM-835, this function emitted N separate per-route overloads\n * (one per route, sometimes two for dynamic routes). When ALL overloads\n * failed, TS would pick an arbitrary failed overload (heuristically the\n * \"last tried\") to render the diagnostic, often pointing at a route\n * completely unrelated to the one the user wrote. The discriminated\n * union sidesteps overload-resolution heuristics entirely.\n *\n * Open property shapes (`Record<string, unknown>` instead of `never`)\n * for `segmentParams` on routes that don't declare them keep generic\n * `LinkProps`-spreading wrappers compiling. (Aligned with TIM-833.)\n *\n * TIM-832: this function still emits the per-route block FIRST and the\n * catch-all block follows, so per-route remains the LAST overload set in\n * resolution order — the one TS reports against on failure. With a\n * discriminated union there is only one call signature in this block,\n * so the resolved-template-vs-pattern ordering reduces to placing the\n * pattern variant before the resolved-template variant inside the union.\n */\nexport function formatTypedLinkOverloads(routes: RouteEntry[], hasSchema?: boolean): string[] {\n const lines: string[] = [];\n const baseProps = LINK_BASE_PROPS_TYPE;\n\n // Build the union variants. Each route contributes one variant for the\n // pattern href (e.g. '/products/[id]') and, for dynamic routes, an\n // additional variant for the resolved-template href (e.g.\n // `/products/${string}`).\n //\n // The PATTERN variant must be listed BEFORE the resolved-template\n // variant for the same route so that when the user writes the literal\n // pattern href (`<Link href=\"/products/[id]\" />`), TS narrows to the\n // pattern variant (which carries the typed `segmentParams` shape)\n // rather than the looser resolved-template variant. Without this\n // ordering, TS would silently match the resolved-template variant\n // first — swallowing typed-segmentParams type errors.\n const variants: string[] = [];\n for (const route of routes) {\n const hasDynamicParams = route.params.length > 0;\n // For routes with no dynamic params, accept an absent OR open-shape\n // segmentParams. `Record<string, unknown>` keeps generic spreads\n // compiling and gives a readable error if a non-object is passed.\n const paramsProp = hasDynamicParams\n ? `segmentParams: ${formatLinkParamsType(route.params, hasSchema)}`\n : 'segmentParams?: Record<string, unknown>';\n\n // TIM-1343: searchParams is no longer typed per-route. There is no\n // convention file to read a definition from, so codegen has nothing to\n // narrow with — the value is either a URLSearchParams built by\n // `definition.buildSearchParams()` (which type-checks its own\n // `Partial<T>` at that call) or a plain String()-coerced record.\n const searchParamsProp = `searchParams?: ${SEARCH_PARAMS_PROP_TYPE}`;\n\n // Pattern variant FIRST (more specific).\n variants.push(\n `${baseProps} & { href: '${route.urlPath}'; ${paramsProp}; ${searchParamsProp} }`\n );\n\n // Resolved-template variant SECOND (looser, matches interpolated hrefs).\n if (hasDynamicParams) {\n const templatePattern = buildResolvedPattern(route);\n if (templatePattern) {\n // TIM-835: `Record<string, never>` for segmentParams forbids ANY\n // provided keys on the resolved-template variant. Without this,\n // the resolved-template would shadow the pattern variant for\n // literal pattern hrefs (`<Link href=\"/products/[id]\" segmentParams={...} />`)\n // because both variants accept the literal `'/products/[id]'`\n // and the looser variant would silently swallow segmentParams\n // type errors.\n variants.push(\n `${baseProps} & { href: \\`${templatePattern}\\`; segmentParams?: Record<string, never>; ${searchParamsProp} }`\n );\n }\n }\n }\n\n lines.push(' interface LinkFunction {');\n if (variants.length === 0) {\n // No page routes — emit nothing. The catch-all block in the second\n // augmentation still provides external/computed-string signatures.\n lines.push(' }');\n return lines;\n }\n lines.push(' (');\n lines.push(' props:');\n for (const variant of variants) {\n lines.push(` | (${variant})`);\n }\n lines.push(` ): import('react').JSX.Element`);\n lines.push(' }');\n\n return lines;\n}\n","/**\n * Route map codegen.\n *\n * Walks the scanned RouteTree and generates a TypeScript declaration file\n * mapping every route to its params and searchParams shapes.\n *\n * This runs at build time and in dev (regenerated on file changes).\n * No runtime overhead — purely static type generation.\n */\n\nimport { existsSync } from 'node:fs';\nimport { join, relative } from 'node:path';\nimport type { RouteTree, SegmentNode } from './types.ts';\nimport type { ParamEntry, RouteEntry } from './codegen-types.ts';\nimport { buildSchemaParamType } from './codegen-shared.ts';\nimport { effectiveUrlSegment, type UrlSegmentIdentity } from './segment-classify.ts';\nimport { formatLinkCatchAllOverloads, formatTypedLinkOverloads } from './link-codegen.ts';\n\n/** Options for route map generation. */\nexport interface CodegenOptions {\n /** Absolute path to the app/ directory. Required for page searchParams detection. */\n appDir?: string;\n /**\n * Absolute path to the directory where the .d.ts file will be written.\n * Used to compute correct relative import paths for page files.\n * Defaults to appDir when not provided (preserves backward compat for tests).\n */\n outputDir?: string;\n /**\n * Whether a global schema (app/schema.ts) exists.\n * Auto-detected from appDir when not explicitly set.\n */\n hasSchema?: boolean;\n}\n\n/**\n * Generate a TypeScript declaration file string from a scanned route tree.\n *\n * The output is a `declare module '@timber-js/app'` block containing the Routes\n * interface that maps every route path to its params and searchParams shape.\n */\nexport function generateRouteMap(tree: RouteTree, options: CodegenOptions = {}): string {\n // Auto-detect schema from appDir when not explicitly set\n const hasSchema = options.hasSchema ?? detectSchema(options.appDir);\n\n const routes: RouteEntry[] = [];\n collectRoutes(tree.root, [], '', routes, hasSchema, false);\n\n // Sort routes alphabetically for deterministic output\n routes.sort((a, b) => a.urlPath.localeCompare(b.urlPath));\n\n // When outputDir differs from appDir, import paths must be relative to outputDir\n const importBase = options.outputDir ?? options.appDir;\n\n return formatDeclarationFile(routes, importBase, hasSchema, options.appDir);\n}\n\n/** Schema file extensions to check, in priority order. */\nconst SCHEMA_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx'];\n\n/**\n * Detect whether app/schema.{ts,tsx,js,jsx} exists.\n */\nfunction detectSchema(appDir: string | undefined): boolean {\n if (!appDir) return false;\n return SCHEMA_EXTENSIONS.some((ext) => existsSync(join(appDir, `schema${ext}`)));\n}\n\n/**\n * Recursively walk the segment tree and collect route entries.\n *\n * A route entry is created for any segment that has a `page` or `route` file.\n * Params accumulate from ancestor dynamic segments.\n */\nfunction collectRoutes(\n node: SegmentNode,\n ancestorParams: ParamEntry[],\n parentTreePath: string,\n routes: RouteEntry[],\n hasSchema: boolean,\n insideSlot: boolean,\n insideIntercepting = false\n): void {\n // Build the tree path (includes groups, slots — uniquely identifies this segment).\n // Root node has empty segmentName, producing treePath = ''.\n const treePath = node.segmentName ? `${parentTreePath}/${node.segmentName}` : parentTreePath;\n // Accumulate params from this segment.\n //\n // The segment is read through `effectiveUrlSegment` for the same reason the\n // runtime coercer is: an intercepting node's own `segmentType` is\n // 'intercepting' and its `paramName` is unset — the param it binds lives in\n // the intercepted name (`(.)[albumSlug]` → `[albumSlug]`). Reading the node\n // directly emitted `{}` for a segment the router does bind and coerce, so\n // the types disagreed with the values for every intercepting page with a\n // dynamic intercepted segment, and for its descendants, which lost the\n // ancestor param too.\n const urlSegment = effectiveUrlSegment(node);\n const params = [...ancestorParams];\n if (urlSegment.paramName) {\n params.push({\n name: urlSegment.paramName,\n bracketName: bracketNameForSegment(urlSegment),\n type: paramTypeForSegment(urlSegment.segmentType),\n });\n }\n\n // Check if this segment is a leaf route (has page or route file)\n const isPage = !!node.page;\n const isApiRoute = !!node.route;\n\n // A segment that hosts code but is not itself addressable — a layout,\n // middleware, or access check above the leaf — still gets a `$segment`\n // module, so `getSegmentParams(SEGMENT_PATH)` is callable from it and needs\n // a matching typed overload. Emitting only leaves left those call sites on\n // the broad `Record<string, string | string[]>` signature, which since\n // TIM-1342 (which deleted `defineSegmentParams`) is their ONLY typed path.\n // design/41-global-params.md's Type Summary promises layouts and middleware\n // exact accumulated params.\n //\n // `isRoutable: false` keeps them out of the urlPath-keyed emissions — the\n // `Routes` interface and `<Link>` variants — where they would publish a URL\n // that no request can reach. They belong only to the segmentPath-keyed\n // APIs: `getSegmentParams`, `useSegmentParams`, `TimberSegmentParams`.\n const hostsCode = !!node.layout || !!node.middleware || !!node.access;\n\n if (isPage || isApiRoute || hostsCode) {\n const entry: RouteEntry = {\n urlPath: node.urlPath,\n segmentPath: treePath || '/',\n params,\n isApiRoute,\n isRoutable: isPage || isApiRoute,\n isSlotRoute: insideSlot,\n isInterceptingRoute: insideIntercepting,\n hasSchema,\n };\n\n routes.push(entry);\n }\n\n // Recurse into children. An intercepting subtree is flagged, and the flag is\n // sticky — the descendants are where the false URLs are. See\n // `RouteEntry.isInterceptingRoute` for what the flag drops and why dedupe\n // alone was not enough.\n for (const child of node.children) {\n const childIntercepting = insideIntercepting || child.segmentType === 'intercepting';\n collectRoutes(child, params, treePath, routes, hasSchema, insideSlot, childIntercepting);\n }\n\n // Recurse into slots (they share the parent's URL path, but may have their own pages)\n for (const slot of Object.values(node.slots)) {\n collectRoutes(slot, params, treePath, routes, hasSchema, true, insideIntercepting);\n }\n}\n\n/**\n * Dedupe route entries by urlPath for urlPath-keyed emissions (the Routes\n * interface and typed Link variants).\n *\n * Parallel-slot pages share their parent's urlPath; emitting one entry per\n * slot page produced duplicate interface keys (TS2300/TS2717 under\n * skipLibCheck:false) and duplicate overloads. The non-slot entry is the\n * canonical one; when only slot entries exist (parent has no page), the\n * lexicographically smallest segmentPath wins so output is independent of\n * slot scan order.\n *\n * The loser is discarded outright. It used to donate its `searchParams`\n * definition to the winner, because a slot could carry one the parent page\n * lacked; since TIM-1343 no route entry carries a searchParams definition at\n * all, there is nothing left to merge.\n *\n * segmentPath-keyed emissions (getSegmentParams, useSegmentParams,\n * TimberSegmentParams) must NOT use this — segmentPaths are already unique\n * and slot entries feed those APIs directly.\n */\nfunction dedupeRoutesByUrlPath(routes: RouteEntry[]): RouteEntry[] {\n const byPath = new Map<string, RouteEntry>();\n for (const route of routes) {\n const existing = byPath.get(route.urlPath);\n if (!existing) {\n byPath.set(route.urlPath, route);\n continue;\n }\n\n const routeWins =\n (existing.isSlotRoute && !route.isSlotRoute) ||\n (!!existing.isSlotRoute === !!route.isSlotRoute &&\n route.segmentPath.localeCompare(existing.segmentPath) < 0);\n byPath.set(route.urlPath, routeWins ? route : existing);\n }\n return [...byPath.values()];\n}\n\n/**\n * Determine the TypeScript type for a segment's param.\n */\n/**\n * Derive the bracket key for schema/codegen lookup.\n * For bare segments like `[id]` this is the segmentName itself.\n * For affixed segments like `img-[id].png` this is `[id]` — the bare bracket form.\n */\nfunction bracketNameForSegment(node: UrlSegmentIdentity): string {\n if (node.paramPrefix || node.paramSuffix) {\n return `[${node.paramName}]`;\n }\n return node.segmentName;\n}\n\nfunction paramTypeForSegment(segmentType: string): ParamEntry['type'] {\n switch (segmentType) {\n case 'catch-all':\n return 'string[]';\n case 'optional-catch-all':\n return 'string[] | undefined';\n default:\n return 'string';\n }\n}\n\n/**\n * Emit the schema-based type exports: TimberSegmentParams, TimberRoute,\n * AllSegmentParams, RoutesWithParams.\n *\n * These types use InferCodecType references so they stay in sync with\n * the schema without hardcoding resolved types.\n *\n * Design doc: design/41-global-params.md §Codegen Output\n */\nfunction formatSchemaTypes(routes: RouteEntry[]): string[] {\n const lines: string[] = [];\n\n lines.push('/** Union of all valid route patterns. */');\n lines.push('export type TimberRoute = keyof TimberSegmentParams;');\n lines.push('');\n lines.push('/** Maps each segment path to its accumulated segment params. */');\n lines.push('export interface TimberSegmentParams {');\n\n for (const route of routes) {\n if (route.params.length === 0) {\n lines.push(` '${route.segmentPath}': {};`);\n } else {\n lines.push(` '${route.segmentPath}': {`);\n for (const p of route.params) {\n lines.push(` ${p.name}: ${buildSchemaParamType(p)};`);\n }\n lines.push(' };');\n }\n }\n\n lines.push('}');\n lines.push('');\n\n // AllSegmentParams — flat union of all param types across all routes\n lines.push('/** All segment params (union of all route params). */');\n lines.push('export type AllSegmentParams = {');\n lines.push(\n ' [K in keyof TimberSegmentParams as TimberSegmentParams[K] extends Record<string, never>'\n );\n lines.push(' ? never');\n lines.push(' : K]: TimberSegmentParams[K];');\n lines.push('} extends infer U');\n lines.push(' ? { [K in keyof U[keyof U]]: U[keyof U][K] }');\n lines.push(' : never;');\n lines.push('');\n\n // RoutesWithParams — filter routes by required param keys\n lines.push(\n '/** Filters TimberRoute to routes whose accumulated params include all listed keys. */'\n );\n lines.push('export type RoutesWithParams<K extends keyof AllSegmentParams> = {');\n lines.push(' [R in TimberRoute]: K extends keyof TimberSegmentParams[R] ? R : never;');\n lines.push('}[TimberRoute];');\n\n return lines;\n}\n\n/**\n * Format the collected routes into a TypeScript declaration file.\n */\nfunction formatDeclarationFile(\n routes: RouteEntry[],\n importBase?: string,\n hasSchema?: boolean,\n appDir?: string\n): string {\n const lines: string[] = [];\n\n lines.push('// This file is auto-generated by timber.js route map codegen.');\n lines.push('// Do not edit manually. Regenerated on build and in dev mode.');\n lines.push('');\n // export {} makes this file a module, so all declare module blocks are\n // augmentations rather than ambient replacements. Without this, the\n // declare module blocks would replace the original module types entirely\n // (removing exports like bindUseQueryStates that aren't listed here).\n lines.push('export {};');\n lines.push('');\n\n if (hasSchema) {\n // Schema-based codegen: import schema and InferCodecType\n // Compute the schema import path relative to the output directory\n const schemaImport =\n appDir && importBase\n ? './' + relative(importBase, join(appDir, 'schema')).replace(/\\\\/g, '/')\n : '../app/schema';\n lines.push(`import type schema from '${schemaImport}';`);\n lines.push(\"import type { InferCodecType, SegmentParamCodec } from '@timber-js/app/params';\");\n lines.push('');\n // Collect all unique bracket keys from routes for key validation\n const allBracketKeys = new Set<string>();\n for (const route of routes) {\n for (const p of route.params) {\n allBracketKeys.add(p.bracketName);\n }\n }\n\n // Augment the ValidDynamicSegmentKeysRegistry so defineSchema constrains keys\n // to known filesystem segments. Unknown keys produce inline TS errors.\n if (allBracketKeys.size > 0) {\n lines.push(\"declare module '@timber-js/app/params' {\");\n lines.push(' interface ValidDynamicSegmentKeysRegistry {');\n for (const key of [...allBracketKeys].sort()) {\n lines.push(` ${JSON.stringify(key)}: true;`);\n }\n lines.push(' }');\n lines.push('}');\n lines.push('');\n }\n // Guard: detect when the schema or params imports silently degraded to\n // `any` because TypeScript could not resolve the import specifier.\n // In a .d.ts an unresolved import does NOT error — every downstream\n // type silently becomes `any`, which satisfies every assertion. Only\n // an explicit IsAny check catches it.\n lines.push('type _IsAny<T> = 0 extends 1 & T ? true : false;');\n lines.push('type _AssertSchemaNotAny<_T extends false = _IsAny<typeof schema>> = true;');\n lines.push('type _AssertCodecTypeNotAny<_T extends false = _IsAny<SegmentParamCodec>> = true;');\n lines.push('');\n // Type-level validation: each schema value must be a Codec or Standard Schema.\n // Running `pnpm run typecheck` will surface errors for invalid values.\n lines.push('type _AssertSchemaValues = {');\n lines.push(\n \" [K in keyof typeof schema['segmentParams']]: typeof schema['segmentParams'][K] extends SegmentParamCodec ? true : ['Error: value for', K, 'is not a valid Codec or Standard Schema'];\"\n );\n lines.push('};');\n // Generic constraint enforces that all values are `true` — if any value is\n // an error tuple, TS reports a real diagnostic instead of silently computing it.\n lines.push(\n 'type _EnforceSchemaValues<_T extends Record<string, true> = _AssertSchemaValues> = true;'\n );\n lines.push('');\n }\n\n // urlPath-keyed emissions use the deduped list (slot pages share their\n // parent's urlPath); segmentPath-keyed emissions below keep all entries.\n //\n // Intercepting subtrees are dropped outright rather than deduped: their\n // computed urlPath is not a route any request can reach, so a path that\n // happens NOT to collide with a real one would otherwise be published as a\n // real route. See `RouteEntry.isInterceptingRoute`.\n const uniqueByUrlPath = dedupeRoutesByUrlPath(\n routes.filter((r) => r.isRoutable && !r.isInterceptingRoute)\n );\n\n lines.push(\"declare module '@timber-js/app' {\");\n lines.push(' interface Routes {');\n\n // TIM-1343: no `searchParams` member. With `params.ts` gone there is no\n // convention file to read a definition from, so the framework has nothing\n // to type it with — and an always-`{}` member is a claim that every route\n // has no search params, which is false. Search-param types now come from\n // the definition object itself, wherever the app chooses to keep it.\n for (const route of uniqueByUrlPath) {\n const paramsType = formatParamsType(route.params, hasSchema);\n\n lines.push(` '${route.urlPath}': {`);\n lines.push(` segmentParams: ${paramsType}`);\n lines.push(` }`);\n }\n\n lines.push(' }');\n\n // When a global schema exists, emit segment param types inside the\n // @timber-js/app module augmentation so that declare module blocks\n // for @timber-js/app/server and @timber-js/app/client can reference\n // AllSegmentParams via `import type { AllSegmentParams } from '@timber-js/app'`.\n if (hasSchema) {\n lines.push('');\n lines.push(...formatSchemaTypes(routes).map((l) => ` ${l}`));\n }\n\n lines.push('}');\n lines.push('');\n\n // useQueryStates + typed Link are urlPath-keyed → deduped list.\n const pageRoutes = uniqueByUrlPath.filter((r) => !r.isApiRoute);\n // getSegmentParams / useSegmentParams are segmentPath-keyed → full list,\n // so slot segments keep their own typed overloads.\n const dynamicRoutes = routes.filter((r) => r.params.length > 0);\n\n // Generate @timber-js/app/server augmentation — typed getSegmentParams\n if (dynamicRoutes.length > 0) {\n lines.push(\"declare module '@timber-js/app/server' {\");\n if (hasSchema) {\n lines.push(\" import type { AllSegmentParams } from '@timber-js/app'\");\n }\n for (const route of dynamicRoutes) {\n const paramsType = formatParamsType(route.params, hasSchema);\n lines.push(\n ` export function getSegmentParams(segmentPath: '${route.segmentPath}'): ${paramsType}`\n );\n }\n lines.push(\n hasSchema\n ? ' export function getSegmentParams(): Partial<AllSegmentParams>'\n : ' export function getSegmentParams(): Record<string, string | string[]>'\n );\n lines.push('}');\n lines.push('');\n }\n\n // Generate overloads for @timber-js/app/client\n\n if (dynamicRoutes.length > 0 || pageRoutes.length > 0) {\n lines.push(\"declare module '@timber-js/app/client' {\");\n lines.push(\" import type { SearchParamsDefinition } from '@timber-js/app/search-params'\");\n if (hasSchema) {\n lines.push(\" import type { AllSegmentParams } from '@timber-js/app'\");\n }\n lines.push('');\n\n // useSegmentParams overloads\n if (dynamicRoutes.length > 0) {\n for (const route of dynamicRoutes) {\n const paramsType = formatParamsType(route.params, hasSchema);\n lines.push(\n ` export function useSegmentParams(segmentPath: '${route.segmentPath}'): ${paramsType}`\n );\n }\n lines.push(\n hasSchema\n ? ' export function useSegmentParams(): Partial<AllSegmentParams>'\n : ' export function useSegmentParams(): Record<string, string | string[]>'\n );\n lines.push('');\n }\n\n // Typed Link overloads — per-route with DIRECT types (no conditionals).\n // Direct types preserve TypeScript's excess property checking.\n //\n // TIM-832: per-route and catch-all are emitted as TWO separate\n // augmentation blocks. Per TS's merging rule \"later overload sets\n // ordered first\", the catch-all block (declared SECOND in this file)\n // ends up FIRST in the merged call-signature list at resolution time,\n // which puts the per-route block LAST — so its error message is the\n // one TypeScript reports when no overload matches. This gives users a\n // clear prop-mismatch error (e.g. \"'string | undefined' is not\n // assignable to 'string | number' on id\") instead of the old\n // confusing \"Type 'string' is not assignable to type 'never'\" cascade.\n if (pageRoutes.length > 0) {\n lines.push(' // Typed Link overloads — per-route (block 1 / emitted first)');\n lines.push(...formatTypedLinkOverloads(pageRoutes, hasSchema));\n lines.push('');\n }\n\n lines.push('}');\n lines.push('');\n }\n\n // TIM-832: catch-all block — emitted as a SEPARATE `declare module`\n // augmentation so TS's \"later overload set first\" rule orders it ahead\n // of the per-route block above at resolution time, leaving per-route as\n // the \"last overload\" whose error TypeScript reports.\n lines.push(\"declare module '@timber-js/app/client' {\");\n lines.push(\" import type { SearchParamsDefinition } from '@timber-js/app/search-params'\");\n lines.push('');\n lines.push(...formatLinkCatchAllOverloads());\n lines.push('}');\n lines.push('');\n\n return lines.join('\\n');\n}\n\n/**\n * Format the params type for a route entry.\n */\nfunction formatParamsType(params: ParamEntry[], hasSchema?: boolean): string {\n if (params.length === 0) {\n return '{}';\n }\n\n // Without a schema a URL segment is an uninterpreted string — that IS\n // its type. Per-segment codec chains used to narrow it here; TIM-1342\n // deleted them along with params.ts.\n const fields = params.map((p) => {\n const codecType = hasSchema ? buildSchemaParamType(p) : p.type;\n return `${p.name}: ${codecType}`;\n });\n return `{ ${fields.join('; ')} }`;\n}\n\n// TIM-1341: `useQueryStates` no longer has a route-string form, so the\n// generated `.d.ts` emits no overloads for it. Its source signature —\n// `useQueryStates(codecs, options?)` — is fully generic over the codec map\n// and needs no per-route augmentation. Reaching another route's codecs is\n// done by importing that route's `searchParams` definition from its\n// `params.ts` and calling `definition.useQueryStates()`, which is typed by\n// inference and cannot fail at runtime on an unpopulated registry.\n// See design/23-search-params.md §\"Client Access\".\n\n// Link overload formatters and helpers (`formatTypedLinkOverloads`,\n// `formatLinkCatchAllOverloads`, `formatLinkParamsType`,\n// `buildResolvedPattern`, `LINK_BASE_PROPS_TYPE`) were extracted to\n// `./link-codegen.ts` (TIM-835) to keep this file under the 500-line\n// cap. They are imported at the top of this file.\n","import { readFileSync, statSync } from 'node:fs';\n\nconst cache = new Map<string, { mtimeMs: number; size: number; content: string }>();\n\n/**\n * Read a file's text content, returning a cached copy when the file\n * hasn't been modified since the last read (checked via mtime + size).\n *\n * Falls back to a fresh read on any stat/read error.\n */\nexport function readFileCached(filePath: string): string {\n try {\n const stat = statSync(filePath);\n const entry = cache.get(filePath);\n if (entry && entry.mtimeMs === stat.mtimeMs && entry.size === stat.size) return entry.content;\n const content = readFileSync(filePath, 'utf-8');\n cache.set(filePath, { mtimeMs: stat.mtimeMs, size: stat.size, content });\n return content;\n } catch {\n cache.delete(filePath);\n return readFileSync(filePath, 'utf-8');\n }\n}\n"],"mappings":"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"}
@@ -5,10 +5,10 @@
5
5
  /** Format a byte count as a human-readable string (e.g. "1.50 kB"). */
6
6
  function formatSize(bytes) {
7
7
  if (bytes < 1024) return `${bytes} B`;
8
- if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(2)} kB`;
9
- return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
8
+ if (bytes < 1048576) return `${(bytes / 1024).toFixed(2)} kB`;
9
+ return `${(bytes / 1048576).toFixed(2)} MB`;
10
10
  }
11
11
  //#endregion
12
12
  export { formatSize as t };
13
13
 
14
- //# sourceMappingURL=format-CfwjgPz9.js.map
14
+ //# sourceMappingURL=format-BKclgVbk.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"format-CfwjgPz9.js","names":[],"sources":["../../src/utils/format.ts"],"sourcesContent":["/**\n * Shared formatting utilities.\n */\n\n/** Format a byte count as a human-readable string (e.g. \"1.50 kB\"). */\nexport function formatSize(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(2)} kB`;\n return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;\n}\n"],"mappings":";;;;;AAKA,SAAgB,WAAW,OAAuB;CAChD,IAAI,QAAQ,MAAM,OAAO,GAAG,MAAM;CAClC,IAAI,QAAQ,OAAO,MAAM,OAAO,IAAI,QAAQ,KAAA,CAAM,QAAQ,CAAC,EAAE;CAC7D,OAAO,IAAI,SAAS,OAAO,MAAA,CAAO,QAAQ,CAAC,EAAE;AAC/C"}
1
+ {"version":3,"file":"format-BKclgVbk.js","names":[],"sources":["../../src/utils/format.ts"],"sourcesContent":["/**\n * Shared formatting utilities.\n */\n\n/** Format a byte count as a human-readable string (e.g. \"1.50 kB\"). */\nexport function formatSize(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(2)} kB`;\n return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;\n}\n"],"mappings":";;;;;AAKA,SAAgB,WAAW,OAAuB;CAChD,IAAI,QAAQ,MAAM,OAAO,GAAG,MAAM;CAClC,IAAI,QAAQ,SAAa,OAAO,IAAI,QAAQ,KAAA,CAAM,QAAQ,CAAC,EAAE;CAC7D,OAAO,IAAI,QAAS,QAAA,CAAc,QAAQ,CAAC,EAAE;AAC/C"}
@@ -0,0 +1,304 @@
1
+ import { i as scanProjectSourceFiles, t as buildSourceExtensions } from "./scan-BoP17W3w.js";
2
+ import { dirname, isAbsolute, join } from "node:path";
3
+ import { createRequire } from "node:module";
4
+ import { mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
5
+ import { Server } from "node:tls";
6
+ import { createHash, randomUUID } from "node:crypto";
7
+ import { mkdir, readFile, rename, stat, writeFile } from "node:fs/promises";
8
+ //#region src/plugins/dev-discovery.ts
9
+ /** Relative path of the discovery directory within the project root. */
10
+ var DEV_DISCOVERY_DIR = join(".timber", "dev");
11
+ /** Best-effort "is this pid alive" — signal 0 probes without killing. */
12
+ function isPidAlive(pid) {
13
+ try {
14
+ process.kill(pid, 0);
15
+ return true;
16
+ } catch (error) {
17
+ return error.code === "EPERM";
18
+ }
19
+ }
20
+ /**
21
+ * Remove sibling records whose pid is no longer alive. Purely a
22
+ * tidiness sweep — consumers already ignore records that fail the
23
+ * pid + probe validation, so missing a stale record here is harmless.
24
+ */
25
+ function sweepDeadRecords(dir) {
26
+ let entries;
27
+ try {
28
+ entries = readdirSync(dir);
29
+ } catch {
30
+ return;
31
+ }
32
+ for (const entry of entries) {
33
+ if (!entry.endsWith(".json")) continue;
34
+ const filePath = join(dir, entry);
35
+ try {
36
+ const record = JSON.parse(readFileSync(filePath, "utf8"));
37
+ if (typeof record.pid === "number" && !isPidAlive(record.pid)) rmSync(filePath, { force: true });
38
+ } catch {}
39
+ }
40
+ }
41
+ /**
42
+ * Register discovery-record lifecycle handlers on the dev server's HTTP
43
+ * server. Writes `.timber/dev/<pid>-<port>-<token>.json` once the server is
44
+ * listening (the bound port is only known then) and removes exactly
45
+ * that record on clean shutdown.
46
+ *
47
+ * All filesystem work is best-effort: a failure to write or remove a
48
+ * record must never break the dev server. `httpServer` is null in
49
+ * middleware mode — no port exists, so there is nothing to advertise.
50
+ * Typed as `net.Server` — the common base of Vite's `http.Server` and
51
+ * `Http2SecureServer` variants, which carries the events and `address()`.
52
+ */
53
+ function registerDevDiscovery(httpServer, root, config) {
54
+ if (!httpServer) return;
55
+ const dir = join(root, DEV_DISCOVERY_DIR);
56
+ let recordPath = null;
57
+ httpServer.on("listening", () => {
58
+ const address = httpServer.address();
59
+ if (address === null || typeof address === "string") return;
60
+ const hmrOpts = typeof config?.server?.hmr === "object" ? config.server.hmr : void 0;
61
+ const wsOpts = typeof config?.server?.ws === "object" ? config.server.ws : void 0;
62
+ const record = {
63
+ port: address.port,
64
+ pid: process.pid,
65
+ host: address.address,
66
+ protocol: httpServer instanceof Server ? "https" : "http",
67
+ base: config?.base && config.base !== "/" ? config.base : void 0,
68
+ hmrPath: wsOpts?.path ?? hmrOpts?.path,
69
+ hmrHost: wsOpts?.host ?? hmrOpts?.host,
70
+ hmrPort: wsOpts?.clientPort || wsOpts?.port || hmrOpts?.clientPort || hmrOpts?.port || void 0,
71
+ hmrProtocol: wsOpts?.protocol ?? hmrOpts?.protocol,
72
+ webSocketToken: config?.webSocketToken,
73
+ configFile: typeof config?.configFile === "string" ? config.configFile : void 0
74
+ };
75
+ recordPath = join(dir, `${record.pid}-${record.port}-${randomUUID()}.json`);
76
+ try {
77
+ mkdirSync(dir, { recursive: true });
78
+ writeFileSync(recordPath, JSON.stringify(record));
79
+ sweepDeadRecords(dir);
80
+ } catch {}
81
+ });
82
+ httpServer.on("close", () => {
83
+ if (!recordPath) return;
84
+ try {
85
+ rmSync(recordPath, { force: true });
86
+ } catch {}
87
+ });
88
+ }
89
+ /** Cache location under the app root. */
90
+ var CACHE_RELATIVE_PATH = join(".timber", "graph-cache.json");
91
+ /**
92
+ * Config/manifest files at the root that participate in the
93
+ * fingerprint. The `.env` family is included because a Vite config
94
+ * using `loadEnv(mode, …)` can select aliases, plugins, the app root,
95
+ * or pageExtensions from it — the disk scan skips dot-prefixed files,
96
+ * so without these entries an `.env.production` edit would never miss
97
+ * the cache (codex review, PR #1056). The build resolves under mode
98
+ * `production`; the development variants are tracked anyway — they are
99
+ * loadable through `loadEnv` with any mode argument, and an absent
100
+ * file contributes nothing.
101
+ */
102
+ var ROOT_MANIFEST_FILES = [
103
+ "package.json",
104
+ "tsconfig.json",
105
+ "timber.config.ts",
106
+ "timber.config.js",
107
+ "timber.config.mjs",
108
+ ".env",
109
+ ".env.local",
110
+ ".env.production",
111
+ ".env.production.local",
112
+ ".env.development",
113
+ ".env.development.local"
114
+ ];
115
+ /** Lockfile names, searched upward from the root (monorepo workspaces). */
116
+ var LOCKFILE_NAMES = [
117
+ "pnpm-lock.yaml",
118
+ "package-lock.json",
119
+ "yarn.lock",
120
+ "bun.lock",
121
+ "bun.lockb"
122
+ ];
123
+ /**
124
+ * Follow a tsconfig/jsconfig `extends` chain, returning every file on
125
+ * it (the start file included). A base config outside the app —
126
+ * `"extends": "../tsconfig.base.json"` with a `paths` mapping — can
127
+ * reshape Vite's resolution without touching anything else the
128
+ * fingerprint tracks. Parsing is a targeted regex, not JSONC: tsconfig
129
+ * allows comments, and all we need is the `extends` value. Package
130
+ * specifiers resolve through Node resolution (an approximation of
131
+ * TypeScript's — the `.json` append covers the common shapes): a
132
+ * WORKSPACE package like `@repo/tsconfig/base.json` is a link, so its
133
+ * edits change neither the lockfile nor any scanned file and must be
134
+ * fingerprinted directly; registry packages resolve too, harmlessly.
135
+ * Cycles and unresolvable targets just end the walk (bounded at 16).
136
+ */
137
+ /**
138
+ * Strip JSONC comments so a commented-out `"extends"` line cannot
139
+ * shadow the real one. String-aware: comment markers inside string
140
+ * literals (a path containing `//`) survive.
141
+ */
142
+ function stripJsonComments(raw) {
143
+ let out = "";
144
+ let inString = false;
145
+ for (let i = 0; i < raw.length; i++) {
146
+ const ch = raw[i];
147
+ if (inString) {
148
+ out += ch;
149
+ if (ch === "\\") {
150
+ out += raw[i + 1] ?? "";
151
+ i++;
152
+ } else if (ch === "\"") inString = false;
153
+ } else if (ch === "\"") {
154
+ inString = true;
155
+ out += ch;
156
+ } else if (ch === "/" && raw[i + 1] === "/") {
157
+ while (i < raw.length && raw[i] !== "\n") i++;
158
+ out += "\n";
159
+ } else if (ch === "/" && raw[i + 1] === "*") {
160
+ i += 2;
161
+ while (i < raw.length && !(raw[i] === "*" && raw[i + 1] === "/")) i++;
162
+ i++;
163
+ } else out += ch;
164
+ }
165
+ return out;
166
+ }
167
+ async function collectTsconfigChain(startFile) {
168
+ const chain = [];
169
+ const queue = [startFile];
170
+ const seen = new Set(queue);
171
+ while (queue.length > 0 && chain.length < 16) {
172
+ const current = queue.shift();
173
+ let raw;
174
+ try {
175
+ raw = stripJsonComments(await readFile(current, "utf8"));
176
+ } catch {
177
+ continue;
178
+ }
179
+ chain.push(current);
180
+ const match = raw.match(/"extends"\s*:\s*("([^"]+)"|\[([^\]]*)\])/);
181
+ if (!match) continue;
182
+ const targets = match[2] ? [match[2]] : (match[3]?.match(/"([^"]+)"/g) ?? []).map((t) => t.slice(1, -1));
183
+ for (const target of targets) {
184
+ let next = null;
185
+ if (isAbsolute(target)) {
186
+ next = target;
187
+ if (!/\.json$/.test(next)) next += ".json";
188
+ } else if (target.startsWith(".")) {
189
+ next = join(dirname(current), target);
190
+ if (!/\.json$/.test(next)) next += ".json";
191
+ } else {
192
+ const req = createRequire(current);
193
+ for (const candidate of [
194
+ target,
195
+ `${target}.json`,
196
+ `${target}/tsconfig.json`
197
+ ]) try {
198
+ next = req.resolve(candidate);
199
+ break;
200
+ } catch {}
201
+ }
202
+ if (next && !seen.has(next)) {
203
+ seen.add(next);
204
+ queue.push(next);
205
+ }
206
+ }
207
+ }
208
+ return chain;
209
+ }
210
+ /** Nearest lockfile at or above `root`, or null. */
211
+ async function findLockfile(root) {
212
+ let dir = root;
213
+ for (;;) {
214
+ for (const name of LOCKFILE_NAMES) {
215
+ const candidate = join(dir, name);
216
+ try {
217
+ await stat(candidate);
218
+ return candidate;
219
+ } catch {}
220
+ }
221
+ const parent = dirname(dir);
222
+ if (parent === dir) return null;
223
+ dir = parent;
224
+ }
225
+ }
226
+ /** `path\0mtime\0size` entries for a file list; absent files contribute nothing. */
227
+ async function fingerprintEntriesForFiles(files) {
228
+ const entries = [];
229
+ for (const file of new Set(files)) try {
230
+ const stats = await stat(file);
231
+ entries.push(`${file}\0${stats.mtimeMs}\0${stats.size}`);
232
+ } catch {}
233
+ return entries;
234
+ }
235
+ /**
236
+ * Entries for the scan-derived base set: project source files, the
237
+ * config, root manifests, and the nearest lockfile. Scan failures are
238
+ * folded into the entry list rather than thrown: an unreadable
239
+ * directory changes the answer's completeness, so it must change the
240
+ * key too.
241
+ */
242
+ async function collectBaseFingerprintEntries(root, configFile, pageExtensions) {
243
+ const extensions = buildSourceExtensions(pageExtensions);
244
+ const sourceFiles = [];
245
+ const failures = [];
246
+ await scanProjectSourceFiles(root, sourceFiles, (failure) => failures.push(`${failure.dir}:${failure.message}`), 0, extensions);
247
+ const tracked = [
248
+ ...sourceFiles,
249
+ configFile,
250
+ ...ROOT_MANIFEST_FILES.map((f) => join(root, f))
251
+ ];
252
+ const configDir = dirname(configFile);
253
+ if (configDir !== root) tracked.push(...ROOT_MANIFEST_FILES.map((f) => join(configDir, f)));
254
+ for (const name of ["tsconfig.json", "jsconfig.json"]) {
255
+ tracked.push(...await collectTsconfigChain(join(root, name)));
256
+ if (configDir !== root) tracked.push(...await collectTsconfigChain(join(configDir, name)));
257
+ }
258
+ const lockfile = await findLockfile(root);
259
+ if (lockfile) tracked.push(lockfile);
260
+ return [...failures, ...await fingerprintEntriesForFiles(tracked)];
261
+ }
262
+ /** Hash an entry list order-independently. */
263
+ function hashFingerprintEntries(entries) {
264
+ const unique = [...new Set(entries)].sort();
265
+ return createHash("sha256").update(unique.join("\n")).digest("hex");
266
+ }
267
+ /**
268
+ * Fingerprint the project state `timber graph`'s answers depend on:
269
+ * the scan-derived base set plus `extraFiles` (the cache's
270
+ * trackedExtras on the hit path; config dependencies + report files on
271
+ * the write path).
272
+ */
273
+ async function computeProjectFingerprint(root, configFile, extraFiles = [], pageExtensions) {
274
+ const entries = await collectBaseFingerprintEntries(root, configFile, pageExtensions);
275
+ entries.push(...await fingerprintEntriesForFiles(extraFiles));
276
+ return hashFingerprintEntries(entries);
277
+ }
278
+ /** Read the cache file; anything unreadable or version-mismatched is a miss. */
279
+ async function readGraphCache(root) {
280
+ try {
281
+ const raw = await readFile(join(root, CACHE_RELATIVE_PATH), "utf8");
282
+ const parsed = JSON.parse(raw);
283
+ if (parsed.version !== 2 || typeof parsed.fingerprint !== "string" || typeof parsed.configFile !== "string" || !Array.isArray(parsed.trackedExtras) || typeof parsed.report !== "object" || parsed.report === null) return null;
284
+ return parsed;
285
+ } catch {
286
+ return null;
287
+ }
288
+ }
289
+ /**
290
+ * Write the cache atomically (temp file + rename) so a concurrent
291
+ * reader — parallel agent invocations are the expected workload —
292
+ * never sees a torn JSON file.
293
+ */
294
+ async function writeGraphCache(root, cache) {
295
+ const target = join(root, CACHE_RELATIVE_PATH);
296
+ await mkdir(dirname(target), { recursive: true });
297
+ const temp = `${target}.${process.pid}.tmp`;
298
+ await writeFile(temp, JSON.stringify(cache));
299
+ await rename(temp, target);
300
+ }
301
+ //#endregion
302
+ export { readGraphCache as a, isPidAlive as c, hashFingerprintEntries as i, registerDevDiscovery as l, computeProjectFingerprint as n, writeGraphCache as o, fingerprintEntriesForFiles as r, DEV_DISCOVERY_DIR as s, collectBaseFingerprintEntries as t };
303
+
304
+ //# sourceMappingURL=graph-cache-CP4GEmf9.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graph-cache-CP4GEmf9.js","names":[],"sources":["../../src/plugins/dev-discovery.ts","../../src/analyze/graph-cache.ts"],"sourcesContent":["/**\n * Dev-server discovery records — `.timber/dev/<pid>-<port>-<token>.json`.\n *\n * On startup each dev server writes `{ port, pid, host, protocol }` to its own record\n * file under the app's `.timber/dev/` so the `timber graph` CLI and the\n * VS Code extension can discover a running dev server for the\n * live-graph fast path.\n *\n * One record file per server, one writer per file. This is what makes\n * multi-server roots (Vite auto-increments the port across processes;\n * programmatic embeddings run several servers in one process) safe\n * without any coordination: there is no shared file to displace, no\n * ownership to arbitrate, and no shutdown-order dependency — each\n * server removes exactly its own record.\n *\n * Consumers must list the directory and trust a record only if its pid\n * is alive AND a probe request to the endpoint succeeds; anything else\n * is treated as absent (silent fallback), never an error. Stale records\n * (crashed servers) are therefore the normal case — startup sweeps\n * dead-pid records best-effort, and consumers validate the rest away.\n *\n * The directory layout and file format are implementation details, not\n * a public contract.\n *\n * Design doc: 47-module-environment-tooling.md §5, 21-dev-server.md.\n */\n\nimport type { Server } from 'node:net';\nimport { Server as TlsServer } from 'node:tls';\nimport { randomUUID } from 'node:crypto';\nimport { mkdirSync, writeFileSync, rmSync, readFileSync, readdirSync } from 'node:fs';\nimport { join } from 'node:path';\n\n/** Relative path of the discovery directory within the project root. */\nexport const DEV_DISCOVERY_DIR = join('.timber', 'dev');\n\nexport interface DevRecord {\n port: number;\n pid: number;\n /**\n * The bound address, as reported by the socket. Consumers probing the\n * endpoint need it for servers bound to non-default interfaces;\n * wildcard addresses (`0.0.0.0`, `::`) should be probed via loopback.\n */\n host: string;\n /**\n * Probe scheme. `server.https` dev servers (including HTTP/2) answer\n * only TLS — an http:// probe of one would fail and wrongly trigger\n * the cold-crawl fallback.\n */\n protocol: 'http' | 'https';\n /**\n * Vite `base` path. When non-root (e.g. `/my-app/`), the WebSocket\n * upgrade path is `base + hmrPath`.\n */\n base?: string;\n /**\n * HMR WebSocket path (from `server.ws.path` or deprecated\n * `server.hmr.path`). Absent when no path is configured (default `/`).\n */\n hmrPath?: string;\n /**\n * Custom WebSocket host override (from `server.ws.host` or deprecated\n * `server.hmr.host`). When set, the WS connection targets this host\n * instead of the HTTP server's bound address.\n */\n hmrHost?: string;\n /**\n * Custom WebSocket port override (from `server.ws.clientPort` ??\n * `server.ws.port`, or deprecated `server.hmr.clientPort` ??\n * `server.hmr.port`). When set, the WS connection targets this port\n * instead of the HTTP server's bound port.\n */\n hmrPort?: number;\n /**\n * Custom WebSocket protocol override (from `server.ws.protocol` or\n * deprecated `server.hmr.protocol`). When set (e.g. `'wss'`), the\n * upgrade request uses the corresponding HTTP scheme.\n */\n hmrProtocol?: string;\n /**\n * Vite 8 WebSocket token (from `config.webSocketToken`). Required for\n * browser WS upgrades; the extension passes it as `?token=` on the\n * WebSocket URL.\n */\n webSocketToken?: string;\n /**\n * Absolute path to the Vite config file this server was started with.\n * When multiple dev servers share a project root but use different\n * configs, the fast path must only accept the record matching the\n * config the CLI resolved (TIM-1393).\n */\n configFile?: string;\n}\n\n/** Best-effort \"is this pid alive\" — signal 0 probes without killing. */\nexport function isPidAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch (error) {\n // EPERM means the process exists but is owned by someone else.\n return (error as NodeJS.ErrnoException).code === 'EPERM';\n }\n}\n\n/**\n * Remove sibling records whose pid is no longer alive. Purely a\n * tidiness sweep — consumers already ignore records that fail the\n * pid + probe validation, so missing a stale record here is harmless.\n */\nfunction sweepDeadRecords(dir: string): void {\n let entries: string[];\n try {\n entries = readdirSync(dir);\n } catch {\n return;\n }\n for (const entry of entries) {\n if (!entry.endsWith('.json')) continue;\n const filePath = join(dir, entry);\n try {\n const record = JSON.parse(readFileSync(filePath, 'utf8')) as Partial<DevRecord>;\n // Remove only records that parse AND name a dead pid. An\n // unreadable file may be a sibling's record mid-write — leave it;\n // consumers ignore records that fail validation anyway.\n if (typeof record.pid === 'number' && !isPidAlive(record.pid)) {\n rmSync(filePath, { force: true });\n }\n } catch {\n // Unreadable — skip, never delete another writer's in-flight file.\n }\n }\n}\n\n/**\n * Register discovery-record lifecycle handlers on the dev server's HTTP\n * server. Writes `.timber/dev/<pid>-<port>-<token>.json` once the server is\n * listening (the bound port is only known then) and removes exactly\n * that record on clean shutdown.\n *\n * All filesystem work is best-effort: a failure to write or remove a\n * record must never break the dev server. `httpServer` is null in\n * middleware mode — no port exists, so there is nothing to advertise.\n * Typed as `net.Server` — the common base of Vite's `http.Server` and\n * `Http2SecureServer` variants, which carries the events and `address()`.\n */\nexport function registerDevDiscovery(\n httpServer: Server | null,\n root: string,\n config?: {\n configFile?: string | false;\n base?: string;\n server?: {\n hmr?:\n | { path?: string; host?: string; port?: number; clientPort?: number; protocol?: string }\n | boolean;\n ws?:\n | { path?: string; host?: string; port?: number; clientPort?: number; protocol?: string }\n | false;\n };\n webSocketToken?: string;\n }\n): void {\n if (!httpServer) return;\n\n const dir = join(root, DEV_DISCOVERY_DIR);\n let recordPath: string | null = null;\n\n httpServer.on('listening', () => {\n const address = httpServer.address();\n // Unix-socket listeners have a string address — no port to advertise.\n if (address === null || typeof address === 'string') return;\n const hmrOpts = typeof config?.server?.hmr === 'object' ? config.server.hmr : undefined;\n const wsOpts = typeof config?.server?.ws === 'object' ? config.server.ws : undefined;\n const record: DevRecord = {\n port: address.port,\n pid: process.pid,\n host: address.address,\n // Vite's https/h2 servers are tls.Server subclasses; plain HTTP\n // and h2c are not.\n protocol: httpServer instanceof TlsServer ? 'https' : 'http',\n base: config?.base && config.base !== '/' ? config.base : undefined,\n hmrPath: wsOpts?.path ?? hmrOpts?.path,\n hmrHost: wsOpts?.host ?? hmrOpts?.host,\n hmrPort:\n wsOpts?.clientPort || wsOpts?.port || hmrOpts?.clientPort || hmrOpts?.port || undefined,\n hmrProtocol: wsOpts?.protocol ?? hmrOpts?.protocol,\n webSocketToken: config?.webSocketToken,\n configFile: typeof config?.configFile === 'string' ? config.configFile : undefined,\n };\n // pid+port alone is not a unique server identity (same port on two\n // interfaces; worker threads and duplicate module copies share a\n // pid but not module state, so a counter cannot disambiguate\n // either). A random token makes the filename unique regardless of\n // process/isolate topology; pid and port stay in the name only for\n // human debuggability.\n recordPath = join(dir, `${record.pid}-${record.port}-${randomUUID()}.json`);\n try {\n mkdirSync(dir, { recursive: true });\n writeFileSync(recordPath, JSON.stringify(record));\n sweepDeadRecords(dir);\n } catch {\n // Best-effort — consumers fall back to the cold crawl.\n }\n });\n\n httpServer.on('close', () => {\n if (!recordPath) return;\n try {\n // Only ever this server's own record — sibling servers' records\n // are separate files and survive any shutdown order untouched.\n rmSync(recordPath, { force: true });\n } catch {\n // Best-effort — a stale record is validated away by consumers.\n }\n });\n}\n","/**\n * graph-cache — the mtime-keyed output cache for `timber graph`.\n *\n * The cold crawl costs seconds (design/47 §6) and its cost is setup +\n * dependency compilation, not project-file count — so the cacheable\n * unit is the classification OUTPUT, never Vite's transforms. One\n * crawl per project state: the first question after a clone pays for\n * the crawl, every subsequent question is a cache hit until the\n * project changes.\n *\n * Validity is a fingerprint over (path, mtime, size) of every project\n * source file — the same disk walk the crawl itself uses (scan.ts), so\n * cache coverage and crawl coverage cannot drift — plus the config\n * files and the nearest lockfile, since dependency changes change\n * resolution (export conditions can flip a file's environment without\n * touching project code).\n *\n * Deliberately vite-free: a cache hit answers without importing Vite.\n *\n * Design docs: 47-module-environment-tooling.md §\"Cold-path policy\".\n */\n\nimport { createHash } from 'node:crypto';\nimport { createRequire } from 'node:module';\nimport { mkdir, readFile, rename, stat, writeFile } from 'node:fs/promises';\nimport { dirname, isAbsolute, join } from 'node:path';\nimport { buildSourceExtensions, scanProjectSourceFiles } from './scan.ts';\nimport type { CrawlReport } from './crawl.ts';\n\n/**\n * JSON contract version (design/47 §9) — one constant for every\n * frontend (CLI and dev-server endpoint), so the two cannot drift.\n * Lives here rather than graph-command.ts because this module is\n * vite-free and both frontends already depend on the analyze core.\n */\nexport const GRAPH_JSON_VERSION = 1;\n\n/** Bump when the report shape or fingerprint inputs change. */\nexport const GRAPH_CACHE_VERSION = 2;\n\n/** Cache location under the app root. */\nconst CACHE_RELATIVE_PATH = join('.timber', 'graph-cache.json');\n\n/**\n * Config/manifest files at the root that participate in the\n * fingerprint. The `.env` family is included because a Vite config\n * using `loadEnv(mode, …)` can select aliases, plugins, the app root,\n * or pageExtensions from it — the disk scan skips dot-prefixed files,\n * so without these entries an `.env.production` edit would never miss\n * the cache (codex review, PR #1056). The build resolves under mode\n * `production`; the development variants are tracked anyway — they are\n * loadable through `loadEnv` with any mode argument, and an absent\n * file contributes nothing.\n */\nconst ROOT_MANIFEST_FILES = [\n 'package.json',\n 'tsconfig.json',\n 'timber.config.ts',\n 'timber.config.js',\n 'timber.config.mjs',\n '.env',\n '.env.local',\n '.env.production',\n '.env.production.local',\n '.env.development',\n '.env.development.local',\n];\n\n/** Lockfile names, searched upward from the root (monorepo workspaces). */\nconst LOCKFILE_NAMES = [\n 'pnpm-lock.yaml',\n 'package-lock.json',\n 'yarn.lock',\n 'bun.lock',\n 'bun.lockb',\n];\n\nexport interface GraphCacheFile {\n version: number;\n fingerprint: string;\n /** The Vite config file the cached crawl booted with. */\n configFile: string;\n /**\n * The resolved pageExtensions from the timber config at crawl time.\n * Stored so the vite-free hit path can reproduce the same scan\n * extension set (custom extensions like 'foo' must be admitted on\n * both the write and the hit path, or the fingerprint disagrees\n * with what was cached).\n */\n pageExtensions?: string[];\n /**\n * Files the fingerprint tracks BEYOND the disk scan, discovered at\n * crawl time: Vite's `configFileDependencies` (a config helper\n * imported from outside the app root can reshape the whole app) and\n * every file the report itself classifies (the graph can reach files\n * the scan skips — `app/.well-known/page.tsx`, a route under a\n * directory named `dist`). The hit path re-stats exactly this list,\n * so an edit to any of them misses the cache.\n */\n trackedExtras: string[];\n report: CrawlReport;\n}\n\n/**\n * Follow a tsconfig/jsconfig `extends` chain, returning every file on\n * it (the start file included). A base config outside the app —\n * `\"extends\": \"../tsconfig.base.json\"` with a `paths` mapping — can\n * reshape Vite's resolution without touching anything else the\n * fingerprint tracks. Parsing is a targeted regex, not JSONC: tsconfig\n * allows comments, and all we need is the `extends` value. Package\n * specifiers resolve through Node resolution (an approximation of\n * TypeScript's — the `.json` append covers the common shapes): a\n * WORKSPACE package like `@repo/tsconfig/base.json` is a link, so its\n * edits change neither the lockfile nor any scanned file and must be\n * fingerprinted directly; registry packages resolve too, harmlessly.\n * Cycles and unresolvable targets just end the walk (bounded at 16).\n */\n/**\n * Strip JSONC comments so a commented-out `\"extends\"` line cannot\n * shadow the real one. String-aware: comment markers inside string\n * literals (a path containing `//`) survive.\n */\nexport function stripJsonComments(raw: string): string {\n let out = '';\n let inString = false;\n for (let i = 0; i < raw.length; i++) {\n const ch = raw[i];\n if (inString) {\n out += ch;\n if (ch === '\\\\') {\n out += raw[i + 1] ?? '';\n i++;\n } else if (ch === '\"') {\n inString = false;\n }\n } else if (ch === '\"') {\n inString = true;\n out += ch;\n } else if (ch === '/' && raw[i + 1] === '/') {\n while (i < raw.length && raw[i] !== '\\n') i++;\n out += '\\n';\n } else if (ch === '/' && raw[i + 1] === '*') {\n i += 2;\n while (i < raw.length && !(raw[i] === '*' && raw[i + 1] === '/')) i++;\n i++;\n } else {\n out += ch;\n }\n }\n return out;\n}\n\nasync function collectTsconfigChain(startFile: string): Promise<string[]> {\n const chain: string[] = [];\n const queue = [startFile];\n const seen = new Set(queue);\n while (queue.length > 0 && chain.length < 16) {\n const current = queue.shift()!;\n let raw: string;\n try {\n raw = stripJsonComments(await readFile(current, 'utf8'));\n } catch {\n continue;\n }\n chain.push(current);\n const match = raw.match(/\"extends\"\\s*:\\s*(\"([^\"]+)\"|\\[([^\\]]*)\\])/);\n if (!match) continue;\n const targets = match[2]\n ? [match[2]]\n : (match[3]?.match(/\"([^\"]+)\"/g) ?? []).map((t) => t.slice(1, -1));\n for (const target of targets) {\n let next: string | null = null;\n if (isAbsolute(target)) {\n // An absolute extends target names the base directly — joining\n // it under the current config's directory would fabricate a\n // path that does not exist and drop the real base.\n next = target;\n if (!/\\.json$/.test(next)) next += '.json';\n } else if (target.startsWith('.')) {\n next = join(dirname(current), target);\n if (!/\\.json$/.test(next)) next += '.json';\n } else {\n const req = createRequire(current);\n for (const candidate of [target, `${target}.json`, `${target}/tsconfig.json`]) {\n try {\n next = req.resolve(candidate);\n break;\n } catch {\n // try the next shape\n }\n }\n }\n if (next && !seen.has(next)) {\n seen.add(next);\n queue.push(next);\n }\n }\n }\n return chain;\n}\n\n/** Nearest lockfile at or above `root`, or null. */\nasync function findLockfile(root: string): Promise<string | null> {\n let dir = root;\n for (;;) {\n for (const name of LOCKFILE_NAMES) {\n const candidate = join(dir, name);\n try {\n await stat(candidate);\n return candidate;\n } catch {\n // keep looking\n }\n }\n const parent = dirname(dir);\n if (parent === dir) return null;\n dir = parent;\n }\n}\n\n/** `path\\0mtime\\0size` entries for a file list; absent files contribute nothing. */\nexport async function fingerprintEntriesForFiles(files: Iterable<string>): Promise<string[]> {\n const entries: string[] = [];\n for (const file of new Set(files)) {\n try {\n const stats = await stat(file);\n entries.push(`${file}\\0${stats.mtimeMs}\\0${stats.size}`);\n } catch {\n // Absent files simply contribute nothing; when one appears\n // later, its entry appears and the fingerprint changes.\n }\n }\n return entries;\n}\n\n/**\n * Entries for the scan-derived base set: project source files, the\n * config, root manifests, and the nearest lockfile. Scan failures are\n * folded into the entry list rather than thrown: an unreadable\n * directory changes the answer's completeness, so it must change the\n * key too.\n */\nexport async function collectBaseFingerprintEntries(\n root: string,\n configFile: string,\n pageExtensions?: readonly string[]\n): Promise<string[]> {\n const extensions = buildSourceExtensions(pageExtensions);\n const sourceFiles: string[] = [];\n const failures: string[] = [];\n await scanProjectSourceFiles(\n root,\n sourceFiles,\n (failure) => failures.push(`${failure.dir}:${failure.message}`),\n 0,\n extensions\n );\n const tracked = [...sourceFiles, configFile, ...ROOT_MANIFEST_FILES.map((f) => join(root, f))];\n // Manifest/.env files beside the CONFIG too, when it lives outside\n // the app root — `loadEnv(mode, process.cwd())` in a monorepo config\n // reads the .env next to the config, not the one under root.\n const configDir = dirname(configFile);\n if (configDir !== root) {\n tracked.push(...ROOT_MANIFEST_FILES.map((f) => join(configDir, f)));\n }\n // ts/jsconfig extends chains: a base config outside the app can carry\n // the `paths` mappings resolution depends on.\n for (const name of ['tsconfig.json', 'jsconfig.json']) {\n tracked.push(...(await collectTsconfigChain(join(root, name))));\n if (configDir !== root) tracked.push(...(await collectTsconfigChain(join(configDir, name))));\n }\n const lockfile = await findLockfile(root);\n if (lockfile) tracked.push(lockfile);\n return [...failures, ...(await fingerprintEntriesForFiles(tracked))];\n}\n\n/** Hash an entry list order-independently. */\nexport function hashFingerprintEntries(entries: string[]): string {\n const unique = [...new Set(entries)].sort();\n return createHash('sha256').update(unique.join('\\n')).digest('hex');\n}\n\n/**\n * Fingerprint the project state `timber graph`'s answers depend on:\n * the scan-derived base set plus `extraFiles` (the cache's\n * trackedExtras on the hit path; config dependencies + report files on\n * the write path).\n */\nexport async function computeProjectFingerprint(\n root: string,\n configFile: string,\n extraFiles: readonly string[] = [],\n pageExtensions?: readonly string[]\n): Promise<string> {\n const entries = await collectBaseFingerprintEntries(root, configFile, pageExtensions);\n entries.push(...(await fingerprintEntriesForFiles(extraFiles)));\n return hashFingerprintEntries(entries);\n}\n\n/** Read the cache file; anything unreadable or version-mismatched is a miss. */\nexport async function readGraphCache(root: string): Promise<GraphCacheFile | null> {\n try {\n const raw = await readFile(join(root, CACHE_RELATIVE_PATH), 'utf8');\n const parsed = JSON.parse(raw) as Partial<GraphCacheFile>;\n if (\n parsed.version !== GRAPH_CACHE_VERSION ||\n typeof parsed.fingerprint !== 'string' ||\n typeof parsed.configFile !== 'string' ||\n !Array.isArray(parsed.trackedExtras) ||\n typeof parsed.report !== 'object' ||\n parsed.report === null\n ) {\n return null;\n }\n return parsed as GraphCacheFile;\n } catch {\n return null;\n }\n}\n\n/**\n * Write the cache atomically (temp file + rename) so a concurrent\n * reader — parallel agent invocations are the expected workload —\n * never sees a torn JSON file.\n */\nexport async function writeGraphCache(root: string, cache: GraphCacheFile): Promise<void> {\n const target = join(root, CACHE_RELATIVE_PATH);\n await mkdir(dirname(target), { recursive: true });\n const temp = `${target}.${process.pid}.tmp`;\n await writeFile(temp, JSON.stringify(cache));\n await rename(temp, target);\n}\n"],"mappings":"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"}