@timber-js/app 0.2.0-alpha.98 → 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 (322) hide show
  1. package/LICENSE +8 -0
  2. package/dist/_chunks/actions-CQ8Z8VGL.js +1061 -0
  3. package/dist/_chunks/actions-CQ8Z8VGL.js.map +1 -0
  4. package/dist/_chunks/build-output-helper-DXnW0qjz.js +61 -0
  5. package/dist/_chunks/build-output-helper-DXnW0qjz.js.map +1 -0
  6. package/dist/_chunks/{define-Itxvcd7F.js → define-B-Q_UMOD.js} +19 -23
  7. package/dist/_chunks/define-B-Q_UMOD.js.map +1 -0
  8. package/dist/_chunks/{define-C77ScO0m.js → define-CfBPoJb0.js} +24 -7
  9. package/dist/_chunks/define-CfBPoJb0.js.map +1 -0
  10. package/dist/_chunks/define-cookie-BjpIt4UC.js +194 -0
  11. package/dist/_chunks/define-cookie-BjpIt4UC.js.map +1 -0
  12. package/dist/_chunks/{format-CYBGxKtc.js → format-Bcn-Iv1x.js} +1 -1
  13. package/dist/_chunks/{format-CYBGxKtc.js.map → format-Bcn-Iv1x.js.map} +1 -1
  14. package/dist/_chunks/handler-store-B-lqaGyh.js +54 -0
  15. package/dist/_chunks/handler-store-B-lqaGyh.js.map +1 -0
  16. package/dist/_chunks/logger-0m8MsKdc.js +291 -0
  17. package/dist/_chunks/logger-0m8MsKdc.js.map +1 -0
  18. package/dist/_chunks/merge-search-params-BphMdht_.js +122 -0
  19. package/dist/_chunks/merge-search-params-BphMdht_.js.map +1 -0
  20. package/dist/_chunks/navigation-root-BCYczjml.js +96 -0
  21. package/dist/_chunks/navigation-root-BCYczjml.js.map +1 -0
  22. package/dist/_chunks/registry-I2ss-lvy.js +20 -0
  23. package/dist/_chunks/registry-I2ss-lvy.js.map +1 -0
  24. package/dist/_chunks/router-ref-h3-UaCQv.js +28 -0
  25. package/dist/_chunks/router-ref-h3-UaCQv.js.map +1 -0
  26. package/dist/_chunks/{schema-bridge-C3xl_vfb.js → schema-bridge-Cxu4l-7p.js} +1 -1
  27. package/dist/_chunks/{schema-bridge-C3xl_vfb.js.map → schema-bridge-Cxu4l-7p.js.map} +1 -1
  28. package/dist/_chunks/{segment-context-fHFLF1PE.js → segment-context-Dx_OizxD.js} +1 -1
  29. package/dist/_chunks/{segment-context-fHFLF1PE.js.map → segment-context-Dx_OizxD.js.map} +1 -1
  30. package/dist/_chunks/{router-ref-C8OCm7g7.js → ssr-data-B4CdH7rE.js} +2 -26
  31. package/dist/_chunks/ssr-data-B4CdH7rE.js.map +1 -0
  32. package/dist/_chunks/{stale-reload-BX5gL1r-.js → stale-reload-Bab885FO.js} +1 -1
  33. package/dist/_chunks/{stale-reload-BX5gL1r-.js.map → stale-reload-Bab885FO.js.map} +1 -1
  34. package/dist/_chunks/tracing-C8V-YGsP.js +329 -0
  35. package/dist/_chunks/tracing-C8V-YGsP.js.map +1 -0
  36. package/dist/_chunks/{use-query-states-BiV5GJgm.js → use-query-states-B2XTqxDR.js} +3 -19
  37. package/dist/_chunks/use-query-states-B2XTqxDR.js.map +1 -0
  38. package/dist/_chunks/{use-params-IOPu7E8t.js → use-segment-params-BkpKAQ7D.js} +9 -95
  39. package/dist/_chunks/use-segment-params-BkpKAQ7D.js.map +1 -0
  40. package/dist/_chunks/{walkers-VOXgavMF.js → walkers-Tg0Alwcg.js} +6 -3
  41. package/dist/_chunks/walkers-Tg0Alwcg.js.map +1 -0
  42. package/dist/_chunks/{dev-warnings-DpGRGoDi.js → warnings-Cg47l5sk.js} +3 -3
  43. package/dist/_chunks/warnings-Cg47l5sk.js.map +1 -0
  44. package/dist/adapters/build-output-helper.d.ts +28 -0
  45. package/dist/adapters/build-output-helper.d.ts.map +1 -0
  46. package/dist/adapters/cloudflare.d.ts.map +1 -1
  47. package/dist/adapters/cloudflare.js +8 -28
  48. package/dist/adapters/cloudflare.js.map +1 -1
  49. package/dist/adapters/nitro.d.ts.map +1 -1
  50. package/dist/adapters/nitro.js +8 -26
  51. package/dist/adapters/nitro.js.map +1 -1
  52. package/dist/adapters/shared.d.ts +16 -0
  53. package/dist/adapters/shared.d.ts.map +1 -0
  54. package/dist/cache/index.js +9 -2
  55. package/dist/cache/index.js.map +1 -1
  56. package/dist/cache/timber-cache.d.ts.map +1 -1
  57. package/dist/client/error-boundary.js +2 -1
  58. package/dist/client/error-boundary.js.map +1 -1
  59. package/dist/client/form.d.ts +10 -24
  60. package/dist/client/form.d.ts.map +1 -1
  61. package/dist/client/index.d.ts +1 -5
  62. package/dist/client/index.d.ts.map +1 -1
  63. package/dist/client/index.js +40 -90
  64. package/dist/client/index.js.map +1 -1
  65. package/dist/client/internal.d.ts +2 -1
  66. package/dist/client/internal.d.ts.map +1 -1
  67. package/dist/client/internal.js +81 -7
  68. package/dist/client/internal.js.map +1 -1
  69. package/dist/client/rsc-fetch.d.ts.map +1 -1
  70. package/dist/client/state.d.ts +1 -1
  71. package/dist/client/use-cookie.d.ts +8 -0
  72. package/dist/client/use-cookie.d.ts.map +1 -1
  73. package/dist/client/{use-params.d.ts → use-segment-params.d.ts} +1 -1
  74. package/dist/client/use-segment-params.d.ts.map +1 -0
  75. package/dist/codec.d.ts +1 -1
  76. package/dist/codec.d.ts.map +1 -1
  77. package/dist/codec.js +2 -2
  78. package/dist/config-types.d.ts +28 -0
  79. package/dist/config-types.d.ts.map +1 -1
  80. package/dist/cookies/define-cookie.d.ts +87 -35
  81. package/dist/cookies/define-cookie.d.ts.map +1 -1
  82. package/dist/cookies/index.d.ts +2 -1
  83. package/dist/cookies/index.d.ts.map +1 -1
  84. package/dist/cookies/index.js +48 -2
  85. package/dist/cookies/index.js.map +1 -0
  86. package/dist/cookies/json-cookie.d.ts +64 -0
  87. package/dist/cookies/json-cookie.d.ts.map +1 -0
  88. package/dist/cookies/validation.d.ts +46 -0
  89. package/dist/cookies/validation.d.ts.map +1 -0
  90. package/dist/{plugins/dev-404-page.d.ts → dev-tools/404-page.d.ts} +1 -1
  91. package/dist/dev-tools/404-page.d.ts.map +1 -0
  92. package/dist/{plugins/dev-browser-logs.d.ts → dev-tools/browser-logs.d.ts} +1 -1
  93. package/dist/dev-tools/browser-logs.d.ts.map +1 -0
  94. package/dist/{plugins/dev-error-page.d.ts → dev-tools/error-page.d.ts} +2 -2
  95. package/dist/dev-tools/error-page.d.ts.map +1 -0
  96. package/dist/{server/dev-holding-server.d.ts → dev-tools/holding-server.d.ts} +1 -1
  97. package/dist/dev-tools/holding-server.d.ts.map +1 -0
  98. package/dist/dev-tools/index.d.ts +31 -0
  99. package/dist/dev-tools/index.d.ts.map +1 -0
  100. package/dist/{server/dev-span-processor.d.ts → dev-tools/instrumentation.d.ts} +26 -6
  101. package/dist/dev-tools/instrumentation.d.ts.map +1 -0
  102. package/dist/{server/dev-logger.d.ts → dev-tools/logger.d.ts} +1 -1
  103. package/dist/dev-tools/logger.d.ts.map +1 -0
  104. package/dist/{plugins/dev-logs.d.ts → dev-tools/logs.d.ts} +1 -1
  105. package/dist/dev-tools/logs.d.ts.map +1 -0
  106. package/dist/{plugins/dev-error-overlay.d.ts → dev-tools/overlay.d.ts} +3 -12
  107. package/dist/dev-tools/overlay.d.ts.map +1 -0
  108. package/dist/dev-tools/stack-classifier.d.ts +34 -0
  109. package/dist/dev-tools/stack-classifier.d.ts.map +1 -0
  110. package/dist/{plugins/dev-terminal-error.d.ts → dev-tools/terminal.d.ts} +2 -2
  111. package/dist/dev-tools/terminal.d.ts.map +1 -0
  112. package/dist/{server/dev-warnings.d.ts → dev-tools/warnings.d.ts} +1 -1
  113. package/dist/dev-tools/warnings.d.ts.map +1 -0
  114. package/dist/index.d.ts +1 -0
  115. package/dist/index.d.ts.map +1 -1
  116. package/dist/index.js +97 -72
  117. package/dist/index.js.map +1 -1
  118. package/dist/plugin-context.d.ts +1 -1
  119. package/dist/plugin-context.d.ts.map +1 -1
  120. package/dist/plugins/adapter-build.d.ts.map +1 -1
  121. package/dist/routing/convention-lint.d.ts.map +1 -1
  122. package/dist/routing/index.js +1 -1
  123. package/dist/routing/scanner.d.ts.map +1 -1
  124. package/dist/routing/status-file-lint.d.ts.map +1 -1
  125. package/dist/search-params/define.d.ts +25 -7
  126. package/dist/search-params/define.d.ts.map +1 -1
  127. package/dist/search-params/index.js +5 -3
  128. package/dist/search-params/index.js.map +1 -1
  129. package/dist/search-params/wrappers.d.ts +2 -2
  130. package/dist/search-params/wrappers.d.ts.map +1 -1
  131. package/dist/segment-params/define.d.ts +23 -6
  132. package/dist/segment-params/define.d.ts.map +1 -1
  133. package/dist/segment-params/index.js +1 -1
  134. package/dist/server/access-gate.d.ts +4 -3
  135. package/dist/server/access-gate.d.ts.map +1 -1
  136. package/dist/server/action-handler.d.ts +15 -6
  137. package/dist/server/action-handler.d.ts.map +1 -1
  138. package/dist/server/als-registry.d.ts +5 -5
  139. package/dist/server/als-registry.d.ts.map +1 -1
  140. package/dist/server/asset-headers.d.ts +1 -15
  141. package/dist/server/asset-headers.d.ts.map +1 -1
  142. package/dist/server/cookie-context.d.ts +170 -0
  143. package/dist/server/cookie-context.d.ts.map +1 -0
  144. package/dist/server/cookie-parsing.d.ts +51 -0
  145. package/dist/server/cookie-parsing.d.ts.map +1 -0
  146. package/dist/server/deny-boundary.d.ts +90 -0
  147. package/dist/server/deny-boundary.d.ts.map +1 -0
  148. package/dist/server/deny-renderer.d.ts.map +1 -1
  149. package/dist/server/early-hints-sender.d.ts.map +1 -1
  150. package/dist/server/index.d.ts +5 -4
  151. package/dist/server/index.d.ts.map +1 -1
  152. package/dist/server/index.js +4 -149
  153. package/dist/server/index.js.map +1 -1
  154. package/dist/server/internal.d.ts +6 -4
  155. package/dist/server/internal.d.ts.map +1 -1
  156. package/dist/server/internal.js +261 -408
  157. package/dist/server/internal.js.map +1 -1
  158. package/dist/server/logger.d.ts +14 -0
  159. package/dist/server/logger.d.ts.map +1 -1
  160. package/dist/server/middleware-runner.d.ts +17 -0
  161. package/dist/server/middleware-runner.d.ts.map +1 -1
  162. package/dist/server/param-coercion.d.ts +26 -0
  163. package/dist/server/param-coercion.d.ts.map +1 -0
  164. package/dist/server/pipeline-helpers.d.ts +14 -7
  165. package/dist/server/pipeline-helpers.d.ts.map +1 -1
  166. package/dist/server/pipeline-outcome.d.ts +49 -0
  167. package/dist/server/pipeline-outcome.d.ts.map +1 -0
  168. package/dist/server/pipeline-phases.d.ts +4 -49
  169. package/dist/server/pipeline-phases.d.ts.map +1 -1
  170. package/dist/server/pipeline.d.ts +0 -2
  171. package/dist/server/pipeline.d.ts.map +1 -1
  172. package/dist/server/request-context.d.ts +22 -159
  173. package/dist/server/request-context.d.ts.map +1 -1
  174. package/dist/server/route-element-builder.d.ts.map +1 -1
  175. package/dist/server/rsc-entry/action-middleware-runner.d.ts +66 -0
  176. package/dist/server/rsc-entry/action-middleware-runner.d.ts.map +1 -0
  177. package/dist/server/rsc-entry/helpers.d.ts +1 -1
  178. package/dist/server/rsc-entry/helpers.d.ts.map +1 -1
  179. package/dist/server/rsc-entry/index.d.ts.map +1 -1
  180. package/dist/server/rsc-entry/render-route.d.ts +50 -0
  181. package/dist/server/rsc-entry/render-route.d.ts.map +1 -0
  182. package/dist/server/rsc-entry/wrap-action-dispatch.d.ts +59 -14
  183. package/dist/server/rsc-entry/wrap-action-dispatch.d.ts.map +1 -1
  184. package/dist/server/state-tree-diff.d.ts.map +1 -1
  185. package/dist/server/tracing.d.ts +1 -1
  186. package/dist/server/tracing.d.ts.map +1 -1
  187. package/dist/server/tree-builder.d.ts +45 -16
  188. package/dist/server/tree-builder.d.ts.map +1 -1
  189. package/dist/server/types.d.ts +48 -0
  190. package/dist/server/types.d.ts.map +1 -1
  191. package/dist/server/utils/escape-html.d.ts +14 -0
  192. package/dist/server/utils/escape-html.d.ts.map +1 -0
  193. package/dist/shims/headers.d.ts +2 -2
  194. package/dist/shims/headers.d.ts.map +1 -1
  195. package/dist/shims/navigation-client.d.ts +3 -1
  196. package/dist/shims/navigation-client.d.ts.map +1 -1
  197. package/dist/shims/navigation.d.ts +9 -4
  198. package/dist/shims/navigation.d.ts.map +1 -1
  199. package/package.json +6 -7
  200. package/src/adapters/build-output-helper.ts +77 -0
  201. package/src/adapters/cloudflare.ts +10 -50
  202. package/src/adapters/nitro.ts +11 -45
  203. package/src/adapters/shared.ts +40 -0
  204. package/src/cache/timber-cache.ts +3 -2
  205. package/src/cli.ts +0 -0
  206. package/src/client/form.tsx +17 -25
  207. package/src/client/index.ts +16 -9
  208. package/src/client/internal.ts +3 -2
  209. package/src/client/router.ts +1 -1
  210. package/src/client/rsc-fetch.ts +15 -0
  211. package/src/client/state.ts +2 -2
  212. package/src/client/use-cookie.ts +29 -0
  213. package/src/codec.ts +3 -7
  214. package/src/config-types.ts +28 -0
  215. package/src/cookies/define-cookie.ts +271 -78
  216. package/src/cookies/index.ts +11 -8
  217. package/src/cookies/json-cookie.ts +105 -0
  218. package/src/cookies/validation.ts +134 -0
  219. package/src/{plugins/dev-404-page.ts → dev-tools/404-page.ts} +2 -7
  220. package/src/{plugins/dev-error-page.ts → dev-tools/error-page.ts} +5 -32
  221. package/src/dev-tools/index.ts +90 -0
  222. package/src/dev-tools/instrumentation.ts +176 -0
  223. package/src/{plugins/dev-logs.ts → dev-tools/logs.ts} +2 -2
  224. package/src/{plugins/dev-error-overlay.ts → dev-tools/overlay.ts} +5 -23
  225. package/src/dev-tools/stack-classifier.ts +75 -0
  226. package/src/{plugins/dev-terminal-error.ts → dev-tools/terminal.ts} +4 -38
  227. package/src/{server/dev-warnings.ts → dev-tools/warnings.ts} +1 -1
  228. package/src/index.ts +11 -3
  229. package/src/plugin-context.ts +1 -1
  230. package/src/plugins/adapter-build.ts +3 -1
  231. package/src/plugins/dev-server.ts +3 -3
  232. package/src/plugins/shims.ts +1 -1
  233. package/src/plugins/static-build.ts +1 -1
  234. package/src/routing/convention-lint.ts +5 -4
  235. package/src/routing/scanner.ts +5 -2
  236. package/src/routing/status-file-lint.ts +4 -2
  237. package/src/search-params/define.ts +71 -15
  238. package/src/search-params/wrappers.ts +9 -2
  239. package/src/segment-params/define.ts +66 -13
  240. package/src/server/access-gate.tsx +9 -8
  241. package/src/server/action-handler.ts +28 -38
  242. package/src/server/als-registry.ts +5 -5
  243. package/src/server/asset-headers.ts +8 -34
  244. package/src/server/cookie-context.ts +468 -0
  245. package/src/server/cookie-parsing.ts +135 -0
  246. package/src/server/{deny-page-resolver.ts → deny-boundary.ts} +78 -14
  247. package/src/server/deny-renderer.ts +2 -7
  248. package/src/server/early-hints-sender.ts +3 -2
  249. package/src/server/fallback-error.ts +1 -1
  250. package/src/server/index.ts +13 -14
  251. package/src/server/internal.ts +10 -3
  252. package/src/server/logger.ts +23 -0
  253. package/src/server/middleware-runner.ts +44 -0
  254. package/src/server/param-coercion.ts +76 -0
  255. package/src/server/pipeline-helpers.ts +37 -13
  256. package/src/server/pipeline-outcome.ts +167 -0
  257. package/src/server/pipeline-phases.ts +27 -209
  258. package/src/server/pipeline.ts +2 -9
  259. package/src/server/request-context.ts +46 -451
  260. package/src/server/route-element-builder.ts +7 -3
  261. package/src/server/rsc-entry/action-middleware-runner.ts +167 -0
  262. package/src/server/rsc-entry/error-renderer.ts +1 -1
  263. package/src/server/rsc-entry/helpers.ts +2 -7
  264. package/src/server/rsc-entry/index.ts +34 -273
  265. package/src/server/rsc-entry/render-route.ts +304 -0
  266. package/src/server/rsc-entry/rsc-payload.ts +1 -1
  267. package/src/server/rsc-entry/ssr-renderer.ts +2 -2
  268. package/src/server/rsc-entry/wrap-action-dispatch.ts +316 -23
  269. package/src/server/ssr-entry.ts +1 -1
  270. package/src/server/state-tree-diff.ts +4 -1
  271. package/src/server/tracing.ts +3 -3
  272. package/src/server/tree-builder.ts +128 -52
  273. package/src/server/types.ts +52 -0
  274. package/src/server/utils/escape-html.ts +20 -0
  275. package/src/shims/headers.ts +3 -3
  276. package/src/shims/navigation-client.ts +4 -3
  277. package/src/shims/navigation.ts +9 -7
  278. package/dist/_chunks/actions-DLnUaR65.js +0 -421
  279. package/dist/_chunks/actions-DLnUaR65.js.map +0 -1
  280. package/dist/_chunks/als-registry-HS0LGUl2.js +0 -41
  281. package/dist/_chunks/als-registry-HS0LGUl2.js.map +0 -1
  282. package/dist/_chunks/debug-ECi_61pb.js +0 -108
  283. package/dist/_chunks/debug-ECi_61pb.js.map +0 -1
  284. package/dist/_chunks/define-C77ScO0m.js.map +0 -1
  285. package/dist/_chunks/define-Itxvcd7F.js.map +0 -1
  286. package/dist/_chunks/define-cookie-BowvzoP0.js +0 -94
  287. package/dist/_chunks/define-cookie-BowvzoP0.js.map +0 -1
  288. package/dist/_chunks/dev-warnings-DpGRGoDi.js.map +0 -1
  289. package/dist/_chunks/merge-search-params-Cm_KIWDX.js +0 -41
  290. package/dist/_chunks/merge-search-params-Cm_KIWDX.js.map +0 -1
  291. package/dist/_chunks/request-context-CK5tZqIP.js +0 -478
  292. package/dist/_chunks/request-context-CK5tZqIP.js.map +0 -1
  293. package/dist/_chunks/router-ref-C8OCm7g7.js.map +0 -1
  294. package/dist/_chunks/tracing-CCYbKn5n.js +0 -238
  295. package/dist/_chunks/tracing-CCYbKn5n.js.map +0 -1
  296. package/dist/_chunks/use-params-IOPu7E8t.js.map +0 -1
  297. package/dist/_chunks/use-query-states-BiV5GJgm.js.map +0 -1
  298. package/dist/_chunks/walkers-VOXgavMF.js.map +0 -1
  299. package/dist/client/use-params.d.ts.map +0 -1
  300. package/dist/plugins/dev-404-page.d.ts.map +0 -1
  301. package/dist/plugins/dev-browser-logs.d.ts.map +0 -1
  302. package/dist/plugins/dev-error-overlay.d.ts.map +0 -1
  303. package/dist/plugins/dev-error-page.d.ts.map +0 -1
  304. package/dist/plugins/dev-logs.d.ts.map +0 -1
  305. package/dist/plugins/dev-terminal-error.d.ts.map +0 -1
  306. package/dist/server/deny-page-resolver.d.ts +0 -52
  307. package/dist/server/deny-page-resolver.d.ts.map +0 -1
  308. package/dist/server/dev-fetch-instrumentation.d.ts +0 -22
  309. package/dist/server/dev-fetch-instrumentation.d.ts.map +0 -1
  310. package/dist/server/dev-holding-server.d.ts.map +0 -1
  311. package/dist/server/dev-logger.d.ts.map +0 -1
  312. package/dist/server/dev-span-processor.d.ts.map +0 -1
  313. package/dist/server/dev-warnings.d.ts.map +0 -1
  314. package/dist/server/page-deny-boundary.d.ts +0 -31
  315. package/dist/server/page-deny-boundary.d.ts.map +0 -1
  316. package/src/server/dev-fetch-instrumentation.ts +0 -96
  317. package/src/server/dev-span-processor.ts +0 -78
  318. package/src/server/page-deny-boundary.tsx +0 -56
  319. /package/src/client/{use-params.ts → use-segment-params.ts} +0 -0
  320. /package/src/{plugins/dev-browser-logs.ts → dev-tools/browser-logs.ts} +0 -0
  321. /package/src/{server/dev-holding-server.ts → dev-tools/holding-server.ts} +0 -0
  322. /package/src/{server/dev-logger.ts → dev-tools/logger.ts} +0 -0
