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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (372) hide show
  1. package/dist/_chunks/actions-CQ8Z8VGL.js +1061 -0
  2. package/dist/_chunks/actions-CQ8Z8VGL.js.map +1 -0
  3. package/dist/_chunks/build-output-helper-DXnW0qjz.js +61 -0
  4. package/dist/_chunks/build-output-helper-DXnW0qjz.js.map +1 -0
  5. package/dist/_chunks/{define-Itxvcd7F.js → define-B-Q_UMOD.js} +19 -23
  6. package/dist/_chunks/define-B-Q_UMOD.js.map +1 -0
  7. package/dist/_chunks/{define-C77ScO0m.js → define-CfBPoJb0.js} +24 -7
  8. package/dist/_chunks/define-CfBPoJb0.js.map +1 -0
  9. package/dist/_chunks/define-cookie-BjpIt4UC.js +194 -0
  10. package/dist/_chunks/define-cookie-BjpIt4UC.js.map +1 -0
  11. package/dist/_chunks/{format-CYBGxKtc.js → format-Bcn-Iv1x.js} +1 -1
  12. package/dist/_chunks/{format-CYBGxKtc.js.map → format-Bcn-Iv1x.js.map} +1 -1
  13. package/dist/_chunks/handler-store-B-lqaGyh.js +54 -0
  14. package/dist/_chunks/handler-store-B-lqaGyh.js.map +1 -0
  15. package/dist/_chunks/logger-0m8MsKdc.js +291 -0
  16. package/dist/_chunks/logger-0m8MsKdc.js.map +1 -0
  17. package/dist/_chunks/merge-search-params-BphMdht_.js +122 -0
  18. package/dist/_chunks/merge-search-params-BphMdht_.js.map +1 -0
  19. package/dist/_chunks/{metadata-routes-DS3eKNmf.js → metadata-routes-BU684ls2.js} +1 -1
  20. package/dist/_chunks/{metadata-routes-DS3eKNmf.js.map → metadata-routes-BU684ls2.js.map} +1 -1
  21. package/dist/_chunks/navigation-root-BCYczjml.js +96 -0
  22. package/dist/_chunks/navigation-root-BCYczjml.js.map +1 -0
  23. package/dist/_chunks/registry-I2ss-lvy.js +20 -0
  24. package/dist/_chunks/registry-I2ss-lvy.js.map +1 -0
  25. package/dist/_chunks/router-ref-h3-UaCQv.js +28 -0
  26. package/dist/_chunks/router-ref-h3-UaCQv.js.map +1 -0
  27. package/dist/_chunks/{schema-bridge-C3xl_vfb.js → schema-bridge-Cxu4l-7p.js} +1 -1
  28. package/dist/_chunks/{schema-bridge-C3xl_vfb.js.map → schema-bridge-Cxu4l-7p.js.map} +1 -1
  29. package/dist/_chunks/segment-classify-BjfuctV2.js +137 -0
  30. package/dist/_chunks/segment-classify-BjfuctV2.js.map +1 -0
  31. package/dist/_chunks/{segment-context-fHFLF1PE.js → segment-context-Dx_OizxD.js} +1 -1
  32. package/dist/_chunks/{segment-context-fHFLF1PE.js.map → segment-context-Dx_OizxD.js.map} +1 -1
  33. package/dist/_chunks/{router-ref-C8OCm7g7.js → ssr-data-B4CdH7rE.js} +2 -26
  34. package/dist/_chunks/ssr-data-B4CdH7rE.js.map +1 -0
  35. package/dist/_chunks/{stale-reload-BX5gL1r-.js → stale-reload-Bab885FO.js} +1 -1
  36. package/dist/_chunks/{stale-reload-BX5gL1r-.js.map → stale-reload-Bab885FO.js.map} +1 -1
  37. package/dist/_chunks/tracing-C8V-YGsP.js +329 -0
  38. package/dist/_chunks/tracing-C8V-YGsP.js.map +1 -0
  39. package/dist/_chunks/{use-query-states-BiV5GJgm.js → use-query-states-B2XTqxDR.js} +3 -19
  40. package/dist/_chunks/use-query-states-B2XTqxDR.js.map +1 -0
  41. package/dist/_chunks/{use-params-IOPu7E8t.js → use-segment-params-BkpKAQ7D.js} +9 -95
  42. package/dist/_chunks/use-segment-params-BkpKAQ7D.js.map +1 -0
  43. package/dist/_chunks/{interception-BbqMCVXa.js → walkers-Tg0Alwcg.js} +66 -87
  44. package/dist/_chunks/walkers-Tg0Alwcg.js.map +1 -0
  45. package/dist/_chunks/{dev-warnings-DpGRGoDi.js → warnings-Cg47l5sk.js} +3 -3
  46. package/dist/_chunks/warnings-Cg47l5sk.js.map +1 -0
  47. package/dist/adapters/build-output-helper.d.ts +28 -0
  48. package/dist/adapters/build-output-helper.d.ts.map +1 -0
  49. package/dist/adapters/cloudflare.d.ts.map +1 -1
  50. package/dist/adapters/cloudflare.js +8 -28
  51. package/dist/adapters/cloudflare.js.map +1 -1
  52. package/dist/adapters/nitro.d.ts.map +1 -1
  53. package/dist/adapters/nitro.js +63 -31
  54. package/dist/adapters/nitro.js.map +1 -1
  55. package/dist/adapters/shared.d.ts +16 -0
  56. package/dist/adapters/shared.d.ts.map +1 -0
  57. package/dist/cache/index.js +9 -2
  58. package/dist/cache/index.js.map +1 -1
  59. package/dist/cache/timber-cache.d.ts.map +1 -1
  60. package/dist/client/error-boundary.js +2 -1
  61. package/dist/client/error-boundary.js.map +1 -1
  62. package/dist/client/form.d.ts +10 -24
  63. package/dist/client/form.d.ts.map +1 -1
  64. package/dist/client/index.d.ts +1 -5
  65. package/dist/client/index.d.ts.map +1 -1
  66. package/dist/client/index.js +41 -91
  67. package/dist/client/index.js.map +1 -1
  68. package/dist/client/internal.d.ts +2 -1
  69. package/dist/client/internal.d.ts.map +1 -1
  70. package/dist/client/internal.js +81 -7
  71. package/dist/client/internal.js.map +1 -1
  72. package/dist/client/rsc-fetch.d.ts.map +1 -1
  73. package/dist/client/state.d.ts +1 -1
  74. package/dist/client/use-cookie.d.ts +8 -0
  75. package/dist/client/use-cookie.d.ts.map +1 -1
  76. package/dist/client/{use-params.d.ts → use-segment-params.d.ts} +1 -1
  77. package/dist/client/use-segment-params.d.ts.map +1 -0
  78. package/dist/codec.d.ts +1 -1
  79. package/dist/codec.d.ts.map +1 -1
  80. package/dist/codec.js +2 -2
  81. package/dist/config-types.d.ts +28 -0
  82. package/dist/config-types.d.ts.map +1 -1
  83. package/dist/cookies/define-cookie.d.ts +87 -35
  84. package/dist/cookies/define-cookie.d.ts.map +1 -1
  85. package/dist/cookies/index.d.ts +2 -1
  86. package/dist/cookies/index.d.ts.map +1 -1
  87. package/dist/cookies/index.js +48 -2
  88. package/dist/cookies/index.js.map +1 -0
  89. package/dist/cookies/json-cookie.d.ts +64 -0
  90. package/dist/cookies/json-cookie.d.ts.map +1 -0
  91. package/dist/cookies/validation.d.ts +46 -0
  92. package/dist/cookies/validation.d.ts.map +1 -0
  93. package/dist/{plugins/dev-404-page.d.ts → dev-tools/404-page.d.ts} +9 -19
  94. package/dist/dev-tools/404-page.d.ts.map +1 -0
  95. package/dist/{plugins/dev-browser-logs.d.ts → dev-tools/browser-logs.d.ts} +1 -1
  96. package/dist/dev-tools/browser-logs.d.ts.map +1 -0
  97. package/dist/{plugins/dev-error-page.d.ts → dev-tools/error-page.d.ts} +2 -2
  98. package/dist/dev-tools/error-page.d.ts.map +1 -0
  99. package/dist/{server/dev-holding-server.d.ts → dev-tools/holding-server.d.ts} +5 -3
  100. package/dist/dev-tools/holding-server.d.ts.map +1 -0
  101. package/dist/dev-tools/index.d.ts +31 -0
  102. package/dist/dev-tools/index.d.ts.map +1 -0
  103. package/dist/{server/dev-span-processor.d.ts → dev-tools/instrumentation.d.ts} +26 -6
  104. package/dist/dev-tools/instrumentation.d.ts.map +1 -0
  105. package/dist/{server/dev-logger.d.ts → dev-tools/logger.d.ts} +1 -1
  106. package/dist/dev-tools/logger.d.ts.map +1 -0
  107. package/dist/{plugins/dev-logs.d.ts → dev-tools/logs.d.ts} +1 -1
  108. package/dist/dev-tools/logs.d.ts.map +1 -0
  109. package/dist/{plugins/dev-error-overlay.d.ts → dev-tools/overlay.d.ts} +3 -12
  110. package/dist/dev-tools/overlay.d.ts.map +1 -0
  111. package/dist/dev-tools/stack-classifier.d.ts +34 -0
  112. package/dist/dev-tools/stack-classifier.d.ts.map +1 -0
  113. package/dist/{plugins/dev-terminal-error.d.ts → dev-tools/terminal.d.ts} +2 -2
  114. package/dist/dev-tools/terminal.d.ts.map +1 -0
  115. package/dist/{server/dev-warnings.d.ts → dev-tools/warnings.d.ts} +1 -1
  116. package/dist/dev-tools/warnings.d.ts.map +1 -0
  117. package/dist/index.d.ts +1 -0
  118. package/dist/index.d.ts.map +1 -1
  119. package/dist/index.js +285 -133
  120. package/dist/index.js.map +1 -1
  121. package/dist/plugin-context.d.ts +1 -1
  122. package/dist/plugin-context.d.ts.map +1 -1
  123. package/dist/plugins/adapter-build.d.ts.map +1 -1
  124. package/dist/plugins/build-report.d.ts +6 -4
  125. package/dist/plugins/build-report.d.ts.map +1 -1
  126. package/dist/routing/convention-lint.d.ts.map +1 -1
  127. package/dist/routing/index.d.ts +5 -3
  128. package/dist/routing/index.d.ts.map +1 -1
  129. package/dist/routing/index.js +3 -3
  130. package/dist/routing/scanner.d.ts +1 -10
  131. package/dist/routing/scanner.d.ts.map +1 -1
  132. package/dist/routing/segment-classify.d.ts +37 -8
  133. package/dist/routing/segment-classify.d.ts.map +1 -1
  134. package/dist/routing/status-file-lint.d.ts.map +1 -1
  135. package/dist/routing/types.d.ts +63 -23
  136. package/dist/routing/types.d.ts.map +1 -1
  137. package/dist/routing/walkers.d.ts +51 -0
  138. package/dist/routing/walkers.d.ts.map +1 -0
  139. package/dist/search-params/define.d.ts +25 -7
  140. package/dist/search-params/define.d.ts.map +1 -1
  141. package/dist/search-params/index.js +5 -3
  142. package/dist/search-params/index.js.map +1 -1
  143. package/dist/search-params/wrappers.d.ts +2 -2
  144. package/dist/search-params/wrappers.d.ts.map +1 -1
  145. package/dist/segment-params/define.d.ts +23 -6
  146. package/dist/segment-params/define.d.ts.map +1 -1
  147. package/dist/segment-params/index.js +1 -1
  148. package/dist/server/access-gate.d.ts +4 -3
  149. package/dist/server/access-gate.d.ts.map +1 -1
  150. package/dist/server/action-handler.d.ts +15 -6
  151. package/dist/server/action-handler.d.ts.map +1 -1
  152. package/dist/server/als-registry.d.ts +5 -5
  153. package/dist/server/als-registry.d.ts.map +1 -1
  154. package/dist/server/asset-headers.d.ts +1 -15
  155. package/dist/server/asset-headers.d.ts.map +1 -1
  156. package/dist/server/cookie-context.d.ts +170 -0
  157. package/dist/server/cookie-context.d.ts.map +1 -0
  158. package/dist/server/cookie-parsing.d.ts +51 -0
  159. package/dist/server/cookie-parsing.d.ts.map +1 -0
  160. package/dist/server/deny-boundary.d.ts +90 -0
  161. package/dist/server/deny-boundary.d.ts.map +1 -0
  162. package/dist/server/deny-renderer.d.ts.map +1 -1
  163. package/dist/server/early-hints-sender.d.ts.map +1 -1
  164. package/dist/server/html-injector-core.d.ts +212 -0
  165. package/dist/server/html-injector-core.d.ts.map +1 -0
  166. package/dist/server/html-injectors.d.ts +59 -59
  167. package/dist/server/html-injectors.d.ts.map +1 -1
  168. package/dist/server/index.d.ts +5 -4
  169. package/dist/server/index.d.ts.map +1 -1
  170. package/dist/server/index.js +4 -149
  171. package/dist/server/index.js.map +1 -1
  172. package/dist/server/internal.d.ts +6 -4
  173. package/dist/server/internal.d.ts.map +1 -1
  174. package/dist/server/internal.js +852 -852
  175. package/dist/server/internal.js.map +1 -1
  176. package/dist/server/logger.d.ts +14 -0
  177. package/dist/server/logger.d.ts.map +1 -1
  178. package/dist/server/middleware-runner.d.ts +17 -0
  179. package/dist/server/middleware-runner.d.ts.map +1 -1
  180. package/dist/server/node-stream-transforms.d.ts +46 -49
  181. package/dist/server/node-stream-transforms.d.ts.map +1 -1
  182. package/dist/server/param-coercion.d.ts +26 -0
  183. package/dist/server/param-coercion.d.ts.map +1 -0
  184. package/dist/server/pipeline-helpers.d.ts +95 -0
  185. package/dist/server/pipeline-helpers.d.ts.map +1 -0
  186. package/dist/server/pipeline-outcome.d.ts +49 -0
  187. package/dist/server/pipeline-outcome.d.ts.map +1 -0
  188. package/dist/server/pipeline-phases.d.ts +52 -0
  189. package/dist/server/pipeline-phases.d.ts.map +1 -0
  190. package/dist/server/pipeline.d.ts +51 -32
  191. package/dist/server/pipeline.d.ts.map +1 -1
  192. package/dist/server/port-resolution.d.ts +117 -0
  193. package/dist/server/port-resolution.d.ts.map +1 -0
  194. package/dist/server/request-context.d.ts +22 -159
  195. package/dist/server/request-context.d.ts.map +1 -1
  196. package/dist/server/route-element-builder.d.ts.map +1 -1
  197. package/dist/server/route-matcher.d.ts +20 -47
  198. package/dist/server/route-matcher.d.ts.map +1 -1
  199. package/dist/server/rsc-entry/action-middleware-runner.d.ts +66 -0
  200. package/dist/server/rsc-entry/action-middleware-runner.d.ts.map +1 -0
  201. package/dist/server/rsc-entry/helpers.d.ts +1 -1
  202. package/dist/server/rsc-entry/helpers.d.ts.map +1 -1
  203. package/dist/server/rsc-entry/index.d.ts.map +1 -1
  204. package/dist/server/rsc-entry/render-route.d.ts +50 -0
  205. package/dist/server/rsc-entry/render-route.d.ts.map +1 -0
  206. package/dist/server/rsc-entry/wrap-action-dispatch.d.ts +119 -0
  207. package/dist/server/rsc-entry/wrap-action-dispatch.d.ts.map +1 -0
  208. package/dist/server/state-tree-diff.d.ts.map +1 -1
  209. package/dist/server/status-code-resolver.d.ts +16 -11
  210. package/dist/server/status-code-resolver.d.ts.map +1 -1
  211. package/dist/server/tracing.d.ts +1 -1
  212. package/dist/server/tracing.d.ts.map +1 -1
  213. package/dist/server/tree-builder.d.ts +45 -16
  214. package/dist/server/tree-builder.d.ts.map +1 -1
  215. package/dist/server/types.d.ts +48 -0
  216. package/dist/server/types.d.ts.map +1 -1
  217. package/dist/server/utils/escape-html.d.ts +14 -0
  218. package/dist/server/utils/escape-html.d.ts.map +1 -0
  219. package/dist/shims/headers.d.ts +2 -2
  220. package/dist/shims/headers.d.ts.map +1 -1
  221. package/dist/shims/navigation-client.d.ts +3 -1
  222. package/dist/shims/navigation-client.d.ts.map +1 -1
  223. package/dist/shims/navigation.d.ts +9 -4
  224. package/dist/shims/navigation.d.ts.map +1 -1
  225. package/dist/utils/directive-parser.d.ts +0 -45
  226. package/dist/utils/directive-parser.d.ts.map +1 -1
  227. package/package.json +1 -1
  228. package/src/adapters/build-output-helper.ts +77 -0
  229. package/src/adapters/cloudflare.ts +10 -50
  230. package/src/adapters/nitro.ts +66 -50
  231. package/src/adapters/shared.ts +40 -0
  232. package/src/cache/timber-cache.ts +3 -2
  233. package/src/client/form.tsx +17 -25
  234. package/src/client/index.ts +16 -9
  235. package/src/client/internal.ts +3 -2
  236. package/src/client/router.ts +1 -1
  237. package/src/client/rsc-fetch.ts +15 -0
  238. package/src/client/state.ts +2 -2
  239. package/src/client/use-cookie.ts +29 -0
  240. package/src/codec.ts +3 -7
  241. package/src/config-types.ts +28 -0
  242. package/src/cookies/define-cookie.ts +271 -78
  243. package/src/cookies/index.ts +11 -8
  244. package/src/cookies/json-cookie.ts +105 -0
  245. package/src/cookies/validation.ts +134 -0
  246. package/src/{plugins/dev-404-page.ts → dev-tools/404-page.ts} +17 -48
  247. package/src/{plugins/dev-error-page.ts → dev-tools/error-page.ts} +5 -32
  248. package/src/{server/dev-holding-server.ts → dev-tools/holding-server.ts} +4 -2
  249. package/src/dev-tools/index.ts +90 -0
  250. package/src/dev-tools/instrumentation.ts +176 -0
  251. package/src/{plugins/dev-logs.ts → dev-tools/logs.ts} +2 -2
  252. package/src/{plugins/dev-error-overlay.ts → dev-tools/overlay.ts} +5 -23
  253. package/src/dev-tools/stack-classifier.ts +75 -0
  254. package/src/{plugins/dev-terminal-error.ts → dev-tools/terminal.ts} +4 -38
  255. package/src/{server/dev-warnings.ts → dev-tools/warnings.ts} +1 -1
  256. package/src/index.ts +95 -34
  257. package/src/plugin-context.ts +1 -1
  258. package/src/plugins/adapter-build.ts +3 -1
  259. package/src/plugins/build-report.ts +13 -22
  260. package/src/plugins/dev-server.ts +3 -3
  261. package/src/plugins/routing.ts +14 -12
  262. package/src/plugins/shims.ts +1 -1
  263. package/src/plugins/static-build.ts +1 -1
  264. package/src/routing/codegen.ts +1 -1
  265. package/src/routing/convention-lint.ts +9 -8
  266. package/src/routing/index.ts +5 -3
  267. package/src/routing/interception.ts +1 -1
  268. package/src/routing/scanner.ts +22 -95
  269. package/src/routing/segment-classify.ts +107 -8
  270. package/src/routing/status-file-lint.ts +7 -5
  271. package/src/routing/types.ts +63 -23
  272. package/src/routing/walkers.ts +90 -0
  273. package/src/search-params/define.ts +71 -15
  274. package/src/search-params/wrappers.ts +9 -2
  275. package/src/segment-params/define.ts +66 -13
  276. package/src/server/access-gate.tsx +9 -8
  277. package/src/server/action-handler.ts +34 -38
  278. package/src/server/als-registry.ts +5 -5
  279. package/src/server/asset-headers.ts +8 -34
  280. package/src/server/cookie-context.ts +468 -0
  281. package/src/server/cookie-parsing.ts +135 -0
  282. package/src/server/{deny-page-resolver.ts → deny-boundary.ts} +78 -14
  283. package/src/server/deny-renderer.ts +7 -12
  284. package/src/server/early-hints-sender.ts +3 -2
  285. package/src/server/fallback-error.ts +2 -2
  286. package/src/server/html-injector-core.ts +403 -0
  287. package/src/server/html-injectors.ts +158 -297
  288. package/src/server/index.ts +13 -14
  289. package/src/server/internal.ts +10 -3
  290. package/src/server/logger.ts +23 -0
  291. package/src/server/middleware-runner.ts +44 -0
  292. package/src/server/node-stream-transforms.ts +108 -248
  293. package/src/server/param-coercion.ts +76 -0
  294. package/src/server/pipeline-helpers.ts +204 -0
  295. package/src/server/pipeline-outcome.ts +167 -0
  296. package/src/server/pipeline-phases.ts +409 -0
  297. package/src/server/pipeline.ts +70 -540
  298. package/src/server/port-resolution.ts +215 -0
  299. package/src/server/request-context.ts +46 -451
  300. package/src/server/route-element-builder.ts +8 -4
  301. package/src/server/route-matcher.ts +28 -60
  302. package/src/server/rsc-entry/action-middleware-runner.ts +167 -0
  303. package/src/server/rsc-entry/api-handler.ts +2 -2
  304. package/src/server/rsc-entry/error-renderer.ts +2 -2
  305. package/src/server/rsc-entry/helpers.ts +2 -7
  306. package/src/server/rsc-entry/index.ts +81 -366
  307. package/src/server/rsc-entry/render-route.ts +304 -0
  308. package/src/server/rsc-entry/rsc-payload.ts +1 -1
  309. package/src/server/rsc-entry/ssr-renderer.ts +2 -2
  310. package/src/server/rsc-entry/wrap-action-dispatch.ts +449 -0
  311. package/src/server/sitemap-generator.ts +1 -1
  312. package/src/server/slot-resolver.ts +1 -1
  313. package/src/server/ssr-entry.ts +1 -1
  314. package/src/server/state-tree-diff.ts +4 -1
  315. package/src/server/status-code-resolver.ts +112 -128
  316. package/src/server/tracing.ts +3 -3
  317. package/src/server/tree-builder.ts +134 -56
  318. package/src/server/types.ts +52 -0
  319. package/src/server/utils/escape-html.ts +20 -0
  320. package/src/shims/headers.ts +3 -3
  321. package/src/shims/navigation-client.ts +4 -3
  322. package/src/shims/navigation.ts +9 -7
  323. package/src/utils/directive-parser.ts +0 -392
  324. package/dist/_chunks/actions-DLnUaR65.js +0 -421
  325. package/dist/_chunks/actions-DLnUaR65.js.map +0 -1
  326. package/dist/_chunks/als-registry-HS0LGUl2.js +0 -41
  327. package/dist/_chunks/als-registry-HS0LGUl2.js.map +0 -1
  328. package/dist/_chunks/debug-ECi_61pb.js +0 -108
  329. package/dist/_chunks/debug-ECi_61pb.js.map +0 -1
  330. package/dist/_chunks/define-C77ScO0m.js.map +0 -1
  331. package/dist/_chunks/define-Itxvcd7F.js.map +0 -1
  332. package/dist/_chunks/define-cookie-BowvzoP0.js +0 -94
  333. package/dist/_chunks/define-cookie-BowvzoP0.js.map +0 -1
  334. package/dist/_chunks/dev-warnings-DpGRGoDi.js.map +0 -1
  335. package/dist/_chunks/interception-BbqMCVXa.js.map +0 -1
  336. package/dist/_chunks/merge-search-params-Cm_KIWDX.js +0 -41
  337. package/dist/_chunks/merge-search-params-Cm_KIWDX.js.map +0 -1
  338. package/dist/_chunks/request-context-CK5tZqIP.js +0 -478
  339. package/dist/_chunks/request-context-CK5tZqIP.js.map +0 -1
  340. package/dist/_chunks/router-ref-C8OCm7g7.js.map +0 -1
  341. package/dist/_chunks/segment-classify-BDNn6EzD.js +0 -65
  342. package/dist/_chunks/segment-classify-BDNn6EzD.js.map +0 -1
  343. package/dist/_chunks/tracing-CCYbKn5n.js +0 -238
  344. package/dist/_chunks/tracing-CCYbKn5n.js.map +0 -1
  345. package/dist/_chunks/use-params-IOPu7E8t.js.map +0 -1
  346. package/dist/_chunks/use-query-states-BiV5GJgm.js.map +0 -1
  347. package/dist/client/use-params.d.ts.map +0 -1
  348. package/dist/plugins/dev-404-page.d.ts.map +0 -1
  349. package/dist/plugins/dev-browser-logs.d.ts.map +0 -1
  350. package/dist/plugins/dev-error-overlay.d.ts.map +0 -1
  351. package/dist/plugins/dev-error-page.d.ts.map +0 -1
  352. package/dist/plugins/dev-logs.d.ts.map +0 -1
  353. package/dist/plugins/dev-terminal-error.d.ts.map +0 -1
  354. package/dist/server/deny-page-resolver.d.ts +0 -52
  355. package/dist/server/deny-page-resolver.d.ts.map +0 -1
  356. package/dist/server/dev-fetch-instrumentation.d.ts +0 -22
  357. package/dist/server/dev-fetch-instrumentation.d.ts.map +0 -1
  358. package/dist/server/dev-holding-server.d.ts.map +0 -1
  359. package/dist/server/dev-logger.d.ts.map +0 -1
  360. package/dist/server/dev-span-processor.d.ts.map +0 -1
  361. package/dist/server/dev-warnings.d.ts.map +0 -1
  362. package/dist/server/manifest-status-resolver.d.ts +0 -58
  363. package/dist/server/manifest-status-resolver.d.ts.map +0 -1
  364. package/dist/server/page-deny-boundary.d.ts +0 -31
  365. package/dist/server/page-deny-boundary.d.ts.map +0 -1
  366. package/src/server/dev-fetch-instrumentation.ts +0 -96
  367. package/src/server/dev-span-processor.ts +0 -78
  368. package/src/server/manifest-status-resolver.ts +0 -215
  369. package/src/server/page-deny-boundary.tsx +0 -56
  370. /package/src/client/{use-params.ts → use-segment-params.ts} +0 -0
  371. /package/src/{plugins/dev-browser-logs.ts → dev-tools/browser-logs.ts} +0 -0
  372. /package/src/{server/dev-logger.ts → dev-tools/logger.ts} +0 -0
