@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
@@ -50,14 +50,14 @@ export interface ManifestSegmentNode {
50
50
  middleware?: ManifestFile;
51
51
  access?: ManifestFile;
52
52
  route?: ManifestFile;
53
+ /** params.ts — isomorphic convention file for segmentParams + searchParams definitions. */
54
+ params?: ManifestFile;
53
55
  error?: ManifestFile;
54
56
  default?: ManifestFile;
55
57
  denied?: ManifestFile;
56
- searchParams?: ManifestFile;
57
58
  statusFiles?: Record<string, ManifestFile>;
58
59
  jsonStatusFiles?: Record<string, ManifestFile>;
59
60
  legacyStatusFiles?: Record<string, ManifestFile>;
60
- prerender?: ManifestFile;
61
61
  /** Metadata route files (sitemap.ts, robots.ts, icon.tsx, etc.) keyed by base name */
62
62
  metadataRoutes?: Record<string, ManifestFile>;
63
63
 
@@ -69,6 +69,13 @@ export interface ManifestSegmentNode {
69
69
  export interface ManifestRoot {
70
70
  root: ManifestSegmentNode;
71
71
  proxy?: ManifestFile;
72
+ /**
73
+ * Global error page: app/global-error.{tsx,ts,jsx,js}
74
+ * Tier 2 — standalone full-page replacement (no layouts) when no
75
+ * segment-level error file is found. SSR-only render.
76
+ * See design/10-error-handling.md §"Tier 2"
77
+ */
78
+ globalError?: ManifestFile;
72
79
  }
73
80
 
74
81
  // ─── Matcher ──────────────────────────────────────────────────────────────
@@ -6,13 +6,13 @@
6
6
  * See design/04-authorization.md §"Auth in API Routes".
7
7
  */
8
8
 
9
- import { withSpan, setSpanAttribute } from '#/server/tracing.js';
10
- import type { ManifestSegmentNode } from '#/server/route-matcher.js';
11
- import type { RouteMatch } from '#/server/pipeline.js';
12
- import { DenySignal, RedirectSignal } from '#/server/primitives.js';
13
- import { handleRouteRequest } from '#/server/route-handler.js';
14
- import type { RouteModule } from '#/server/route-handler.js';
15
- import type { RouteContext } from '#/server/types.js';
9
+ import { withSpan, setSpanAttribute } from '../tracing.js';
10
+ import type { ManifestSegmentNode } from '../route-matcher.js';
11
+ import type { RouteMatch } from '../pipeline.js';
12
+ import { DenySignal, RedirectSignal } from '../primitives.js';
13
+ import { handleRouteRequest } from '../route-handler.js';
14
+ import type { RouteModule } from '../route-handler.js';
15
+ import type { RouteContext } from '../types.js';
16
16
 
17
17
  export async function handleApiRoute(
18
18
  req: Request,
@@ -90,7 +90,7 @@ async function renderApiDeny(
90
90
  segments: ManifestSegmentNode[],
91
91
  responseHeaders: Headers
92
92
  ): Promise<Response> {
93
- const { resolveManifestStatusFile } = await import('#/server/manifest-status-resolver.js');
93
+ const { resolveManifestStatusFile } = await import('../manifest-status-resolver.js');
94
94
 
95
95
  const resolution = resolveManifestStatusFile(deny.status, segments, 'json');
96
96
  if (resolution) {
@@ -1,63 +1,98 @@
1
1
  /**
2
2
  * RSC Error & No-Match Renderers — handles error pages and 404s.
3
3
  *
4
- * Renders error.tsx / status files and 404 pages through the RSC → SSR pipeline.
4
+ * All error pages (TSX and MDX) render through the RSC → SSR pipeline:
5
+ * - TSX ('use client'): Error is converted to SerializableError, wrapped
6
+ * with ErrorReconstituter to reconstitute a real Error on the client.
7
+ * - MDX (server component): Receives plain props ({ status }), no wrapper needed.
8
+ *
9
+ * This unified approach replaces the former SSR-only bypass for TSX error
10
+ * pages. See design/spike-TIM-565-unify-error-pages.md for the full analysis.
11
+ *
12
+ * Design docs: 10-error-handling.md §"Three-Tier Error Page Rendering"
5
13
  */
6
14
 
7
15
  import { createElement } from 'react';
8
- import { renderToReadableStream } from '#/rsc-runtime/rsc.js';
9
-
10
- import type { RouteMatch } from '#/server/pipeline.js';
11
- import { logRenderError } from '#/server/logger.js';
12
- import type { ManifestSegmentNode } from '#/server/route-matcher.js';
13
- import { DenySignal, RenderError } from '#/server/primitives.js';
14
- import type { ClientBootstrapConfig } from '#/server/html-injectors.js';
15
- import { renderDenyPage } from '#/server/deny-renderer.js';
16
- import type { LayoutEntry } from '#/server/deny-renderer.js';
17
- import type { NavContext } from '#/server/ssr-entry.js';
18
- import { createDebugChannelSink, parseCookiesFromHeader } from './helpers.js';
16
+ import { renderToReadableStream } from '../../rsc-runtime/rsc.js';
17
+
18
+ import type { RouteMatch } from '../pipeline.js';
19
+ import { logRenderError } from '../logger.js';
20
+ import type { ManifestSegmentNode } from '../route-matcher.js';
21
+ import { DenySignal, RenderError } from '../primitives.js';
22
+ import type { ClientBootstrapConfig } from '../html-injectors.js';
23
+ import { flightInitScript } from '../flight-scripts.js';
24
+ import { renderDenyPage } from '../deny-renderer.js';
25
+ import type { LayoutEntry } from '../deny-renderer.js';
26
+ import type { NavContext } from '../ssr-entry.js';
27
+ import { createDebugChannelSink } from './helpers.js';
28
+ import { getCookiesForSsr } from '../request-context.js';
19
29
  import { callSsr } from './ssr-bridge.js';
30
+ import { teeWithErrorPropagation } from '../stream-utils.js';
31
+ import { isDevMode } from '../debug.js';
32
+ import { ErrorReconstituter } from '../../client/error-reconstituter.js';
33
+ import type { SerializableError } from '../../client/error-reconstituter.js';
20
34
 
21
35
  /**
22
- * Render an error page for unhandled throws or RenderError outside Suspense.
23
- *
24
- * Walks the segment chain from leaf to root looking for:
25
- * 1. Specific status file (e.g. 503.tsx) matching the error's status
26
- * 2. 5xx.tsx category catch-all
27
- * 3. error.tsx
28
- *
29
- * Renders the found component with { error, digest, reset } props
30
- * wrapped in layouts, with the correct HTTP status code.
36
+ * A manifest file reference with lazy import and path.
37
+ * Mirrors the shape from ManifestSegmentNode but kept local to avoid
38
+ * exporting internal types from route-matcher.
31
39
  */
32
- export async function renderErrorPage(
33
- error: unknown,
34
- status: number,
35
- segments: ManifestSegmentNode[],
36
- layoutComponents: LayoutEntry[],
37
- req: Request,
38
- match: RouteMatch,
39
- responseHeaders: Headers,
40
- clientBootstrap: ClientBootstrapConfig
41
- ): Promise<Response> {
42
- const h = createElement as (...args: unknown[]) => React.ReactElement;
40
+ export interface GlobalErrorFile {
41
+ load: () => Promise<unknown>;
42
+ filePath: string;
43
+ }
43
44
 
44
- // Walk segments from leaf to root to find the error component
45
- let errorComponent: ((...args: unknown[]) => unknown) | null = null;
46
- let foundSegmentIndex = -1;
45
+ /** MDX/markdown extensions server components that need RSC rendering. */
46
+ const MDX_EXTENSIONS = new Set(['.mdx', '.md']);
47
+
48
+ /**
49
+ * Check if a file path ends with an MDX/markdown extension.
50
+ */
51
+ function isMdxFile(filePath: string): boolean {
52
+ const dotIndex = filePath.lastIndexOf('.');
53
+ if (dotIndex === -1) return false;
54
+ return MDX_EXTENSIONS.has(filePath.slice(dotIndex));
55
+ }
47
56
 
57
+ /**
58
+ * Result of walking the segment chain for an error file.
59
+ */
60
+ interface ErrorFileResolution {
61
+ /** The loaded component */
62
+ component: (...args: unknown[]) => unknown;
63
+ /** Index of the segment where the file was found */
64
+ segmentIndex: number;
65
+ /** Whether the file is MDX (server component) */
66
+ isMdx: boolean;
67
+ }
68
+
69
+ /**
70
+ * Walk segments from leaf to root to find the nearest error file.
71
+ *
72
+ * Fallback chain at each segment:
73
+ * 1. Specific status file (e.g., 503.tsx) — exact match
74
+ * 2. 5xx.tsx — category catch-all
75
+ * 3. error.tsx — catches all unmatched errors
76
+ */
77
+ async function resolveErrorFile(
78
+ status: number,
79
+ segments: ManifestSegmentNode[]
80
+ ): Promise<ErrorFileResolution | null> {
48
81
  for (let i = segments.length - 1; i >= 0; i--) {
49
82
  const segment = segments[i];
50
83
 
51
- // Check specific status file (e.g. 503.tsx)
52
84
  if (segment.statusFiles) {
85
+ // Check specific status file (e.g. 503.tsx)
53
86
  const statusKey = String(status);
54
87
  const specificFile = segment.statusFiles[statusKey];
55
88
  if (specificFile) {
56
89
  const mod = (await specificFile.load()) as Record<string, unknown>;
57
90
  if (mod.default) {
58
- errorComponent = mod.default as (...args: unknown[]) => unknown;
59
- foundSegmentIndex = i;
60
- break;
91
+ return {
92
+ component: mod.default as (...args: unknown[]) => unknown,
93
+ segmentIndex: i,
94
+ isMdx: isMdxFile(specificFile.filePath),
95
+ };
61
96
  }
62
97
  }
63
98
 
@@ -66,9 +101,11 @@ export async function renderErrorPage(
66
101
  if (categoryFile && status >= 500 && status <= 599) {
67
102
  const mod = (await categoryFile.load()) as Record<string, unknown>;
68
103
  if (mod.default) {
69
- errorComponent = mod.default as (...args: unknown[]) => unknown;
70
- foundSegmentIndex = i;
71
- break;
104
+ return {
105
+ component: mod.default as (...args: unknown[]) => unknown,
106
+ segmentIndex: i,
107
+ isMdx: isMdxFile(categoryFile.filePath),
108
+ };
72
109
  }
73
110
  }
74
111
  }
@@ -77,60 +114,228 @@ export async function renderErrorPage(
77
114
  if (segment.error) {
78
115
  const mod = (await segment.error.load()) as Record<string, unknown>;
79
116
  if (mod.default) {
80
- errorComponent = mod.default as (...args: unknown[]) => unknown;
81
- foundSegmentIndex = i;
82
- break;
117
+ return {
118
+ component: mod.default as (...args: unknown[]) => unknown,
119
+ segmentIndex: i,
120
+ isMdx: isMdxFile(segment.error.filePath),
121
+ };
83
122
  }
84
123
  }
85
124
  }
86
125
 
87
- // No error component found — fall back to bare response
88
- if (!errorComponent) {
126
+ return null;
127
+ }
128
+
129
+ /**
130
+ * Build the layouts-to-wrap list for an error file at a given segment index.
131
+ */
132
+ function getLayoutsForErrorFile(
133
+ segments: ManifestSegmentNode[],
134
+ layoutComponents: LayoutEntry[],
135
+ foundSegmentIndex: number
136
+ ): LayoutEntry[] {
137
+ const resolvedSegments = new Set(segments.slice(0, foundSegmentIndex + 1));
138
+ return layoutComponents.filter((lc) => resolvedSegments.has(lc.segment));
139
+ }
140
+
141
+ /**
142
+ * Convert an error to a SerializableError for RSC Flight serialization.
143
+ * Stack traces are stripped in production (gated by isDevMode).
144
+ *
145
+ * See design/13-security.md §"Debug Flag Security Boundary" — uses isDevMode(),
146
+ * not isDebug(), because this data crosses the RSC → client boundary.
147
+ */
148
+ function toSerializableError(error: unknown): SerializableError {
149
+ const err = error instanceof Error ? error : new Error(String(error));
150
+ return {
151
+ message: err.message,
152
+ name: err.name,
153
+ ...(isDevMode() && err.stack ? { stack: err.stack } : {}),
154
+ };
155
+ }
156
+
157
+ /**
158
+ * Render an error page for unhandled throws or RenderError outside Suspense.
159
+ *
160
+ * All error pages (TSX and MDX) go through RSC → SSR:
161
+ * - TSX: ErrorReconstituter wrapper converts SerializableError → real Error
162
+ * - MDX: Plain props ({ status }), no wrapper needed
163
+ */
164
+ export async function renderErrorPage(
165
+ error: unknown,
166
+ status: number,
167
+ segments: ManifestSegmentNode[],
168
+ layoutComponents: LayoutEntry[],
169
+ req: Request,
170
+ match: RouteMatch,
171
+ responseHeaders: Headers,
172
+ clientBootstrap: ClientBootstrapConfig,
173
+ globalError?: GlobalErrorFile
174
+ ): Promise<Response> {
175
+ // Walk segments from leaf to root to find the error component
176
+ const resolution = await resolveErrorFile(status, segments);
177
+
178
+ // Tier 2: No segment-level error file — try global-error.tsx
179
+ if (!resolution && globalError) {
180
+ return renderGlobalErrorPage(
181
+ error,
182
+ status,
183
+ globalError,
184
+ req,
185
+ match,
186
+ responseHeaders,
187
+ clientBootstrap
188
+ );
189
+ }
190
+
191
+ // Tier 3: No error component found at any level — bare response
192
+ if (!resolution) {
89
193
  return new Response(null, { status, headers: responseHeaders });
90
194
  }
91
195
 
92
- // Build digest prop for RenderError, null for unhandled errors
93
- const digest =
94
- error instanceof RenderError ? { code: error.code, data: error.digest.data } : null;
196
+ const { component: errorComponent, segmentIndex, isMdx } = resolution;
197
+ const layoutsToWrap = getLayoutsForErrorFile(segments, layoutComponents, segmentIndex);
95
198
 
96
- // Error pages receive { error, digest, reset } per design/10-error-handling.md
97
- let element = h(errorComponent, {
98
- error: error instanceof Error ? error : new Error(String(error)),
99
- digest,
100
- reset: undefined, // reset is only meaningful on the client
101
- });
199
+ return renderErrorPageViaRsc(
200
+ error,
201
+ errorComponent,
202
+ isMdx,
203
+ status,
204
+ layoutsToWrap,
205
+ req,
206
+ match,
207
+ responseHeaders,
208
+ clientBootstrap
209
+ );
210
+ }
102
211
 
103
- // Wrap in layouts from root up to the segment where the error file was found
104
- const resolvedSegments = new Set(segments.slice(0, foundSegmentIndex + 1));
105
- const layoutsToWrap = layoutComponents.filter((lc) => resolvedSegments.has(lc.segment));
212
+ /**
213
+ * Render an error page through the RSC → SSR pipeline.
214
+ *
215
+ * Handles both TSX and MDX error pages:
216
+ * - TSX: Wrapped with ErrorReconstituter to reconstitute Error on client
217
+ * - MDX: Receives plain props ({ status }), rendered as server component
218
+ */
219
+ async function renderErrorPageViaRsc(
220
+ error: unknown,
221
+ errorComponent: (...args: unknown[]) => unknown,
222
+ isMdx: boolean,
223
+ status: number,
224
+ layoutsToWrap: LayoutEntry[],
225
+ req: Request,
226
+ match: RouteMatch,
227
+ responseHeaders: Headers,
228
+ clientBootstrap: ClientBootstrapConfig
229
+ ): Promise<Response> {
230
+ const h = createElement as (...args: unknown[]) => React.ReactElement;
231
+ const url = new URL(req.url);
232
+
233
+ let element: React.ReactElement;
234
+
235
+ if (isMdx) {
236
+ // MDX error pages receive plain props — no Error serialization issue
237
+ element = h(errorComponent, { status });
238
+ } else {
239
+ // TSX error pages: wrap with ErrorReconstituter for Error reconstitution.
240
+ // The ErrorReconstituter is a 'use client' component that receives
241
+ // SerializableError (plain object) and reconstitutes a real Error
242
+ // before passing to the user's error component.
243
+ const serializedError = toSerializableError(error);
244
+ const digest =
245
+ error instanceof RenderError ? { code: error.code, data: error.digest.data } : null;
246
+
247
+ element = h(ErrorReconstituter, {
248
+ error: serializedError,
249
+ digest,
250
+ reset: undefined,
251
+ component: errorComponent,
252
+ });
253
+ }
254
+
255
+ // Wrap in layouts
106
256
  for (let i = layoutsToWrap.length - 1; i >= 0; i--) {
107
257
  const { component } = layoutsToWrap[i];
108
258
  element = h(component, null, element);
109
259
  }
110
260
 
111
- // Render to fresh RSC Flight stream
112
- const rscStream = renderToReadableStream(element, {
113
- onError(err: unknown) {
114
- logRenderError({ method: req.method, path: new URL(req.url).pathname, error: err });
115
- },
116
- debugChannel: createDebugChannelSink(),
117
- });
118
-
119
- const [ssrStream, inlineStream] = rscStream.tee();
120
-
121
- const navContext: NavContext = {
122
- pathname: new URL(req.url).pathname,
123
- params: match.params,
124
- searchParams: Object.fromEntries(new URL(req.url).searchParams),
125
- statusCode: status,
126
- responseHeaders,
127
- headHtml: '',
128
- bootstrapScriptContent: clientBootstrap.bootstrapScriptContent,
129
- rscStream: inlineStream,
130
- cookies: parseCookiesFromHeader(req.headers.get('cookie') ?? ''),
131
- };
261
+ // Render through RSC SSR (same path as deny pages)
262
+ try {
263
+ const rscStream = renderToReadableStream(element, {
264
+ signal: req.signal,
265
+ onError(err: unknown) {
266
+ logRenderError({ method: req.method, path: url.pathname, error: err });
267
+ },
268
+ debugChannel: createDebugChannelSink(),
269
+ });
270
+
271
+ const [ssrStream, inlineStream] = teeWithErrorPropagation(rscStream);
272
+
273
+ // Build params bootstrap script so useSegmentParams() works on hydration.
274
+ // Without this, error pages on dynamic routes hydrate with empty params.
275
+ const paramsScript =
276
+ Object.keys(match.params).length === 0
277
+ ? ''
278
+ : `<script>self.__timber_params=${JSON.stringify(match.params)}</script>`;
279
+
280
+ const navContext: NavContext = {
281
+ pathname: url.pathname,
282
+ params: match.params,
283
+ searchParams: Object.fromEntries(url.searchParams),
284
+ statusCode: status,
285
+ responseHeaders,
286
+ headHtml: paramsScript + flightInitScript(),
287
+ bootstrapScriptContent: clientBootstrap.bootstrapScriptContent,
288
+ rscStream: inlineStream,
289
+ signal: req.signal,
290
+ cookies: getCookiesForSsr(),
291
+ };
292
+
293
+ return callSsr(ssrStream, navContext);
294
+ } catch (renderError) {
295
+ // Error page itself failed to render — fall through to bare response.
296
+ // This guards against recursive errors in user error page code.
297
+ logRenderError({ method: req.method, path: url.pathname, error: renderError });
298
+ return new Response(null, { status, headers: responseHeaders });
299
+ }
300
+ }
301
+
302
+ /**
303
+ * Tier 2 — Render global-error.tsx as a standalone full-page replacement.
304
+ *
305
+ * No layout wrapping. The component must provide its own <html> and <body>.
306
+ * All formats (TSX and MDX) go through RSC → SSR.
307
+ *
308
+ * See design/10-error-handling.md §"Tier 2 — Global Error Page"
309
+ */
310
+ async function renderGlobalErrorPage(
311
+ error: unknown,
312
+ status: number,
313
+ globalError: GlobalErrorFile,
314
+ req: Request,
315
+ match: RouteMatch,
316
+ responseHeaders: Headers,
317
+ clientBootstrap: ClientBootstrapConfig
318
+ ): Promise<Response> {
319
+ const mod = (await globalError.load()) as Record<string, unknown>;
320
+ if (!mod.default) {
321
+ return new Response(null, { status, headers: responseHeaders });
322
+ }
132
323
 
133
- return callSsr(ssrStream, navContext);
324
+ const component = mod.default as (...args: unknown[]) => unknown;
325
+ const isMdx = isMdxFile(globalError.filePath);
326
+
327
+ // Reuse the unified RSC path with no layouts
328
+ return renderErrorPageViaRsc(
329
+ error,
330
+ component,
331
+ isMdx,
332
+ status,
333
+ [], // No layouts — global-error is standalone
334
+ req,
335
+ match,
336
+ responseHeaders,
337
+ clientBootstrap
338
+ );
134
339
  }
135
340
 
136
341
  /**
@@ -4,8 +4,8 @@
4
4
  * Small, stateless functions used across the RSC entry modules.
5
5
  */
6
6
 
7
- import type { ManifestSegmentNode } from '#/server/route-matcher.js';
8
- import { RedirectSignal } from '#/server/primitives.js';
7
+ import type { ManifestSegmentNode } from '../route-matcher.js';
8
+ import { RedirectSignal } from '../primitives.js';
9
9
 
10
10
  /** RSC content type for client navigation payload requests. */
11
11
  export const RSC_CONTENT_TYPE = 'text/x-component';
@@ -20,9 +20,6 @@ export const RSC_CONTENT_TYPE = 'text/x-component';
20
20
  * stream that we drain and discard.
21
21
  *
22
22
  * See design/13-security.md §"Server component source leak"
23
- *
24
- * TODO: In the future, expose this debug data to the browser in dev mode
25
- * for inline error overlays (e.g. component stack traces).
26
23
  */
27
24
  export function createDebugChannelSink(): { readable: ReadableStream; writable: WritableStream } {
28
25
  const sink = new TransformStream();
@@ -34,6 +31,138 @@ export function createDebugChannelSink(): { readable: ReadableStream; writable:
34
31
  };
35
32
  }
36
33
 
34
+ // ─── Debug Channel Collector (dev mode only) ────────────────────────────
35
+
36
+ /**
37
+ * Parsed component debug info extracted from the Flight debug channel.
38
+ *
39
+ * Contains only component names, environment labels, and stack frames —
40
+ * never source code or props. See design/13-security.md §"Server source
41
+ * never reaches the client".
42
+ */
43
+ export interface DebugComponentEntry {
44
+ name: string;
45
+ env: string | null;
46
+ key: string | null;
47
+ stack: unknown[] | null;
48
+ }
49
+
50
+ /**
51
+ * A debug channel that collects Flight debug rows instead of discarding them.
52
+ *
53
+ * Used in dev mode to capture server component tree information for the
54
+ * Vite error overlay. The collector provides the same `{ readable, writable }`
55
+ * shape as the discard sink, plus methods to retrieve collected data.
56
+ *
57
+ * Security: only component names, environments, and stack frames are
58
+ * extracted — props and source code are stripped. In production builds,
59
+ * use `createDebugChannelSink()` instead (this function is never called).
60
+ */
61
+ export interface DebugChannelCollector {
62
+ readable: ReadableStream;
63
+ writable: WritableStream;
64
+ /** Get the raw collected text from the debug channel. */
65
+ getCollectedText(): string;
66
+ /** Get parsed component entries (names, stacks — no props or source). */
67
+ getComponents(): DebugComponentEntry[];
68
+ }
69
+
70
+ export function createDebugChannelCollector(): DebugChannelCollector {
71
+ const chunks: string[] = [];
72
+ const decoder = new TextDecoder();
73
+
74
+ const sink = new TransformStream();
75
+
76
+ // Collect chunks from the readable side instead of discarding them.
77
+ sink.readable
78
+ .pipeTo(
79
+ new WritableStream({
80
+ write(chunk: Uint8Array) {
81
+ chunks.push(decoder.decode(chunk, { stream: true }));
82
+ },
83
+ close() {
84
+ // Flush any remaining bytes in the decoder
85
+ const remaining = decoder.decode();
86
+ if (remaining) chunks.push(remaining);
87
+ },
88
+ })
89
+ )
90
+ .catch(() => {
91
+ // Stream abort — request cancelled. Not an error.
92
+ });
93
+
94
+ return {
95
+ readable: new ReadableStream(), // no commands to send to Flight
96
+ writable: sink.writable,
97
+ getCollectedText() {
98
+ return chunks.join('');
99
+ },
100
+ getComponents() {
101
+ return parseDebugRows(chunks.join(''));
102
+ },
103
+ };
104
+ }
105
+
106
+ /**
107
+ * Parse React Flight debug rows into component entries.
108
+ *
109
+ * The Flight debug channel writes rows in `hexId:json\n` format. Each row
110
+ * with a JSON object containing a `name` field is a component debug info
111
+ * entry. Rows without `name` (timing rows, reference rows like `D"$id"`)
112
+ * are skipped.
113
+ *
114
+ * Security: `props` are explicitly stripped from parsed entries — they may
115
+ * contain rendered output or user data. Only `name`, `env`, `key`, and
116
+ * `stack` are retained.
117
+ */
118
+ export function parseDebugRows(text: string): DebugComponentEntry[] {
119
+ if (!text) return [];
120
+
121
+ const entries: DebugComponentEntry[] = [];
122
+ const lines = text.split('\n');
123
+
124
+ for (const line of lines) {
125
+ if (!line) continue;
126
+
127
+ // Flight row format: hexId:payload
128
+ const colonIdx = line.indexOf(':');
129
+ if (colonIdx === -1) continue;
130
+
131
+ let payload = line.slice(colonIdx + 1);
132
+
133
+ // React Flight debug rows may have a type-tag prefix before the JSON
134
+ // object (e.g., "D{...}" for debug info rows). Strip the single-char
135
+ // tag so the JSON parser sees the object. Non-JSON payloads like
136
+ // D"$a" reference rows are caught by the JSON.parse try/catch below.
137
+ // See TIM-556.
138
+ if (payload.length > 0 && payload[0] !== '{' && payload[0] !== '[' && payload[0] !== '"') {
139
+ payload = payload.slice(1);
140
+ }
141
+
142
+ // Skip payloads that still aren't JSON objects after tag stripping
143
+ if (!payload.startsWith('{')) continue;
144
+
145
+ try {
146
+ const parsed = JSON.parse(payload);
147
+ if (typeof parsed !== 'object' || parsed === null) continue;
148
+ if (typeof parsed.name !== 'string') continue;
149
+
150
+ // Strip props — may contain source-derived data or user data.
151
+ // Only retain: name, env, key, stack.
152
+ entries.push({
153
+ name: parsed.name,
154
+ env: parsed.env ?? null,
155
+ key: parsed.key ?? null,
156
+ stack: Array.isArray(parsed.stack) ? parsed.stack : null,
157
+ });
158
+ } catch {
159
+ // Malformed JSON — skip this row
160
+ }
161
+ }
162
+
163
+ return entries;
164
+ }
165
+
37
166
  /**
38
167
  * Build segment metadata for the X-Timber-Segments response header.
39
168
  * Describes the rendered segment chain with async status, enabling