@@ -0,0 +1,304 @@
1
+ /**
2
+ * Route renderer — turns a matched route into a Response.
3
+ *
4
+ * Extracted from `rsc-entry/index.ts` so the entry can stay focused on
5
+ * pipeline construction. This module owns the steps that happen *after*
6
+ * the pipeline has matched a route and before it commits a response:
7
+ * element-tree build, RSC Flight render, and dispatch to either the SSR
8
+ * HTML path or the RSC payload path. It also handles the
9
+ * synchronous-signal escape hatches (deny / redirect / render error
10
+ * thrown during stream creation) so the entry doesn't carry that logic.
11
+ *
12
+ * The function is parameterized over its dependencies — `clientBootstrap`,
13
+ * `clientJsDisabled`, `rootSegment`, `globalError` — so the entry passes
14
+ * them in once at handler-construction time and they ride along on every
15
+ * call. No globals, no closures over the entry's createRequestHandler
16
+ * scope.
17
+ *
18
+ * Design docs: 02-rendering-pipeline.md, 19-client-navigation.md.
19
+ */
20
+
21
+ import type { BuildManifest } from '../build-manifest.js';
22
+ import {
23
+ buildFontPreloadTags,
24
+ buildModulepreloadTags,
25
+ collectRouteFonts,
26
+ collectRouteModulepreloads,
27
+ } from '../build-manifest.js';
28
+ import { isDevMode } from '../debug.js';
29
+ import type { LayoutEntry } from '../deny-renderer.js';
30
+ import { renderDenyPage, renderDenyPageAsRsc } from '../deny-renderer.js';
31
+ import type { ClientBootstrapConfig } from '../html-injectors.js';
32
+ import type { InterceptionContext, RouteMatch } from '../pipeline.js';
33
+ import { buildRouteElement, ParamCoercionError } from '../route-element-builder.js';
34
+ import type { ManifestSegmentNode } from '../route-matcher.js';
35
+ import { recordTiming } from '../server-timing.js';
36
+ import { requestContextAls } from '../als-registry.js';
37
+ import { parseClientStateTree } from '../state-tree-diff.js';
38
+
39
+ import { handleApiRoute } from './api-handler.js';
40
+ import { renderErrorPage, renderNoMatchPage } from './error-renderer.js';
41
+ import {
42
+ buildRedirectResponse,
43
+ createDebugChannelSink,
44
+ escapeHtml,
45
+ isRscPayloadRequest,
46
+ } from './helpers.js';
47
+ import { buildRscPayloadResponse } from './rsc-payload.js';
48
+ import { renderRscStream } from './rsc-stream.js';
49
+ import { renderSsrResponse } from './ssr-renderer.js';
50
+ import { callSsr } from './ssr-bridge.js';
51
+
52
+ /**
53
+ * Static-per-handler dependencies for `renderRoute` — captured once at
54
+ * handler construction so each per-request call site only has to pass
55
+ * the request-specific arguments.
56
+ */
57
+ export interface RenderRouteDeps {
58
+ clientBootstrap: ClientBootstrapConfig;
59
+ clientJsDisabled: boolean;
60
+ rootSegment: ManifestSegmentNode;
61
+ buildManifest: BuildManifest;
62
+ globalError?: { load: () => Promise<unknown>; filePath: string };
63
+ }
64
+
65
+ /**
66
+ * Render a matched route to an HTML Response via RSC → SSR pipeline,
67
+ * or return a raw RSC Flight stream for client-side navigation requests.
68
+ *
69
+ * 1. Load page/layout components from the segment chain
70
+ * 2. Resolve metadata
71
+ * 3. Render to RSC Flight stream (serializes "use client" as references)
72
+ * 4. If Accept: text/x-component → return RSC stream directly
73
+ * Otherwise → pass RSC stream to SSR entry for HTML rendering
74
+ */
75
+ export async function renderRoute(
76
+ req: Request,
77
+ match: RouteMatch,
78
+ responseHeaders: Headers,
79
+ deps: RenderRouteDeps,
80
+ interception?: InterceptionContext
81
+ ): Promise<Response> {
82
+ const { clientBootstrap, clientJsDisabled, rootSegment, buildManifest, globalError } = deps;
83
+ const segments = match.segments;
84
+ const leaf = segments[segments.length - 1];
85
+
86
+ // API routes (route.ts) — run access.ts standalone then dispatch to handler.
87
+ // No React render pass — AccessGate is not used, React.cache is not active.
88
+ // See design/04-authorization.md §"Auth in API Routes".
89
+ if (leaf.route && !leaf.page) {
90
+ return handleApiRoute(req, match, segments, responseHeaders);
91
+ }
92
+
93
+ // Parse X-Timber-State-Tree for RSC payload requests (client navigation).
94
+ // The state tree lists sync segments the client has cached — the server
95
+ // skips re-rendering those layouts for a smaller, faster RSC payload.
96
+ // Only used for RSC requests — HTML requests always get a full render.
97
+ // See design/19-client-navigation.md §"X-Timber-State-Tree Header"
98
+ const clientStateTree = isRscPayloadRequest(req) ? parseClientStateTree(req) : null;
99
+
100
+ // Build the React element tree — loads modules, collects access checks,
101
+ // resolves metadata. Access checks are NOT run here — they run inside
102
+ // AccessPreRunner during renderToReadableStream so that access.ts and
103
+ // render components share the same React.cache scope (TIM-662).
104
+ //
105
+ // DenySignal/RedirectSignal from access checks are caught by onError
106
+ // during stream consumption and handled by renderSsrResponse /
107
+ // buildRscPayloadResponse.
108
+ let routeResult;
109
+ const _buildStart = performance.now();
110
+ try {
111
+ routeResult = await buildRouteElement(req, match, interception, clientStateTree);
112
+ } catch (error) {
113
+ // Param coercion failed — render the custom 404 page (status files / not-found).
114
+ // Previously returned a bare Response(null, { status: 404 }) which bypassed
115
+ // custom not-found pages. Now routes through renderNoMatchPage so apps with
116
+ // 404.tsx / not-found status files render their custom page.
117
+ if (error instanceof ParamCoercionError) {
118
+ return renderNoMatchPage(req, rootSegment, responseHeaders, clientBootstrap);
119
+ }
120
+ // No PageComponent found — same treatment as param coercion: render custom 404.
121
+ if (error instanceof Error && error.message.startsWith('No page component')) {
122
+ return renderNoMatchPage(req, rootSegment, responseHeaders, clientBootstrap);
123
+ }
124
+ throw error;
125
+ }
126
+
127
+ const _buildEnd = performance.now();
128
+ recordTiming({
129
+ name: 'build',
130
+ dur: Math.round(_buildEnd - _buildStart),
131
+ desc: 'build element tree',
132
+ });
133
+
134
+ const { element, headElements, layoutComponents, deferSuspenseFor, skippedSegments } =
135
+ routeResult;
136
+
137
+ // Build head HTML for injection into the SSR output.
138
+ // Collects CSS, fonts, and modulepreload from the build manifest for matched segments.
139
+ // In dev mode the manifest is empty — Vite HMR handles CSS/JS.
140
+ //
141
+ // Link headers (for 103 Early Hints) are emitted by the earlyHints pipeline
142
+ // stage before middleware runs. Here we only emit the <head> HTML fallback tags
143
+ // — these ensure resources load even on platforms without Early Hints support.
144
+ let headHtml = '';
145
+
146
+ // CSS is handled by the RSC plugin via ReactDOM.preinit() with
147
+ // data-precedence attributes. This injects <link rel="stylesheet">
148
+ // tags during the RSC render phase — before our headHtml injection.
149
+ // We do NOT emit additional <link rel="stylesheet"> tags here because:
150
+ // 1. React's Float system deduplicates them, making ours redundant
151
+ // 2. The duplicate reference confuses React's client-side preload
152
+ // deduplication, causing "preload ignored" browser warnings
153
+ //
154
+ // CSS URLs are still collected for Early Hints (Link headers) in
155
+ // buildEarlyHintsHeaders() — those are HTTP headers, not DOM elements,
156
+ // so they don't conflict with Float.
157
+
158
+ // Font CSS is NOT inlined here anymore (TIM-828). The transform hook
159
+ // injects a side-effect `import 'virtual:timber-font-css/<hash>.css'`
160
+ // into each file that calls a font function. @vitejs/plugin-rsc's
161
+ // `collectCss` walks the RSC module graph, finds the virtual CSS
162
+ // module, and React Float `preinit()`s it as
163
+ // `<link rel="stylesheet" data-rsc-css-href=...>` — the same path
164
+ // component CSS rides on. Dev and production share a single path,
165
+ // with no `globalThis` channel and no dev/prod skew.
166
+ //
167
+ // Font preload hints (distinct from the @font-face CSS) still flow
168
+ // through `BuildManifest.fonts[importer]` and are emitted below plus
169
+ // as 103 Early Hints via `buildEarlyHintsHeaders()`.
170
+ const fontEntries = collectRouteFonts(segments, buildManifest);
171
+ if (fontEntries.length > 0) {
172
+ headHtml += buildFontPreloadTags(fontEntries);
173
+ }
174
+
175
+ // Skip modulepreload tags when client JavaScript is disabled — no JS to preload.
176
+ if (!clientJsDisabled) {
177
+ const preloadUrls = collectRouteModulepreloads(segments, buildManifest);
178
+ if (preloadUrls.length > 0) {
179
+ headHtml += buildModulepreloadTags(preloadUrls);
180
+ }
181
+ }
182
+
183
+ for (const el of headElements) {
184
+ if (el.tag === 'title' && el.content) {
185
+ headHtml += `<title>${escapeHtml(el.content)}</title>`;
186
+ } else if (el.attrs) {
187
+ const attrs = Object.entries(el.attrs)
188
+ .filter(([, v]) => v != null)
189
+ .map(([k, v]) => `${k}="${escapeHtml(v as string)}"`)
190
+ .join(' ');
191
+ headHtml += `<${el.tag} ${attrs}>`;
192
+ }
193
+ }
194
+
195
+ // In dev mode, inject the browser log forwarding script so console
196
+ // errors/warnings from the browser appear in the server terminal.
197
+ // Set by timber-dev-browser-logs plugin via globalThis (TIM-575).
198
+ if (isDevMode()) {
199
+ const devLogScript = (globalThis as Record<string, unknown>).__timber_dev_browser_log_script as
200
+ | string
201
+ | undefined;
202
+ if (devLogScript) {
203
+ headHtml += devLogScript;
204
+ }
205
+ }
206
+
207
+ // Render to RSC Flight stream with signal tracking.
208
+ const _rscStart = performance.now();
209
+ const { rscStream, signals, getDebugComponents } = renderRscStream(element, req);
210
+
211
+ // Store the debug components getter in ALS so onPipelineError can
212
+ // include component tree context for render-phase errors (dev mode only).
213
+ // Per-request via ALS — no cross-request race. See TIM-557.
214
+ const alsStore = requestContextAls.getStore();
215
+ if (alsStore) {
216
+ alsStore.debugComponentsGetter = getDebugComponents;
217
+ }
218
+ recordTiming({
219
+ name: 'rsc-init',
220
+ dur: Math.round(performance.now() - _rscStart),
221
+ desc: 'RSC stream init',
222
+ });
223
+
224
+ // Synchronous redirect — redirect() in access.ts or a non-async component
225
+ // throws during renderToReadableStream creation. Return HTTP redirect.
226
+ if (signals.redirectSignal) {
227
+ return buildRedirectResponse(req, signals.redirectSignal, responseHeaders);
228
+ }
229
+
230
+ // Synchronous deny — deny() in a non-async component throws during
231
+ // renderToReadableStream creation, caught in the try/catch above.
232
+ if (signals.denySignal) {
233
+ if (isRscPayloadRequest(req)) {
234
+ return renderDenyPageAsRsc(
235
+ signals.denySignal,
236
+ segments,
237
+ layoutComponents as LayoutEntry[],
238
+ responseHeaders,
239
+ createDebugChannelSink
240
+ );
241
+ }
242
+ return renderDenyPage(
243
+ signals.denySignal,
244
+ segments,
245
+ layoutComponents as LayoutEntry[],
246
+ req,
247
+ match,
248
+ responseHeaders,
249
+ clientBootstrap,
250
+ createDebugChannelSink,
251
+ callSsr
252
+ );
253
+ }
254
+
255
+ // Synchronous render error — renderToReadableStream threw before
256
+ // creating the stream. Render the error page with correct 5xx status.
257
+ // (Async render errors are tracked in onError and handled after SSR.)
258
+ if (signals.renderError && !rscStream) {
259
+ return renderErrorPage(
260
+ signals.renderError.error,
261
+ signals.renderError.status,
262
+ segments,
263
+ layoutComponents as LayoutEntry[],
264
+ req,
265
+ match,
266
+ responseHeaders,
267
+ clientBootstrap,
268
+ globalError
269
+ );
270
+ }
271
+
272
+ // For RSC payload requests (client navigation), return the RSC Flight
273
+ // stream directly — skip SSR HTML rendering entirely.
274
+ // See design/19-client-navigation.md §"RSC Payload Handling"
275
+ if (isRscPayloadRequest(req)) {
276
+ return buildRscPayloadResponse(
277
+ req,
278
+ rscStream!,
279
+ signals,
280
+ segments,
281
+ layoutComponents,
282
+ headElements,
283
+ match,
284
+ responseHeaders,
285
+ skippedSegments
286
+ );
287
+ }
288
+
289
+ // Pipe through SSR for HTML rendering with streaming Suspense support.
290
+ return renderSsrResponse({
291
+ req,
292
+ rscStream: rscStream!,
293
+ signals,
294
+ segments,
295
+ layoutComponents,
296
+ match,
297
+ responseHeaders,
298
+ clientBootstrap,
299
+ clientJsDisabled,
300
+ headHtml,
301
+ deferSuspenseFor,
302
+ globalError,
303
+ });
304
+ }
@@ -205,7 +205,7 @@ export async function buildRscPayloadResponse(
205
205
  // Check if a DenySignal was caught in-tree by AccessGate or
206
206
  // PageDenyBoundary. The deny page is in the Flight stream as a normal
207
207
  // element — use the deny status for the HTTP response. See TIM-666.
208
- const { getDenyStatus } = await import('../deny-page-resolver.js');
208
+ const { getDenyStatus } = await import('../deny-boundary.js');
209
209
  const denyStatus = getDenyStatus();
210
210
 
211
211
  return new Response(patchedStream, {
@@ -28,8 +28,8 @@ import {
28
28
  createDebugChannelSink,
29
29
  isAbortError,
30
30
  } from './helpers.js';
31
- import { getCookiesForSsr } from '../request-context.js';
32
- import { getDenyStatus } from '../deny-page-resolver.js';
31
+ import { getCookiesForSsr } from '../cookie-context.js';
32
+ import { getDenyStatus } from '../deny-boundary.js';
33
33
  import { renderErrorPage, type GlobalErrorFile } from './error-renderer.js';
34
34
  import { callSsr } from './ssr-bridge.js';
35
35
  import type { RenderSignals } from './rsc-stream.js';