@@ -7,20 +7,18 @@
7
7
  export type { AccessContext } from './types';
8
8
  export type { MiddlewareContext } from './types';
9
9
  export type { RouteContext } from './types';
10
- export type { Metadata, MetadataRoute } from './types';
10
+ export type { Metadata, MetadataRoute, MetadataHandler, MetadataResult } from './types';
11
11
 
12
- // Request Context — ALS-backed getHeaders(), getCookies(), and getSearchParams()
12
+ // Request Context — ALS-backed accessors (all sync)
13
+ // Prefer defineSearchParams().get() and defineSegmentParams().get() for typed access.
13
14
  // Design doc: design/04-authorization.md §"AccessContext does not include cookies or headers"
14
- // Design doc: design/23-search-params.md §"Server Integration"
15
- export {
16
- getHeaders,
17
- getHeader,
18
- getCookies,
19
- getCookie,
20
- getSearchParams,
21
- getSegmentParams,
22
- } from './request-context';
23
- export type { ReadonlyHeaders, RequestCookies, CookieOptions } from './request-context';
15
+ export { getHeaders, getSegmentParams } from './request-context';
16
+ export type { ReadonlyHeaders } from './request-context';
17
+
18
+ // Cookie Context — ALS-backed getCookieJar() escape hatch
19
+ // Design doc: design/29-cookies.md
20
+ export { getCookieJar } from './cookie-context';
21
+ export type { RequestCookies, CookieOptions, SetCookieOptions } from './cookie-context';
24
22
 
