@timber-js/app 0.2.0-alpha.208 → 0.2.0-alpha.209

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 (520) hide show
  1. package/dist/_chunks/{primitives-DLAnvsrk.js → actions-BerlqoXA.js} +640 -410
  2. package/dist/_chunks/actions-BerlqoXA.js.map +1 -0
  3. package/dist/_chunks/{als-registry-C6kcfprT.js → als-registry-DaxkVjt5.js} +6 -2
  4. package/dist/_chunks/als-registry-DaxkVjt5.js.map +1 -0
  5. package/dist/_chunks/{build-manifest-DTmSGLRz.js → build-manifest-DWppEdLB.js} +2 -51
  6. package/dist/_chunks/build-manifest-DWppEdLB.js.map +1 -0
  7. package/dist/_chunks/{build-output-helper-9dqCv1pZ.js → build-output-helper-RV_BRcF1.js} +50 -53
  8. package/dist/_chunks/build-output-helper-RV_BRcF1.js.map +1 -0
  9. package/dist/_chunks/{cache-api-CUD9Ezaq.js → cache-api-CR23J_NC.js} +64 -32
  10. package/dist/_chunks/cache-api-CR23J_NC.js.map +1 -0
  11. package/dist/_chunks/{chains-B4bgOl2g.js → chains-CpFg56UB.js} +2 -2
  12. package/dist/_chunks/{chains-B4bgOl2g.js.map → chains-CpFg56UB.js.map} +1 -1
  13. package/dist/_chunks/{cli-check-G8o30ldg.js → cli-check-C6Ev6wBO.js} +11 -52
  14. package/dist/_chunks/cli-check-C6Ev6wBO.js.map +1 -0
  15. package/dist/_chunks/cli-context-By7k2xVo.js +70 -0
  16. package/dist/_chunks/cli-context-By7k2xVo.js.map +1 -0
  17. package/dist/_chunks/{cli-schema-sync--LZKs8Zn.js → cli-schema-sync-CbT2AUUI.js} +3 -3
  18. package/dist/_chunks/{cli-schema-sync--LZKs8Zn.js.map → cli-schema-sync-CbT2AUUI.js.map} +1 -1
  19. package/dist/_chunks/{cloudflare-BFb__LYG.js → cloudflare-Byxl8FWP.js} +96 -53
  20. package/dist/_chunks/{cloudflare-BFb__LYG.js.map → cloudflare-Byxl8FWP.js.map} +1 -1
  21. package/dist/_chunks/{convention-lint-5KzoMg_A.js → convention-lint-jKTwKwPe.js} +21 -6
  22. package/dist/_chunks/convention-lint-jKTwKwPe.js.map +1 -0
  23. package/dist/_chunks/{dev-server-BzBxgYAV.js → dev-server-C4WZdB7L.js} +15 -167
  24. package/dist/_chunks/dev-server-C4WZdB7L.js.map +1 -0
  25. package/dist/_chunks/{error-boundary-BrHmG-2X.js → error-boundary-tA7kVfs4.js} +84 -104
  26. package/dist/_chunks/error-boundary-tA7kVfs4.js.map +1 -0
  27. package/dist/_chunks/{fs-identity-D7vb6GdI.js → fs-identity-BRKuL215.js} +3 -4
  28. package/dist/_chunks/fs-identity-BRKuL215.js.map +1 -0
  29. package/dist/_chunks/{graph-cache-CP4GEmf9.js → graph-cache-CYiUS2aS.js} +4 -5
  30. package/dist/_chunks/graph-cache-CYiUS2aS.js.map +1 -0
  31. package/dist/_chunks/{json-lossy-check-ClNvBM_3.js → json-lossy-check-ip0Qi0MT.js} +6 -3
  32. package/dist/_chunks/json-lossy-check-ip0Qi0MT.js.map +1 -0
  33. package/dist/_chunks/{live-graph-Bm5WTvcr.js → live-graph-Dv-JJCZw.js} +3 -3
  34. package/dist/_chunks/{live-graph-Bm5WTvcr.js.map → live-graph-Dv-JJCZw.js.map} +1 -1
  35. package/dist/_chunks/{logger-k2DQ4EUf.js → logger-DiDt5ppH.js} +192 -51
  36. package/dist/_chunks/logger-DiDt5ppH.js.map +1 -0
  37. package/dist/_chunks/{navigation-root-mHSK9psY.js → navigation-root-BQfo1-kG.js} +2 -2
  38. package/dist/_chunks/{navigation-root-mHSK9psY.js.map → navigation-root-BQfo1-kG.js.map} +1 -1
  39. package/dist/_chunks/{plugin-context-8idfR3UZ.js → plugin-context-BPBPc3lL.js} +28 -4
  40. package/dist/_chunks/plugin-context-BPBPc3lL.js.map +1 -0
  41. package/dist/_chunks/{poison-scan-DG5eI2_z.js → poison-scan-CpeT6_OJ.js} +2 -2
  42. package/dist/_chunks/{poison-scan-DG5eI2_z.js.map → poison-scan-CpeT6_OJ.js.map} +1 -1
  43. package/dist/_chunks/render-timeout-DOf_BBcO.js +46 -0
  44. package/dist/_chunks/render-timeout-DOf_BBcO.js.map +1 -0
  45. package/dist/_chunks/{rsc-payload-path-B_LBodc2.js → rsc-payload-path-dffcs4jp.js} +9 -3
  46. package/dist/_chunks/{rsc-payload-path-B_LBodc2.js.map → rsc-payload-path-dffcs4jp.js.map} +1 -1
  47. package/dist/_chunks/{scanner-BQ5sPIJ7.js → scanner-Bw0oq1HB.js} +2 -2
  48. package/dist/_chunks/{scanner-BQ5sPIJ7.js.map → scanner-Bw0oq1HB.js.map} +1 -1
  49. package/dist/_chunks/{navigation-context-D0TU0Jog.js → segment-context-xtPUGfdq.js} +36 -34
  50. package/dist/_chunks/segment-context-xtPUGfdq.js.map +1 -0
  51. package/dist/_chunks/{singleflight-2lUWfcAk.js → singleflight-BeVMMKi0.js} +4 -3
  52. package/dist/_chunks/{singleflight-2lUWfcAk.js.map → singleflight-BeVMMKi0.js.map} +1 -1
  53. package/dist/_chunks/{sizeof-lXffQ1Vr.js → sizeof-UwzwB1uM.js} +3 -2
  54. package/dist/_chunks/sizeof-UwzwB1uM.js.map +1 -0
  55. package/dist/_chunks/ssr-data-nA3I70_n.js +49 -0
  56. package/dist/_chunks/ssr-data-nA3I70_n.js.map +1 -0
  57. package/dist/_chunks/{state-FippDgxN.js → state-BhNGPsdi.js} +6 -8
  58. package/dist/_chunks/state-BhNGPsdi.js.map +1 -0
  59. package/dist/_chunks/{status-page-marker-CNbpUOPB.js → status-page-marker-gaihi0KZ.js} +53 -20
  60. package/dist/_chunks/status-page-marker-gaihi0KZ.js.map +1 -0
  61. package/dist/_chunks/{walkers-CN2N_bA-.js → walkers-BXExhzzk.js} +2 -2
  62. package/dist/_chunks/{walkers-CN2N_bA-.js.map → walkers-BXExhzzk.js.map} +1 -1
  63. package/dist/_chunks/write-file-atomic-D1qWqHb4.js +32 -0
  64. package/dist/_chunks/write-file-atomic-D1qWqHb4.js.map +1 -0
  65. package/dist/adapters/build-output-helper.d.ts +7 -7
  66. package/dist/adapters/build-output-helper.d.ts.map +1 -1
  67. package/dist/adapters/canonicalize-module.d.ts +8 -0
  68. package/dist/adapters/canonicalize-module.d.ts.map +1 -0
  69. package/dist/adapters/cloudflare-dev.js +1 -1
  70. package/dist/adapters/cloudflare-kv-cache.js +2 -2
  71. package/dist/adapters/cloudflare.d.ts +30 -8
  72. package/dist/adapters/cloudflare.d.ts.map +1 -1
  73. package/dist/adapters/cloudflare.js +2 -2
  74. package/dist/adapters/compress-module.d.ts +8 -0
  75. package/dist/adapters/compress-module.d.ts.map +1 -1
  76. package/dist/adapters/fs-identity.d.ts +2 -3
  77. package/dist/adapters/fs-identity.d.ts.map +1 -1
  78. package/dist/adapters/nitro-presets.d.ts +0 -6
  79. package/dist/adapters/nitro-presets.d.ts.map +1 -1
  80. package/dist/adapters/nitro-preview.d.ts +43 -11
  81. package/dist/adapters/nitro-preview.d.ts.map +1 -1
  82. package/dist/adapters/nitro.d.ts +1 -13
  83. package/dist/adapters/nitro.d.ts.map +1 -1
  84. package/dist/adapters/nitro.js +291 -176
  85. package/dist/adapters/nitro.js.map +1 -1
  86. package/dist/adapters/shared.d.ts +7 -2
  87. package/dist/adapters/shared.d.ts.map +1 -1
  88. package/dist/adapters/types.d.ts +105 -21
  89. package/dist/adapters/types.d.ts.map +1 -1
  90. package/dist/adapters/types.js +0 -0
  91. package/dist/analyze/crawl-entry.js +3 -3
  92. package/dist/analyze/graph-cache.d.ts.map +1 -1
  93. package/dist/analyze/graph-command.js +4 -4
  94. package/dist/cache/index.js +2 -2
  95. package/dist/cache/json-lossy-check.d.ts.map +1 -1
  96. package/dist/cache/singleflight.d.ts.map +1 -1
  97. package/dist/cache/sizeof.d.ts.map +1 -1
  98. package/dist/cache/stores/memory.js +1 -1
  99. package/dist/cache/tag-aware-handler.d.ts.map +1 -1
  100. package/dist/cdn/workers-cache-purge.js +1 -1
  101. package/dist/cli-check.d.ts +7 -37
  102. package/dist/cli-check.d.ts.map +1 -1
  103. package/dist/cli-context.d.ts +54 -0
  104. package/dist/cli-context.d.ts.map +1 -0
  105. package/dist/cli.d.ts +9 -9
  106. package/dist/cli.d.ts.map +1 -1
  107. package/dist/cli.js +20 -24
  108. package/dist/cli.js.map +1 -1
  109. package/dist/client/browser-dev.d.ts +1 -1
  110. package/dist/client/browser-entry/action-dispatch.d.ts.map +1 -1
  111. package/dist/client/browser-entry/hydrate.d.ts +19 -29
  112. package/dist/client/browser-entry/hydrate.d.ts.map +1 -1
  113. package/dist/client/browser-entry/index.d.ts +7 -7
  114. package/dist/client/browser-entry/post-hydration.d.ts +6 -9
  115. package/dist/client/browser-entry/post-hydration.d.ts.map +1 -1
  116. package/dist/client/browser-entry/router-init.d.ts +6 -11
  117. package/dist/client/browser-entry/router-init.d.ts.map +1 -1
  118. package/dist/client/error-boundary.d.ts +4 -3
  119. package/dist/client/error-boundary.d.ts.map +1 -1
  120. package/dist/client/error-boundary.js +1 -1
  121. package/dist/client/error-reconstituter.d.ts +3 -9
  122. package/dist/client/error-reconstituter.d.ts.map +1 -1
  123. package/dist/client/history.d.ts +12 -11
  124. package/dist/client/history.d.ts.map +1 -1
  125. package/dist/client/index.d.ts +0 -1
  126. package/dist/client/index.d.ts.map +1 -1
  127. package/dist/client/index.js +5 -6
  128. package/dist/client/index.js.map +1 -1
  129. package/dist/client/internal.d.ts +3 -3
  130. package/dist/client/internal.d.ts.map +1 -1
  131. package/dist/client/internal.js +68 -104
  132. package/dist/client/internal.js.map +1 -1
  133. package/dist/client/navigation-commit.d.ts +16 -7
  134. package/dist/client/navigation-commit.d.ts.map +1 -1
  135. package/dist/client/navigation-context.d.ts +3 -5
  136. package/dist/client/navigation-context.d.ts.map +1 -1
  137. package/dist/client/react-root.d.ts +3 -2
  138. package/dist/client/react-root.d.ts.map +1 -1
  139. package/dist/client/router-pipeline.d.ts +1 -6
  140. package/dist/client/router-pipeline.d.ts.map +1 -1
  141. package/dist/client/router-types.d.ts +45 -39
  142. package/dist/client/router-types.d.ts.map +1 -1
  143. package/dist/client/router-url.d.ts +24 -0
  144. package/dist/client/router-url.d.ts.map +1 -0
  145. package/dist/client/router.d.ts.map +1 -1
  146. package/dist/client/rsc-fetch.d.ts +8 -4
  147. package/dist/client/rsc-fetch.d.ts.map +1 -1
  148. package/dist/client/ssr-data.d.ts +18 -64
  149. package/dist/client/ssr-data.d.ts.map +1 -1
  150. package/dist/client/state.d.ts +2 -5
  151. package/dist/client/state.d.ts.map +1 -1
  152. package/dist/client/use-pathname.d.ts +1 -1
  153. package/dist/config-types.d.ts +2 -1
  154. package/dist/config-types.d.ts.map +1 -1
  155. package/dist/config-validation.d.ts.map +1 -1
  156. package/dist/cookies/index.js +1 -1
  157. package/dist/dev-tools/dev-page-shell.d.ts +17 -0
  158. package/dist/dev-tools/dev-page-shell.d.ts.map +1 -1
  159. package/dist/dev-tools/index.d.ts +1 -1
  160. package/dist/dev-tools/index.d.ts.map +1 -1
  161. package/dist/dev-tools/instrumentation.d.ts +17 -2
  162. package/dist/dev-tools/instrumentation.d.ts.map +1 -1
  163. package/dist/dev-tools/overlay.d.ts +6 -27
  164. package/dist/dev-tools/overlay.d.ts.map +1 -1
  165. package/dist/index.d.ts.map +1 -1
  166. package/dist/index.js +531 -373
  167. package/dist/index.js.map +1 -1
  168. package/dist/plugin-context.d.ts +26 -40
  169. package/dist/plugin-context.d.ts.map +1 -1
  170. package/dist/plugins/build-manifest.d.ts.map +1 -1
  171. package/dist/plugins/build-report.d.ts +15 -2
  172. package/dist/plugins/build-report.d.ts.map +1 -1
  173. package/dist/plugins/dev-server.d.ts.map +1 -1
  174. package/dist/plugins/entries.d.ts.map +1 -1
  175. package/dist/plugins/fonts.d.ts +19 -1
  176. package/dist/plugins/fonts.d.ts.map +1 -1
  177. package/dist/plugins/post-build.d.ts +110 -0
  178. package/dist/plugins/post-build.d.ts.map +1 -0
  179. package/dist/plugins/prebuilt-capture.d.ts +11 -4
  180. package/dist/plugins/prebuilt-capture.d.ts.map +1 -1
  181. package/dist/plugins/prebuilt.d.ts +17 -1
  182. package/dist/plugins/prebuilt.d.ts.map +1 -1
  183. package/dist/plugins/prerender-sugar.d.ts.map +1 -1
  184. package/dist/plugins/rsc-assets-manifest.d.ts +33 -0
  185. package/dist/plugins/rsc-assets-manifest.d.ts.map +1 -0
  186. package/dist/plugins/shims.d.ts.map +1 -1
  187. package/dist/plugins/static-build.d.ts +32 -9
  188. package/dist/plugins/static-build.d.ts.map +1 -1
  189. package/dist/routing/codegen-write.d.ts +0 -9
  190. package/dist/routing/codegen-write.d.ts.map +1 -1
  191. package/dist/routing/index.js +2 -2
  192. package/dist/routing/segment-codegen.d.ts.map +1 -1
  193. package/dist/server/access-gate.d.ts.map +1 -1
  194. package/dist/server/action-handler.d.ts +5 -4
  195. package/dist/server/action-handler.d.ts.map +1 -1
  196. package/dist/server/actions.d.ts +46 -7
  197. package/dist/server/actions.d.ts.map +1 -1
  198. package/dist/server/als-registry.d.ts +58 -5
  199. package/dist/server/als-registry.d.ts.map +1 -1
  200. package/dist/server/children-interception.d.ts.map +1 -1
  201. package/dist/server/cookie-context.d.ts +2 -23
  202. package/dist/server/cookie-context.d.ts.map +1 -1
  203. package/dist/server/csp-nonce.d.ts +0 -9
  204. package/dist/server/csp-nonce.d.ts.map +1 -1
  205. package/dist/server/csrf.d.ts +27 -4
  206. package/dist/server/csrf.d.ts.map +1 -1
  207. package/dist/server/debug.d.ts +0 -1
  208. package/dist/server/debug.d.ts.map +1 -1
  209. package/dist/server/default-logger.d.ts +2 -1
  210. package/dist/server/default-logger.d.ts.map +1 -1
  211. package/dist/server/deny-boundary.d.ts +12 -6
  212. package/dist/server/deny-boundary.d.ts.map +1 -1
  213. package/dist/server/deny-renderer.d.ts +2 -2
  214. package/dist/server/deny-renderer.d.ts.map +1 -1
  215. package/dist/server/error-boundary-wrapper.d.ts +7 -3
  216. package/dist/server/error-boundary-wrapper.d.ts.map +1 -1
  217. package/dist/server/fallback-error.d.ts +9 -2
  218. package/dist/server/fallback-error.d.ts.map +1 -1
  219. package/dist/server/head-response.d.ts +4 -4
  220. package/dist/server/html-injectors.d.ts.map +1 -1
  221. package/dist/server/index.d.ts +0 -1
  222. package/dist/server/index.d.ts.map +1 -1
  223. package/dist/server/index.js +3 -4
  224. package/dist/server/index.js.map +1 -1
  225. package/dist/server/instrumentation.d.ts +10 -3
  226. package/dist/server/instrumentation.d.ts.map +1 -1
  227. package/dist/server/internal.d.ts +1 -4
  228. package/dist/server/internal.d.ts.map +1 -1
  229. package/dist/server/internal.js +961 -581
  230. package/dist/server/internal.js.map +1 -1
  231. package/dist/server/json-status-response.d.ts +34 -0
  232. package/dist/server/json-status-response.d.ts.map +1 -0
  233. package/dist/server/logger.d.ts +26 -0
  234. package/dist/server/logger.d.ts.map +1 -1
  235. package/dist/server/param-coercion.d.ts +11 -6
  236. package/dist/server/param-coercion.d.ts.map +1 -1
  237. package/dist/server/pipeline-helpers.d.ts +95 -7
  238. package/dist/server/pipeline-helpers.d.ts.map +1 -1
  239. package/dist/server/pipeline-interception.d.ts +11 -0
  240. package/dist/server/pipeline-interception.d.ts.map +1 -1
  241. package/dist/server/pipeline-outcome.d.ts +11 -1
  242. package/dist/server/pipeline-outcome.d.ts.map +1 -1
  243. package/dist/server/pipeline-phases.d.ts +2 -2
  244. package/dist/server/pipeline-phases.d.ts.map +1 -1
  245. package/dist/server/pipeline.d.ts +23 -35
  246. package/dist/server/pipeline.d.ts.map +1 -1
  247. package/dist/server/prebuilt/capture-render.d.ts +25 -0
  248. package/dist/server/prebuilt/capture-render.d.ts.map +1 -0
  249. package/dist/server/prebuilt/capture-routes.d.ts +37 -0
  250. package/dist/server/prebuilt/capture-routes.d.ts.map +1 -0
  251. package/dist/server/prebuilt/capture-state.d.ts +79 -35
  252. package/dist/server/prebuilt/capture-state.d.ts.map +1 -1
  253. package/dist/server/prebuilt/lifetime.d.ts +38 -0
  254. package/dist/server/prebuilt/lifetime.d.ts.map +1 -0
  255. package/dist/server/prebuilt/slots.d.ts +7 -0
  256. package/dist/server/prebuilt/slots.d.ts.map +1 -1
  257. package/dist/server/prebuilt/synthetic-store.d.ts.map +1 -1
  258. package/dist/server/prebuilt-builder.d.ts.map +1 -1
  259. package/dist/server/prebuilt-runtime.d.ts.map +1 -1
  260. package/dist/server/primitives.d.ts +6 -81
  261. package/dist/server/primitives.d.ts.map +1 -1
  262. package/dist/server/react-cache-bridge.d.ts +59 -0
  263. package/dist/server/react-cache-bridge.d.ts.map +1 -0
  264. package/dist/server/react-cache-scope.d.ts +49 -0
  265. package/dist/server/react-cache-scope.d.ts.map +1 -0
  266. package/dist/server/render-timeout.d.ts +15 -0
  267. package/dist/server/render-timeout.d.ts.map +1 -1
  268. package/dist/server/render-utils.d.ts +9 -0
  269. package/dist/server/render-utils.d.ts.map +1 -1
  270. package/dist/server/request-context.d.ts +26 -5
  271. package/dist/server/request-context.d.ts.map +1 -1
  272. package/dist/server/route-element-builder.d.ts.map +1 -1
  273. package/dist/server/route-handler.d.ts.map +1 -1
  274. package/dist/server/route-matcher.d.ts +7 -0
  275. package/dist/server/route-matcher.d.ts.map +1 -1
  276. package/dist/server/rsc-entry/action-middleware-runner.d.ts +22 -22
  277. package/dist/server/rsc-entry/action-middleware-runner.d.ts.map +1 -1
  278. package/dist/server/rsc-entry/api-handler.d.ts +7 -0
  279. package/dist/server/rsc-entry/api-handler.d.ts.map +1 -1
  280. package/dist/server/rsc-entry/deny-fallback.d.ts +32 -6
  281. package/dist/server/rsc-entry/deny-fallback.d.ts.map +1 -1
  282. package/dist/server/rsc-entry/error-renderer.d.ts +11 -18
  283. package/dist/server/rsc-entry/error-renderer.d.ts.map +1 -1
  284. package/dist/server/rsc-entry/helpers.d.ts +0 -1
  285. package/dist/server/rsc-entry/helpers.d.ts.map +1 -1
  286. package/dist/server/rsc-entry/index.d.ts +5 -15
  287. package/dist/server/rsc-entry/index.d.ts.map +1 -1
  288. package/dist/server/rsc-entry/render-route.d.ts +5 -6
  289. package/dist/server/rsc-entry/render-route.d.ts.map +1 -1
  290. package/dist/server/rsc-entry/render-signals.d.ts +65 -0
  291. package/dist/server/rsc-entry/render-signals.d.ts.map +1 -0
  292. package/dist/server/rsc-entry/revalidate-renderer.d.ts +6 -16
  293. package/dist/server/rsc-entry/revalidate-renderer.d.ts.map +1 -1
  294. package/dist/server/rsc-entry/rsc-payload.d.ts +7 -3
  295. package/dist/server/rsc-entry/rsc-payload.d.ts.map +1 -1
  296. package/dist/server/rsc-entry/rsc-stream.d.ts +8 -53
  297. package/dist/server/rsc-entry/rsc-stream.d.ts.map +1 -1
  298. package/dist/server/rsc-entry/ssr-bridge.d.ts +2 -2
  299. package/dist/server/rsc-entry/ssr-bridge.d.ts.map +1 -1
  300. package/dist/server/rsc-entry/ssr-renderer.d.ts +9 -4
  301. package/dist/server/rsc-entry/ssr-renderer.d.ts.map +1 -1
  302. package/dist/server/rsc-entry/wrap-action-dispatch.d.ts +1 -1
  303. package/dist/server/rsc-entry/wrap-action-dispatch.d.ts.map +1 -1
  304. package/dist/server/signal-identity.d.ts +43 -0
  305. package/dist/server/signal-identity.d.ts.map +1 -0
  306. package/dist/server/slot-resolver.d.ts +4 -3
  307. package/dist/server/slot-resolver.d.ts.map +1 -1
  308. package/dist/server/slot-subtree-contain.d.ts.map +1 -1
  309. package/dist/server/ssr-bridge-types.d.ts +91 -53
  310. package/dist/server/ssr-bridge-types.d.ts.map +1 -1
  311. package/dist/server/ssr-entry.d.ts +8 -5
  312. package/dist/server/ssr-entry.d.ts.map +1 -1
  313. package/dist/server/ssr-render.d.ts +26 -40
  314. package/dist/server/ssr-render.d.ts.map +1 -1
  315. package/dist/server/ssr-wrappers.d.ts +1 -1
  316. package/dist/server/static-generator.d.ts.map +1 -1
  317. package/dist/server/stream-utils.d.ts +12 -0
  318. package/dist/server/stream-utils.d.ts.map +1 -1
  319. package/dist/server/tracing.d.ts +12 -16
  320. package/dist/server/tracing.d.ts.map +1 -1
  321. package/dist/server/types.d.ts +1 -1
  322. package/dist/server/version-skew.d.ts +3 -3
  323. package/dist/shared/csrf-reject.d.ts +16 -0
  324. package/dist/shared/csrf-reject.d.ts.map +1 -0
  325. package/dist/shared/error-digest.d.ts +28 -0
  326. package/dist/shared/error-digest.d.ts.map +1 -0
  327. package/dist/shared/payload-root.d.ts +11 -10
  328. package/dist/shared/payload-root.d.ts.map +1 -1
  329. package/dist/shared/rsc-payload-path.d.ts +6 -0
  330. package/dist/shared/rsc-payload-path.d.ts.map +1 -1
  331. package/dist/utils/write-file-atomic.d.ts +17 -0
  332. package/dist/utils/write-file-atomic.d.ts.map +1 -0
  333. package/docs/api/30-api-server.mdx +17 -14
  334. package/docs/api/32-api-cache.mdx +51 -23
  335. package/docs/api/34-api-config.mdx +34 -6
  336. package/docs/api/36-cli.mdx +11 -1
  337. package/docs/learn/03-fetching-data.mdx +17 -1
  338. package/docs/learn/04-access-control.mdx +1 -1
  339. package/docs/learn/10-caching.mdx +29 -11
  340. package/docs/learn/11-middleware.mdx +8 -2
  341. package/docs/learn/12-error-handling.mdx +6 -1
  342. package/package.json +5 -1
  343. package/src/adapters/build-output-helper.ts +55 -53
  344. package/src/adapters/canonicalize-module.ts +29 -0
  345. package/src/adapters/cloudflare.ts +105 -63
  346. package/src/adapters/compress-module.ts +16 -5
  347. package/src/adapters/fs-identity.ts +2 -3
  348. package/src/adapters/nitro-presets.ts +0 -15
  349. package/src/adapters/nitro-preview.ts +272 -136
  350. package/src/adapters/nitro.ts +119 -82
  351. package/src/adapters/shared.ts +23 -14
  352. package/src/adapters/types.ts +110 -21
  353. package/src/analyze/graph-cache.ts +3 -4
  354. package/src/cache/json-lossy-check.ts +3 -9
  355. package/src/cache/singleflight.ts +5 -2
  356. package/src/cache/sizeof.ts +3 -1
  357. package/src/cache/tag-aware-handler.ts +43 -26
  358. package/src/cli-check.ts +8 -98
  359. package/src/cli-context.ts +119 -0
  360. package/src/cli.ts +19 -28
  361. package/src/client/browser-dev.ts +1 -1
  362. package/src/client/browser-entry/action-dispatch.ts +26 -4
  363. package/src/client/browser-entry/hydrate.ts +53 -58
  364. package/src/client/browser-entry/index.ts +16 -27
  365. package/src/client/browser-entry/post-hydration.ts +6 -40
  366. package/src/client/browser-entry/router-init.ts +66 -84
  367. package/src/client/error-boundary.tsx +9 -41
  368. package/src/client/error-reconstituter.tsx +10 -5
  369. package/src/client/history.ts +18 -21
  370. package/src/client/index.ts +0 -1
  371. package/src/client/internal.ts +2 -7
  372. package/src/client/navigation-commit.ts +35 -18
  373. package/src/client/navigation-context.ts +3 -39
  374. package/src/client/react-root.ts +3 -2
  375. package/src/client/router-pipeline.ts +65 -128
  376. package/src/client/router-types.ts +46 -44
  377. package/src/client/router-url.ts +32 -0
  378. package/src/client/router.ts +43 -22
  379. package/src/client/rsc-fetch.ts +117 -155
  380. package/src/client/ssr-data.ts +19 -80
  381. package/src/client/state.ts +2 -9
  382. package/src/client/use-pathname.ts +1 -1
  383. package/src/config-types.ts +2 -1
  384. package/src/config-validation.ts +2 -1
  385. package/src/dev-tools/dev-page-shell.ts +18 -0
  386. package/src/dev-tools/index.ts +0 -2
  387. package/src/dev-tools/instrumentation.ts +50 -2
  388. package/src/dev-tools/overlay.ts +8 -238
  389. package/src/index.ts +24 -14
  390. package/src/plugin-context.ts +39 -40
  391. package/src/plugins/build-manifest.ts +4 -11
  392. package/src/plugins/build-report.ts +70 -70
  393. package/src/plugins/dev-server.ts +24 -44
  394. package/src/plugins/entries.ts +2 -1
  395. package/src/plugins/fonts.ts +35 -7
  396. package/src/plugins/post-build.ts +327 -0
  397. package/src/plugins/prebuilt-capture.ts +28 -32
  398. package/src/plugins/prebuilt.ts +54 -39
  399. package/src/plugins/prerender-sugar.ts +0 -4
  400. package/src/plugins/{adapter-build.ts → rsc-assets-manifest.ts} +20 -109
  401. package/src/plugins/shims.ts +1 -0
  402. package/src/plugins/static-build.ts +42 -74
  403. package/src/routing/codegen-write.ts +25 -3
  404. package/src/routing/segment-codegen.ts +3 -2
  405. package/src/rsc-runtime/vendor-types.d.ts +8 -0
  406. package/src/server/access-gate.tsx +2 -1
  407. package/src/server/action-client.ts +1 -1
  408. package/src/server/action-handler.ts +253 -110
  409. package/src/server/actions.ts +73 -25
  410. package/src/server/als-registry.ts +73 -5
  411. package/src/server/chain-url-parts.ts +1 -1
  412. package/src/server/children-interception.ts +2 -1
  413. package/src/server/cookie-context.ts +18 -48
  414. package/src/server/csp-nonce.ts +0 -12
  415. package/src/server/csrf.ts +140 -8
  416. package/src/server/debug.ts +0 -1
  417. package/src/server/default-logger.ts +2 -1
  418. package/src/server/deny-boundary.ts +25 -13
  419. package/src/server/deny-renderer.ts +30 -97
  420. package/src/server/error-boundary-wrapper.ts +7 -3
  421. package/src/server/fallback-error.ts +26 -11
  422. package/src/server/flight-scripts.ts +1 -1
  423. package/src/server/head-response.ts +4 -4
  424. package/src/server/html-injector-core.ts +2 -2
  425. package/src/server/html-injectors.ts +2 -4
  426. package/src/server/index.ts +0 -4
  427. package/src/server/instrumentation.ts +10 -3
  428. package/src/server/internal.ts +1 -6
  429. package/src/server/json-status-response.ts +105 -0
  430. package/src/server/logger.ts +39 -3
  431. package/src/server/param-coercion.ts +15 -12
  432. package/src/server/pipeline-helpers.ts +257 -12
  433. package/src/server/pipeline-interception.ts +133 -0
  434. package/src/server/pipeline-outcome.ts +103 -31
  435. package/src/server/pipeline-phases.ts +127 -237
  436. package/src/server/pipeline.ts +204 -207
  437. package/src/server/prebuilt/capture-render.ts +118 -0
  438. package/src/server/prebuilt/capture-routes.ts +104 -0
  439. package/src/server/prebuilt/capture-state.ts +155 -48
  440. package/src/server/prebuilt/lifetime.ts +151 -0
  441. package/src/server/prebuilt/slots.ts +14 -2
  442. package/src/server/prebuilt/synthetic-store.ts +3 -0
  443. package/src/server/prebuilt-builder.ts +30 -320
  444. package/src/server/prebuilt-runtime.ts +125 -129
  445. package/src/server/primitives.ts +8 -168
  446. package/src/server/react-cache-bridge.ts +124 -0
  447. package/src/server/react-cache-scope.ts +104 -0
  448. package/src/server/render-timeout.ts +20 -0
  449. package/src/server/render-utils.ts +11 -1
  450. package/src/server/request-context.ts +36 -9
  451. package/src/server/route-element-builder.ts +11 -6
  452. package/src/server/route-handler.ts +36 -23
  453. package/src/server/route-matcher.ts +15 -1
  454. package/src/server/rsc-entry/action-middleware-runner.ts +79 -123
  455. package/src/server/rsc-entry/api-handler.ts +27 -103
  456. package/src/server/rsc-entry/deny-fallback.ts +93 -26
  457. package/src/server/rsc-entry/error-renderer.ts +39 -91
  458. package/src/server/rsc-entry/helpers.ts +2 -4
  459. package/src/server/rsc-entry/index.ts +64 -58
  460. package/src/server/rsc-entry/render-route.ts +15 -80
  461. package/src/server/rsc-entry/render-signals.ts +108 -0
  462. package/src/server/rsc-entry/revalidate-renderer.ts +56 -57
  463. package/src/server/rsc-entry/rsc-payload.ts +28 -61
  464. package/src/server/rsc-entry/rsc-stream.ts +113 -213
  465. package/src/server/rsc-entry/ssr-bridge.ts +2 -2
  466. package/src/server/rsc-entry/ssr-renderer.ts +182 -263
  467. package/src/server/rsc-entry/wrap-action-dispatch.ts +12 -14
  468. package/src/server/signal-identity.ts +112 -0
  469. package/src/server/slot-resolver.ts +18 -18
  470. package/src/server/slot-subtree-contain.ts +2 -1
  471. package/src/server/ssr-bridge-types.ts +92 -56
  472. package/src/server/ssr-entry.ts +87 -111
  473. package/src/server/ssr-render.ts +55 -64
  474. package/src/server/ssr-wrappers.tsx +2 -2
  475. package/src/server/static-generator.ts +7 -42
  476. package/src/server/static-not-found.ts +3 -3
  477. package/src/server/stream-utils.ts +51 -0
  478. package/src/server/tracing.ts +58 -75
  479. package/src/server/types.ts +1 -1
  480. package/src/server/version-skew.ts +5 -5
  481. package/src/shared/csrf-reject.ts +15 -0
  482. package/src/shared/error-digest.ts +46 -0
  483. package/src/shared/payload-root.ts +11 -10
  484. package/src/shared/rsc-payload-path.ts +8 -1
  485. package/src/utils/write-file-atomic.ts +32 -0
  486. package/dist/_chunks/actions-jNrXdzgs.js +0 -346
  487. package/dist/_chunks/actions-jNrXdzgs.js.map +0 -1
  488. package/dist/_chunks/als-registry-C6kcfprT.js.map +0 -1
  489. package/dist/_chunks/build-manifest-DTmSGLRz.js.map +0 -1
  490. package/dist/_chunks/build-output-helper-9dqCv1pZ.js.map +0 -1
  491. package/dist/_chunks/cache-api-CUD9Ezaq.js.map +0 -1
  492. package/dist/_chunks/cli-check-G8o30ldg.js.map +0 -1
  493. package/dist/_chunks/convention-lint-5KzoMg_A.js.map +0 -1
  494. package/dist/_chunks/dev-server-BzBxgYAV.js.map +0 -1
  495. package/dist/_chunks/error-boundary-BrHmG-2X.js.map +0 -1
  496. package/dist/_chunks/fs-identity-D7vb6GdI.js.map +0 -1
  497. package/dist/_chunks/graph-cache-CP4GEmf9.js.map +0 -1
  498. package/dist/_chunks/json-lossy-check-ClNvBM_3.js.map +0 -1
  499. package/dist/_chunks/logger-k2DQ4EUf.js.map +0 -1
  500. package/dist/_chunks/navigation-context-D0TU0Jog.js.map +0 -1
  501. package/dist/_chunks/plugin-context-8idfR3UZ.js.map +0 -1
  502. package/dist/_chunks/primitives-DLAnvsrk.js.map +0 -1
  503. package/dist/_chunks/segment-context-D9_89u34.js +0 -34
  504. package/dist/_chunks/segment-context-D9_89u34.js.map +0 -1
  505. package/dist/_chunks/sizeof-lXffQ1Vr.js.map +0 -1
  506. package/dist/_chunks/ssr-data-D6T6Y3ef.js +0 -60
  507. package/dist/_chunks/ssr-data-D6T6Y3ef.js.map +0 -1
  508. package/dist/_chunks/state-FippDgxN.js.map +0 -1
  509. package/dist/_chunks/status-page-marker-CNbpUOPB.js.map +0 -1
  510. package/dist/client/types.d.ts +0 -14
  511. package/dist/client/types.d.ts.map +0 -1
  512. package/dist/plugins/adapter-build.d.ts +0 -38
  513. package/dist/plugins/adapter-build.d.ts.map +0 -1
  514. package/dist/server/asset-headers.d.ts +0 -32
  515. package/dist/server/asset-headers.d.ts.map +0 -1
  516. package/dist/server/dev-source-map.d.ts +0 -22
  517. package/dist/server/dev-source-map.d.ts.map +0 -1
  518. package/src/client/types.ts +0 -21
  519. package/src/server/asset-headers.ts +0 -59
  520. package/src/server/dev-source-map.ts +0 -31
