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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (372) hide show
  1. package/dist/_chunks/actions-CQ8Z8VGL.js +1061 -0
  2. package/dist/_chunks/actions-CQ8Z8VGL.js.map +1 -0
  3. package/dist/_chunks/build-output-helper-DXnW0qjz.js +61 -0
  4. package/dist/_chunks/build-output-helper-DXnW0qjz.js.map +1 -0
  5. package/dist/_chunks/{define-Itxvcd7F.js → define-B-Q_UMOD.js} +19 -23
  6. package/dist/_chunks/define-B-Q_UMOD.js.map +1 -0
  7. package/dist/_chunks/{define-C77ScO0m.js → define-CfBPoJb0.js} +24 -7
  8. package/dist/_chunks/define-CfBPoJb0.js.map +1 -0
  9. package/dist/_chunks/define-cookie-BjpIt4UC.js +194 -0
  10. package/dist/_chunks/define-cookie-BjpIt4UC.js.map +1 -0
  11. package/dist/_chunks/{format-CYBGxKtc.js → format-Bcn-Iv1x.js} +1 -1
  12. package/dist/_chunks/{format-CYBGxKtc.js.map → format-Bcn-Iv1x.js.map} +1 -1
  13. package/dist/_chunks/handler-store-B-lqaGyh.js +54 -0
  14. package/dist/_chunks/handler-store-B-lqaGyh.js.map +1 -0
  15. package/dist/_chunks/logger-0m8MsKdc.js +291 -0
  16. package/dist/_chunks/logger-0m8MsKdc.js.map +1 -0
  17. package/dist/_chunks/merge-search-params-BphMdht_.js +122 -0
  18. package/dist/_chunks/merge-search-params-BphMdht_.js.map +1 -0
  19. package/dist/_chunks/{metadata-routes-DS3eKNmf.js → metadata-routes-BU684ls2.js} +1 -1
  20. package/dist/_chunks/{metadata-routes-DS3eKNmf.js.map → metadata-routes-BU684ls2.js.map} +1 -1
  21. package/dist/_chunks/navigation-root-BCYczjml.js +96 -0
  22. package/dist/_chunks/navigation-root-BCYczjml.js.map +1 -0
  23. package/dist/_chunks/registry-I2ss-lvy.js +20 -0
  24. package/dist/_chunks/registry-I2ss-lvy.js.map +1 -0
  25. package/dist/_chunks/router-ref-h3-UaCQv.js +28 -0
  26. package/dist/_chunks/router-ref-h3-UaCQv.js.map +1 -0
  27. package/dist/_chunks/{schema-bridge-C3xl_vfb.js → schema-bridge-Cxu4l-7p.js} +1 -1
  28. package/dist/_chunks/{schema-bridge-C3xl_vfb.js.map → schema-bridge-Cxu4l-7p.js.map} +1 -1
  29. package/dist/_chunks/segment-classify-BjfuctV2.js +137 -0
  30. package/dist/_chunks/segment-classify-BjfuctV2.js.map +1 -0
  31. package/dist/_chunks/{segment-context-fHFLF1PE.js → segment-context-Dx_OizxD.js} +1 -1
  32. package/dist/_chunks/{segment-context-fHFLF1PE.js.map → segment-context-Dx_OizxD.js.map} +1 -1
  33. package/dist/_chunks/{router-ref-C8OCm7g7.js → ssr-data-B4CdH7rE.js} +2 -26
  34. package/dist/_chunks/ssr-data-B4CdH7rE.js.map +1 -0
  35. package/dist/_chunks/{stale-reload-BX5gL1r-.js → stale-reload-Bab885FO.js} +1 -1
  36. package/dist/_chunks/{stale-reload-BX5gL1r-.js.map → stale-reload-Bab885FO.js.map} +1 -1
  37. package/dist/_chunks/tracing-C8V-YGsP.js +329 -0
  38. package/dist/_chunks/tracing-C8V-YGsP.js.map +1 -0
  39. package/dist/_chunks/{use-query-states-BiV5GJgm.js → use-query-states-B2XTqxDR.js} +3 -19
  40. package/dist/_chunks/use-query-states-B2XTqxDR.js.map +1 -0
  41. package/dist/_chunks/{use-params-IOPu7E8t.js → use-segment-params-BkpKAQ7D.js} +9 -95
  42. package/dist/_chunks/use-segment-params-BkpKAQ7D.js.map +1 -0
  43. package/dist/_chunks/{interception-BbqMCVXa.js → walkers-Tg0Alwcg.js} +66 -87
  44. package/dist/_chunks/walkers-Tg0Alwcg.js.map +1 -0
  45. package/dist/_chunks/{dev-warnings-DpGRGoDi.js → warnings-Cg47l5sk.js} +3 -3
  46. package/dist/_chunks/warnings-Cg47l5sk.js.map +1 -0
  47. package/dist/adapters/build-output-helper.d.ts +28 -0
  48. package/dist/adapters/build-output-helper.d.ts.map +1 -0
  49. package/dist/adapters/cloudflare.d.ts.map +1 -1
  50. package/dist/adapters/cloudflare.js +8 -28
  51. package/dist/adapters/cloudflare.js.map +1 -1
  52. package/dist/adapters/nitro.d.ts.map +1 -1
  53. package/dist/adapters/nitro.js +63 -31
  54. package/dist/adapters/nitro.js.map +1 -1
  55. package/dist/adapters/shared.d.ts +16 -0
  56. package/dist/adapters/shared.d.ts.map +1 -0
  57. package/dist/cache/index.js +9 -2
  58. package/dist/cache/index.js.map +1 -1
  59. package/dist/cache/timber-cache.d.ts.map +1 -1
  60. package/dist/client/error-boundary.js +2 -1
  61. package/dist/client/error-boundary.js.map +1 -1
  62. package/dist/client/form.d.ts +10 -24
  63. package/dist/client/form.d.ts.map +1 -1
  64. package/dist/client/index.d.ts +1 -5
  65. package/dist/client/index.d.ts.map +1 -1
  66. package/dist/client/index.js +41 -91
  67. package/dist/client/index.js.map +1 -1
  68. package/dist/client/internal.d.ts +2 -1
  69. package/dist/client/internal.d.ts.map +1 -1
  70. package/dist/client/internal.js +81 -7
  71. package/dist/client/internal.js.map +1 -1
  72. package/dist/client/rsc-fetch.d.ts.map +1 -1
  73. package/dist/client/state.d.ts +1 -1
  74. package/dist/client/use-cookie.d.ts +8 -0
  75. package/dist/client/use-cookie.d.ts.map +1 -1
  76. package/dist/client/{use-params.d.ts → use-segment-params.d.ts} +1 -1
  77. package/dist/client/use-segment-params.d.ts.map +1 -0
  78. package/dist/codec.d.ts +1 -1
  79. package/dist/codec.d.ts.map +1 -1
  80. package/dist/codec.js +2 -2
  81. package/dist/config-types.d.ts +28 -0
  82. package/dist/config-types.d.ts.map +1 -1
  83. package/dist/cookies/define-cookie.d.ts +87 -35
  84. package/dist/cookies/define-cookie.d.ts.map +1 -1
  85. package/dist/cookies/index.d.ts +2 -1
  86. package/dist/cookies/index.d.ts.map +1 -1
  87. package/dist/cookies/index.js +48 -2
  88. package/dist/cookies/index.js.map +1 -0
  89. package/dist/cookies/json-cookie.d.ts +64 -0
  90. package/dist/cookies/json-cookie.d.ts.map +1 -0
  91. package/dist/cookies/validation.d.ts +46 -0
  92. package/dist/cookies/validation.d.ts.map +1 -0
  93. package/dist/{plugins/dev-404-page.d.ts → dev-tools/404-page.d.ts} +9 -19
  94. package/dist/dev-tools/404-page.d.ts.map +1 -0
  95. package/dist/{plugins/dev-browser-logs.d.ts → dev-tools/browser-logs.d.ts} +1 -1
  96. package/dist/dev-tools/browser-logs.d.ts.map +1 -0
  97. package/dist/{plugins/dev-error-page.d.ts → dev-tools/error-page.d.ts} +2 -2
  98. package/dist/dev-tools/error-page.d.ts.map +1 -0
  99. package/dist/{server/dev-holding-server.d.ts → dev-tools/holding-server.d.ts} +5 -3
  100. package/dist/dev-tools/holding-server.d.ts.map +1 -0
  101. package/dist/dev-tools/index.d.ts +31 -0
  102. package/dist/dev-tools/index.d.ts.map +1 -0
  103. package/dist/{server/dev-span-processor.d.ts → dev-tools/instrumentation.d.ts} +26 -6
  104. package/dist/dev-tools/instrumentation.d.ts.map +1 -0
  105. package/dist/{server/dev-logger.d.ts → dev-tools/logger.d.ts} +1 -1
  106. package/dist/dev-tools/logger.d.ts.map +1 -0
  107. package/dist/{plugins/dev-logs.d.ts → dev-tools/logs.d.ts} +1 -1
  108. package/dist/dev-tools/logs.d.ts.map +1 -0
  109. package/dist/{plugins/dev-error-overlay.d.ts → dev-tools/overlay.d.ts} +3 -12
  110. package/dist/dev-tools/overlay.d.ts.map +1 -0
  111. package/dist/dev-tools/stack-classifier.d.ts +34 -0
  112. package/dist/dev-tools/stack-classifier.d.ts.map +1 -0
  113. package/dist/{plugins/dev-terminal-error.d.ts → dev-tools/terminal.d.ts} +2 -2
  114. package/dist/dev-tools/terminal.d.ts.map +1 -0
  115. package/dist/{server/dev-warnings.d.ts → dev-tools/warnings.d.ts} +1 -1
  116. package/dist/dev-tools/warnings.d.ts.map +1 -0
  117. package/dist/index.d.ts +1 -0
  118. package/dist/index.d.ts.map +1 -1
  119. package/dist/index.js +285 -133
  120. package/dist/index.js.map +1 -1
  121. package/dist/plugin-context.d.ts +1 -1
  122. package/dist/plugin-context.d.ts.map +1 -1
  123. package/dist/plugins/adapter-build.d.ts.map +1 -1
  124. package/dist/plugins/build-report.d.ts +6 -4
  125. package/dist/plugins/build-report.d.ts.map +1 -1
  126. package/dist/routing/convention-lint.d.ts.map +1 -1
  127. package/dist/routing/index.d.ts +5 -3
  128. package/dist/routing/index.d.ts.map +1 -1
  129. package/dist/routing/index.js +3 -3
  130. package/dist/routing/scanner.d.ts +1 -10
  131. package/dist/routing/scanner.d.ts.map +1 -1
  132. package/dist/routing/segment-classify.d.ts +37 -8
  133. package/dist/routing/segment-classify.d.ts.map +1 -1
  134. package/dist/routing/status-file-lint.d.ts.map +1 -1
  135. package/dist/routing/types.d.ts +63 -23
  136. package/dist/routing/types.d.ts.map +1 -1
  137. package/dist/routing/walkers.d.ts +51 -0
  138. package/dist/routing/walkers.d.ts.map +1 -0
  139. package/dist/search-params/define.d.ts +25 -7
  140. package/dist/search-params/define.d.ts.map +1 -1
  141. package/dist/search-params/index.js +5 -3
  142. package/dist/search-params/index.js.map +1 -1
  143. package/dist/search-params/wrappers.d.ts +2 -2
  144. package/dist/search-params/wrappers.d.ts.map +1 -1
  145. package/dist/segment-params/define.d.ts +23 -6
  146. package/dist/segment-params/define.d.ts.map +1 -1
  147. package/dist/segment-params/index.js +1 -1
  148. package/dist/server/access-gate.d.ts +4 -3
  149. package/dist/server/access-gate.d.ts.map +1 -1
  150. package/dist/server/action-handler.d.ts +15 -6
  151. package/dist/server/action-handler.d.ts.map +1 -1
  152. package/dist/server/als-registry.d.ts +5 -5
  153. package/dist/server/als-registry.d.ts.map +1 -1
  154. package/dist/server/asset-headers.d.ts +1 -15
  155. package/dist/server/asset-headers.d.ts.map +1 -1
  156. package/dist/server/cookie-context.d.ts +170 -0
  157. package/dist/server/cookie-context.d.ts.map +1 -0
  158. package/dist/server/cookie-parsing.d.ts +51 -0
  159. package/dist/server/cookie-parsing.d.ts.map +1 -0
  160. package/dist/server/deny-boundary.d.ts +90 -0
  161. package/dist/server/deny-boundary.d.ts.map +1 -0
  162. package/dist/server/deny-renderer.d.ts.map +1 -1
  163. package/dist/server/early-hints-sender.d.ts.map +1 -1
  164. package/dist/server/html-injector-core.d.ts +212 -0
  165. package/dist/server/html-injector-core.d.ts.map +1 -0
  166. package/dist/server/html-injectors.d.ts +59 -59
  167. package/dist/server/html-injectors.d.ts.map +1 -1
  168. package/dist/server/index.d.ts +5 -4
  169. package/dist/server/index.d.ts.map +1 -1
  170. package/dist/server/index.js +4 -149
  171. package/dist/server/index.js.map +1 -1
  172. package/dist/server/internal.d.ts +6 -4
  173. package/dist/server/internal.d.ts.map +1 -1
  174. package/dist/server/internal.js +852 -852
  175. package/dist/server/internal.js.map +1 -1
  176. package/dist/server/logger.d.ts +14 -0
  177. package/dist/server/logger.d.ts.map +1 -1
  178. package/dist/server/middleware-runner.d.ts +17 -0
  179. package/dist/server/middleware-runner.d.ts.map +1 -1
  180. package/dist/server/node-stream-transforms.d.ts +46 -49
  181. package/dist/server/node-stream-transforms.d.ts.map +1 -1
  182. package/dist/server/param-coercion.d.ts +26 -0
  183. package/dist/server/param-coercion.d.ts.map +1 -0
  184. package/dist/server/pipeline-helpers.d.ts +95 -0
  185. package/dist/server/pipeline-helpers.d.ts.map +1 -0
  186. package/dist/server/pipeline-outcome.d.ts +49 -0
  187. package/dist/server/pipeline-outcome.d.ts.map +1 -0
  188. package/dist/server/pipeline-phases.d.ts +52 -0
  189. package/dist/server/pipeline-phases.d.ts.map +1 -0
  190. package/dist/server/pipeline.d.ts +51 -32
  191. package/dist/server/pipeline.d.ts.map +1 -1
  192. package/dist/server/port-resolution.d.ts +117 -0
  193. package/dist/server/port-resolution.d.ts.map +1 -0
  194. package/dist/server/request-context.d.ts +22 -159
  195. package/dist/server/request-context.d.ts.map +1 -1
  196. package/dist/server/route-element-builder.d.ts.map +1 -1
  197. package/dist/server/route-matcher.d.ts +20 -47
  198. package/dist/server/route-matcher.d.ts.map +1 -1
  199. package/dist/server/rsc-entry/action-middleware-runner.d.ts +66 -0
  200. package/dist/server/rsc-entry/action-middleware-runner.d.ts.map +1 -0
  201. package/dist/server/rsc-entry/helpers.d.ts +1 -1
  202. package/dist/server/rsc-entry/helpers.d.ts.map +1 -1
  203. package/dist/server/rsc-entry/index.d.ts.map +1 -1
  204. package/dist/server/rsc-entry/render-route.d.ts +50 -0
  205. package/dist/server/rsc-entry/render-route.d.ts.map +1 -0
  206. package/dist/server/rsc-entry/wrap-action-dispatch.d.ts +119 -0
  207. package/dist/server/rsc-entry/wrap-action-dispatch.d.ts.map +1 -0
  208. package/dist/server/state-tree-diff.d.ts.map +1 -1
  209. package/dist/server/status-code-resolver.d.ts +16 -11
  210. package/dist/server/status-code-resolver.d.ts.map +1 -1
  211. package/dist/server/tracing.d.ts +1 -1
  212. package/dist/server/tracing.d.ts.map +1 -1
  213. package/dist/server/tree-builder.d.ts +45 -16
  214. package/dist/server/tree-builder.d.ts.map +1 -1
  215. package/dist/server/types.d.ts +48 -0
  216. package/dist/server/types.d.ts.map +1 -1
  217. package/dist/server/utils/escape-html.d.ts +14 -0
  218. package/dist/server/utils/escape-html.d.ts.map +1 -0
  219. package/dist/shims/headers.d.ts +2 -2
  220. package/dist/shims/headers.d.ts.map +1 -1
  221. package/dist/shims/navigation-client.d.ts +3 -1
  222. package/dist/shims/navigation-client.d.ts.map +1 -1
  223. package/dist/shims/navigation.d.ts +9 -4
  224. package/dist/shims/navigation.d.ts.map +1 -1
  225. package/dist/utils/directive-parser.d.ts +0 -45
  226. package/dist/utils/directive-parser.d.ts.map +1 -1
  227. package/package.json +1 -1
  228. package/src/adapters/build-output-helper.ts +77 -0
  229. package/src/adapters/cloudflare.ts +10 -50
  230. package/src/adapters/nitro.ts +66 -50
  231. package/src/adapters/shared.ts +40 -0
  232. package/src/cache/timber-cache.ts +3 -2
  233. package/src/client/form.tsx +17 -25
  234. package/src/client/index.ts +16 -9
  235. package/src/client/internal.ts +3 -2
  236. package/src/client/router.ts +1 -1
  237. package/src/client/rsc-fetch.ts +15 -0
  238. package/src/client/state.ts +2 -2
  239. package/src/client/use-cookie.ts +29 -0
  240. package/src/codec.ts +3 -7
  241. package/src/config-types.ts +28 -0
  242. package/src/cookies/define-cookie.ts +271 -78
  243. package/src/cookies/index.ts +11 -8
  244. package/src/cookies/json-cookie.ts +105 -0
  245. package/src/cookies/validation.ts +134 -0
  246. package/src/{plugins/dev-404-page.ts → dev-tools/404-page.ts} +17 -48
  247. package/src/{plugins/dev-error-page.ts → dev-tools/error-page.ts} +5 -32
  248. package/src/{server/dev-holding-server.ts → dev-tools/holding-server.ts} +4 -2
  249. package/src/dev-tools/index.ts +90 -0
  250. package/src/dev-tools/instrumentation.ts +176 -0
  251. package/src/{plugins/dev-logs.ts → dev-tools/logs.ts} +2 -2
  252. package/src/{plugins/dev-error-overlay.ts → dev-tools/overlay.ts} +5 -23
  253. package/src/dev-tools/stack-classifier.ts +75 -0
  254. package/src/{plugins/dev-terminal-error.ts → dev-tools/terminal.ts} +4 -38
  255. package/src/{server/dev-warnings.ts → dev-tools/warnings.ts} +1 -1
  256. package/src/index.ts +95 -34
  257. package/src/plugin-context.ts +1 -1
  258. package/src/plugins/adapter-build.ts +3 -1
  259. package/src/plugins/build-report.ts +13 -22
  260. package/src/plugins/dev-server.ts +3 -3
  261. package/src/plugins/routing.ts +14 -12
  262. package/src/plugins/shims.ts +1 -1
  263. package/src/plugins/static-build.ts +1 -1
  264. package/src/routing/codegen.ts +1 -1
  265. package/src/routing/convention-lint.ts +9 -8
  266. package/src/routing/index.ts +5 -3
  267. package/src/routing/interception.ts +1 -1
  268. package/src/routing/scanner.ts +22 -95
  269. package/src/routing/segment-classify.ts +107 -8
  270. package/src/routing/status-file-lint.ts +7 -5
  271. package/src/routing/types.ts +63 -23
  272. package/src/routing/walkers.ts +90 -0
  273. package/src/search-params/define.ts +71 -15
  274. package/src/search-params/wrappers.ts +9 -2
  275. package/src/segment-params/define.ts +66 -13
  276. package/src/server/access-gate.tsx +9 -8
  277. package/src/server/action-handler.ts +34 -38
  278. package/src/server/als-registry.ts +5 -5
  279. package/src/server/asset-headers.ts +8 -34
  280. package/src/server/cookie-context.ts +468 -0
  281. package/src/server/cookie-parsing.ts +135 -0
  282. package/src/server/{deny-page-resolver.ts → deny-boundary.ts} +78 -14
  283. package/src/server/deny-renderer.ts +7 -12
  284. package/src/server/early-hints-sender.ts +3 -2
  285. package/src/server/fallback-error.ts +2 -2
  286. package/src/server/html-injector-core.ts +403 -0
  287. package/src/server/html-injectors.ts +158 -297
  288. package/src/server/index.ts +13 -14
  289. package/src/server/internal.ts +10 -3
  290. package/src/server/logger.ts +23 -0
  291. package/src/server/middleware-runner.ts +44 -0
  292. package/src/server/node-stream-transforms.ts +108 -248
  293. package/src/server/param-coercion.ts +76 -0
  294. package/src/server/pipeline-helpers.ts +204 -0
  295. package/src/server/pipeline-outcome.ts +167 -0
  296. package/src/server/pipeline-phases.ts +409 -0
  297. package/src/server/pipeline.ts +70 -540
  298. package/src/server/port-resolution.ts +215 -0
  299. package/src/server/request-context.ts +46 -451
  300. package/src/server/route-element-builder.ts +8 -4
  301. package/src/server/route-matcher.ts +28 -60
  302. package/src/server/rsc-entry/action-middleware-runner.ts +167 -0
  303. package/src/server/rsc-entry/api-handler.ts +2 -2
  304. package/src/server/rsc-entry/error-renderer.ts +2 -2
  305. package/src/server/rsc-entry/helpers.ts +2 -7
  306. package/src/server/rsc-entry/index.ts +81 -366
  307. package/src/server/rsc-entry/render-route.ts +304 -0
  308. package/src/server/rsc-entry/rsc-payload.ts +1 -1
  309. package/src/server/rsc-entry/ssr-renderer.ts +2 -2
  310. package/src/server/rsc-entry/wrap-action-dispatch.ts +449 -0
  311. package/src/server/sitemap-generator.ts +1 -1
  312. package/src/server/slot-resolver.ts +1 -1
  313. package/src/server/ssr-entry.ts +1 -1
  314. package/src/server/state-tree-diff.ts +4 -1
  315. package/src/server/status-code-resolver.ts +112 -128
  316. package/src/server/tracing.ts +3 -3
  317. package/src/server/tree-builder.ts +134 -56
  318. package/src/server/types.ts +52 -0
  319. package/src/server/utils/escape-html.ts +20 -0
  320. package/src/shims/headers.ts +3 -3
  321. package/src/shims/navigation-client.ts +4 -3
  322. package/src/shims/navigation.ts +9 -7
  323. package/src/utils/directive-parser.ts +0 -392
  324. package/dist/_chunks/actions-DLnUaR65.js +0 -421
  325. package/dist/_chunks/actions-DLnUaR65.js.map +0 -1
  326. package/dist/_chunks/als-registry-HS0LGUl2.js +0 -41
  327. package/dist/_chunks/als-registry-HS0LGUl2.js.map +0 -1
  328. package/dist/_chunks/debug-ECi_61pb.js +0 -108
  329. package/dist/_chunks/debug-ECi_61pb.js.map +0 -1
  330. package/dist/_chunks/define-C77ScO0m.js.map +0 -1
  331. package/dist/_chunks/define-Itxvcd7F.js.map +0 -1
  332. package/dist/_chunks/define-cookie-BowvzoP0.js +0 -94
  333. package/dist/_chunks/define-cookie-BowvzoP0.js.map +0 -1
  334. package/dist/_chunks/dev-warnings-DpGRGoDi.js.map +0 -1
  335. package/dist/_chunks/interception-BbqMCVXa.js.map +0 -1
  336. package/dist/_chunks/merge-search-params-Cm_KIWDX.js +0 -41
  337. package/dist/_chunks/merge-search-params-Cm_KIWDX.js.map +0 -1
  338. package/dist/_chunks/request-context-CK5tZqIP.js +0 -478
  339. package/dist/_chunks/request-context-CK5tZqIP.js.map +0 -1
  340. package/dist/_chunks/router-ref-C8OCm7g7.js.map +0 -1
  341. package/dist/_chunks/segment-classify-BDNn6EzD.js +0 -65
  342. package/dist/_chunks/segment-classify-BDNn6EzD.js.map +0 -1
  343. package/dist/_chunks/tracing-CCYbKn5n.js +0 -238
  344. package/dist/_chunks/tracing-CCYbKn5n.js.map +0 -1
  345. package/dist/_chunks/use-params-IOPu7E8t.js.map +0 -1
  346. package/dist/_chunks/use-query-states-BiV5GJgm.js.map +0 -1
  347. package/dist/client/use-params.d.ts.map +0 -1
  348. package/dist/plugins/dev-404-page.d.ts.map +0 -1
  349. package/dist/plugins/dev-browser-logs.d.ts.map +0 -1
  350. package/dist/plugins/dev-error-overlay.d.ts.map +0 -1
  351. package/dist/plugins/dev-error-page.d.ts.map +0 -1
  352. package/dist/plugins/dev-logs.d.ts.map +0 -1
  353. package/dist/plugins/dev-terminal-error.d.ts.map +0 -1
  354. package/dist/server/deny-page-resolver.d.ts +0 -52
  355. package/dist/server/deny-page-resolver.d.ts.map +0 -1
  356. package/dist/server/dev-fetch-instrumentation.d.ts +0 -22
  357. package/dist/server/dev-fetch-instrumentation.d.ts.map +0 -1
  358. package/dist/server/dev-holding-server.d.ts.map +0 -1
  359. package/dist/server/dev-logger.d.ts.map +0 -1
  360. package/dist/server/dev-span-processor.d.ts.map +0 -1
  361. package/dist/server/dev-warnings.d.ts.map +0 -1
  362. package/dist/server/manifest-status-resolver.d.ts +0 -58
  363. package/dist/server/manifest-status-resolver.d.ts.map +0 -1
  364. package/dist/server/page-deny-boundary.d.ts +0 -31
  365. package/dist/server/page-deny-boundary.d.ts.map +0 -1
  366. package/src/server/dev-fetch-instrumentation.ts +0 -96
  367. package/src/server/dev-span-processor.ts +0 -78
  368. package/src/server/manifest-status-resolver.ts +0 -215
  369. package/src/server/page-deny-boundary.tsx +0 -56
  370. /package/src/client/{use-params.ts → use-segment-params.ts} +0 -0
  371. /package/src/{plugins/dev-browser-logs.ts → dev-tools/browser-logs.ts} +0 -0
  372. /package/src/{server/dev-logger.ts → dev-tools/logger.ts} +0 -0