25
23
  // Runtime primitives
26
24
  export {
@@ -52,8 +50,9 @@ export type {
52
50
  ValidationErrors,
53
51
  } from './action-client';
54
52
 
55
- // FormData Preprocessing
56
- export { parseFormData, coerce } from './form-data';
53
+ // FormData Preprocessing — internal only, not part of public API.
54
+ // Schema libraries (Zod z.coerce.*, Valibot v.transform(), ArkType morphs)
55
+ // handle form coercion. Kept for action-client/action-handler internals.
57
56
 
58
57
  // Form Flash (no-JS error round-trip)
59
58
  export { getFormFlash } from './form-flash';
@@ -13,12 +13,18 @@
13
13
 
14
14
  // ── Request Context internals ────────────────────────────────────────────
15
15
  export {
16
+ getSearchParams,
17
+ getSegmentParams,
18
+ getHeader,
16
19
  setSegmentParams,
17
20
  runWithRequestContext,
18
21
  setMutableCookieContext,
19
22
  markResponseFlushed,
20
- getSetCookieHeaders,
21
23
  } from './request-context.js';
24
+ export { getCookie, getSetCookieHeaders } from './cookie-context.js';
25
+
26
+ // ── Form-data coercion (internal) ────────────────────────────────────────
27
+ export { coerce } from './form-data.js';
22
28
 
23
29
  // ── Signal classes (instanceof targets for pipeline catch logic) ──────────
24
30
  export { DenySignal, RedirectSignal, RenderError } from './primitives.js';
@@ -60,6 +66,7 @@ export type {
60
66
  TreeBuilderConfig,
61
67
  TreeBuildResult,
62
68
  LoadedModule,
69
+ LoadedComponent,
63
70
  ModuleLoader,
64
71
  AccessGateProps,
65
72
  SlotAccessGateProps,
@@ -157,8 +164,8 @@ export {
157
164
  warnSlowSlotWithoutSuspense,
158
165
  setViteServer,
159
166
  WarningId,
160
- } from './dev-warnings.js';
161
- export type { DevWarningConfig } from './dev-warnings.js';
167
+ } from '../dev-tools/warnings.js';
168
+ export type { DevWarningConfig } from '../dev-tools/warnings.js';
162
169
 
163
170
  // ── Route Handler ────────────────────────────────────────────────────────
164
171
  export { handleRouteRequest, resolveAllowedMethods } from './route-handler.js';
@@ -10,6 +10,7 @@
10
10
 
11
11
  import { getTraceStore } from './tracing.js';
12
12
  import { createDefaultLogger } from './default-logger.js';
13
+ import { isDevMode } from './debug.js';
13
14
 
14
15
  // ─── Logger Interface ─────────────────────────────────────────────────────
15
16
 
@@ -156,3 +157,25 @@ export function logSwrRefetchFailed(data: { cacheKey: string; error: unknown }):
156
157
  export function logCacheMiss(data: { cacheKey: string }): void {
157
158
  _logger.debug('timber.cache MISS', withTraceContext(data));
158
159
  }
160
+
161
+ // ─── Swallow Helper ───────────────────────────────────────────────────────
162
+
163
+ /**
164
+ * Log an intentionally swallowed error. Provides observability into catch
165
+ * blocks that are deliberately empty — the error is consumed, never rethrown.
166
+ *
167
+ * Default level: `warn` in dev (so the overlay surfaces patterns), `debug`
168
+ * in production (low noise unless TIMBER_DEBUG is set). Pass `opts.level`
169
+ * to override.
170
+ *
171
+ * **Infallible** — swallow() itself never throws, even if the logger is
172
+ * broken. A thrown swallow would turn a benign catch into a crash.
173
+ */
174
+ export function swallow(err: unknown, reason: string, opts?: { level?: 'debug' | 'warn' }): void {
175
+ try {
176
+ const level = opts?.level ?? (isDevMode() ? 'warn' : 'debug');
177
+ _logger[level](`swallowed: ${reason}`, withTraceContext({ error: err }));
178
+ } catch {
179
+ // swallow() must never throw.
180
+ }
181
+ }
@@ -57,3 +57,47 @@ export async function runMiddlewareChain(
57
57
  }
58
58
  return undefined;
59
59
  }