@@ -8,10 +8,11 @@
8
8
  * See design/17-logging.md §"Production Logging"
9
9
  */
10
10
 
11
- import { getTraceStore } from './tracing.ts';
11
+ import { generateTraceId, getTraceStore } from './tracing.ts';
12
12
  import { createDefaultLogger } from './default-logger.ts';
13
13
  import { isDevMode } from './debug.ts';
14
- import { isControlFlowSignal } from './primitives.ts';
14
+ import { isControlFlowSignal } from './signal-identity.ts';
15
+ import type { CsrfRejectReason } from './csrf.ts';
15
16
 
16
17
  // ─── Logger Interface ─────────────────────────────────────────────────────
17
18
 
@@ -56,7 +57,7 @@ function withTraceContext(data?: Record<string, unknown>): Record<string, unknow
56
57
  const store = getTraceStore();
57
58
  const enriched: Record<string, unknown> = { ...data };
58
59
  if (store) {
59
- enriched.trace_id = store.traceId;
60
+ enriched.trace_id = store.request.traceId;
60
61
  if (store.spanId) {
61
62
  enriched.span_id = store.spanId;
62
63
  }
@@ -66,6 +67,24 @@ function withTraceContext(data?: Record<string, unknown>): Record<string, unknow
66
67
 
67
68
  // ─── Framework Event Emitters ─────────────────────────────────────────────
68
69
 
70
+ /**
71
+ * Log a CSRF rejection. Level: warn — the 403 body is deliberately terse,
72
+ * so this line is the only place the rejected origin is visible (TIM-1459).
73
+ * `detail` names the observed origin and, where one exists, the expected one.
74
+ *
75
+ * The CSRF gate runs at the pipeline boundary, before the pipeline opens its
76
+ * trace scope, so a rejection mints its own `trace_id` — the field is always
77
+ * present (design/17-logging.md §"trace_id is Always Set").
78
+ */
79
+ export function logCsrfRejected(
80
+ detail: string,
81
+ data: { method: string; path: string; reason: CsrfRejectReason }
82
+ ): void {
83
+ const fields = withTraceContext(data);
84
+ fields.trace_id ??= generateTraceId();
85
+ _logger.warn(`CSRF origin check failed: ${detail}`, fields);
86
+ }
87
+
69
88
  /** Log a completed request. Level: info. */
70
89
  export function logRequestCompleted(data: {
71
90
  method: string;
@@ -110,6 +129,12 @@ export function logMiddlewareError(data: { method: string; path: string; error:
110
129
  _logger.error('unhandled error in middleware phase', withTraceContext(data));
111
130
  }
112
131
 
132
+ /** Log unhandled error in route setup (matching, param coercion). Level: error. */
133
+ export function logRouteSetupError(data: { method: string; path: string; error: unknown }): void {
134
+ if (isControlFlowSignal(data.error)) return;
135
+ _logger.error('unhandled error in route setup', withTraceContext(data));
136
+ }
137
+
113
138
  /** Log unhandled render-phase error. Level: error. */
114
139
  export function logRenderError(data: {
115
140
  method: string;
@@ -158,6 +183,17 @@ export function logSwrRefetchFailed(data: { cacheKey: string; error: unknown }):
158
183
  _logger.warn('staleWhileRevalidate refetch failed', withTraceContext(data));
159
184
  }
160
185
 
186
+ /** Log a cache value that loses fidelity through JSON serialization. Level: warn. */
187
+ export function logLossyCacheValue(data: { cacheKey: string; types: string }): void {
188
+ _logger.warn(
189
+ 'cache value contains types that lose fidelity through JSON serialization in ' +
190
+ 'Redis/KV cache handlers: Date becomes an ISO string, Map/Set become {}, BigInt throws. ' +
191
+ 'The MemoryCacheHandler preserves them. Transform values to JSON-safe types before ' +
192
+ 'caching, or use MemoryCacheHandler for values that require type preservation.',
193
+ withTraceContext(data)
194
+ );
195
+ }
196
+
161
197
  /** Log cache miss. Level: debug. */
162
198
  export function logCacheMiss(data: { cacheKey: string }): void {
163
199
  _logger.debug('timber.cache MISS', withTraceContext(data));
@@ -118,7 +118,12 @@ export function coerceSlotParams(
118
118
  }
119
119
 
120
120
  /**
121
- * Run segment param coercion on the matched route's segments.
121
+ * Coerce the matched route's raw segment params and return the result.
122
+ *
123
+ * Reads `match.rawSegmentParams` and never writes to `match`: the caller
124
+ * decides where the coerced params go. Reading the raw record makes the call
125
+ * repeatable — coercing a match twice runs every codec on the URL value both
126
+ * times, never on its own output.
122
127
  *
123
128
  * Codecs come from `app/schema.ts` and nowhere else (TIM-1342 completed
124
129
  * design/41-global-params.md migration step 9). A route with no schema, or
@@ -132,24 +137,21 @@ export function coerceSlotParams(
132
137
  * See design/41-global-params.md §"Pipeline Integration"
133
138
  *
134
139
  * Stays `async` with nothing to await: it is injected as a
135
- * `CoerceSegmentParamsFn` into the revalidation renderer and the action
136
- * middleware runner, both of which await it. Narrowing the return type to
137
- * `void` would break those contracts and remove a microtask boundary the
138
- * pipeline's ordering has always had, for no gain.
140
+ * `CoerceSegmentParamsFn` into the revalidation renderer, which awaits
141
+ * it. Making it synchronous would
142
+ * change that contract and remove a microtask boundary the pipeline's
143
+ * ordering has always had, for no gain.
139
144
  */
140
145
  // eslint-disable-next-line @typescript-eslint/require-await -- see above
141
- export async function coerceSegmentParams(match: RouteMatch): Promise<void> {
146
+ export async function coerceSegmentParams(match: RouteMatch): Promise<CoercedParams> {
142
147
  const globalCodecs = _globalCodecs;
143
- // Unconditionally install a null-prototype target so the invariant
144
- // "match.segmentParams is null-prototype" holds from the first line,
145
- // regardless of whether any segment has a codec.
148
+ // Always a null-prototype record, whether or not any segment has a codec.
146
149
  const mergeTarget: CoercedParams = Object.create(null);
147
- for (const key of Object.keys(match.segmentParams)) {
150
+ for (const key of Object.keys(match.rawSegmentParams)) {
148
151
  if (key !== '__proto__') {
149
- mergeTarget[key] = match.segmentParams[key as keyof typeof match.segmentParams];
152
+ mergeTarget[key] = match.rawSegmentParams[key];
150
153
  }
151
154
  }
152
- match.segmentParams = mergeTarget;
153
155
 
154
156
  // TIM-931/TIM-936: When a global schema is available, use it for coercion
155
157
  // instead of per-segment params.ts files. The global codec map is keyed
@@ -195,4 +197,5 @@ export async function coerceSegmentParams(match: RouteMatch): Promise<void> {
195
197
  mergeTarget[key] = normalizeParamValue(parsed, key);
196
198
  }
197
199
  }
200
+ return mergeTarget;
198
201
  }
@@ -11,11 +11,22 @@
11
11
 
12
12
  import type { ProxyExport } from './proxy.ts';
13
13
  import { getSetCookieHeaders } from './cookie-context.ts';
14
- import { callOnRequestError } from './instrumentation.ts';
14
+ import {
15
+ callOnRequestError,
16
+ hasOnRequestError,
17
+ type InstrumentationErrorContext,
18
+ } from './instrumentation.ts';
15
19
  import { getTraceId } from './tracing.ts';
20
+ import { requestContextAls } from './als-registry.ts';
16
21
  import { RedirectSignal } from './primitives.ts';
22
+ import { isControlFlowSignal } from './signal-identity.ts';
23
+ import { logRenderError, swallow } from './logger.ts';
24
+ import { isAbortError } from './render-utils.ts';
25
+ import { loadModule, ModuleLoadError, type ManifestLoader } from './safe-load.ts';
26
+ import { getWaitUntil } from './waituntil-bridge.ts';
27
+ import { isApiRouteChain, type ManifestSegmentNode } from './route-matcher.ts';
17
28
  import type { ProxyConfig } from './pipeline.ts';
18
- import { isRscRequest } from '../shared/rsc-media-type.ts';
29
+ import type { PhaseName } from './pipeline-outcome.ts';
19
30
 
20
31
  // ─── Prototype-Pollution-Safe Sanitizer ────────────────────────────────────
21
32
 
@@ -70,6 +81,32 @@ export function applyCookieJar(headers: Headers): void {
70
81
  }
71
82
  }
72
83
 
84
+ /**
85
+ * Bring a bag the jar was already applied to up to date with the jar.
86
+ *
87
+ * `applied` is what `getSetCookieHeaders()` returned when the jar was
88
+ * applied to `headers`. Values the jar no longer holds (a later write to the
89
+ * same name replaced them) are removed, and values it gained are appended.
90
+ * Every cookie then appears once, with its last write (design/29-cookies.md
91
+ * §"Last-Write-Wins for Same Name"). `Set-Cookie` values that did not come
92
+ * from the jar, such as a middleware appending to `ctx.headers` directly,
93
+ * are left alone.
94
+ */
95
+ export function syncCookieJar(headers: Headers, applied: readonly string[]): void {
96
+ const current = getSetCookieHeaders();
97
+ const stale = applied.filter((value) => !current.includes(value));
98
+ const added = current.filter((value) => !applied.includes(value));
99
+ if (stale.length === 0 && added.length === 0) return;
100
+
101
+ const kept = headers.getSetCookie();
102
+ for (const value of stale) {
103
+ const index = kept.indexOf(value);
104
+ if (index !== -1) kept.splice(index, 1);
105
+ }
106
+ headers.delete('Set-Cookie');
107
+ for (const value of [...kept, ...added]) headers.append('Set-Cookie', value);
108
+ }
109
+
73
110
  /**
74
111
  * Merge framework-managed response headers onto a terminal response without
75
112
  * overwriting headers the terminal response already set itself.
@@ -111,22 +148,43 @@ export function cloneWithMutableHeaders(response: Response): Response {
111
148
  });
112
149
  }
113
150
 
151
+ // ─── Response Headers ──────────────────────────────────────────────────────
152
+
153
+ /**
154
+ * A fresh response header bag for a page the pipeline renders, seeded with
155
+ * the default dynamic-page cache policy.
156
+ *
157
+ * Without a Cache-Control header, CDNs (particularly Cloudflare) may attempt
158
+ * to buffer/process the response differently, causing intermittent
159
+ * multi-second delays. This matches Next.js's default behavior. Every bag a
160
+ * rendered page can be written onto starts here — a matched route, the
161
+ * no-match 404, and a deny page for a proxy-phase signal — so none of them
162
+ * leaves without a cache policy. Middleware and route handlers may replace it.
163
+ */
164
+ export function dynamicResponseHeaders(): Headers {
165
+ return new Headers({
166
+ 'Cache-Control': 'private, no-cache, no-store, max-age=0, must-revalidate',
167
+ });
168
+ }
169
+
114
170
  // ─── Redirect Builder ──────────────────────────────────────────────────────
115
171
 
116
172
  /**
117
173
  * Build a redirect Response from a RedirectSignal.
118
174
  *
119
- * For RSC payload requests (client navigation), returns 204 + X-Timber-Redirect
120
- * so the client router can perform a soft SPA redirect. A raw 302 would be
121
- * turned into an opaque redirect by fetch({redirect:'manual'}), crashing
122
- * createFromFetch. See design/19-client-navigation.md.
175
+ * For a client navigation (an RSC payload request for a page), returns 204 +
176
+ * X-Timber-Redirect so the client router can perform a soft SPA redirect. A
177
+ * raw 302 would be turned into an opaque redirect by
178
+ * fetch({redirect:'manual'}), crashing createFromFetch. See
179
+ * design/19-client-navigation.md. The caller decides what counts as a
180
+ * navigation: a route.ts match answers with a real 3xx whatever it accepts.
123
181
  */
124
182
  export function buildRedirectResponse(
125
183
  signal: RedirectSignal,
126
- req: Request,
127
- headers: Headers
184
+ headers: Headers,
185
+ navigation: boolean
128
186
  ): Response {
129
- if (isRscRequest(req)) {
187
+ if (navigation) {
130
188
  headers.set('X-Timber-Redirect', signal.location);
131
189
  return new Response(null, { status: 204, headers });
132
190
  }
@@ -136,24 +194,211 @@ export function buildRedirectResponse(
136
194
 
137
195
  // ─── Instrumentation ───────────────────────────────────────────────────────
138
196
 
197
+ /**
198
+ * Where an error was raised: a pipeline phase, a server action, or a
199
+ * handler that answers outside the page pipeline — a metadata route or the
200
+ * auto-generated sitemap — which reports like a route.ts handler.
201
+ */
202
+ export type ErrorSource = PhaseName | 'action' | 'route';
203
+
139
204
  /**
140
205
  * Fire the user's onRequestError hook with request context.
141
- * Extracts request info from the Request object and calls the instrumentation hook.
206
+ *
207
+ * The one place a pipeline phase becomes the `phase` / `routeType` the hook
208
+ * reports, so every caller agrees. A render-phase error on a route.ts match
209
+ * is phase `'route'`, because the handler is what ran. Middleware reports
210
+ * as `'handler'`, its public name.
142
211
  */
143
212
  export async function fireOnRequestError(
144
213
  error: unknown,
145
214
  req: Request,
146
- phase: 'proxy' | 'handler' | 'render' | 'action' | 'route'
215
+ source: ErrorSource,
216
+ segments?: readonly ManifestSegmentNode[]
217
+ ): Promise<void> {
218
+ // One rule for every caller (design/17 §"Which errors reach it"): outcomes
219
+ // and aborts are not failures, and neither is anything a request raises
220
+ // after its client left — a driver throwing "connection closed" is not a bug.
221
+ if (!isReportableError(error) || req.signal?.aborted) return;
222
+ if (isRepeatedLoadFailure(error)) return;
223
+ await callHook(error, req, source, segments);
224
+ }
225
+
226
+ /**
227
+ * Module evaluations whose failure was already reported. A module that fails
228
+ * to evaluate rethrows the same error on every later import, and
229
+ * `loadModule()` wraps each one in a new `ModuleLoadError` with that error
230
+ * as its `cause`. One broken module is one bug until it changes, however many
231
+ * loads trip over it in a request or across requests: the page's boundaries,
232
+ * then the fallback page, then the next request. A WeakSet, so a module that
233
+ * is re-evaluated (an HMR edit) gets a new cause and is reported again.
234
+ */
235
+ const reportedLoadFailures = new WeakSet<object>();
236
+
237
+ /**
238
+ * The same for a module that threw a primitive (`throw 'broken'`), which a
239
+ * WeakSet cannot hold. Keyed on the file and the value instead, so a module
240
+ * re-broken after an edit with the very same value is not reported again.
241
+ * It grows with the number of broken modules, not with requests.
242
+ */
243
+ const reportedPrimitiveLoadFailures = new Set<string>();
244
+
245
+ function isRepeatedLoadFailure(error: unknown): boolean {
246
+ if (!(error instanceof ModuleLoadError)) return false;
247
+ const cause: unknown = error.cause;
248
+ if (typeof cause === 'object' && cause !== null) {
249
+ if (reportedLoadFailures.has(cause)) return true;
250
+ reportedLoadFailures.add(cause);
251
+ return false;
252
+ }
253
+ const key = `${error.filePath}\0${typeof cause}\0${String(cause)}`;
254
+ if (reportedPrimitiveLoadFailures.has(key)) return true;
255
+ reportedPrimitiveLoadFailures.add(key);
256
+ return false;
257
+ }
258
+
259
+ /**
260
+ * Call the hook with request context, with no filtering. Only
261
+ * `fireOnRequestError()` and background reporting call it: background work
262
+ * runs after the response, so its request having aborted says nothing about
263
+ * the failure.
264
+ */
265
+ async function callHook(
266
+ error: unknown,
267
+ req: Request,
268
+ source: ErrorSource,
269
+ segments?: readonly ManifestSegmentNode[]
147
270
  ): Promise<void> {
271
+ if (!hasOnRequestError()) return;
148
272
  const url = new URL(req.url);
149
273
  const headersObj: Record<string, string> = {};
150
274
  req.headers.forEach((v, k) => {
151
275
  headersObj[k] = v;
152
276
  });
277
+ const apiRoute = source === 'route' || (segments !== undefined && isApiRouteChain(segments));
153
278
 
154
279
  await callOnRequestError(
155
280
  error,
156
281
  { method: req.method, path: url.pathname, headers: headersObj },
157
- { phase, routePath: url.pathname, routeType: 'page', traceId: getTraceId() }
282
+ {
283
+ phase: reportedPhase(source, apiRoute),
284
+ routePath: url.pathname,
285
+ routeType: source === 'action' ? 'action' : apiRoute ? 'route' : 'page',
286
+ traceId: getTraceId(),
287
+ }
158
288
  );
159
289
  }
290
+
291
+ /**
292
+ * Whether an error is a failure `onRequestError` should hear about.
293
+ *
294
+ * Control-flow signals are outcomes the code chose — a `deny()` picks the
295
+ * page's status and data — and an abort is the client leaving. Callers answering
296
+ * a live request also skip everything once `req.signal` has aborted: a
297
+ * driver that throws "connection closed" after a disconnect is not a bug.
298
+ */
299
+ export function isReportableError(error: unknown): boolean {
300
+ return !isControlFlowSignal(error) && !isAbortError(error);
301
+ }
302
+
303
+ /**
304
+ * Fire the hook without making the caller wait, for callers that cannot:
305
+ * Flight's `onError` is synchronous, and background work has no caller.
306
+ * The promise is registered with the request's `waitUntil` so platforms
307
+ * that stop work when the response ends (Workers, Lambda) let the hook
308
+ * finish. The hook's own failures are caught in `callOnRequestError()`.
309
+ */
310
+ function fireInBackground(report: Promise<void>): void {
311
+ getWaitUntil()?.(
312
+ report.catch((hookError: unknown) => swallow(hookError, 'onRequestError could not be called'))
313
+ );
314
+ }
315
+
316
+ /**
317
+ * Log and report a render error that was answered in place rather than
318
+ * handed to the pipeline: a slot that degraded to its fallback, a Flight
319
+ * error row, a fallback page that failed and fell back again. The pipeline
320
+ * never sees these, so this is their one report. Errors that are not
321
+ * reportable (`isReportableError()`), or that arrive after the request
322
+ * aborted, are only logged.
323
+ */
324
+ export function reportRenderError(
325
+ error: unknown,
326
+ req: Request,
327
+ details: { errorId?: string; segments?: readonly ManifestSegmentNode[] } = {}
328
+ ): void {
329
+ const { errorId, segments } = details;
330
+ logRenderError({ method: req.method, path: new URL(req.url).pathname, error, errorId });
331
+ fireInBackground(fireOnRequestError(error, req, 'render', segments));
332
+ }
333
+
334
+ /**
335
+ * Load a module the render can do without — a status page, an error page,
336
+ * `global-error.tsx`, a fallback page's layout — or report why it could not
337
+ * load and return `null`, so the next tier answers instead of the failure
338
+ * taking the page down. Every such load goes through here, whether it builds
339
+ * a boundary for a normal render or a fallback page, so a broken module is
340
+ * never skipped silently.
341
+ */
342
+ export function loadFallbackModule(
343
+ file: ManifestLoader,
344
+ req: Request,
345
+ segments?: readonly ManifestSegmentNode[]
346
+ ): Promise<Record<string, unknown> | null> {
347
+ return loadModule(file).catch((loadError: unknown) => {
348
+ reportRenderError(loadError, req, { segments });
349
+ return null;
350
+ });
351
+ }
352
+
353
+ /**
354
+ * Run a `cache.component` stale-while-revalidate re-render as a singleflight
355
+ * body, and log and report its failure once for the flight. The stale
356
+ * payload already answered, so nothing else sees the failure.
357
+ *
358
+ * - **Once per flight.** A `.catch` on the flight's promise would run once
359
+ * per request that joined it, so the report happens inside the body. A
360
+ * body still running when the singleflight times out is reported then,
361
+ * with the timeout error (the signal's reason), and its later rejection
362
+ * is dropped.
363
+ * - **Against the request whose stale read started it**, read from the
364
+ * request context the work runs in. With no request (the build-time
365
+ * capture store) it is only logged.
366
+ * - **As phase `'render'`, route type `'page`.** A `cache.component` is a
367
+ * server component, so the read that started this was a page render (a
368
+ * revalidated page tree included). The request's own abort does not
369
+ * suppress the report: the work runs after the response either way.
370
+ */
371
+ export async function runBackgroundFlight(
372
+ signal: AbortSignal,
373
+ work: () => Promise<unknown>,
374
+ log: (error: unknown) => void
375
+ ): Promise<void> {
376
+ const failed = (error: unknown): void => {
377
+ log(error);
378
+ const request = requestContextAls.getStore()?.request;
379
+ if (request && isReportableError(error)) fireInBackground(callHook(error, request, 'render'));
380
+ };
381
+ const onTimeout = (): void => failed(signal.reason);
382
+ signal.addEventListener('abort', onTimeout, { once: true });
383
+ try {
384
+ await work();
385
+ } catch (error) {
386
+ if (!signal.aborted) failed(error);
387
+ } finally {
388
+ signal.removeEventListener('abort', onTimeout);
389
+ }
390
+ }
391
+
392
+ function reportedPhase(
393
+ source: ErrorSource,
394
+ apiRoute: boolean
395
+ ): InstrumentationErrorContext['phase'] {
396
+ switch (source) {
397
+ case 'middleware':
398
+ return 'handler';
399
+ case 'render':
400
+ return apiRoute ? 'route' : 'render';
401
+ default:
402
+ return source;
403
+ }
404
+ }
@@ -11,6 +11,11 @@
11
11
 
12
12
  import { classifyUrlSegment } from '../routing/segment-classify.ts';
13
13
  import type { InterceptionRewrite } from '../routing/interception.ts';
14
+ import { computeSegmentTreePaths } from '../routing/segment-keys.ts';
15
+ import { isRscRequest } from '../shared/rsc-media-type.ts';
16
+ import { canonicalize } from './canonicalize.ts';
17
+ import { parseClientStateTree } from './state-tree-diff.ts';
18
+ import type { InterceptionContext, PipelineConfig, RouteMatch } from './pipeline.ts';
14
19
 
15
20
  /**
16
21
  * Check if an intercepting route applies for this soft navigation.
@@ -150,3 +155,131 @@ export function pathnameMatchesPattern(pathname: string, pattern: string): boole
150
155
 
151
156
  return pi === pathParts.length;
152
157
  }
158
+
159
+ // ─── Pipeline Resolution ──────────────────────────────────────────────────
160
+
161
+ /**
162
+ * Resolve which route renders a soft navigation that an intercepting route
163
+ * may claim. Returns the canonical match unchanged when no rewrite applies.
164
+ *
165
+ * Runs during route setup, so anything it throws is a `'setup'` outcome.
166
+ */
167
+ export function resolveInterceptedRoute(
168
+ config: PipelineConfig,
169
+ req: Request,
170
+ canonicalPathname: string,
171
+ canonicalMatch: RouteMatch | null
172
+ ): { match: RouteMatch | null; interception?: InterceptionContext } {
173
+ const stripTrailingSlash = config.stripTrailingSlash ?? true;
174
+ let match = canonicalMatch;
175
+ let interception: InterceptionContext | undefined;
176
+
177
+ // Stage 2a: Intercepting route resolution (modal pattern).
178
+ // Only honored on RSC client-navigation requests (Accept: text/x-component)
179
+ // to prevent spoofing from plain HTML navigations or raw HTTP clients.
180
+ // The header value must be a valid relative pathname — reject schemes,
181
+ // authority, and control characters. Canonicalize before matching.
182
+ if (isRscRequest(req) && config.interceptionRewrites?.length) {
183
+ const rawSourceUrl = req.headers.get('X-Timber-URL');
184
+ const validatedSourceUrl = rawSourceUrl
185
+ ? validateInterceptionHeader(rawSourceUrl, stripTrailingSlash)
186
+ : null;
187
+ if (validatedSourceUrl) {
188
+ // The scope of an interception is the segment that owns the intercepting
189
+ // slot, so the guard asks whether the route the user is LEAVING renders
190
+ // that segment. Two independent answers to "what is on screen?", tried in
191
+ // order — deferred behind a thunk so neither runs unless a rewrite's
192
+ // target pattern matches.
193
+ //
194
+ // 1. What the client says it has mounted (X-Timber-State-Tree
195
+ // `treePaths`). Authoritative about tree shape and nothing else.
196
+ // 2. What re-matching X-Timber-URL implies. Correct while the address bar
197
+ // still holds the source page, which stops being true the moment a
198
+ // modal is open — the address is then the *intercepted* URL and
199
+ // re-matching it resolves through whichever group owns the canonical
200
+ // page (TIM-1282).
201
+ //
202
+ // Neither is trusted: both are client-supplied, and satisfying the scope
203
+ // only selects which build-time rewrite applies. The rendered pathname is
204
+ // always the rewrite's own `interceptingPrefix`, the server re-derives the
205
+ // rendered chain from it below, and every access.ts runs regardless.
206
+ //
207
+ // They are handed over as two sets in that order rather than one union:
208
+ // each describes a single rendered chain, and only within one chain does
209
+ // "the deepest scope" name the innermost owner (TIM-1318). Unioned, a
210
+ // deeper scope from the address-implied canonical branch — which after
211
+ // (2) may be a route group the client never mounted — would outrank the
212
+ // owner actually on screen.
213
+ const resolveSourceScopes = () => {
214
+ const mounted = new Set(parseClientStateTree(req)?.treePaths ?? []);
215
+ const sourceRoute = config.matchRoute(validatedSourceUrl);
216
+ const addressed = new Set(sourceRoute ? computeSegmentTreePaths(sourceRoute.segments) : []);
217
+ const scopes = [mounted, addressed].filter((scope) => scope.size > 0);
218
+ // Neither channel produced a tree — no scope can contain anything, and
219
+ // an origin that matches no route is not a legitimate soft navigation.
220
+ return scopes.length > 0 ? scopes : null;
221
+ };
222
+ const rewrite = findInterceptionMatch(
223
+ canonicalPathname,
224
+ resolveSourceScopes,
225
+ config.interceptionRewrites
226
+ );
227
+ if (rewrite?.mode === 'children') {
228
+ // Sibling layout swap (TIM-1280). Nothing is re-matched: the resolver
229
+ // addresses the owner directory by tree path and returns the chain
230
+ // `[…owner ancestors, interceptingNode, …descendants]`, which renders
231
+ // as an ordinary route. `interception` stays unset — there is no slot
232
+ // to intercept into, and setting it would additionally arm the modal
233
+ // path for every slot in the tree we are about to render.
234
+ //
235
+ // A null here means the scope named nothing in this manifest, or no
236
+ // intercepting child matched the target. Both fall through to the
237
+ // canonical route, which is what a hard navigation renders.
238
+ const interceptedMatch = config.matchInterceptedRoute?.(
239
+ canonicalPathname,
240
+ rewrite.interceptingScope
241
+ );
242
+ if (interceptedMatch) match = interceptedMatch;
243
+ } else if (rewrite) {
244
+ const sourceMatch = config.matchRoute(rewrite.interceptingPrefix);
245
+ // The scope test above was about the route the user is LEAVING. The
246
+ // route we are about to render is a different one —
247
+ // `interceptingPrefix` is URL-derived, and route groups are
248
+ // URL-invisible, so `/` can resolve into a sibling group that does not
249
+ // own the slot. Rendering that would reproduce the TIM-1278 failure on
250
+ // a valid tree: the rewrite hijacks the navigation and no slot exists
251
+ // to render the intercepting page. Fall through to the canonical
252
+ // target route instead.
253
+ const rendersTheSlot =
254
+ sourceMatch !== null &&
255
+ computeSegmentTreePaths(sourceMatch.segments).includes(rewrite.interceptingScope);
256
+ if (rendersTheSlot) {
257
+ match = sourceMatch;
258
+ interception = { targetPathname: canonicalPathname };
259
+ }
260
+ }
261
+ }
262
+ }
263
+
264
+ return { match, interception };
265
+ }
266
+
267
+ /**
268
+ * Validate and canonicalize the X-Timber-URL header value.
269
+ *
270
+ * Returns the canonical pathname if valid, or null if rejected:
271
+ * - Must start with '/' (relative pathname, no scheme/authority)
272
+ * - Must not contain control characters
273
+ * - Must pass canonicalization (no encoded separators, null bytes, etc.)
274
+ */
275
+ function validateInterceptionHeader(raw: string, stripTrailingSlash: boolean): string | null {
276
+ if (!raw.startsWith('/')) return null;
277
+ if (raw.startsWith('//')) return null;
278
+ for (let i = 0; i < raw.length; i++) {
279
+ const code = raw.charCodeAt(i);
280
+ if (code <= 0x1f || code === 0x7f) return null;
281
+ }
282
+ const result = canonicalize(raw, stripTrailingSlash);
283
+ if (!result.ok) return null;
284
+ return result.pathname;
285
+ }