@timber-js/app 0.2.0-alpha.6 → 0.2.0-alpha.61

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 (406) hide show
  1. package/LICENSE +8 -0
  2. package/dist/_chunks/{als-registry-B7DbZ2hS.js → als-registry-Ba7URUIn.js} +1 -1
  3. package/dist/_chunks/als-registry-Ba7URUIn.js.map +1 -0
  4. package/dist/_chunks/chunk-DYhsFzuS.js +33 -0
  5. package/dist/_chunks/{debug-gwlJkDuf.js → debug-ECi_61pb.js} +2 -2
  6. package/dist/_chunks/debug-ECi_61pb.js.map +1 -0
  7. package/dist/_chunks/define-CT98cU9c.js +121 -0
  8. package/dist/_chunks/define-CT98cU9c.js.map +1 -0
  9. package/dist/_chunks/define-TK8C1M3x.js +279 -0
  10. package/dist/_chunks/define-TK8C1M3x.js.map +1 -0
  11. package/dist/_chunks/define-cookie-BWr_52kY.js +93 -0
  12. package/dist/_chunks/define-cookie-BWr_52kY.js.map +1 -0
  13. package/dist/_chunks/error-boundary-DpZJBCqh.js +211 -0
  14. package/dist/_chunks/error-boundary-DpZJBCqh.js.map +1 -0
  15. package/dist/_chunks/{format-DviM89f0.js → format-cX7wzEp2.js} +2 -2
  16. package/dist/_chunks/{format-DviM89f0.js.map → format-cX7wzEp2.js.map} +1 -1
  17. package/dist/_chunks/{interception-BOoWmLUA.js → interception-Cey5DCGr.js} +129 -77
  18. package/dist/_chunks/interception-Cey5DCGr.js.map +1 -0
  19. package/dist/_chunks/{metadata-routes-Cjmvi3rQ.js → metadata-routes-BU684ls2.js} +1 -1
  20. package/dist/_chunks/{metadata-routes-Cjmvi3rQ.js.map → metadata-routes-BU684ls2.js.map} +1 -1
  21. package/dist/_chunks/{request-context-DIkVh_jG.js → request-context-rju2rbga.js} +97 -69
  22. package/dist/_chunks/request-context-rju2rbga.js.map +1 -0
  23. package/dist/_chunks/segment-context-CyaM1mrD.js +72 -0
  24. package/dist/_chunks/segment-context-CyaM1mrD.js.map +1 -0
  25. package/dist/_chunks/stale-reload-BSSym1MJ.js +64 -0
  26. package/dist/_chunks/stale-reload-BSSym1MJ.js.map +1 -0
  27. package/dist/_chunks/{tracing-Cwn7697K.js → tracing-VYETCQsg.js} +17 -3
  28. package/dist/_chunks/{tracing-Cwn7697K.js.map → tracing-VYETCQsg.js.map} +1 -1
  29. package/dist/_chunks/{use-query-states-D5KaffOK.js → use-query-states-wEXY2JQB.js} +1 -1
  30. package/dist/_chunks/use-query-states-wEXY2JQB.js.map +1 -0
  31. package/dist/_chunks/wrappers-BaG1bnM3.js +63 -0
  32. package/dist/_chunks/wrappers-BaG1bnM3.js.map +1 -0
  33. package/dist/adapters/compress-module.d.ts.map +1 -1
  34. package/dist/adapters/nitro.d.ts +17 -1
  35. package/dist/adapters/nitro.d.ts.map +1 -1
  36. package/dist/adapters/nitro.js +56 -13
  37. package/dist/adapters/nitro.js.map +1 -1
  38. package/dist/cache/fast-hash.d.ts +22 -0
  39. package/dist/cache/fast-hash.d.ts.map +1 -0
  40. package/dist/cache/index.d.ts +5 -2
  41. package/dist/cache/index.d.ts.map +1 -1
  42. package/dist/cache/index.js +90 -20
  43. package/dist/cache/index.js.map +1 -1
  44. package/dist/cache/register-cached-function.d.ts.map +1 -1
  45. package/dist/cache/singleflight.d.ts +18 -1
  46. package/dist/cache/singleflight.d.ts.map +1 -1
  47. package/dist/cache/timber-cache.d.ts +1 -1
  48. package/dist/cache/timber-cache.d.ts.map +1 -1
  49. package/dist/client/error-boundary.d.ts +10 -1
  50. package/dist/client/error-boundary.d.ts.map +1 -1
  51. package/dist/client/error-boundary.js +1 -125
  52. package/dist/client/error-reconstituter.d.ts +54 -0
  53. package/dist/client/error-reconstituter.d.ts.map +1 -0
  54. package/dist/client/form.d.ts +2 -2
  55. package/dist/client/form.d.ts.map +1 -1
  56. package/dist/client/index.d.ts +3 -2
  57. package/dist/client/index.d.ts.map +1 -1
  58. package/dist/client/index.js +433 -252
  59. package/dist/client/index.js.map +1 -1
  60. package/dist/client/link-pending-store.d.ts +78 -0
  61. package/dist/client/link-pending-store.d.ts.map +1 -0
  62. package/dist/client/link.d.ts +23 -9
  63. package/dist/client/link.d.ts.map +1 -1
  64. package/dist/client/navigation-context.d.ts +2 -2
  65. package/dist/client/navigation-context.d.ts.map +1 -1
  66. package/dist/client/router.d.ts +25 -3
  67. package/dist/client/router.d.ts.map +1 -1
  68. package/dist/client/rsc-fetch.d.ts +36 -2
  69. package/dist/client/rsc-fetch.d.ts.map +1 -1
  70. package/dist/client/segment-cache.d.ts +1 -1
  71. package/dist/client/segment-cache.d.ts.map +1 -1
  72. package/dist/client/segment-context.d.ts +1 -1
  73. package/dist/client/segment-context.d.ts.map +1 -1
  74. package/dist/client/segment-merger.d.ts.map +1 -1
  75. package/dist/client/segment-outlet.d.ts +63 -0
  76. package/dist/client/segment-outlet.d.ts.map +1 -0
  77. package/dist/client/stale-reload.d.ts +15 -0
  78. package/dist/client/stale-reload.d.ts.map +1 -1
  79. package/dist/client/top-loader.d.ts +1 -1
  80. package/dist/client/top-loader.d.ts.map +1 -1
  81. package/dist/client/transition-root.d.ts +1 -1
  82. package/dist/client/transition-root.d.ts.map +1 -1
  83. package/dist/client/use-params.d.ts +3 -3
  84. package/dist/client/use-params.d.ts.map +1 -1
  85. package/dist/client/use-query-states.d.ts +1 -1
  86. package/dist/client/use-query-states.d.ts.map +1 -1
  87. package/dist/codec.d.ts +21 -0
  88. package/dist/codec.d.ts.map +1 -0
  89. package/dist/cookies/define-cookie.d.ts +34 -13
  90. package/dist/cookies/define-cookie.d.ts.map +1 -1
  91. package/dist/cookies/index.js +1 -83
  92. package/dist/fonts/css.d.ts +1 -0
  93. package/dist/fonts/css.d.ts.map +1 -1
  94. package/dist/index.d.ts +127 -35
  95. package/dist/index.d.ts.map +1 -1
  96. package/dist/index.js +665 -242
  97. package/dist/index.js.map +1 -1
  98. package/dist/params/define.d.ts +100 -0
  99. package/dist/params/define.d.ts.map +1 -0
  100. package/dist/params/index.d.ts +8 -0
  101. package/dist/params/index.d.ts.map +1 -0
  102. package/dist/params/index.js +4 -0
  103. package/dist/plugins/adapter-build.d.ts +1 -1
  104. package/dist/plugins/adapter-build.d.ts.map +1 -1
  105. package/dist/plugins/build-manifest.d.ts +2 -2
  106. package/dist/plugins/build-manifest.d.ts.map +1 -1
  107. package/dist/plugins/build-report.d.ts +3 -3
  108. package/dist/plugins/build-report.d.ts.map +1 -1
  109. package/dist/plugins/client-chunks.d.ts +32 -0
  110. package/dist/plugins/client-chunks.d.ts.map +1 -0
  111. package/dist/plugins/content.d.ts +1 -1
  112. package/dist/plugins/content.d.ts.map +1 -1
  113. package/dist/plugins/dev-browser-logs.d.ts +84 -0
  114. package/dist/plugins/dev-browser-logs.d.ts.map +1 -0
  115. package/dist/plugins/dev-error-overlay.d.ts +26 -1
  116. package/dist/plugins/dev-error-overlay.d.ts.map +1 -1
  117. package/dist/plugins/dev-logs.d.ts +1 -1
  118. package/dist/plugins/dev-logs.d.ts.map +1 -1
  119. package/dist/plugins/dev-server.d.ts +1 -1
  120. package/dist/plugins/dev-server.d.ts.map +1 -1
  121. package/dist/plugins/entries.d.ts +1 -1
  122. package/dist/plugins/entries.d.ts.map +1 -1
  123. package/dist/plugins/fonts.d.ts +9 -2
  124. package/dist/plugins/fonts.d.ts.map +1 -1
  125. package/dist/plugins/mdx.d.ts +1 -1
  126. package/dist/plugins/mdx.d.ts.map +1 -1
  127. package/dist/plugins/routing.d.ts +1 -1
  128. package/dist/plugins/routing.d.ts.map +1 -1
  129. package/dist/plugins/server-bundle.d.ts.map +1 -1
  130. package/dist/plugins/shims.d.ts +6 -5
  131. package/dist/plugins/shims.d.ts.map +1 -1
  132. package/dist/plugins/static-build.d.ts +1 -1
  133. package/dist/plugins/static-build.d.ts.map +1 -1
  134. package/dist/routing/codegen.d.ts +2 -2
  135. package/dist/routing/codegen.d.ts.map +1 -1
  136. package/dist/routing/index.js +1 -1
  137. package/dist/routing/scanner.d.ts.map +1 -1
  138. package/dist/routing/status-file-lint.d.ts +2 -1
  139. package/dist/routing/status-file-lint.d.ts.map +1 -1
  140. package/dist/routing/types.d.ts +16 -4
  141. package/dist/routing/types.d.ts.map +1 -1
  142. package/dist/rsc-runtime/rsc.d.ts +1 -1
  143. package/dist/rsc-runtime/rsc.d.ts.map +1 -1
  144. package/dist/rsc-runtime/ssr.d.ts +12 -0
  145. package/dist/rsc-runtime/ssr.d.ts.map +1 -1
  146. package/dist/search-params/codecs.d.ts +1 -1
  147. package/dist/search-params/define.d.ts +159 -0
  148. package/dist/search-params/define.d.ts.map +1 -0
  149. package/dist/search-params/index.d.ts +4 -5
  150. package/dist/search-params/index.d.ts.map +1 -1
  151. package/dist/search-params/index.js +4 -474
  152. package/dist/search-params/registry.d.ts +1 -1
  153. package/dist/search-params/wrappers.d.ts +53 -0
  154. package/dist/search-params/wrappers.d.ts.map +1 -0
  155. package/dist/server/access-gate.d.ts +4 -0
  156. package/dist/server/access-gate.d.ts.map +1 -1
  157. package/dist/server/action-client.d.ts.map +1 -1
  158. package/dist/server/action-encryption.d.ts +76 -0
  159. package/dist/server/action-encryption.d.ts.map +1 -0
  160. package/dist/server/action-handler.d.ts.map +1 -1
  161. package/dist/server/actions.d.ts +1 -1
  162. package/dist/server/actions.d.ts.map +1 -1
  163. package/dist/server/als-registry.d.ts +25 -4
  164. package/dist/server/als-registry.d.ts.map +1 -1
  165. package/dist/server/build-manifest.d.ts +2 -2
  166. package/dist/server/build-manifest.d.ts.map +1 -1
  167. package/dist/server/debug.d.ts +1 -1
  168. package/dist/server/default-logger.d.ts +22 -0
  169. package/dist/server/default-logger.d.ts.map +1 -0
  170. package/dist/server/deny-renderer.d.ts.map +1 -1
  171. package/dist/server/early-hints.d.ts +13 -5
  172. package/dist/server/early-hints.d.ts.map +1 -1
  173. package/dist/server/error-boundary-wrapper.d.ts +4 -0
  174. package/dist/server/error-boundary-wrapper.d.ts.map +1 -1
  175. package/dist/server/fallback-error.d.ts +4 -3
  176. package/dist/server/fallback-error.d.ts.map +1 -1
  177. package/dist/server/flight-injection-state.d.ts +66 -0
  178. package/dist/server/flight-injection-state.d.ts.map +1 -0
  179. package/dist/server/flight-scripts.d.ts +42 -0
  180. package/dist/server/flight-scripts.d.ts.map +1 -0
  181. package/dist/server/flush.d.ts.map +1 -1
  182. package/dist/server/form-data.d.ts +29 -0
  183. package/dist/server/form-data.d.ts.map +1 -1
  184. package/dist/server/html-injectors.d.ts +51 -11
  185. package/dist/server/html-injectors.d.ts.map +1 -1
  186. package/dist/server/index.d.ts +4 -2
  187. package/dist/server/index.d.ts.map +1 -1
  188. package/dist/server/index.js +1977 -1648
  189. package/dist/server/index.js.map +1 -1
  190. package/dist/server/logger.d.ts +25 -7
  191. package/dist/server/logger.d.ts.map +1 -1
  192. package/dist/server/node-stream-transforms.d.ts +113 -0
  193. package/dist/server/node-stream-transforms.d.ts.map +1 -0
  194. package/dist/server/pipeline-interception.d.ts +1 -1
  195. package/dist/server/pipeline-interception.d.ts.map +1 -1
  196. package/dist/server/pipeline.d.ts +20 -6
  197. package/dist/server/pipeline.d.ts.map +1 -1
  198. package/dist/server/primitives.d.ts +30 -3
  199. package/dist/server/primitives.d.ts.map +1 -1
  200. package/dist/server/render-timeout.d.ts +51 -0
  201. package/dist/server/render-timeout.d.ts.map +1 -0
  202. package/dist/server/request-context.d.ts +65 -38
  203. package/dist/server/request-context.d.ts.map +1 -1
  204. package/dist/server/route-element-builder.d.ts +7 -0
  205. package/dist/server/route-element-builder.d.ts.map +1 -1
  206. package/dist/server/route-handler.d.ts.map +1 -1
  207. package/dist/server/route-matcher.d.ts +9 -2
  208. package/dist/server/route-matcher.d.ts.map +1 -1
  209. package/dist/server/rsc-entry/api-handler.d.ts +2 -2
  210. package/dist/server/rsc-entry/api-handler.d.ts.map +1 -1
  211. package/dist/server/rsc-entry/error-renderer.d.ts +26 -13
  212. package/dist/server/rsc-entry/error-renderer.d.ts.map +1 -1
  213. package/dist/server/rsc-entry/helpers.d.ts +48 -5
  214. package/dist/server/rsc-entry/helpers.d.ts.map +1 -1
  215. package/dist/server/rsc-entry/index.d.ts +8 -3
  216. package/dist/server/rsc-entry/index.d.ts.map +1 -1
  217. package/dist/server/rsc-entry/rsc-payload.d.ts +3 -3
  218. package/dist/server/rsc-entry/rsc-payload.d.ts.map +1 -1
  219. package/dist/server/rsc-entry/rsc-stream.d.ts +10 -1
  220. package/dist/server/rsc-entry/rsc-stream.d.ts.map +1 -1
  221. package/dist/server/rsc-entry/ssr-bridge.d.ts +1 -1
  222. package/dist/server/rsc-entry/ssr-bridge.d.ts.map +1 -1
  223. package/dist/server/rsc-entry/ssr-renderer.d.ts +19 -4
  224. package/dist/server/rsc-entry/ssr-renderer.d.ts.map +1 -1
  225. package/dist/server/slot-resolver.d.ts +1 -1
  226. package/dist/server/slot-resolver.d.ts.map +1 -1
  227. package/dist/server/ssr-entry.d.ts +22 -0
  228. package/dist/server/ssr-entry.d.ts.map +1 -1
  229. package/dist/server/ssr-render.d.ts +39 -21
  230. package/dist/server/ssr-render.d.ts.map +1 -1
  231. package/dist/server/ssr-wrappers.d.ts +50 -0
  232. package/dist/server/ssr-wrappers.d.ts.map +1 -0
  233. package/dist/server/status-code-resolver.d.ts +1 -1
  234. package/dist/server/status-code-resolver.d.ts.map +1 -1
  235. package/dist/server/stream-utils.d.ts +36 -0
  236. package/dist/server/stream-utils.d.ts.map +1 -0
  237. package/dist/server/tracing.d.ts +10 -0
  238. package/dist/server/tracing.d.ts.map +1 -1
  239. package/dist/server/tree-builder.d.ts +20 -13
  240. package/dist/server/tree-builder.d.ts.map +1 -1
  241. package/dist/server/types.d.ts +1 -3
  242. package/dist/server/types.d.ts.map +1 -1
  243. package/dist/server/version-skew.d.ts +61 -0
  244. package/dist/server/version-skew.d.ts.map +1 -0
  245. package/dist/server/waituntil-bridge.d.ts.map +1 -1
  246. package/dist/shared/merge-search-params.d.ts +22 -0
  247. package/dist/shared/merge-search-params.d.ts.map +1 -0
  248. package/dist/shims/font-google.d.ts +1 -1
  249. package/dist/shims/font-google.d.ts.map +1 -1
  250. package/dist/shims/navigation-client.d.ts +1 -1
  251. package/dist/shims/navigation-client.d.ts.map +1 -1
  252. package/dist/shims/navigation.d.ts +1 -1
  253. package/dist/shims/navigation.d.ts.map +1 -1
  254. package/dist/utils/state-machine.d.ts +80 -0
  255. package/dist/utils/state-machine.d.ts.map +1 -0
  256. package/package.json +17 -17
  257. package/src/adapters/compress-module.ts +24 -4
  258. package/src/adapters/nitro.ts +58 -9
  259. package/src/cache/fast-hash.ts +34 -0
  260. package/src/cache/index.ts +5 -2
  261. package/src/cache/register-cached-function.ts +7 -3
  262. package/src/cache/singleflight.ts +62 -4
  263. package/src/cache/timber-cache.ts +40 -29
  264. package/src/cli.ts +0 -0
  265. package/src/client/browser-entry.ts +151 -99
  266. package/src/client/error-boundary.tsx +18 -1
  267. package/src/client/error-reconstituter.tsx +65 -0
  268. package/src/client/form.tsx +2 -2
  269. package/src/client/index.ts +10 -1
  270. package/src/client/link-pending-store.ts +136 -0
  271. package/src/client/link.tsx +137 -22
  272. package/src/client/navigation-context.ts +6 -5
  273. package/src/client/router.ts +117 -60
  274. package/src/client/rsc-fetch.ts +90 -2
  275. package/src/client/segment-cache.ts +1 -1
  276. package/src/client/segment-context.ts +6 -1
  277. package/src/client/segment-merger.ts +2 -8
  278. package/src/client/segment-outlet.tsx +86 -0
  279. package/src/client/stale-reload.ts +73 -6
  280. package/src/client/top-loader.tsx +10 -9
  281. package/src/client/transition-root.tsx +20 -2
  282. package/src/client/use-params.ts +4 -4
  283. package/src/client/use-query-states.ts +2 -2
  284. package/src/codec.ts +21 -0
  285. package/src/cookies/define-cookie.ts +71 -20
  286. package/src/fonts/css.ts +2 -1
  287. package/src/index.ts +297 -85
  288. package/src/params/define.ts +327 -0
  289. package/src/params/index.ts +28 -0
  290. package/src/plugins/adapter-build.ts +8 -2
  291. package/src/plugins/build-manifest.ts +13 -2
  292. package/src/plugins/build-report.ts +3 -3
  293. package/src/plugins/cache-transform.ts +1 -1
  294. package/src/plugins/client-chunks.ts +65 -0
  295. package/src/plugins/content.ts +1 -1
  296. package/src/plugins/dev-browser-logs.ts +284 -0
  297. package/src/plugins/dev-error-overlay.ts +70 -1
  298. package/src/plugins/dev-logs.ts +1 -1
  299. package/src/plugins/dev-server.ts +41 -7
  300. package/src/plugins/entries.ts +6 -8
  301. package/src/plugins/fonts.ts +102 -55
  302. package/src/plugins/mdx.ts +1 -1
  303. package/src/plugins/routing.ts +57 -17
  304. package/src/plugins/server-action-exports.ts +1 -1
  305. package/src/plugins/server-bundle.ts +32 -1
  306. package/src/plugins/shims.ts +69 -31
  307. package/src/plugins/static-build.ts +10 -6
  308. package/src/routing/codegen.ts +109 -88
  309. package/src/routing/scanner.ts +86 -7
  310. package/src/routing/status-file-lint.ts +3 -2
  311. package/src/routing/types.ts +17 -4
  312. package/src/rsc-runtime/rsc.ts +2 -0
  313. package/src/rsc-runtime/ssr.ts +50 -0
  314. package/src/rsc-runtime/vendor-types.d.ts +7 -0
  315. package/src/search-params/codecs.ts +1 -1
  316. package/src/search-params/define.ts +518 -0
  317. package/src/search-params/index.ts +12 -18
  318. package/src/search-params/registry.ts +1 -1
  319. package/src/search-params/wrappers.ts +85 -0
  320. package/src/server/access-gate.tsx +40 -9
  321. package/src/server/action-client.ts +8 -2
  322. package/src/server/action-encryption.ts +144 -0
  323. package/src/server/action-handler.ts +20 -3
  324. package/src/server/actions.ts +1 -1
  325. package/src/server/als-registry.ts +25 -4
  326. package/src/server/build-manifest.ts +10 -4
  327. package/src/server/compress.ts +25 -7
  328. package/src/server/debug.ts +1 -1
  329. package/src/server/default-logger.ts +99 -0
  330. package/src/server/deny-renderer.ts +5 -3
  331. package/src/server/early-hints.ts +36 -15
  332. package/src/server/error-boundary-wrapper.ts +58 -15
  333. package/src/server/fallback-error.ts +29 -14
  334. package/src/server/flight-injection-state.ts +113 -0
  335. package/src/server/flight-scripts.ts +62 -0
  336. package/src/server/flush.ts +2 -1
  337. package/src/server/form-data.ts +76 -0
  338. package/src/server/html-injectors.ts +277 -117
  339. package/src/server/index.ts +9 -4
  340. package/src/server/logger.ts +44 -36
  341. package/src/server/node-stream-transforms.ts +509 -0
  342. package/src/server/pipeline-interception.ts +1 -1
  343. package/src/server/pipeline.ts +148 -41
  344. package/src/server/primitives.ts +47 -5
  345. package/src/server/render-timeout.ts +108 -0
  346. package/src/server/request-context.ts +125 -119
  347. package/src/server/route-element-builder.ts +107 -115
  348. package/src/server/route-handler.ts +2 -1
  349. package/src/server/route-matcher.ts +9 -2
  350. package/src/server/rsc-entry/api-handler.ts +8 -8
  351. package/src/server/rsc-entry/error-renderer.ts +286 -81
  352. package/src/server/rsc-entry/helpers.ts +134 -5
  353. package/src/server/rsc-entry/index.ts +177 -76
  354. package/src/server/rsc-entry/rsc-payload.ts +91 -18
  355. package/src/server/rsc-entry/rsc-stream.ts +74 -18
  356. package/src/server/rsc-entry/ssr-bridge.ts +2 -2
  357. package/src/server/rsc-entry/ssr-renderer.ts +152 -34
  358. package/src/server/slot-resolver.ts +231 -220
  359. package/src/server/ssr-entry.ts +211 -32
  360. package/src/server/ssr-render.ts +289 -67
  361. package/src/server/ssr-wrappers.tsx +139 -0
  362. package/src/server/status-code-resolver.ts +1 -1
  363. package/src/server/stream-utils.ts +213 -0
  364. package/src/server/tracing.ts +23 -0
  365. package/src/server/tree-builder.ts +92 -58
  366. package/src/server/types.ts +1 -3
  367. package/src/server/version-skew.ts +104 -0
  368. package/src/server/waituntil-bridge.ts +4 -1
  369. package/src/shared/merge-search-params.ts +55 -0
  370. package/src/shims/font-google.ts +1 -1
  371. package/src/shims/navigation-client.ts +1 -1
  372. package/src/shims/navigation.ts +2 -1
  373. package/src/utils/state-machine.ts +111 -0
  374. package/dist/_chunks/als-registry-B7DbZ2hS.js.map +0 -1
  375. package/dist/_chunks/debug-gwlJkDuf.js.map +0 -1
  376. package/dist/_chunks/interception-BOoWmLUA.js.map +0 -1
  377. package/dist/_chunks/request-context-DIkVh_jG.js.map +0 -1
  378. package/dist/_chunks/ssr-data-MjmprTmO.js +0 -88
  379. package/dist/_chunks/ssr-data-MjmprTmO.js.map +0 -1
  380. package/dist/_chunks/use-cookie-DX-l1_5E.js +0 -91
  381. package/dist/_chunks/use-cookie-DX-l1_5E.js.map +0 -1
  382. package/dist/_chunks/use-query-states-D5KaffOK.js.map +0 -1
  383. package/dist/client/error-boundary.js.map +0 -1
  384. package/dist/client/link-status-provider.d.ts +0 -11
  385. package/dist/client/link-status-provider.d.ts.map +0 -1
  386. package/dist/cookies/index.js.map +0 -1
  387. package/dist/plugins/dynamic-transform.d.ts +0 -72
  388. package/dist/plugins/dynamic-transform.d.ts.map +0 -1
  389. package/dist/search-params/analyze.d.ts +0 -54
  390. package/dist/search-params/analyze.d.ts.map +0 -1
  391. package/dist/search-params/builtin-codecs.d.ts +0 -105
  392. package/dist/search-params/builtin-codecs.d.ts.map +0 -1
  393. package/dist/search-params/create.d.ts +0 -106
  394. package/dist/search-params/create.d.ts.map +0 -1
  395. package/dist/search-params/index.js.map +0 -1
  396. package/dist/server/prerender.d.ts +0 -77
  397. package/dist/server/prerender.d.ts.map +0 -1
  398. package/dist/server/response-cache.d.ts +0 -53
  399. package/dist/server/response-cache.d.ts.map +0 -1
  400. package/src/client/link-status-provider.tsx +0 -30
  401. package/src/plugins/dynamic-transform.ts +0 -161
  402. package/src/search-params/analyze.ts +0 -192
  403. package/src/search-params/builtin-codecs.ts +0 -228
  404. package/src/search-params/create.ts +0 -321
  405. package/src/server/prerender.ts +0 -139
  406. package/src/server/response-cache.ts +0 -277