60
+
61
+ // ─── Per-Request Middleware Bypass ─────────────────────────────────────────
62
+
63
+ /**
64
+ * Per-request marker for synthetic re-render requests that should NOT
65
+ * re-execute `middleware.ts`. The action-dispatch wrapper runs middleware
66
+ * once on the inbound action POST; when validation fails on the no-JS
67
+ * path, it builds a synthetic GET that flows through the normal pipeline
68
+ * to render the page with `getFormFlash()` data. Without this marker, the
69
+ * pipeline would run middleware a second time on that synthetic GET.
70
+ *
71
+ * The set is keyed by the synthetic Request object itself, so the entry
72
+ * lives exactly as long as the request and is garbage-collected with it.
73
+ * Cannot be set or detected by user code — there is no header, no URL
74
+ * parameter, nothing on the wire that an attacker could spoof.
75
+ *
76
+ * See TIM-871.
77
+ *
78
+ * @internal — framework use only.
79
+ */
80
+ const middlewareBypassRequests = new WeakSet<Request>();
81
+
82
+ /**
83
+ * Mark a request so the pipeline skips its middleware phase.
84
+ *
85
+ * Used by `wrap-action-dispatch.ts` for the no-JS form-rerender path.
86
+ *
87
+ * @internal
88
+ */
89
+ export function markRequestBypassMiddleware(req: Request): void {
90
+ middlewareBypassRequests.add(req);
91
+ }
92
+
93
+ /**
94
+ * Check whether a request was marked to bypass middleware.
95
+ *
96
+ * Called by `handleRequest` in pipeline-phases.ts before invoking the
97
+ * middleware phase. Returns false for any request not explicitly marked.
98
+ *
99
+ * @internal
100
+ */
101
+ export function shouldBypassMiddleware(req: Request): boolean {
102
+ return middlewareBypassRequests.has(req);
103
+ }
@@ -1,13 +1,14 @@
1
1
  /**
2
2
  * Node.js native stream transforms for SSR HTML post-processing.
3
3
  *
4
- * These are Node.js Transform stream equivalents of the Web Stream
5
- * transforms in html-injectors.ts. Used on Node.js/Bun where native
6
- * streams (C++ backed) are faster than Web Streams (JS reimplementation).
7
- *
8
- * The transforms are pure string operations on HTML chunks the same
9
- * logic as the Web Stream versions, just wrapped in Node.js Transform
10
- * instead of Web TransformStream.
4
+ * Node.js `Transform` wrappers around the pure helpers in
5
+ * `html-injector-core.ts`. Used on Node.js / Bun where C++-backed
6
+ * native streams are significantly faster than the JS-implemented
7
+ * Web Streams. The Web `TransformStream` equivalents live in
8
+ * `html-injectors.ts` and wrap the same core keep the two files
9
+ * byte-for-byte equivalent in behavior. If you fix a streaming bug
10
+ * here, it almost certainly belongs in the core (`html-injector-core.ts`),
11
+ * not in this wrapper.
11
12
  *
12
13
  * Architecture:
13
14
  * renderToPipeableStream → pipe(errorHandler) → pipe(headInjector)
@@ -20,11 +21,21 @@
20
21
  import { Transform } from 'node:stream';
21
22
  import { createGzip, constants } from 'node:zlib';
22
23
 
24
+ import {
25
+ BufferAggregator,
26
+ FlightInjectorCore,
27
+ SUFFIX_BYTES,
28
+ createInjectorState,
29
+ createSuffixState,
30
+ injectorFlush,
31
+ processInjectorChunk,
32
+ processSuffixChunk,
33
+ suffixFlush,
34
+ } from './html-injector-core.js';
35
+ import { logStreamingError } from './logger.js';
36
+
23
37
  // ─── Buffered Transform ──────────────────────────────────────────────────────
24
38
 
25
- /**
26
- * Options for the Node.js buffered transform.
27
- */
28
39
  export interface NodeBufferedTransformOptions {
29
40
  /**
30
41
  * Flush synchronously once the buffer reaches this many bytes.
@@ -35,51 +46,44 @@ export interface NodeBufferedTransformOptions {
35
46
  }
36
47
 
37
48
  /**
38
- * Node.js Transform that buffers incoming chunks and coalesces them
49
+ * Node.js `Transform` that buffers incoming chunks and coalesces them
39
50
  * within a single event loop tick.
40
51
  *
41
- * Equivalent to createBufferedTransformStream() in html-injectors.ts.
42
- * React Fizz may emit multiple micro-chunks within a single flush.
43
- * Without buffering, downstream transforms (especially flight injection)
44
- * could see chunk boundaries in the middle of HTML tags or attributes.
45
- *
46
- * This transform collects all chunks that arrive in the same tick and
47
- * emits them as a single concatenated Buffer on the next `setImmediate`.
52
+ * Equivalent to `createBufferedTransformStream` in `html-injectors.ts` —
53
+ * the actual buffer math lives in `BufferAggregator`. This wrapper only
54
+ * owns the `setImmediate` scheduling and the push to the Node `Transform`.
48
55
  *
49
- * **Not a polling loop.** Uses a single-shot `setImmediate` per flush
50
- * cycle — no recursive scheduling, no busy-wait. See design/02 §"No Polling".
51
- *
52
- * Inspired by Next.js `createBufferedTransformStream`.
56
+ * **Not a polling loop.** Single-shot `setImmediate` per flush cycle —
57
+ * no recursive scheduling. See design/02 §"No Polling".
53
58
  */
54
59
  export function createNodeBufferedTransform(options: NodeBufferedTransformOptions = {}): Transform {
55
- const { maxBufferByteLength = Infinity } = options;
56
-
57
- let bufferedChunks: Buffer[] = [];
58
- let bufferByteLength = 0;
60
+ const buffer = new BufferAggregator(options.maxBufferByteLength ?? Infinity);
59
61
  let pendingImmediate: ReturnType<typeof setImmediate> | null = null;
60
62
 
63
+ const flushBuffer = () => {
64
+ const merged = buffer.drain();
65
+ if (merged) transform.push(merged);
66
+ };
67
+
61
68
  const transform = new Transform({
62
69
  transform(chunk: Buffer, _encoding, callback) {
63
- bufferedChunks.push(chunk);
64
- bufferByteLength += chunk.byteLength;
65
-
66
- if (bufferByteLength >= maxBufferByteLength) {
67
- // Synchronous flush — buffer is too large to hold
70
+ const overCap = buffer.append(chunk);
71
+ if (overCap) {
72
+ // Buffer too large to hold — flush synchronously now.
68
73
  flushBuffer();
69
74
  } else if (!pendingImmediate) {
70
75
  // Schedule a deferred flush for end of this tick.
71
- // Single-shot setImmediate — NOT a recursive loop.
72
- // See design/02 §"No Polling".
76
+ // Single-shot setImmediate — NOT a recursive loop. See design/02
77
+ // §"No Polling".
73
78
  pendingImmediate = setImmediate(() => {
74
79
  pendingImmediate = null;
75
80
  flushBuffer();
76
81
  });
77
82
  }
78
-
79
83
  callback();
80
84
  },
81
85
  flush(callback) {
82
- // Cancel any pending deferred flush and flush synchronously
86
+ // Cancel any pending deferred flush and flush synchronously.
83
87
  if (pendingImmediate) {
84
88
  clearImmediate(pendingImmediate);
85
89
  pendingImmediate = null;
@@ -89,94 +93,52 @@ export function createNodeBufferedTransform(options: NodeBufferedTransformOption
89
93
  },
90
94
  });
91
95
 
92
- function flushBuffer() {
93
- if (bufferedChunks.length === 0) return;
94
-
95
- const merged = Buffer.concat(bufferedChunks, bufferByteLength);
96
- bufferedChunks = [];
97
- bufferByteLength = 0;
98
- transform.push(merged);
99
- }
100
-
101
96
  return transform;
102
97
  }
103
98
 
104
- // ─── Injection Transforms ────────────────────────────────────────────────────
105
-
106
- import { createMachine } from '../utils/state-machine.js';
107
- import { flightChunkScript } from './flight-scripts.js';
108
- import {
109
- flightInjectionTransitions,
110
- isHtmlDone,
111
- isPullDone,
112
- type FlightInjectionState,
113
- type FlightInjectionEvent,
114
- } from './flight-injection-state.js';
115
- import { withTimeout, RenderTimeoutError } from './render-timeout.js';
116
- import { logStreamingError } from './logger.js';
117
-
118
99
  // ─── Move Suffix Transform ───────────────────────────────────────────────────
119
100
 
120
- const SUFFIX = '</body></html>';
121
- const SUFFIX_BUF = Buffer.from(SUFFIX, 'utf-8');
122
-
123
101
  /**
124
- * Node.js Transform that moves `</body></html>` to the end of the stream.
102
+ * Node.js `Transform` that moves `</body></html>` to the end of the stream.
125
103
  *
126
- * Equivalent to createMoveSuffixStream() in html-injectors.ts.
127
- * Strips the suffix when first encountered and re-emits it in flush().
128
- * If no suffix is found, it's appended anyway for well-formed HTML.
104
+ * Equivalent to `createMoveSuffixStream` in `html-injectors.ts`. Strips
105
+ * the suffix on first sight and re-emits it from `flush()`. If the
106
+ * suffix never appeared in the input, it is appended anyway so output
107
+ * is well-formed.
129
108
  */
130
109
  export function createNodeMoveSuffixTransform(): Transform {
131
- let foundSuffix = false;
110
+ const state = createSuffixState();
132
111
 
133
112
  return new Transform({
134
113
  transform(chunk: Buffer, _encoding, callback) {
135
- if (foundSuffix) {
114
+ const result = processSuffixChunk(state, chunk);
115
+ if (result.kind === 'passthrough' || result.kind === 'noSuffix') {
136
116
  this.push(chunk);
137
117
  callback();
138
118
  return;
139
119
  }
140
-
141
- const text = chunk.toString('utf-8');
142
- const idx = text.indexOf(SUFFIX);
143
- if (idx === -1) {
144
- this.push(chunk);
145
- callback();
146
- return;
147
- }
148
-
149
- foundSuffix = true;
150
-
151
- // If the entire chunk is exactly the suffix, skip it
152
- if (chunk.byteLength === SUFFIX_BUF.byteLength) {
153
- callback();
154
- return;
155
- }
156
-
157
- // Emit content before the suffix
158
- const before = text.slice(0, idx);
159
- const after = text.slice(idx + SUFFIX.length);
160
- if (before) this.push(Buffer.from(before, 'utf-8'));
161
- if (after) this.push(Buffer.from(after, 'utf-8'));
120
+ if (result.before) this.push(Buffer.from(result.before));
121
+ if (result.after) this.push(Buffer.from(result.after));
162
122
  callback();
163
123
  },
164
124
  flush(callback) {
165
- // Always emit the suffix at the very end
166
- this.push(SUFFIX_BUF);
125
+ this.push(Buffer.from(suffixFlush(state)));
167
126
  callback();
168
127
  },
169
128
  });
170
129
  }
171
130
 
131
+ // Re-export for tests / consumers that need the suffix bytes constant.
132
+ export { SUFFIX_BYTES };
133
+
172
134
  // ─── Head Injection ──────────────────────────────────────────────────────────
173
135
 
174
136
  /**
175
- * Node.js Transform that injects HTML content before </head>.
137
+ * Node.js `Transform` that injects HTML content before `</head>`.
176
138
  *
177
- * Equivalent to injectHead() in html-injectors.ts. Streams chunks
139
+ * Equivalent to `injectHead` in `html-injectors.ts`. Streams chunks
178
140
  * through immediately, keeping only a small trailing buffer to handle
179
- * </head> split across chunk boundaries.
141
+ * `</head>` split across chunk boundaries.
180
142
  */
181
143
  export function createNodeHeadInjector(headHtml: string): Transform {
182
144
  if (!headHtml) {
@@ -187,41 +149,21 @@ export function createNodeHeadInjector(headHtml: string): Transform {
187
149
  });
188
150
  }
189
151
 
190
- const target = '</head>';
191
- const tailLen = target.length - 1;
192
- let injected = false;
193
- let tail = '';
152
+ const state = createInjectorState({ content: headHtml, targetTag: '</head>' });
194
153
 
195
154
  return new Transform({
196
155
  transform(chunk: Buffer, _encoding, callback) {
197
- if (injected) {
156
+ const result = processInjectorChunk(state, chunk);
157
+ if (result.kind === 'passthrough') {
198
158
  callback(null, chunk);
199
159
  return;
200
160
  }
201
-
202
- const text = tail + chunk.toString('utf-8');
203
- const tagIndex = text.indexOf(target);
204
-
205
- if (tagIndex !== -1) {
206
- const before = text.slice(0, tagIndex);
207
- const after = text.slice(tagIndex);
208
- this.push(Buffer.from(before + headHtml + after, 'utf-8'));
209
- injected = true;
210
- tail = '';
211
- callback();
212
- } else {
213
- const safeEnd = Math.max(0, text.length - tailLen);
214
- if (safeEnd > 0) {
215
- this.push(Buffer.from(text.slice(0, safeEnd), 'utf-8'));
216
- }
217
- tail = text.slice(safeEnd);
218
- callback();
219
- }
161
+ if (result.bytes) this.push(Buffer.from(result.bytes));
162
+ callback();
220
163
  },
221
164
  flush(callback) {
222
- if (!injected && tail) {
223
- this.push(Buffer.from(tail, 'utf-8'));
224
- }
165
+ const leftover = injectorFlush(state);
166
+ if (leftover) this.push(Buffer.from(leftover));
225
167
  callback();
226
168
  },
227
169
  });
@@ -229,35 +171,37 @@ export function createNodeHeadInjector(headHtml: string): Transform {
229
171
 
230
172
  // ─── RSC Flight Injection ────────────────────────────────────────────────────
231
173
 
232
- /**
233
- * Node.js Transform that merges RSC script tags into the HTML stream.
234
- *
235
- * Reads RSC chunks from the provided ReadableStream and injects them
236
- * as `<script>` tags between HTML chunks. Scripts are buffered in
237
- * pending[] and only drained from transform() (after a complete HTML
238
- * chunk) or flush() — never pushed directly from the pull loop.
239
- *
240
- * Suffix stripping (</body></html>) is handled upstream by
241
- * createNodeMoveSuffixTransform. This transform only interleaves
242
- * RSC scripts at safe chunk boundaries.
243
- *
244
- * The RSC stream is a Web ReadableStream (from the tee'd RSC Flight
245
- * stream). We read from it using the Web API — this is the one bridge
246
- * point between Web Streams and Node.js streams in the pipeline.
247
- */
248
174
  /**
249
175
  * Options for the Node.js flight injector.
250
176
  */
251
177
  export interface NodeFlightInjectorOptions {
252
178
  /**
253
- * Timeout in milliseconds for individual RSC stream reads.
254
- * If a single `rscReader.read()` call does not resolve within
255
- * this duration, the read is aborted and the stream errors with
256
- * a RenderTimeoutError. Default: 30000 (30s).
179
+ * Timeout in milliseconds for individual RSC stream reads. If a single
180
+ * `rscReader.read()` call does not resolve within this duration, the
181
+ * read is aborted and the stream errors with a `RenderTimeoutError`.
182
+ * Default: 30000 (30s).
257
183
  */
258
184
  renderTimeoutMs?: number;
259
185
  }
260
186
 
187
+ /**
188
+ * Node.js `Transform` that merges RSC script tags into the HTML stream.
189
+ *
190
+ * Equivalent to `createFlightInjectionTransform` in `html-injectors.ts`.
191
+ * The state machine, pending queue, and pull loop all live in
192
+ * `FlightInjectorCore` — this wrapper only shuffles bytes between the
193
+ * core and the Node `Transform.push()` API.
194
+ *
195
+ * Suffix stripping (`</body></html>`) is handled upstream by
196
+ * `createNodeMoveSuffixTransform`. RSC scripts are buffered in
197
+ * `core.pending[]` and only drained from `transform()` (after a complete
198
+ * HTML chunk) or `flush()` — never mid-tag. See design/02
199
+ * §"Scripts at Chunk Boundaries Only".
200
+ *
201
+ * The RSC stream is a Web `ReadableStream` (from the tee'd RSC Flight
202
+ * stream). The core reads from it using the Web API — this is the one
203
+ * bridge point between Web Streams and Node.js streams in the pipeline.
204
+ */
261
205
  export function createNodeFlightInjector(
262
206
  rscStream: ReadableStream<Uint8Array> | undefined,
263
207
  options?: NodeFlightInjectorOptions
@@ -270,136 +214,53 @@ export function createNodeFlightInjector(
270
214
  });
271
215
  }
272
216
 
273
- const timeoutMs = options?.renderTimeoutMs ?? 30_000;
274
- const rscReader = rscStream.getReader();
275
- const decoder = new TextDecoder('utf-8', { fatal: true });
217
+ const core = new FlightInjectorCore(rscStream, options?.renderTimeoutMs);
276
218
 
277
- const machine = createMachine<FlightInjectionState, FlightInjectionEvent>({
278
- initial: { phase: 'init' },
279
- transitions: flightInjectionTransitions,
280
- });
281
-
282
- // Stored promise from pullLoop — awaited in flush() via .then()
283
- // instead of polling. See design/02 §"No Polling".
284
- let pullPromise: Promise<void> | null = null;
285
-
286
- // RSC script chunks waiting to be drained at a safe boundary.
287
- // pullLoop buffers here; transform() and flush() drain.
288
- // RSC script chunks waiting to be drained at a safe boundary.
289
- // pullLoop buffers here; transform(), flush(), and pullLoop's
290
- // post-yield drain all consume from this buffer.
291
- const pending: Buffer[] = [];
292
- async function pullLoop(): Promise<void> {
293
- // Yield once so the first transform() call can process the shell
294
- // HTML chunk before we start reading RSC data.
295
- await new Promise<void>((r) => setImmediate(r));
296
- try {
297
- for (;;) {
298
- // Guard each RSC read with a timeout so a permanently hung
299
- // RSC stream eventually aborts instead of blocking forever.
300
- // See design/02-rendering-pipeline.md §"Streaming Constraints".
301
- const readPromise = rscReader.read();
302
- const { done, value } =
303
- timeoutMs > 0
304
- ? await withTimeout(readPromise, timeoutMs, 'RSC stream read timed out')
305
- : await readPromise;
306
- if (done) {
307
- machine.send({ type: 'PULL_DONE' });
308
- return;
309
- }
310
- const decoded = decoder.decode(value, { stream: true });
311
- const scriptBuf = Buffer.from(flightChunkScript(decoded), 'utf-8');
312
- // Buffer the script — drained by the next transform() call,
313
- // flush(), or by the scheduled drain below.
314
- pending.push(scriptBuf);
315
- // Yield between reads so HTML chunks get priority in the event
316
- // loop — but only while HTML is still streaming. Once flush()
317
- // fires, read without yielding to drain remaining RSC data.
318
- if (!isHtmlDone(machine.state)) {
319
- await new Promise<void>((r) => setImmediate(r));
320
- // After yielding, if no transform() call drained the buffer
321
- // (i.e., no new HTML chunk arrived), drain now. This ensures
322
- // RSC flight data reaches the client at shell-flush time —
323
- // without this, hydration blocks on createFromReadableStream
324
- // waiting for data that's stuck in pending[].
325
- // This is safe: we're between event loop ticks, so no
326
- // transform() call is mid-execution (no mid-tag risk).
327
- if (pending.length > 0) {
328
- drainPending();
329
- }
330
- }
331
- }
332
- } catch (err) {
333
- // On timeout, cancel the RSC reader to release resources.
334
- if (err instanceof RenderTimeoutError) {
335
- rscReader.cancel(err).catch(() => {});
336
- }
337
- machine.send({ type: 'PULL_ERROR', error: err });
338
- }
339
- }
340
-
341
- /** Drain all buffered RSC script chunks to the transform output. */
342
- function drainPending(): void {
343
- while (pending.length > 0) {
344
- transform.push(pending.shift()!);
345
- }
346
- }
347
-
348
- // No bootstrap script here — the init script is in <head> via
349
- // flightInitScript() (see flight-scripts.ts). This ensures __timber_f
350
- // exists before any chunk scripts execute.
219
+ const drain = (): void => {
220
+ while (core.pending.length > 0) transform.push(Buffer.from(core.pending.shift()!));
221
+ };
351
222
 
352
223
  const transform = new Transform({
353
224
  transform(chunk: Buffer, _encoding, callback) {
354
- const isFirst = machine.state.phase === 'init';
355
- if (isFirst) {
356
- machine.send({ type: 'FIRST_CHUNK' });
357
- }
225
+ const wasInit = core.isInit;
226
+ if (wasInit) core.notifyFirstChunk();
358
227
 
359
228
  // Emit the HTML chunk, then drain any buffered RSC scripts.
360
229
  // Scripts always come AFTER a complete HTML chunk — never mid-tag.
361
- // The buffered transform upstream (TIM-528) ensures each chunk is
362
- // a coherent HTML fragment. Suffix stripping is handled upstream
363
- // by createNodeMoveSuffixTransform (TIM-530).
230
+ // The buffered transform upstream (TIM-528) ensures coherent chunks.
231
+ // Suffix stripping is upstream via createNodeMoveSuffixTransform (TIM-530).
364
232
  transform.push(chunk);
365
- drainPending();
233
+ drain();
366
234
 
367
235
  // Start the pull loop on the first HTML chunk.
368
- if (isFirst) {
369
- pullPromise = pullLoop();
370
- }
236
+ if (wasInit) core.ensurePullLoop(drain);
371
237
  callback();
372
238
  },
373
239
  flush(callback) {
374
- // All HTML chunks have been emitted. Transition to flushing
375
- // the pull loop will stop yielding between RSC reads since
376
- // isHtmlDone() now returns true.
377
- machine.send({ type: 'HTML_DONE' });
240
+ // All HTML chunks have been emitted. Pull loop stops yielding.
241
+ core.notifyHtmlDone();
378
242
 
379
243
  const finish = () => {
380
- // Drain any remaining buffered RSC scripts
381
- drainPending();
382
- if (machine.state.phase === 'error') {
383
- const err = machine.state.error;
244
+ drain();
245
+ const err = core.terminalError;
246
+ if (err !== null) {
384
247
  transform.destroy(err instanceof Error ? err : new Error(String(err)));
385
248
  return;
386
249
  }
387
250
  callback();
388
251
  };
389
252
 
390
- if (isPullDone(machine.state)) {
253
+ if (core.isPullDone) {
391
254
  finish();
392
255
  return;
393
256
  }
394
- // Wait for the RSC pull loop promise to resolve instead of
395
- // polling with setImmediate. No CPU spin, no busy-poll —
396
- // just a Promise chain. See design/02 §"No Polling".
397
- if (!pullPromise) {
398
- pullPromise = pullLoop();
399
- }
400
- pullPromise.then(finish, (err) => {
401
- machine.send({ type: 'PULL_ERROR', error: err });
402
- finish();
257
+ // Wait on the pull-loop promise instead of polling. Zero CPU cost
258
+ // while waiting. See design/02 §"No Polling".
259
+ core.ensurePullLoop(drain).then(finish, (err) => {
260
+ // ensurePullLoop's underlying loop catches its own errors and
261
+ // sends PULL_ERROR — this catch is defence-in-depth for any
262
+ // synchronous throws inside the .then() chain itself.
263
+ transform.destroy(err instanceof Error ? err : new Error(String(err)));
403
264
  });
404
265
  },
405
266
  });
@@ -459,7 +320,6 @@ const COMPRESSIBLE_TYPES = new Set([
459
320
  'application/xml',
460
321
  'application/xhtml+xml',
461
322
  'application/rss+xml',
462
- 'application/atom+xml',
463
323
  'image/svg+xml',
464
324
  ]);
465
325