@@ -0,0 +1,77 @@
1
+ // Shared build-output helper for adapter buildOutput() methods.
2
+ //
3
+ // Steps 1–5 of every adapter's buildOutput are platform-agnostic:
4
+ // 1. Copy client assets to the public/static directory
5
+ // 2. Write _headers file for static asset cache control
6
+ // 3. Copy RSC + SSR server bundles
7
+ // 4. Write manifest-init module (if present)
8
+ //
9
+ // Only the final steps (entry codegen, platform config) vary per adapter.
10
+ //
11
+ // IMPORTANT: This module must remain a leaf — no imports from ../server/,
12
+ // ../client/, or Vite-dependent code. See adapters/shared.ts header.
13
+
14
+ import { writeFile, mkdir, cp } from 'node:fs/promises';
15
+ import { join } from 'node:path';
16
+ import { generateHeadersFile } from './shared.js';
17
+ import type { TimberConfig } from './types';
18
+
19
+ /** Options for the shared build output steps. */
20
+ export interface BuildOutputBaseOptions {
21
+ /** Resolved timber config. */
22
+ config: TimberConfig;
23
+ /** Root build directory (e.g. `.timber/build`). */
24
+ buildDir: string;
25
+ /** Adapter-specific output directory (e.g. `.timber/build/nitro`). */
26
+ outDir: string;
27
+ /**
28
+ * Name of the public/static directory within outDir.
29
+ * Nitro uses 'public', Cloudflare uses 'static'.
30
+ */
31
+ publicDirName: string;
32
+ }
33
+
34
+ /**
35
+ * Run the platform-agnostic build output steps shared by all adapters:
36
+ *
37
+ * 1. Create the output directory
38
+ * 2. Copy client assets to the public/static directory
39
+ * 3. Write the `_headers` file for static asset cache control
40
+ * 4. Copy RSC and SSR server bundles into the output directory
41
+ * 5. Write the manifest-init module if present
42
+ *
43
+ * Returns the resolved public directory path for further adapter-specific use.
44
+ */
45
+ export async function runSharedBuildSteps(opts: BuildOutputBaseOptions): Promise<string> {
46
+ const { config, buildDir, outDir, publicDirName } = opts;
47
+
48
+ // 1. Create the output directory.
49
+ await mkdir(outDir, { recursive: true });
50
+
51
+ // 2. Copy client assets to public/static directory.
52
+ // When client JavaScript is disabled, skip .js files — only CSS,
53
+ // fonts, images, and other static assets are needed.
54
+ const clientDir = join(buildDir, 'client');
55
+ const publicDir = join(outDir, publicDirName);
56
+ await mkdir(publicDir, { recursive: true });
57
+ await cp(clientDir, publicDir, {
58
+ recursive: true,
59
+ filter: config.clientJavascriptDisabled ? (src: string) => !src.endsWith('.js') : undefined,
60
+ }).catch(() => {
61
+ // Client dir may not exist when client JavaScript is disabled
62
+ });
63
+
64
+ // 3. Write _headers file for static asset cache control.
65
+ await writeFile(join(publicDir, '_headers'), generateHeadersFile());
66
+
67
+ // 4. Copy RSC + SSR server bundles into the output directory.
68
+ await cp(join(buildDir, 'rsc'), join(outDir, 'rsc'), { recursive: true });
69
+ await cp(join(buildDir, 'ssr'), join(outDir, 'ssr'), { recursive: true }).catch(() => {});
70
+
71
+ // 5. Write manifest-init module if present.
72
+ if (config.manifestInit) {
73
+ await writeFile(join(outDir, '_timber-manifest-init.js'), config.manifestInit);
74
+ }
75
+
76
+ return publicDir;
77
+ }
@@ -3,28 +3,12 @@
3
3
  // Primary deployment target. Generates a Workers-compatible entry point