@@ -10,10 +10,10 @@
10
10
  * See design/29-cookies.md for cookie mutation semantics.
11
11
  */
12
12
 
13
- import { createHmac, timingSafeEqual } from 'node:crypto';
14
- import type { Routes } from '#/index.js';
15
13
  import { requestContextAls, type RequestContextStore, type CookieEntry } from './als-registry.js';
16
14
  import { isDebug } from './debug.js';
15
+ import { _setRawSearchParamsFn } from '../search-params/define.js';
16
+ import { _setRawSegmentParamsFn } from '../params/define.js';
17
17
 
18
18
  // Re-export the ALS for framework-internal consumers that need direct access.
19
19
  export { requestContextAls };
@@ -22,30 +22,6 @@ export { requestContextAls };
22
22
  // the ALS context persists for the entire request lifecycle including
23
23
  // async stream consumption by React's renderToReadableStream.
24
24
 
25
- // ─── Cookie Signing Secrets ──────────────────────────────────────────────
26
-
27
- /**
28
- * Module-level cookie signing secrets. Index 0 is the newest (used for signing).
29
- * All entries are tried for verification (key rotation support).
30
- *
31
- * Set by the framework at startup via `setCookieSecrets()`.
32
- * See design/29-cookies.md §"Signed Cookies"
33
- */
34
- let _cookieSecrets: string[] = [];
35
-
36
- /**
37
- * Configure the cookie signing secrets.
38
- *
39
- * Called by the framework during server initialization with values from
40
- * `cookies.secret` or `cookies.secrets` in timber.config.ts.
41
- *
42
- * The first secret (index 0) is used for signing new cookies.
43
- * All secrets are tried for verification (supports key rotation).
44
- */
45
- export function setCookieSecrets(secrets: string[]): void {
46
- _cookieSecrets = secrets.filter(Boolean);
47
- }
48
-
49
25
  // ─── Public API ───────────────────────────────────────────────────────────
50
26
 
51
27
  /**
@@ -109,12 +85,6 @@ export function cookies(): RequestCookies {
109
85
  return map.size;
110
86
  },
111
87
 
112
- getSigned(name: string): string | undefined {
113
- const raw = map.get(name);
114
- if (!raw || _cookieSecrets.length === 0) return undefined;
115
- return verifySignedCookie(raw, _cookieSecrets);
116
- },
117
-
118
88
  set(name: string, value: string, options?: CookieOptions): void {
119
89
  assertMutable(store, 'set');
120
90
  if (store.flushed) {
@@ -127,21 +97,10 @@ export function cookies(): RequestCookies {
127
97
  }
128
98
  return;
129
99
  }
130
- let storedValue = value;
131
- if (options?.signed) {
132
- if (_cookieSecrets.length === 0) {
133
- throw new Error(
134
- `[timber] cookies().set('${name}', ..., { signed: true }) requires ` +
135
- `cookies.secret or cookies.secrets in timber.config.ts.`
136
- );
137
- }
138
- storedValue = signCookieValue(value, _cookieSecrets[0]);
139
- }
140
100
  const opts = { ...DEFAULT_COOKIE_OPTIONS, ...options };
141
- store.cookieJar.set(name, { name, value: storedValue, options: opts });
142
- // Read-your-own-writes: update the parsed cookies map with the signed value
143
- // so getSigned() can verify it in the same request
144
- map.set(name, storedValue);
101
+ store.cookieJar.set(name, { name, value, options: opts });
102
+ // Read-your-own-writes: update the parsed cookies map
103
+ map.set(name, value);
145
104
  },
146
105
 
147
106
  delete(name: string, options?: Pick<CookieOptions, 'path' | 'domain'>): void {
@@ -190,41 +149,112 @@ export function cookies(): RequestCookies {
190
149
  }
191
150
 
192
151
  /**
193
- * Returns a Promise resolving to the current request's search params.
152
+ * Returns a Promise resolving to the current request's raw URLSearchParams.
153
+ *
154
+ * For typed, parsed search params, import the definition from params.ts
155
+ * and call `.load()` or `.parse()`:
194
156
  *
195
- * In `page.tsx`, `middleware.ts`, and `access.ts` the framework pre-parses the
196
- * route's `search-params.ts` definition and the Promise resolves to the typed
197
- * object. In all other server component contexts it resolves to raw
198
- * `URLSearchParams`.
157
+ * ```ts
158
+ * import { searchParams } from './params'
159
+ * const parsed = await searchParams.load()
160
+ * ```
199
161
  *
200
- * Returned as a Promise to match the `params` prop convention and to allow
201
- * future partial pre-rendering support where param resolution may be deferred.
162
+ * Or explicitly:
163
+ *
164
+ * ```ts
165
+ * import { rawSearchParams } from '@timber-js/app/server'
166
+ * import { searchParams } from './params'
167
+ * const parsed = searchParams.parse(await rawSearchParams())
168
+ * ```
202
169
  *
203
170
  * Throws if called outside a request context.
204
171
  */