4
4
  // and wrangler.jsonc configuration. See design/11-platform.md §"Cloudflare Workers".
5
5
 
6
- import { writeFile, mkdir, cp } from 'node:fs/promises';
6
+ import { writeFile } from 'node:fs/promises';
7
7
  import { execFile } from 'node:child_process';
8
8
  import { join, relative } from 'node:path';
9
9
  import { AsyncLocalStorage } from 'node:async_hooks';
10
10
  import type { TimberPlatformAdapter, TimberConfig } from './types';
11
- // Inlined from server/asset-headers.ts — adapters are loaded by Node at
12
- // Vite startup time, before Vite's module resolver is available, so cross-
13
- // directory .ts imports don't resolve.
14
- const IMMUTABLE_CACHE = 'public, max-age=31536000, immutable';
15
- const STATIC_CACHE = 'public, max-age=3600, must-revalidate';
16
-
17
- function generateHeadersFile(): string {
18
- return `# Auto-generated by @timber-js/app — static asset cache headers.
19
- # See design/25-production-deployments.md §"CDN / Edge Cache"
20
-
21
- /assets/*
22
- Cache-Control: ${IMMUTABLE_CACHE}
23
-
24
- /*
25
- Cache-Control: ${STATIC_CACHE}
26
- `;
27
- }
11
+ import { runSharedBuildSteps } from './build-output-helper.js';
28
12
 