205
- export function searchParams<R extends keyof Routes>(): Promise<Routes[R]['searchParams']>;
206
- export function searchParams(): Promise<URLSearchParams | Record<string, unknown>>;
207
- export function searchParams(): Promise<URLSearchParams | Record<string, unknown>> {
172
+ export function rawSearchParams(): Promise<URLSearchParams> {
208
173
  const store = requestContextAls.getStore();
209
174
  if (!store) {
210
175
  throw new Error(
211
- '[timber] searchParams() called outside of a request context. ' +
176
+ '[timber] rawSearchParams() called outside of a request context. ' +
212
177
  'It can only be used in middleware, access checks, server components, and server actions.'
213
178
  );
214
179
  }
215
180
  return store.searchParamsPromise;
216
181
  }
217
182
 
183
+ // Eagerly register rawSearchParams with the search-params module so
184
+ // searchParams.load() can call it synchronously without a dynamic import.
185
+ // Dynamic imports lose ALS context in React's RSC Flight renderer,
186
+ // breaking rawSearchParams() in parallel slot pages. See TIM-523.
187
+ _setRawSearchParamsFn(rawSearchParams);
188
+
189
+ // Eagerly register rawSegmentParams with the params module so
190
+ // segmentParams.load() can call it synchronously without a dynamic import.
191
+ // Same pattern as search params — dynamic imports lose ALS context. See TIM-523.
192
+ _setRawSegmentParamsFn(rawSegmentParams);
193
+
218
194
  /**
219
- * Replace the search params Promise for the current request with one that
220
- * resolves to the typed parsed result from the route's search-params.ts.
221
- * Called by the framework before rendering the page not for app code.
195
+ * Returns a Promise resolving to the current request's coerced segment params.
196
+ *
197
+ * Segment params are set by the pipeline after route matching and param
198
+ * coercion (via params.ts codecs). When no params.ts exists, values are
199
+ * raw strings. When codecs are defined, values are already coerced
200
+ * (e.g., `id` is a `number` if `defineSegmentParams({ id: z.coerce.number() })`).
201
+ *
202
+ * This is the primary way page and layout components access route params:
203
+ *
204
+ * ```ts
205
+ * import { rawSegmentParams } from '@timber-js/app/server'
206
+ *
207
+ * export default async function Page() {
208
+ * const { slug } = await rawSegmentParams()
209
+ * // ...
210
+ * }
211
+ * ```
212
+ *
213
+ * Throws if called outside a request context.
222
214
  */
223
- export function setParsedSearchParams(parsed: Record<string, unknown>): void {
215
+ export function rawSegmentParams(): Promise<Record<string, string | string[]>> {
224
216
  const store = requestContextAls.getStore();
225
- if (store) {
226
- store.searchParamsPromise = Promise.resolve(parsed);
217
+ if (!store) {
218
+ throw new Error(
219
+ '[timber] rawSegmentParams() called outside of a request context. ' +
220
+ 'It can only be used in middleware, access checks, server components, and server actions.'
221
+ );
222
+ }
223
+ if (!store.segmentParamsPromise) {
224
+ throw new Error(
225
+ '[timber] rawSegmentParams() called before route matching completed. ' +
226
+ 'Segment params are not available until after the route is matched.'
227
+ );
228
+ }
229
+ return store.segmentParamsPromise;
230
+ }
231
+
232
+ /**
233
+ * Set the segment params promise on the current request context.
234
+ * Called by the pipeline after route matching and param coercion.
235
+ *
236
+ * @internal — framework use only
237
+ */
238
+ export function setSegmentParams(params: Record<string, string | string[]>): void {
239
+ const store = requestContextAls.getStore();
240
+ if (!store) {
241
+ throw new Error('[timber] setSegmentParams() called outside of a request context.');
227
242
  }
243
+ store.segmentParamsPromise = Promise.resolve(params);
244
+ }
245
+
246
+ /**
247
+ * Returns the raw search string from the current request URL (e.g. "?foo=bar").
248
+ * Synchronous — safe for use in `redirect()` which throws synchronously.
249
+ *
250
+ * Returns empty string if called outside a request context (non-throwing for
251
+ * use in redirect's optional preserveSearchParams path).
252
+ *
253
+ * @internal — used by redirect() for preserveSearchParams support.
254
+ */
255
+ export function getRequestSearchString(): string {
256
+ const store = requestContextAls.getStore();
257
+ return store?.searchString ?? '';
228
258
  }
229
259
 
230
260
  // ─── Types ────────────────────────────────────────────────────────────────
@@ -257,12 +287,6 @@ export interface CookieOptions {
257
287
  sameSite?: 'strict' | 'lax' | 'none';
258
288
  /** Partitioned (CHIPS) — isolate cookie per top-level site. Default: false. */
259
289
  partitioned?: boolean;
260
- /**
261
- * Sign the cookie value with HMAC-SHA256 for integrity verification.
262
- * Requires `cookies.secret` or `cookies.secrets` in timber.config.ts.
263
- * See design/29-cookies.md §"Signed Cookies".
264
- */
265
- signed?: boolean;
266
290
  }
267
291
 
268
292
  const DEFAULT_COOKIE_OPTIONS: CookieOptions = {
@@ -287,14 +311,6 @@ export interface RequestCookies {
287
311
  getAll(): Array<{ name: string; value: string }>;
288
312
  /** Number of cookies. */
289
313
  readonly size: number;
290
- /**
291
- * Get a signed cookie value, verifying its HMAC-SHA256 signature.
292
- * Returns undefined if the cookie is missing, the signature is invalid,
293
- * or no secrets are configured. Never throws.
294
- *
295
- * See design/29-cookies.md §"Signed Cookies"
296
- */
297
- getSigned(name: string): string | undefined;
298
314
  /** Set a cookie. Only available in mutable contexts (middleware, actions, route handlers). */
299
315
  set(name: string, value: string, options?: CookieOptions): void;
300
316
  /** Delete a cookie. Only available in mutable contexts. */
@@ -316,11 +332,13 @@ export interface RequestCookies {
316
332
  */
317
333
  export function runWithRequestContext<T>(req: Request, fn: () => T): T {
318
334
  const originalCopy = new Headers(req.headers);
335
+ const parsedUrl = new URL(req.url);
319
336
  const store: RequestContextStore = {
320
337
  headers: freezeHeaders(req.headers),
321
338
  originalHeaders: originalCopy,
322
339
  cookieHeader: req.headers.get('cookie') ?? '',
323
- searchParamsPromise: Promise.resolve(new URL(req.url).searchParams),
340
+ searchParamsPromise: Promise.resolve(parsedUrl.searchParams),
341
+ searchString: parsedUrl.search,
324
342
  cookieJar: new Map(),
325
343
  flushed: false,
326
344
  mutableContext: false,
@@ -354,6 +372,35 @@ export function markResponseFlushed(): void {
354
372
  }
355
373
  }
356
374
 
375
+ /**
376
+ * Build a Map of cookie name → value reflecting the current request's
377
+ * read-your-own-writes state. Includes incoming cookies plus any
378
+ * mutations from cookies().set() / cookies().delete() in the same request.
379
+ *
380
+ * Used by SSR renderers to populate NavContext.cookies so that
381
+ * useCookie()'s server snapshot matches the actual response state.
382
+ *
383
+ * See design/29-cookies.md §"Read-Your-Own-Writes"
384
+ * See design/triage/TIM-441-cookie-api-triage.md §4
385
+ */
386
+ export function getCookiesForSsr(): Map<string, string> {
387
+ const store = requestContextAls.getStore();
388
+ if (!store) {
389
+ throw new Error('[timber] getCookiesForSsr() called outside of a request context.');
390
+ }
391
+
392
+ // Trigger lazy parsing if not yet done
393
+ if (!store.parsedCookies) {
394
+ store.parsedCookies = parseCookieHeader(store.cookieHeader);
395
+ }
396
+
397
+ // The parsedCookies map already reflects read-your-own-writes:
398
+ // - cookies().set() updates the map via map.set(name, value)
399
+ // - cookies().delete() removes from the map via map.delete(name)
400
+ // Return a copy so callers can't mutate the internal map.
401
+ return new Map(store.parsedCookies);
402
+ }
403
+
357
404
  /**
358
405
  * Collect all Set-Cookie headers from the cookie jar.
359
406
  * Called by the framework at flush time to apply cookies to the response.
@@ -467,47 +514,6 @@ function parseCookieHeader(header: string): Map<string, string> {
467
514
  return map;
468
515
  }
469
516
 
470
- // ─── Cookie Signing ──────────────────────────────────────────────────────
471
-
472
- /**
473
- * Sign a cookie value with HMAC-SHA256.
474
- * Returns `value.hex_signature`.
475
- */
476
- function signCookieValue(value: string, secret: string): string {
477
- const signature = createHmac('sha256', secret).update(value).digest('hex');
478
- return `${value}.${signature}`;
479
- }
480
-
481
- /**
482
- * Verify a signed cookie value against an array of secrets.
483
- * Returns the original value if any secret produces a matching signature,
484
- * or undefined if none match. Uses timing-safe comparison.
485
- *
486
- * The signed format is `value.hex_signature` — split at the last `.`.
487
- */
488
- function verifySignedCookie(raw: string, secrets: string[]): string | undefined {
489
- const lastDot = raw.lastIndexOf('.');
490
- if (lastDot <= 0 || lastDot === raw.length - 1) return undefined;
491
-
492
- const value = raw.slice(0, lastDot);
493
- const signature = raw.slice(lastDot + 1);
494
-
495
- // Hex-encoded SHA-256 is always 64 chars
496
- if (signature.length !== 64) return undefined;
497
-
498
- const signatureBuffer = Buffer.from(signature, 'hex');
499
- // If the hex decode produced fewer bytes, the signature was not valid hex
500
- if (signatureBuffer.length !== 32) return undefined;
501
-
502
- for (const secret of secrets) {
503
- const expected = createHmac('sha256', secret).update(value).digest();
504
- if (timingSafeEqual(expected, signatureBuffer)) {
505
- return value;
506
- }
507
- }
508
- return undefined;
509
- }
510
-
511
517
  /** Serialize a CookieEntry into a Set-Cookie header value. */
512
518
  function serializeCookieEntry(entry: CookieEntry): string {
513
519
  const parts = [`${entry.name}=${entry.value}`];
@@ -27,13 +27,25 @@ import { METADATA_ROUTE_CONVENTIONS, getMetadataRouteAutoLink } from './metadata
27
27
  import { DenySignal, RedirectSignal } from './primitives.js';
28
28
  import { AccessGate } from './access-gate.js';
29
29
  import { resolveSlotElement } from './slot-resolver.js';
30
- import { SegmentProvider } from '#/client/segment-context.js';
31
- import { setParsedSearchParams } from './request-context.js';
32
- import type { SearchParamsDefinition } from '#/search-params/create.js';
30
+ import { SegmentProvider } from '../client/segment-context.js';
31
+
33
32
  import { wrapSegmentWithErrorBoundaries } from './error-boundary-wrapper.js';
34
33
  import type { InterceptionContext } from './pipeline.js';
35
34
  import { shouldSkipSegment } from './state-tree-diff.js';
36
35
 
36
+ // ─── Param Coercion Error ─────────────────────────────────────────────────
37
+
38
+ /**
39
+ * Thrown when a defineSegmentParams codec's parse() fails.
40
+ * The pipeline catches this and responds with 404.
41
+ */
42
+ export class ParamCoercionError extends Error {
43
+ constructor(message: string) {
44
+ super(message);
45
+ this.name = 'ParamCoercionError';
46
+ }
47
+ }
48
+
37
49
  // ─── Types ────────────────────────────────────────────────────────────────
38
50
 
39
51
  /** Head element for client-side metadata updates. */
@@ -84,6 +96,64 @@ export class RouteSignalWithContext extends Error {
84
96
  }
85
97
  }
86
98
 
99
+ // ─── Module Processing Helpers ─────────────────────────────────────────────
100
+
101
+ /**
102
+ * Reject the legacy `generateMetadata` export with a helpful migration message.
103
+ * Throws if the module exports `generateMetadata` instead of `metadata`.
104
+ */
105
+ function rejectLegacyGenerateMetadata(mod: Record<string, unknown>, filePath: string): void {
106
+ if ('generateMetadata' in mod) {
107
+ throw new Error(
108
+ `${filePath}: "generateMetadata" is not a valid export. ` +
109
+ `Export an async function named "metadata" instead.\n\n` +
110
+ ` // Before\n` +
111
+ ` export async function generateMetadata({ params }) { ... }\n\n` +
112
+ ` // After\n` +
113
+ ` export async function metadata() { ... }`
114
+ );
115
+ }
116
+ }
117
+
118
+ /**
119
+ * Extract and resolve metadata from a module (layout or page).
120
+ * Handles both static metadata objects and async metadata functions.
121
+ * Returns the resolved Metadata, or null if none exported.
122
+ *
123
+ * Metadata functions no longer receive { params } — they access params
124
+ * via rawSegmentParams() from ALS, same as page/layout components.
125
+ */
126
+ async function extractMetadata(
127
+ mod: Record<string, unknown>,
128
+ segment: ManifestSegmentNode
129
+ ): Promise<Metadata | null> {
130
+ if (typeof mod.metadata === 'function') {
131
+ type MetadataFn = () => Promise<Metadata>;
132
+ return (
133
+ (await withSpan(
134
+ 'timber.metadata',
135
+ { 'timber.segment': segment.segmentName ?? segment.urlPath },
136
+ () => (mod.metadata as MetadataFn)()
137
+ )) ?? null
138
+ );
139
+ }
140
+ if (mod.metadata) {
141
+ return mod.metadata as Metadata;
142
+ }
143
+ return null;
144
+ }
145
+
146
+ /**
147
+ * Extract `deferSuspenseFor` from a module and return the maximum
148
+ * of the current value and the module's value.
149
+ */
150
+ function extractDeferSuspenseFor(mod: Record<string, unknown>, current: number): number {
151
+ if (typeof mod.deferSuspenseFor === 'number' && mod.deferSuspenseFor > current) {
152
+ return mod.deferSuspenseFor;
153
+ }
154
+ return current;
155
+ }
156
+
87
157
  // ─── Builder ──────────────────────────────────────────────────────────────
88
158
 
89
159
  /**
@@ -104,9 +174,6 @@ export async function buildRouteElement(
104
174
  ): Promise<RouteElementResult> {
105
175
  const segments = match.segments as unknown as ManifestSegmentNode[];
106
176
 
107
- // Params are passed as a Promise to match Next.js 15+ convention.
108
- const paramsPromise = Promise.resolve(match.params);
109
-
110
177
  // Load all modules along the segment chain
111
178
  const metadataEntries: Array<{ metadata: Metadata; isPage: boolean }> = [];
112
179
  const layoutComponents: LayoutComponentEntry[] = [];
@@ -126,87 +193,34 @@ export async function buildRouteElement(
126
193
  segment,
127
194
  });
128
195
  }
129
- // Reject legacy generateMetadata export — use `export async function metadata()` instead
130
- if ('generateMetadata' in mod) {
131
- const filePath = segment.layout.filePath ?? segment.urlPath;
132
- throw new Error(
133
- `${filePath}: "generateMetadata" is not a valid export. ` +
134
- `Export an async function named "metadata" instead.\n\n` +
135
- ` // Before\n` +
136
- ` export async function generateMetadata({ params }) { ... }\n\n` +
137
- ` // After\n` +
138
- ` export async function metadata({ params }) { ... }`
139
- );
140
- }
141
- // Unified metadata export: static object or async function
142
- if (typeof mod.metadata === 'function') {
143
- type MetadataFn = (props: Record<string, unknown>) => Promise<Metadata>;
144
- const generated = await withSpan(
145
- 'timber.metadata',
146
- { 'timber.segment': segment.segmentName ?? segment.urlPath },
147
- () => (mod.metadata as MetadataFn)({ params: paramsPromise })
148
- );
149
- if (generated) {
150
- metadataEntries.push({ metadata: generated, isPage: false });
151
- }
152
- } else if (mod.metadata) {
153
- metadataEntries.push({ metadata: mod.metadata as Metadata, isPage: false });
154
- }
155
- // deferSuspenseFor hold window — max across all segments
156
- if (typeof mod.deferSuspenseFor === 'number' && mod.deferSuspenseFor > deferSuspenseFor) {
157
- deferSuspenseFor = mod.deferSuspenseFor;
196
+
197
+ // Param coercion is handled in the pipeline (Stage 2c) before
198
+ // middleware and rendering. See coerceSegmentParams() in pipeline.ts.
199
+
200
+ rejectLegacyGenerateMetadata(mod, segment.layout.filePath ?? segment.urlPath);
201
+ const layoutMetadata = await extractMetadata(mod, segment);
202
+ if (layoutMetadata) {
203
+ metadataEntries.push({ metadata: layoutMetadata, isPage: false });
158
204
  }
205
+ deferSuspenseFor = extractDeferSuspenseFor(mod, deferSuspenseFor);
159
206
  }
160
207
 
161
208
  // Load page (leaf segment only)
162
209
  if (isLeaf && segment.page) {
163
- // Load and apply search-params.ts definition before rendering so
164
- // searchParams() from @timber-js/app/server returns parsed typed values.
165
- if (segment.searchParams) {
166
- const spMod = (await segment.searchParams.load()) as {
167
- default?: SearchParamsDefinition<Record<string, unknown>>;
168
- };
169
- if (spMod.default) {
170
- const rawSearchParams = new URL(req.url).searchParams;
171
- const parsed = spMod.default.parse(rawSearchParams);
172
- setParsedSearchParams(parsed);
173
- }
174
- }
175
-
176
210
  const mod = (await segment.page.load()) as Record<string, unknown>;
211
+
212
+ // Param coercion is handled in the pipeline (Stage 2c) before
213
+ // middleware and rendering. See coerceSegmentParams() in pipeline.ts.
214
+
177
215
  if (mod.default) {
178
216
  PageComponent = mod.default as (...args: unknown[]) => unknown;
179
217
  }
180
- // Reject legacy generateMetadata export — use `export async function metadata()` instead
181
- if ('generateMetadata' in mod) {
182
- const filePath = segment.page.filePath ?? segment.urlPath;
183
- throw new Error(
184
- `${filePath}: "generateMetadata" is not a valid export. ` +
185
- `Export an async function named "metadata" instead.\n\n` +
186
- ` // Before\n` +
187
- ` export async function generateMetadata({ params }) { ... }\n\n` +
188
- ` // After\n` +
189
- ` export async function metadata({ params }) { ... }`
190
- );
191
- }
192
- // Unified metadata export: static object or async function
193
- if (typeof mod.metadata === 'function') {
194
- type MetadataFn = (props: Record<string, unknown>) => Promise<Metadata>;
195
- const generated = await withSpan(
196
- 'timber.metadata',
197
- { 'timber.segment': segment.segmentName ?? segment.urlPath },
198
- () => (mod.metadata as MetadataFn)({ params: paramsPromise })
199
- );
200
- if (generated) {
201
- metadataEntries.push({ metadata: generated, isPage: true });
202
- }
203
- } else if (mod.metadata) {
204
- metadataEntries.push({ metadata: mod.metadata as Metadata, isPage: true });
205
- }
206
- // deferSuspenseFor hold window — max across all segments
207
- if (typeof mod.deferSuspenseFor === 'number' && mod.deferSuspenseFor > deferSuspenseFor) {
208
- deferSuspenseFor = mod.deferSuspenseFor;
218
+ rejectLegacyGenerateMetadata(mod, segment.page.filePath ?? segment.urlPath);
219
+ const pageMetadata = await extractMetadata(mod, segment);
220
+ if (pageMetadata) {
221
+ metadataEntries.push({ metadata: pageMetadata, isPage: true });
209
222
  }
223
+ deferSuspenseFor = extractDeferSuspenseFor(mod, deferSuspenseFor);
210
224
  }
211
225
  }
212
226
 
@@ -227,7 +241,7 @@ export async function buildRouteElement(
227
241
  if (segment.access) {
228
242
  const accessMod = (await segment.access.load()) as Record<string, unknown>;
229
243
  const accessFn = accessMod.default as
230
- | ((ctx: { params: Record<string, string | string[]>; searchParams: unknown }) => unknown)
244
+ | ((ctx: { params: Record<string, string | string[]> }) => unknown)
231
245
  | undefined;
232
246
  if (accessFn) {
233
247
  try {
@@ -236,7 +250,7 @@ export async function buildRouteElement(
236
250
  { 'timber.segment': segment.segmentName ?? 'unknown' },
237
251
  async () => {
238
252
  try {
239
- await accessFn({ params: match.params, searchParams: {} });
253
+ await accessFn({ params: match.params });
240
254
  await setSpanAttribute('timber.result', 'pass');
241
255
  accessVerdicts.set(si, 'pass');
242
256
  } catch (error) {
@@ -302,10 +316,7 @@ export async function buildRouteElement(
302
316
  );
303
317
  };
304
318
 
305
- let element = h(TracedPage, {
306
- params: paramsPromise,
307
- searchParams: {},
308
- });
319
+ let element = h(TracedPage, {});
309
320
 
310
321
  // Build a lookup of layout components by segment for O(1) access.
311
322
  const layoutBySegment = new Map(
@@ -352,12 +363,7 @@ export async function buildRouteElement(
352
363
  // same urlPath (e.g., /(marketing) and /(app) both have "/"),
353
364
  // which would cause the wrong cached layout to be reused
354
365
  const skip =
355
- shouldSkipSegment(
356
- segment.urlPath,
357
- layoutComponent,
358
- isLeaf,
359
- clientStateTree ?? null
360
- ) &&
366
+ shouldSkipSegment(segment.urlPath, layoutComponent, isLeaf, clientStateTree ?? null) &&
361
367
  hasRenderedLayoutBelow &&
362
368
  segment.segmentType !== 'group';
363
369
 
@@ -385,13 +391,11 @@ export async function buildRouteElement(
385
391
  if (segment.access) {
386
392
  const accessMod = (await segment.access.load()) as Record<string, unknown>;
387
393
  const accessFn = accessMod.default as
388
- | ((ctx: { params: Record<string, string | string[]>; searchParams: unknown }) => unknown)
394
+ | ((ctx: { params: Record<string, string | string[]> }) => unknown)
389
395
  | undefined;
390
396
  if (accessFn) {
391
397
  element = h(AccessGate, {
392
398
  accessFn,
393
- params: match.params,
394
- searchParams: {},
395
399
  segmentName: segment.segmentName,
396
400
  verdict: accessVerdicts.get(i),
397
401
  children: element,
@@ -408,7 +412,6 @@ export async function buildRouteElement(
408
412
  slotProps[slotName] = await resolveSlotElement(
409
413
  slotNode as ManifestSegmentNode,
410
414
  match,
411
- paramsPromise,
412
415
  h,
413
416
  interception
414
417
  );
@@ -417,39 +420,28 @@ export async function buildRouteElement(
417
420
  const segmentPath = segment.urlPath.split('/');
418
421
  const parallelRouteKeys = Object.keys(segment.slots ?? {});
419
422
 
420
- // Wrap the layout component in an OTEL span.
421
- // For route groups, urlPath is "/" (groups don't add URL segments), so
422
- // include the directory name to distinguish e.g. "layout /(pre-release)"
423
- // from the root "layout /".
424
- const segmentForSpan = segment;
425
- const layoutComponentForSpan = layoutComponent;
426
- const segmentLabel =
427
- segmentForSpan.segmentType === 'group'
428
- ? `${segmentForSpan.urlPath === '/' ? '' : segmentForSpan.urlPath}/${segmentForSpan.segmentName}`
429
- : segmentForSpan.urlPath;
430
- const TracedLayout = async (props: Record<string, unknown>) => {
431
- return withSpan('timber.layout', { 'timber.segment': segmentLabel }, () =>
432
- (layoutComponentForSpan as (props: Record<string, unknown>) => unknown)(props)
433
- );
434
- };
435
-
436
- // segmentId uniquely identifies this segment for client-side element
437
- // caching. For route groups, urlPath is shared with the parent (both "/"),
438
- // so we include the group name to distinguish them. Without this, the
439
- // segment merger's element cache would conflate root and group elements.
423
+ // For route groups, urlPath is shared with the parent (both "/"),
424
+ // so include the group name to distinguish them. Used for both OTEL
425
+ // span labels and client-side element caching (segmentId).
440
426
  const segmentId =
441
427
  segment.segmentType === 'group'
442
428
  ? `${segment.urlPath === '/' ? '' : segment.urlPath}/${segment.segmentName}`
443
429
  : segment.urlPath;
444
430
 
431
+ // Wrap the layout component in an OTEL span
432
+ const layoutComponentRef = layoutComponent;
433
+ const TracedLayout = async (props: Record<string, unknown>) => {
434
+ return withSpan('timber.layout', { 'timber.segment': segmentId }, () =>
435
+ (layoutComponentRef as (props: Record<string, unknown>) => unknown)(props)
436
+ );
437
+ };
438
+
445
439
  element = h(SegmentProvider, {
446
440
  segments: segmentPath,
447
441
  segmentId,
448
442
  parallelRouteKeys,
449
443
  children: h(TracedLayout, {
450
444
  ...slotProps,
451
- params: paramsPromise,
452
- searchParams: {},
453
445
  children: element,
454
446
  }),
455
447
  });
@@ -9,6 +9,7 @@
9
9
  */
10
10
 
11
11
  import type { RouteContext } from './types.js';
12
+ import { logRouteError } from './logger.js';
12
13
 
13
14
  // ─── Types ───────────────────────────────────────────────────────────────
14
15
 
@@ -122,7 +123,7 @@ async function runHandler(handler: RouteHandler, ctx: RouteContext): Promise<Res
122
123
  const res = await handler(ctx);
123
124
  return mergeResponseHeaders(res, ctx.headers);
124
125
  } catch (error) {
125
- console.error('[timber] Uncaught error in route.ts handler:', error);
126
+ logRouteError({ method: ctx.req.method, path: new URL(ctx.req.url).pathname, error });
126
127
  return new Response(null, { status: 500 });
127
128
  }
128
129
  }