29
13
  // ─── Bindings passthrough ─────────────────────────────────────────────────
30
14
  // ALS stores the env object per-request so server components and middleware
@@ -243,39 +227,15 @@ export function cloudflare(options: CloudflareAdapterOptions = {}): TimberPlatfo
243
227
 
244
228
  async buildOutput(config: TimberConfig, buildDir: string) {
245
229
  const outDir = join(buildDir, 'cloudflare');
246
- await mkdir(outDir, { recursive: true });
247
-
248
- // Copy client assets to static output.
249
- // When client JavaScript is disabled, skip .js files — only CSS,
250
- // fonts, images, and other static assets are needed.
251
- const clientDir = join(buildDir, 'client');
252
- const staticDir = join(outDir, 'static');
253
- await mkdir(staticDir, { recursive: true });
254
- await cp(clientDir, staticDir, {
255
- recursive: true,
256
- filter: config.clientJavascriptDisabled ? (src: string) => !src.endsWith('.js') : undefined,
257
- }).catch(() => {
258
- // Client dir may not exist when client JavaScript is disabled
259
- });
260
230
 
261
- // Write _headers file for static asset cache control.
262
- // Cloudflare Workers Static Assets reads this to set Cache-Control
263
- // headers on responses. Hashed assets get immutable; others get 1h.
264
- await writeFile(join(staticDir, '_headers'), generateHeadersFile());
265
-
266
- // Copy server bundles (rsc + ssr) into the output directory.
267
- // These are already fully bundled by Vite with resolve.noExternal: true.
268
- const rscDir = join(buildDir, 'rsc');
269
- const ssrDir = join(buildDir, 'ssr');
270
- await cp(rscDir, join(outDir, 'rsc'), { recursive: true });
271
- await cp(ssrDir, join(outDir, 'ssr'), { recursive: true });
272
-
273
- // Write the build manifest init module (if manifest data was produced).
274
- // This must be imported before the RSC handler so the global is set
275
- // when virtual:timber-build-manifest evaluates.
276
- if (config.manifestInit) {
277
- await writeFile(join(outDir, '_timber-manifest-init.js'), config.manifestInit);
278
- }
231
+ // Steps 1–5: shared across all adapters (mkdir, copy client/rsc/ssr,
232
+ // write _headers, write manifest-init).
233
+ await runSharedBuildSteps({
234
+ config,
235
+ buildDir,
236
+ outDir,
237
+ publicDirName: 'static',
238
+ });
279
239
 
280
240
  // Compile optional worker handlers (queue, scheduled, etc.)
281
241
  // Uses Vite's build API to bundle the TypeScript source into ESM.
@@ -5,28 +5,13 @@
5
5
  // compression, graceful shutdown, static file serving, and platform quirks.
6
6
  // See design/11-platform.md and design/25-production-deployments.md.
7
7
 
8
- import { writeFile, readFile, mkdir, cp } from 'node:fs/promises';
8
+ import { writeFile, readFile } from 'node:fs/promises';
9
9
  import { execFile } from 'node:child_process';
10
10
  import { join, relative } from 'node:path';
11
11
  import type { TimberPlatformAdapter, TimberConfig } from './types';
12
12
  import { generateCompressModule } from './compress-module.js';
13
- // Inlined from server/asset-headers.ts — adapters are loaded by Node at
14
- // Vite startup time, before Vite's module resolver is available, so cross-
15
- // directory .ts imports don't resolve.
16
- const IMMUTABLE_CACHE = 'public, max-age=31536000, immutable';
17
- const STATIC_CACHE = 'public, max-age=3600, must-revalidate';
18
-
19
- function generateHeadersFile(): string {
20
- return `# Auto-generated by @timber-js/app — static asset cache headers.
21
- # See design/25-production-deployments.md §"CDN / Edge Cache"
22
-
23
- /assets/*
24
- Cache-Control: ${IMMUTABLE_CACHE}
25
-
26
- /*
27
- Cache-Control: ${STATIC_CACHE}
28
- `;
29
- }
13
+ import { IMMUTABLE_CACHE } from './shared.js';
14
+ import { runSharedBuildSteps } from './build-output-helper.js';
30
15
 
31
16
  // ─── Presets ─────────────────────────────────────────────────────────────────
32
17
 
@@ -202,40 +187,21 @@ export function nitro(options: NitroAdapterOptions = {}): TimberPlatformAdapter
202
187
 
203
188
  async buildOutput(config: TimberConfig, buildDir: string) {
204
189
  const outDir = join(buildDir, 'nitro');
205
- await mkdir(outDir, { recursive: true });
206
-
207
- // Copy client assets to public directory.
208
- // When client JavaScript is disabled, skip .js files — only CSS,
209
- // fonts, images, and other static assets are needed.
210
- const clientDir = join(buildDir, 'client');
211
- const publicDir = join(outDir, 'public');
212
- await mkdir(publicDir, { recursive: true });
213
- await cp(clientDir, publicDir, {
214
- recursive: true,
215
- filter: config.clientJavascriptDisabled ? (src: string) => !src.endsWith('.js') : undefined,
216
- }).catch(() => {
217
- // Client dir may not exist when client JavaScript is disabled
218
- });
219
190
 
220
- // Write _headers file for platforms that support it (Netlify, etc.).
221
- // See design/25-production-deployments.md §"CDN / Edge Cache"
222
- await writeFile(join(publicDir, '_headers'), generateHeadersFile());
223
-
224
- // Write the build manifest init module (if manifest data was produced).
225
- if (config.manifestInit) {
226
- await writeFile(join(outDir, '_timber-manifest-init.js'), config.manifestInit);
227
- }
191
+ // Steps 1–5: shared across all adapters (mkdir, copy client/rsc/ssr,
192
+ // write _headers, write manifest-init).
193
+ await runSharedBuildSteps({
194
+ config,
195
+ buildDir,
196
+ outDir,
197
+ publicDirName: 'public',
198
+ });
228
199
 
229
200
  // Write the compression helper module for runtime use.
230
201
  // See design/25-production-deployments.md — self-hosted deployments
231
202
  // need application-level compression (Cloudflare handles it at the edge).
232
203
  await writeFile(join(outDir, '_compress.mjs'), generateCompressModule());
233
204
 
234
- // Copy rsc/ssr build output into the nitro dir so imports stay local
235
- // during the Nitro bundling step (avoids broken relative paths in output).
236
- await cp(join(buildDir, 'rsc'), join(outDir, 'rsc'), { recursive: true });
237
- await cp(join(buildDir, 'ssr'), join(outDir, 'ssr'), { recursive: true }).catch(() => {});
238
-
239
205
  // Prepend the manifest assignment directly into the RSC entry so
240
206
  // globalThis.__TIMBER_BUILD_MANIFEST__ is set before any module reads it.
241
207
  // This must be top-level code, not an import, because rollup tree-shakes
@@ -486,11 +452,25 @@ const MIME_TYPES = {
486
452
  };
487
453
 
488
454
  const publicDir = join(__dirname, '${publicDir}');
489
- const port = parseInt(process.env.PORT || '3000', 10);
455
+
456
+ // Port resolution (TIM-842):
457
+ // - Default to 3000
458
+ // - If PORT env var is set, honor it strictly (fail loudly on conflict)
459
+ // - Otherwise auto-bump from 3000 until a free port is found
460
+ // Mirrors the dev server behavior so timber dev/preview/production all
461
+ // behave the same way around port selection.
462
+ const envPort = process.env.PORT ? parseInt(process.env.PORT, 10) : null;
463
+ const portIsExplicit = envPort != null && Number.isFinite(envPort) && envPort > 0;
464
+ const startPort = portIsExplicit ? envPort : 3000;
490
465
  const host = process.env.HOST || process.env.HOSTNAME || 'localhost';
491
466
 
467
+ // Set after listenWithBump() resolves so request handlers can build
468
+ // absolute URLs from the actual bound port (which may differ from
469
+ // startPort after an auto-bump).
470
+ let boundPort = startPort;
471
+
492
472
  const server = createServer(async (req, res) => {
493
- const url = new URL(req.url || '/', \`http://\${host}:\${port}\`);
473
+ const url = new URL(req.url || '/', \`http://\${host}:\${boundPort}\`);
494
474
 
495
475
  // Try serving static files from the public directory first.
496
476
  const filePath = join(publicDir, url.pathname);
@@ -631,13 +611,49 @@ const server = createServer(async (req, res) => {
631
611
  }
632
612
  });
633
613
 
634
- server.listen(port, host, () => {
614
+ function listenWithBump(port, attempt = 0) {
615
+ return new Promise((resolveListen, rejectListen) => {
616
+ const onError = (err) => {
617
+ server.removeListener('listening', onListening);
618
+ if (err && err.code === 'EADDRINUSE' && !portIsExplicit && attempt < 100) {
619
+ if (attempt === 0) {
620
+ console.log(\` [timber] Port \${port} in use, trying \${port + 1}...\`);
621
+ }
622
+ listenWithBump(port + 1, attempt + 1).then(resolveListen, rejectListen);
623
+ return;
624
+ }
625
+ rejectListen(err);
626
+ };
627
+ const onListening = () => {
628
+ server.removeListener('error', onError);
629
+ resolveListen(port);
630
+ };
631
+ server.once('error', onError);
632
+ server.once('listening', onListening);
633
+ server.listen(port, host);
634
+ });
635
+ }
636
+
637
+ try {
638
+ boundPort = await listenWithBump(startPort);
639
+ if (boundPort !== startPort) {
640
+ console.log();
641
+ console.log(\` [timber] Port \${startPort} in use, using \${boundPort}\`);
642
+ }
635
643
  console.log();
636
644
  console.log(' ⚡ timber preview server running at:');
637
645
  console.log();
638
- console.log(\` ➜ http://\${host}:\${port}\`);
646
+ console.log(\` ➜ http://\${host}:\${boundPort}\`);
639
647
  console.log();
640
- });
648
+ } catch (err) {
649
+ if (err && err.code === 'EADDRINUSE') {
650
+ console.error(\`[timber] Port \${startPort} is already in use. \` +
651
+ 'Set PORT to a free port, or unset PORT to auto-pick.');
652
+ } else {
653
+ console.error('[timber] Failed to start preview server:', err);
654
+ }
655
+ process.exit(1);
656
+ }
641
657
  `;
642
658
  }
643
659
 
@@ -0,0 +1,40 @@
1
+ // Shared adapter utilities — leaf module with zero deps on server/, client/,
2
+ // or any Vite-dependent code.
3
+ //
4
+ // Adapters are loaded by Node at Vite startup time, before Vite's module
5
+ // resolver is available. This module uses only Node built-ins and can be
6
+ // imported by both adapters via a relative path.
7
+ //
8
+ // IMPORTANT: Do NOT add imports from ../server/, ../client/, ../plugins/,
9
+ // or any module that transitively depends on Vite. A test in
10
+ // tests/adapters/shared-no-forbidden-imports.test.ts enforces this.
11
+
12
+ // ─── Static asset cache headers ──────────────────────────────────────────
13
+
14
+ /** Cache-Control value for hashed (immutable) assets. */
15
+ export const IMMUTABLE_CACHE = 'public, max-age=31536000, immutable';
16
+
17
+ /** Cache-Control value for unhashed static assets. */
18
+ export const STATIC_CACHE = 'public, max-age=3600, must-revalidate';
19
+
20
+ /**
21
+ * Generate a `_headers` file for static asset cache control.
22
+ *
23
+ * The `_headers` file is a platform convention supported by Cloudflare Workers
24
+ * Static Assets, Cloudflare Pages, and Netlify. It maps URL patterns to
25
+ * HTTP response headers.
26
+ *
27
+ * Vite places all hashed chunks under `/assets/` — these get immutable caching.
28
+ * Everything else (favicon.ico, robots.txt, etc.) gets a shorter cache.
29
+ */
30
+ export function generateHeadersFile(): string {
31
+ return `# Auto-generated by @timber-js/app — static asset cache headers.
32
+ # See design/25-production-deployments.md §"CDN / Edge Cache"
33
+
34
+ /assets/*
35
+ Cache-Control: ${IMMUTABLE_CACHE}
36
+
37
+ /*
38
+ Cache-Control: ${STATIC_CACHE}
39
+ `;
40
+ }
@@ -2,6 +2,7 @@ import type { CacheHandler, CacheOptions } from './index';
2
2
  import { stableStringify } from './stable-stringify';
3
3
  import { createSingleflight } from './singleflight';
4
4
  import { addSpanEventSync } from '../server/tracing.js';
5
+ import { logSwrRefetchFailed } from '../server/logger.js';
5
6
  import { fnv1aHash } from './fast-hash.js';
6
7
 
7
8
  const defaultSingleflight = createSingleflight();
@@ -91,9 +92,9 @@ export function createCache<Fn extends (...args: any[]) => Promise<any>>(
91
92
  const fresh = await fn(...args);
92
93
  const tags = resolveTags(opts, args);
93
94
  await handler.set(key, fresh, { ttl: opts.ttl, tags });
94
- } catch {
95
+ } catch (err) {
95
96
  // Failed refetch — stale entry continues to be served.
96
- // Error is swallowed per design doc: "Error is logged."
97
+ logSwrRefetchFailed({ cacheKey: key, error: err });
97
98
  }
98
99
  }).catch(() => {
99
100
  // Singleflight promise rejection handled — stale continues.
@@ -27,13 +27,15 @@ export type UseActionStateFn<TData> = (
27
27
  ) => Promise<ActionResult<TData>>;
28
28
 
29
29
  /**
30
- * Return type of useActionState — matches React 19's useActionState return.
31
- * [result, formAction, isPending]
30
+ * Return type of useActionState.
31
+ * [result, formAction, isPending, errors]
32
+ * The 4th element is auto-derived from result via useFormErrors logic.
32
33
  */
33
34
  export type UseActionStateReturn<TData> = [
34
35
  result: ActionResult<TData> | null,
35
36
  formAction: (formData: FormData) => void,
36
37
  isPending: boolean,
38
+ errors: FormErrorsResult,
37
39
  ];
38
40
 
39
41
  // ─── useActionState ──────────────────────────────────────────────────────
@@ -73,7 +75,13 @@ export function useActionState<TData>(
73
75
  ): UseActionStateReturn<TData> {
74
76
  // FormFlashData is structurally compatible with ActionResult at runtime —
75
77
  // the cast satisfies React's generic inference which would otherwise widen TData.
76
- return reactUseActionState(action, initialState as ActionResult<TData> | null, permalink);
78
+ const [result, formAction, isPending] = reactUseActionState(
79
+ action,
80
+ initialState as ActionResult<TData> | null,
81
+ permalink
82
+ );
83
+ const errors = deriveFormErrors(result);
84
+ return [result, formAction, isPending, errors];
77
85
  }
78
86
 
79
87
  // ─── useFormAction ───────────────────────────────────────────────────────
@@ -114,9 +122,9 @@ export function useFormAction<TData = unknown, TInput = unknown>(
114
122
  return [execute, isPending];
115
123
  }
116
124
 
117
- // ─── useFormErrors ──────────────────────────────────────────────────────
125
+ // ─── Form error extraction ────────────────────────────────────────────────
118
126
 
119
- /** Return type of useFormErrors(). */
127
+ /** Return type of the errors element in useActionState. */
120
128
  export interface FormErrorsResult {
121
129
  /** Per-field validation errors keyed by field name. */
122
130
  fieldErrors: Record<string, string[]>;
@@ -131,27 +139,11 @@ export interface FormErrorsResult {
131
139
  }
132
140
 
133
141
  /**
134
- * Extract per-field and form-level errors from an ActionResult.
135
- *
136
- * Pure function (no internal hooks) follows React naming convention
137
- * since it's used in render. Accepts the result from `useActionState`
138
- * or flash data from `getFormFlash()`.
139
- *
140
- * @example
141
- * ```tsx
142
- * const [result, action, isPending] = useActionState(createTodo, null)
143
- * const errors = useFormErrors(result)
144
- *
145
- * return (
146
- * <form action={action}>
147
- * <input name="title" />
148
- * {errors.getFieldError('title') && <p>{errors.getFieldError('title')}</p>}
149
- * {errors.formErrors.map(e => <p key={e}>{e}</p>)}
150
- * </form>
151
- * )
152
- * ```
142
+ * Derive FormErrorsResult from an action result.
143
+ * Used internally by useActionState 4th tuple element.
144
+ * @internal — exported for test access only.
153
145
  */
154
- export function useFormErrors<TData>(
146
+ export function deriveFormErrors<TData>(
155
147
  result:
156
148
  | ActionResult<TData>
157
149
  | {
@@ -66,26 +66,33 @@ export type { LinkStatus } from './use-link-status';
66
66
  export { useRouter } from './use-router';
67
67
  export type { AppRouterInstance } from './use-router';
68
68
  export { usePathname } from './use-pathname';
69
- export { useSearchParams } from './use-search-params';
69
+ // useSearchParams removed from public exports — lives in next/navigation shim only.
70
70
  export { useSelectedLayoutSegment, useSelectedLayoutSegments } from './use-selected-layout-segment';
71
71
 
72
72
  // Forms
73
- export { useActionState, useFormAction, useFormErrors } from './form';
73
+ export { useActionState, useFormAction } from './form';
74
74
  export type { UseActionStateFn, UseActionStateReturn, FormErrorsResult } from './form';
75
75
 
76
- // Params
77
- export { useSegmentParams } from './use-params';
76
+ // Params — useSegmentParams lives on defineSegmentParams().useSegmentParams()
77
+ // and in the next/navigation shim for library compat.
78
78
 
79
- // Query states (URL-synced search params)
80
- export { useQueryStates } from './use-query-states';
79
+ // Query states useQueryStates lives on defineSearchParams().useQueryStates()
80
+ // and is available via direct import for advanced cases.
81
81
 
82
- // Cookies
83
- export { useCookie } from './use-cookie';
82
+ // Cookies — useCookie lives on defineCookie().useCookie().
83
+ // Types still exported for advanced use.
84
84
  export type { ClientCookieOptions, CookieSetter } from './use-cookie';
85
85
 
86
86
  // Register the client cookie module with defineCookie's lazy reference.
87
87
  // This runs at module load time in the client/SSR environment, wiring up
88
88
  // the useCookie hook without a top-level import in define-cookie.ts.
89
89
  import * as _useCookieMod from './use-cookie.js';
90
- import { _registerUseCookieModule } from '../cookies/define-cookie.js';
90
+ import { _registerUseCookieModule, _registerFromSchema } from '../cookies/define-cookie.js';
91
+ import { fromSchema } from '../schema-bridge.js';
91
92
  _registerUseCookieModule(_useCookieMod);
93
+ _registerFromSchema(fromSchema);
94
+
95
+ // Register the client useSegmentParams hook with defineSegmentParams.
96
+ import { useSegmentParams as _useSegmentParams } from './use-segment-params.js';
97
+ import { _registerUseSegmentParams } from '../segment-params/define.js';
98
+ _registerUseSegmentParams(_useSegmentParams);
@@ -38,8 +38,9 @@ export type { SegmentNode, StateTree } from './segment-cache.js';
38
38
  export { HistoryStack } from './history.js';
39
39
  export type { HistoryEntry } from './history.js';
40
40
 
41
- // ── Params (internal setter) ─────────────────────────────────────────────
42
- export { setCurrentParams } from './use-params.js';
41
+ // ── Params (internal setter + raw hooks) ─────────────────────────────────
42
+ export { setCurrentParams, useSegmentParams } from './use-segment-params.js';
43
+ export { useSearchParams } from './use-search-params.js';
43
44
 
44
45
  // ── Navigation context ───────────────────────────────────────────────────
45
46
  export {
@@ -5,7 +5,7 @@ import { SegmentCache, PrefetchCache, buildSegmentTree } from './segment-cache';
5
5
  import type { SegmentInfo } from './segment-cache';
6
6
  import { HistoryStack } from './history';
7
7
  import type { HeadElement } from './head';
8
- import { setCurrentParams } from './use-params.js';
8
+ import { setCurrentParams } from './use-segment-params.js';
9
9
  import {
10
10
  setNavigationState,
11
11
  getNavigationState,
@@ -124,6 +124,17 @@ export function buildRscHeaders(
124
124
 
125
125
  // ─── Response Header Extraction ──────────────────────────────────
126
126
 
127
+ /** Dev-only warning for malformed framework headers. Tree-shaken in production. */
128
+ function warnMalformedHeader(headerName: string, raw: string): void {
129
+ if (process.env.NODE_ENV !== 'production') {
130
+ const preview = raw.length > 200 ? raw.slice(0, 200) + '…' : raw;
131
+ console.warn(
132
+ `[timber] Malformed ${headerName} header \u2014 JSON.parse failed. ` +
133
+ `This indicates a framework bug or header corruption. Raw (first 200 chars): ${preview}`
134
+ );
135
+ }
136
+ }
137
+
127
138
  /**
128
139
  * Extract head elements from the X-Timber-Head response header.
129
140
  * Returns null if the header is missing or malformed.
@@ -134,6 +145,7 @@ export function extractHeadElements(response: Response): HeadElement[] | null {
134
145
  try {
135
146
  return JSON.parse(decodeURIComponent(header));
136
147
  } catch {
148
+ warnMalformedHeader('X-Timber-Head', header);
137
149
  return null;
138
150
  }
139
151
  }
@@ -152,6 +164,7 @@ export function extractSegmentInfo(response: Response): SegmentInfo[] | null {
152
164
  try {
153
165
  return JSON.parse(header);
154
166
  } catch {
167
+ warnMalformedHeader('X-Timber-Segments', header);
155
168
  return null;
156
169
  }
157
170
  }
@@ -171,6 +184,7 @@ export function extractSkippedSegments(response: Response): string[] | null {
171
184
  const parsed = JSON.parse(header);
172
185
  return Array.isArray(parsed) ? parsed : null;
173
186
  } catch {
187
+ warnMalformedHeader('X-Timber-Skipped-Segments', header);
174
188
  return null;
175
189
  }
176
190
  }
@@ -187,6 +201,7 @@ export function extractParams(response: Response): Record<string, string | strin
187
201
  try {
188
202
  return JSON.parse(header);
189
203
  } catch {
204
+ warnMalformedHeader('X-Timber-Params', header);
190
205
  return null;
191
206
  }
192
207
  }
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * ALL mutable module-level state that must have singleton semantics across
5
5
  * the client bundle lives here. Individual modules (router-ref.ts, ssr-data.ts,
6
- * use-params.ts, use-search-params.ts, unload-guard.ts) import from this file
6
+ * use-segment-params.ts, use-search-params.ts, unload-guard.ts) import from this file
7
7
  * and re-export thin wrapper functions.
8
8
  *
9
9
  * Why: In Vite dev, a module is instantiated separately if reached via different
@@ -50,7 +50,7 @@ export function _setCurrentSsrData(data: SsrData | undefined): void {
50
50
  currentSsrData = data;
51
51
  }
52
52
 
53
- // ─── Route Params (from use-params.ts) ──────────────────────────────────────
53
+ // ─── Route Params (from use-segment-params.ts) ──────────────────────────────────────
54
54
 
55
55
  /** Current route params snapshot — replaced (not mutated) on each navigation. */
56
56
  export let currentParams: Record<string, string | string[]> = {};
@@ -10,6 +10,7 @@
10
10
 
11
11
  import { useSyncExternalStore } from 'react';
12
12
  import { getSsrData } from './ssr-data.js';
13
+ import { assertValidCookieName } from '../cookies/validation.js';
13
14
 
14
15
  // ─── Types ────────────────────────────────────────────────────────────────
15
16
 
@@ -68,6 +69,17 @@ function notify(name: string): void {
68
69
  }
69
70
  }
70
71
 
72
+ /**
73
+ * Notify useCookie subscribers that a cookie value changed.
74
+ * Called by defineCookie's imperative client .set()/.delete() methods
75
+ * so mounted useCookie() consumers re-render.
76
+ *
77
+ * @internal — framework use only
78
+ */
79
+ export function notifyCookieChange(name: string): void {
80
+ notify(name);
81
+ }
82
+
71
83
  // ─── Hook ─────────────────────────────────────────────────────────────────
72
84
 
73
85
  /**
@@ -103,8 +115,25 @@ export function useCookie(
103
115
 
104
116
  const value = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
105
117
 
118
+ // Validate the name once per hook instance — names are typically
119
+ // stable string literals, so this catches the bug at first render
120
+ // rather than on every write. Throws via `assertValidCookieName` with
121
+ // a precise error if the name violates RFC 7230 token rules.
122
+ assertValidCookieName(name);
123
+
106
124
  const setCookie: CookieSetter = (newValue: string, options?: ClientCookieOptions) => {
125
+ if (typeof newValue !== 'string') {
126
+ throw new Error(
127
+ `[timber] useCookie(${JSON.stringify(name)}): value must be a string, got ${typeof newValue}.\n` +
128
+ ` To store a JSON-serializable value, use defineCookie + jsonCookieCodec from\n` +
129
+ ` '@timber-js/app/cookies' and call its useCookie() hook instead.`
130
+ );
131
+ }
107
132
  const merged = { ...defaultOptions, ...options };
133
+ // Auto-encode mirrors the server's `cookies().set()` contract:
134
+ // values are URL-encoded on write, URL-decoded on read, so the
135
+ // logical bytes round-trip losslessly. See design/29-cookies.md
136
+ // §"Encoding Contract".
108
137
  document.cookie = `${name}=${encodeURIComponent(newValue)}${serializeOptions(merged)}`;
109
138
  notify(name);
110
139
  };
package/src/codec.ts CHANGED
@@ -39,11 +39,7 @@ export type JsonSerializable =
39
39
 
40
40
  // ─── Standard Schema bridge ──────────────────────────────────────────────
41
41
 
42
- export {
43
- fromSchema,
44
- fromArraySchema,
45
- validateSync,
46
- isStandardSchema,
47
- isCodec,
48
- } from './schema-bridge.js';
42
+ // fromSchema is internal — defineSearchParams and defineCookie auto-detect
43
+ // Standard Schema objects. Users should pass raw schemas directly.
44
+ export { fromArraySchema, validateSync, isStandardSchema, isCodec } from './schema-bridge.js';
49
45
  export type { StandardSchemaV1, StandardSchemaResult } from './schema-bridge.js';