@timber-js/app 0.2.0-alpha.207 → 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 (526) 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-BndF-3Td.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-DwQBrLBz.js → status-page-marker-gaihi0KZ.js} +140 -28
  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 +20 -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 +95 -108
  132. package/dist/client/internal.js.map +1 -1
  133. package/dist/client/navigation-commit.d.ts +20 -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 +3 -8
  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/segment-cache.d.ts +44 -4
  149. package/dist/client/segment-cache.d.ts.map +1 -1
  150. package/dist/client/ssr-data.d.ts +18 -64
  151. package/dist/client/ssr-data.d.ts.map +1 -1
  152. package/dist/client/state.d.ts +2 -5
  153. package/dist/client/state.d.ts.map +1 -1
  154. package/dist/client/use-pathname.d.ts +1 -1
  155. package/dist/config-types.d.ts +2 -1
  156. package/dist/config-types.d.ts.map +1 -1
  157. package/dist/config-validation.d.ts.map +1 -1
  158. package/dist/cookies/index.js +1 -1
  159. package/dist/dev-tools/dev-page-shell.d.ts +17 -0
  160. package/dist/dev-tools/dev-page-shell.d.ts.map +1 -1
  161. package/dist/dev-tools/index.d.ts +1 -1
  162. package/dist/dev-tools/index.d.ts.map +1 -1
  163. package/dist/dev-tools/instrumentation.d.ts +17 -2
  164. package/dist/dev-tools/instrumentation.d.ts.map +1 -1
  165. package/dist/dev-tools/overlay.d.ts +6 -27
  166. package/dist/dev-tools/overlay.d.ts.map +1 -1
  167. package/dist/index.d.ts.map +1 -1
  168. package/dist/index.js +632 -373
  169. package/dist/index.js.map +1 -1
  170. package/dist/plugin-context.d.ts +26 -40
  171. package/dist/plugin-context.d.ts.map +1 -1
  172. package/dist/plugins/build-manifest.d.ts.map +1 -1
  173. package/dist/plugins/build-report.d.ts +15 -2
  174. package/dist/plugins/build-report.d.ts.map +1 -1
  175. package/dist/plugins/dev-server.d.ts.map +1 -1
  176. package/dist/plugins/entries.d.ts.map +1 -1
  177. package/dist/plugins/fonts.d.ts +19 -1
  178. package/dist/plugins/fonts.d.ts.map +1 -1
  179. package/dist/plugins/post-build.d.ts +110 -0
  180. package/dist/plugins/post-build.d.ts.map +1 -0
  181. package/dist/plugins/prebuilt-capture.d.ts +11 -4
  182. package/dist/plugins/prebuilt-capture.d.ts.map +1 -1
  183. package/dist/plugins/prebuilt.d.ts +17 -1
  184. package/dist/plugins/prebuilt.d.ts.map +1 -1
  185. package/dist/plugins/prerender-sugar.d.ts.map +1 -1
  186. package/dist/plugins/react-resolve.d.ts +49 -0
  187. package/dist/plugins/react-resolve.d.ts.map +1 -0
  188. package/dist/plugins/rsc-assets-manifest.d.ts +33 -0
  189. package/dist/plugins/rsc-assets-manifest.d.ts.map +1 -0
  190. package/dist/plugins/shims.d.ts.map +1 -1
  191. package/dist/plugins/static-build.d.ts +32 -9
  192. package/dist/plugins/static-build.d.ts.map +1 -1
  193. package/dist/routing/codegen-write.d.ts +0 -9
  194. package/dist/routing/codegen-write.d.ts.map +1 -1
  195. package/dist/routing/index.js +2 -2
  196. package/dist/routing/segment-codegen.d.ts.map +1 -1
  197. package/dist/server/access-gate.d.ts.map +1 -1
  198. package/dist/server/action-handler.d.ts +5 -4
  199. package/dist/server/action-handler.d.ts.map +1 -1
  200. package/dist/server/actions.d.ts +46 -7
  201. package/dist/server/actions.d.ts.map +1 -1
  202. package/dist/server/als-registry.d.ts +58 -5
  203. package/dist/server/als-registry.d.ts.map +1 -1
  204. package/dist/server/children-interception.d.ts.map +1 -1
  205. package/dist/server/cookie-context.d.ts +2 -23
  206. package/dist/server/cookie-context.d.ts.map +1 -1
  207. package/dist/server/csp-nonce.d.ts +0 -9
  208. package/dist/server/csp-nonce.d.ts.map +1 -1
  209. package/dist/server/csrf.d.ts +27 -4
  210. package/dist/server/csrf.d.ts.map +1 -1
  211. package/dist/server/debug.d.ts +0 -1
  212. package/dist/server/debug.d.ts.map +1 -1
  213. package/dist/server/default-logger.d.ts +2 -1
  214. package/dist/server/default-logger.d.ts.map +1 -1
  215. package/dist/server/deny-boundary.d.ts +12 -6
  216. package/dist/server/deny-boundary.d.ts.map +1 -1
  217. package/dist/server/deny-renderer.d.ts +2 -2
  218. package/dist/server/deny-renderer.d.ts.map +1 -1
  219. package/dist/server/error-boundary-wrapper.d.ts +7 -3
  220. package/dist/server/error-boundary-wrapper.d.ts.map +1 -1
  221. package/dist/server/fallback-error.d.ts +9 -2
  222. package/dist/server/fallback-error.d.ts.map +1 -1
  223. package/dist/server/head-response.d.ts +4 -4
  224. package/dist/server/html-injectors.d.ts.map +1 -1
  225. package/dist/server/index.d.ts +0 -1
  226. package/dist/server/index.d.ts.map +1 -1
  227. package/dist/server/index.js +3 -4
  228. package/dist/server/index.js.map +1 -1
  229. package/dist/server/instrumentation.d.ts +10 -3
  230. package/dist/server/instrumentation.d.ts.map +1 -1
  231. package/dist/server/internal.d.ts +1 -4
  232. package/dist/server/internal.d.ts.map +1 -1
  233. package/dist/server/internal.js +961 -581
  234. package/dist/server/internal.js.map +1 -1
  235. package/dist/server/json-status-response.d.ts +34 -0
  236. package/dist/server/json-status-response.d.ts.map +1 -0
  237. package/dist/server/logger.d.ts +26 -0
  238. package/dist/server/logger.d.ts.map +1 -1
  239. package/dist/server/param-coercion.d.ts +11 -6
  240. package/dist/server/param-coercion.d.ts.map +1 -1
  241. package/dist/server/pipeline-helpers.d.ts +95 -7
  242. package/dist/server/pipeline-helpers.d.ts.map +1 -1
  243. package/dist/server/pipeline-interception.d.ts +11 -0
  244. package/dist/server/pipeline-interception.d.ts.map +1 -1
  245. package/dist/server/pipeline-outcome.d.ts +11 -1
  246. package/dist/server/pipeline-outcome.d.ts.map +1 -1
  247. package/dist/server/pipeline-phases.d.ts +2 -2
  248. package/dist/server/pipeline-phases.d.ts.map +1 -1
  249. package/dist/server/pipeline.d.ts +23 -35
  250. package/dist/server/pipeline.d.ts.map +1 -1
  251. package/dist/server/prebuilt/capture-render.d.ts +25 -0
  252. package/dist/server/prebuilt/capture-render.d.ts.map +1 -0
  253. package/dist/server/prebuilt/capture-routes.d.ts +37 -0
  254. package/dist/server/prebuilt/capture-routes.d.ts.map +1 -0
  255. package/dist/server/prebuilt/capture-state.d.ts +79 -35
  256. package/dist/server/prebuilt/capture-state.d.ts.map +1 -1
  257. package/dist/server/prebuilt/lifetime.d.ts +38 -0
  258. package/dist/server/prebuilt/lifetime.d.ts.map +1 -0
  259. package/dist/server/prebuilt/slots.d.ts +7 -0
  260. package/dist/server/prebuilt/slots.d.ts.map +1 -1
  261. package/dist/server/prebuilt/synthetic-store.d.ts.map +1 -1
  262. package/dist/server/prebuilt-builder.d.ts.map +1 -1
  263. package/dist/server/prebuilt-runtime.d.ts.map +1 -1
  264. package/dist/server/primitives.d.ts +6 -81
  265. package/dist/server/primitives.d.ts.map +1 -1
  266. package/dist/server/react-cache-bridge.d.ts +59 -0
  267. package/dist/server/react-cache-bridge.d.ts.map +1 -0
  268. package/dist/server/react-cache-scope.d.ts +49 -0
  269. package/dist/server/react-cache-scope.d.ts.map +1 -0
  270. package/dist/server/render-timeout.d.ts +15 -0
  271. package/dist/server/render-timeout.d.ts.map +1 -1
  272. package/dist/server/render-utils.d.ts +9 -0
  273. package/dist/server/render-utils.d.ts.map +1 -1
  274. package/dist/server/request-context.d.ts +26 -5
  275. package/dist/server/request-context.d.ts.map +1 -1
  276. package/dist/server/route-element-builder.d.ts.map +1 -1
  277. package/dist/server/route-handler.d.ts.map +1 -1
  278. package/dist/server/route-matcher.d.ts +7 -0
  279. package/dist/server/route-matcher.d.ts.map +1 -1
  280. package/dist/server/rsc-entry/action-middleware-runner.d.ts +22 -22
  281. package/dist/server/rsc-entry/action-middleware-runner.d.ts.map +1 -1
  282. package/dist/server/rsc-entry/api-handler.d.ts +7 -0
  283. package/dist/server/rsc-entry/api-handler.d.ts.map +1 -1
  284. package/dist/server/rsc-entry/deny-fallback.d.ts +32 -6
  285. package/dist/server/rsc-entry/deny-fallback.d.ts.map +1 -1
  286. package/dist/server/rsc-entry/error-renderer.d.ts +11 -18
  287. package/dist/server/rsc-entry/error-renderer.d.ts.map +1 -1
  288. package/dist/server/rsc-entry/helpers.d.ts +0 -1
  289. package/dist/server/rsc-entry/helpers.d.ts.map +1 -1
  290. package/dist/server/rsc-entry/index.d.ts +5 -15
  291. package/dist/server/rsc-entry/index.d.ts.map +1 -1
  292. package/dist/server/rsc-entry/render-route.d.ts +5 -6
  293. package/dist/server/rsc-entry/render-route.d.ts.map +1 -1
  294. package/dist/server/rsc-entry/render-signals.d.ts +65 -0
  295. package/dist/server/rsc-entry/render-signals.d.ts.map +1 -0
  296. package/dist/server/rsc-entry/revalidate-renderer.d.ts +6 -16
  297. package/dist/server/rsc-entry/revalidate-renderer.d.ts.map +1 -1
  298. package/dist/server/rsc-entry/rsc-payload.d.ts +7 -3
  299. package/dist/server/rsc-entry/rsc-payload.d.ts.map +1 -1
  300. package/dist/server/rsc-entry/rsc-stream.d.ts +8 -53
  301. package/dist/server/rsc-entry/rsc-stream.d.ts.map +1 -1
  302. package/dist/server/rsc-entry/ssr-bridge.d.ts +2 -2
  303. package/dist/server/rsc-entry/ssr-bridge.d.ts.map +1 -1
  304. package/dist/server/rsc-entry/ssr-renderer.d.ts +9 -4
  305. package/dist/server/rsc-entry/ssr-renderer.d.ts.map +1 -1
  306. package/dist/server/rsc-entry/wrap-action-dispatch.d.ts +1 -1
  307. package/dist/server/rsc-entry/wrap-action-dispatch.d.ts.map +1 -1
  308. package/dist/server/signal-identity.d.ts +43 -0
  309. package/dist/server/signal-identity.d.ts.map +1 -0
  310. package/dist/server/slot-resolver.d.ts +4 -3
  311. package/dist/server/slot-resolver.d.ts.map +1 -1
  312. package/dist/server/slot-subtree-contain.d.ts.map +1 -1
  313. package/dist/server/ssr-bridge-types.d.ts +91 -53
  314. package/dist/server/ssr-bridge-types.d.ts.map +1 -1
  315. package/dist/server/ssr-entry.d.ts +8 -5
  316. package/dist/server/ssr-entry.d.ts.map +1 -1
  317. package/dist/server/ssr-render.d.ts +26 -40
  318. package/dist/server/ssr-render.d.ts.map +1 -1
  319. package/dist/server/ssr-wrappers.d.ts +1 -1
  320. package/dist/server/static-generator.d.ts.map +1 -1
  321. package/dist/server/stream-utils.d.ts +12 -0
  322. package/dist/server/stream-utils.d.ts.map +1 -1
  323. package/dist/server/tracing.d.ts +12 -16
  324. package/dist/server/tracing.d.ts.map +1 -1
  325. package/dist/server/types.d.ts +1 -1
  326. package/dist/server/version-skew.d.ts +3 -3
  327. package/dist/shared/csrf-reject.d.ts +16 -0
  328. package/dist/shared/csrf-reject.d.ts.map +1 -0
  329. package/dist/shared/error-digest.d.ts +28 -0
  330. package/dist/shared/error-digest.d.ts.map +1 -0
  331. package/dist/shared/payload-root.d.ts +11 -10
  332. package/dist/shared/payload-root.d.ts.map +1 -1
  333. package/dist/shared/rsc-payload-path.d.ts +6 -0
  334. package/dist/shared/rsc-payload-path.d.ts.map +1 -1
  335. package/dist/utils/write-file-atomic.d.ts +17 -0
  336. package/dist/utils/write-file-atomic.d.ts.map +1 -0
  337. package/docs/api/30-api-server.mdx +17 -14
  338. package/docs/api/32-api-cache.mdx +51 -23
  339. package/docs/api/34-api-config.mdx +34 -6
  340. package/docs/api/36-cli.mdx +11 -1
  341. package/docs/learn/03-fetching-data.mdx +17 -1
  342. package/docs/learn/04-access-control.mdx +1 -1
  343. package/docs/learn/10-caching.mdx +29 -11
  344. package/docs/learn/11-middleware.mdx +8 -2
  345. package/docs/learn/12-error-handling.mdx +6 -1
  346. package/package.json +7 -3
  347. package/src/adapters/build-output-helper.ts +55 -53
  348. package/src/adapters/canonicalize-module.ts +29 -0
  349. package/src/adapters/cloudflare.ts +105 -63
  350. package/src/adapters/compress-module.ts +16 -5
  351. package/src/adapters/fs-identity.ts +2 -3
  352. package/src/adapters/nitro-presets.ts +0 -15
  353. package/src/adapters/nitro-preview.ts +272 -136
  354. package/src/adapters/nitro.ts +119 -82
  355. package/src/adapters/shared.ts +23 -14
  356. package/src/adapters/types.ts +110 -21
  357. package/src/analyze/graph-cache.ts +3 -4
  358. package/src/cache/json-lossy-check.ts +3 -9
  359. package/src/cache/singleflight.ts +5 -2
  360. package/src/cache/sizeof.ts +3 -1
  361. package/src/cache/tag-aware-handler.ts +43 -26
  362. package/src/cli-check.ts +8 -98
  363. package/src/cli-context.ts +119 -0
  364. package/src/cli.ts +19 -28
  365. package/src/client/browser-dev.ts +1 -1
  366. package/src/client/browser-entry/action-dispatch.ts +26 -4
  367. package/src/client/browser-entry/hydrate.ts +53 -58
  368. package/src/client/browser-entry/index.ts +16 -27
  369. package/src/client/browser-entry/post-hydration.ts +6 -40
  370. package/src/client/browser-entry/router-init.ts +66 -84
  371. package/src/client/error-boundary.tsx +9 -41
  372. package/src/client/error-reconstituter.tsx +10 -5
  373. package/src/client/history.ts +35 -21
  374. package/src/client/index.ts +0 -1
  375. package/src/client/internal.ts +2 -7
  376. package/src/client/navigation-commit.ts +41 -18
  377. package/src/client/navigation-context.ts +3 -39
  378. package/src/client/react-root.ts +3 -2
  379. package/src/client/router-pipeline.ts +96 -132
  380. package/src/client/router-types.ts +46 -44
  381. package/src/client/router-url.ts +32 -0
  382. package/src/client/router.ts +62 -21
  383. package/src/client/rsc-fetch.ts +117 -155
  384. package/src/client/segment-cache.ts +110 -11
  385. package/src/client/ssr-data.ts +19 -80
  386. package/src/client/state.ts +2 -9
  387. package/src/client/use-pathname.ts +1 -1
  388. package/src/config-types.ts +2 -1
  389. package/src/config-validation.ts +2 -1
  390. package/src/dev-tools/dev-page-shell.ts +18 -0
  391. package/src/dev-tools/index.ts +0 -2
  392. package/src/dev-tools/instrumentation.ts +50 -2
  393. package/src/dev-tools/overlay.ts +8 -238
  394. package/src/index.ts +26 -14
  395. package/src/plugin-context.ts +39 -40
  396. package/src/plugins/build-manifest.ts +4 -11
  397. package/src/plugins/build-report.ts +70 -70
  398. package/src/plugins/dev-server.ts +24 -44
  399. package/src/plugins/entries.ts +2 -1
  400. package/src/plugins/fonts.ts +35 -7
  401. package/src/plugins/post-build.ts +327 -0
  402. package/src/plugins/prebuilt-capture.ts +28 -32
  403. package/src/plugins/prebuilt.ts +54 -39
  404. package/src/plugins/prerender-sugar.ts +0 -4
  405. package/src/plugins/react-resolve.ts +149 -0
  406. package/src/plugins/{adapter-build.ts → rsc-assets-manifest.ts} +20 -109
  407. package/src/plugins/shims.ts +1 -0
  408. package/src/plugins/static-build.ts +42 -74
  409. package/src/routing/codegen-write.ts +25 -3
  410. package/src/routing/segment-codegen.ts +3 -2
  411. package/src/rsc-runtime/vendor-types.d.ts +8 -0
  412. package/src/server/access-gate.tsx +2 -1
  413. package/src/server/action-client.ts +1 -1
  414. package/src/server/action-handler.ts +253 -110
  415. package/src/server/actions.ts +73 -25
  416. package/src/server/als-registry.ts +73 -5
  417. package/src/server/chain-url-parts.ts +1 -1
  418. package/src/server/children-interception.ts +2 -1
  419. package/src/server/cookie-context.ts +18 -48
  420. package/src/server/csp-nonce.ts +0 -12
  421. package/src/server/csrf.ts +140 -8
  422. package/src/server/debug.ts +0 -1
  423. package/src/server/default-logger.ts +2 -1
  424. package/src/server/deny-boundary.ts +25 -13
  425. package/src/server/deny-renderer.ts +30 -97
  426. package/src/server/error-boundary-wrapper.ts +7 -3
  427. package/src/server/fallback-error.ts +26 -11
  428. package/src/server/flight-scripts.ts +1 -1
  429. package/src/server/head-response.ts +4 -4
  430. package/src/server/html-injector-core.ts +2 -2
  431. package/src/server/html-injectors.ts +2 -4
  432. package/src/server/index.ts +0 -4
  433. package/src/server/instrumentation.ts +10 -3
  434. package/src/server/internal.ts +1 -6
  435. package/src/server/json-status-response.ts +105 -0
  436. package/src/server/logger.ts +39 -3
  437. package/src/server/param-coercion.ts +15 -12
  438. package/src/server/pipeline-helpers.ts +257 -12
  439. package/src/server/pipeline-interception.ts +133 -0
  440. package/src/server/pipeline-outcome.ts +103 -31
  441. package/src/server/pipeline-phases.ts +127 -237
  442. package/src/server/pipeline.ts +204 -207
  443. package/src/server/prebuilt/capture-render.ts +118 -0
  444. package/src/server/prebuilt/capture-routes.ts +104 -0
  445. package/src/server/prebuilt/capture-state.ts +155 -48
  446. package/src/server/prebuilt/lifetime.ts +151 -0
  447. package/src/server/prebuilt/slots.ts +14 -2
  448. package/src/server/prebuilt/synthetic-store.ts +3 -0
  449. package/src/server/prebuilt-builder.ts +30 -320
  450. package/src/server/prebuilt-runtime.ts +125 -129
  451. package/src/server/primitives.ts +8 -168
  452. package/src/server/react-cache-bridge.ts +124 -0
  453. package/src/server/react-cache-scope.ts +104 -0
  454. package/src/server/render-timeout.ts +20 -0
  455. package/src/server/render-utils.ts +11 -1
  456. package/src/server/request-context.ts +36 -9
  457. package/src/server/route-element-builder.ts +11 -6
  458. package/src/server/route-handler.ts +36 -23
  459. package/src/server/route-matcher.ts +15 -1
  460. package/src/server/rsc-entry/action-middleware-runner.ts +79 -123
  461. package/src/server/rsc-entry/api-handler.ts +27 -103
  462. package/src/server/rsc-entry/deny-fallback.ts +93 -26
  463. package/src/server/rsc-entry/error-renderer.ts +39 -91
  464. package/src/server/rsc-entry/helpers.ts +2 -4
  465. package/src/server/rsc-entry/index.ts +64 -58
  466. package/src/server/rsc-entry/render-route.ts +15 -80
  467. package/src/server/rsc-entry/render-signals.ts +108 -0
  468. package/src/server/rsc-entry/revalidate-renderer.ts +56 -57
  469. package/src/server/rsc-entry/rsc-payload.ts +28 -61
  470. package/src/server/rsc-entry/rsc-stream.ts +113 -213
  471. package/src/server/rsc-entry/ssr-bridge.ts +2 -2
  472. package/src/server/rsc-entry/ssr-renderer.ts +182 -263
  473. package/src/server/rsc-entry/wrap-action-dispatch.ts +12 -14
  474. package/src/server/signal-identity.ts +112 -0
  475. package/src/server/slot-resolver.ts +18 -18
  476. package/src/server/slot-subtree-contain.ts +2 -1
  477. package/src/server/ssr-bridge-types.ts +92 -56
  478. package/src/server/ssr-entry.ts +87 -111
  479. package/src/server/ssr-render.ts +55 -64
  480. package/src/server/ssr-wrappers.tsx +2 -2
  481. package/src/server/static-generator.ts +7 -42
  482. package/src/server/static-not-found.ts +3 -3
  483. package/src/server/stream-utils.ts +51 -0
  484. package/src/server/tracing.ts +58 -75
  485. package/src/server/types.ts +1 -1
  486. package/src/server/version-skew.ts +5 -5
  487. package/src/shared/csrf-reject.ts +15 -0
  488. package/src/shared/error-digest.ts +46 -0
  489. package/src/shared/payload-root.ts +11 -10
  490. package/src/shared/rsc-payload-path.ts +8 -1
  491. package/src/utils/write-file-atomic.ts +32 -0
  492. package/dist/_chunks/actions-jNrXdzgs.js +0 -346
  493. package/dist/_chunks/actions-jNrXdzgs.js.map +0 -1
  494. package/dist/_chunks/als-registry-C6kcfprT.js.map +0 -1
  495. package/dist/_chunks/build-manifest-DTmSGLRz.js.map +0 -1
  496. package/dist/_chunks/build-output-helper-9dqCv1pZ.js.map +0 -1
  497. package/dist/_chunks/cache-api-CUD9Ezaq.js.map +0 -1
  498. package/dist/_chunks/cli-check-G8o30ldg.js.map +0 -1
  499. package/dist/_chunks/convention-lint-5KzoMg_A.js.map +0 -1
  500. package/dist/_chunks/dev-server-BzBxgYAV.js.map +0 -1
  501. package/dist/_chunks/error-boundary-BndF-3Td.js.map +0 -1
  502. package/dist/_chunks/fs-identity-D7vb6GdI.js.map +0 -1
  503. package/dist/_chunks/graph-cache-CP4GEmf9.js.map +0 -1
  504. package/dist/_chunks/json-lossy-check-ClNvBM_3.js.map +0 -1
  505. package/dist/_chunks/logger-k2DQ4EUf.js.map +0 -1
  506. package/dist/_chunks/navigation-context-D0TU0Jog.js.map +0 -1
  507. package/dist/_chunks/plugin-context-8idfR3UZ.js.map +0 -1
  508. package/dist/_chunks/primitives-DLAnvsrk.js.map +0 -1
  509. package/dist/_chunks/segment-context-D9_89u34.js +0 -34
  510. package/dist/_chunks/segment-context-D9_89u34.js.map +0 -1
  511. package/dist/_chunks/sizeof-lXffQ1Vr.js.map +0 -1
  512. package/dist/_chunks/ssr-data-D6T6Y3ef.js +0 -60
  513. package/dist/_chunks/ssr-data-D6T6Y3ef.js.map +0 -1
  514. package/dist/_chunks/state-FippDgxN.js.map +0 -1
  515. package/dist/_chunks/status-page-marker-DwQBrLBz.js.map +0 -1
  516. package/dist/client/types.d.ts +0 -14
  517. package/dist/client/types.d.ts.map +0 -1
  518. package/dist/plugins/adapter-build.d.ts +0 -38
  519. package/dist/plugins/adapter-build.d.ts.map +0 -1
  520. package/dist/server/asset-headers.d.ts +0 -32
  521. package/dist/server/asset-headers.d.ts.map +0 -1
  522. package/dist/server/dev-source-map.d.ts +0 -22
  523. package/dist/server/dev-source-map.d.ts.map +0 -1
  524. package/src/client/types.ts +0 -21
  525. package/src/server/asset-headers.ts +0 -59
  526. package/src/server/dev-source-map.ts +0 -31
@@ -12,7 +12,6 @@
12
12
  // See design/19-client-navigation.md for the full architecture.
13
13
 
14
14
  import { SegmentCache, PrefetchCache } from './segment-cache.ts';
15
- import type { SegmentInfo } from '../shared/segment-info.ts';
16
15
  import { HistoryStack } from './history.ts';
17
16
  import { createNavigationCommitter } from './navigation-commit.ts';
18
17
 
@@ -39,7 +38,13 @@ export function createRouter(deps: RouterDeps): RouterInstance {
39
38
 
40
39
  // Every write that makes a page "current", and the read that reports the
41
40
  // current page back to the server. See navigation-commit.ts.
42
- const { prepareNavigation, currentStateTree, updateSegmentCache } = createNavigationCommitter({
41
+ const {
42
+ prepareNavigation,
43
+ currentStateTree,
44
+ committedSegmentInfo,
45
+ isSegmentReuseSuppressed,
46
+ restoreSegmentReuse,
47
+ } = createNavigationCommitter({
43
48
  segmentCache,
44
49
  historyStack,
45
50
  clientSegmentCache: () => deps.clientSegmentCache,
@@ -60,15 +65,16 @@ export function createRouter(deps: RouterDeps): RouterInstance {
60
65
  } = lifecycle;
61
66
 
62
67
  // Fetch → commit → hand to React. See router-pipeline.ts.
63
- const { performNavigationFetch, renderViaTransition, resolveForFallback } =
64
- createNavigationPipeline({
65
- deps,
66
- prefetchCache,
67
- currentStateTree,
68
- prepareNavigation,
69
- markHandedOff,
70
- forgetOlderHandoffs,
71
- });
68
+ const { performNavigationFetch, renderViaTransition } = createNavigationPipeline({
69
+ deps,
70
+ prefetchCache,
71
+ currentStateTree,
72
+ prepareNavigation,
73
+ isSegmentReuseSuppressed,
74
+ restoreSegmentReuse,
75
+ markHandedOff,
76
+ forgetOlderHandoffs,
77
+ });
72
78
 
73
79
  // Post-paint scroll effects.
74
80
  const { restoreScrollAfterPaint, scrollToHashAfterPaint } = createScrollEffects(deps);
@@ -223,15 +229,14 @@ export function createRouter(deps: RouterDeps): RouterInstance {
223
229
  undefined,
224
230
  owner.fetchAbort.signal
225
231
  );
226
- const payload = await resolveForFallback(result.payload);
227
232
  const params = await result.params;
228
233
  const { navState, commit } = prepareNavigation(url, {
229
- payload,
234
+ payload: result.payload,
230
235
  params,
231
236
  segmentInfo: result.segmentInfo,
232
237
  skippedSegments: result.skippedSegments,
233
238
  });
234
- return { ...result, payload, params, navState, commit };
239
+ return { ...result, params, navState, commit };
235
240
  },
236
241
  opts.onCommit
237
242
  );
@@ -428,6 +433,7 @@ export function createRouter(deps: RouterDeps): RouterInstance {
428
433
  onPendingChange,
429
434
  prefetch,
430
435
  epoch: () => lifecycle.epoch(),
436
+ isEpochCurrent: (epoch) => lifecycle.isEpochCurrent(epoch),
431
437
 
432
438
  async applyActionResult(epoch, tree) {
433
439
  if (!lifecycle.isEpochCurrent(epoch)) return false;
@@ -453,7 +459,12 @@ export function createRouter(deps: RouterDeps): RouterInstance {
453
459
  const currentUrl = deps.getCurrentUrl();
454
460
  const payloadTree = readPayloadTree(tree);
455
461
  const params = readPublishedParams(tree);
456
- const existingEntry = historyStack.get(currentUrl);
462
+ // The response carries no segment metadata; the page it re-renders is
463
+ // the one the last commit made current, and the committer kept that
464
+ // page's. Not a history-stack lookup by `currentUrl`: the stack is keyed
465
+ // by the spelling `navigate()` was given, which need not be
466
+ // `window.location`'s (TIM-1541).
467
+ const segmentInfo = committedSegmentInfo();
457
468
 
458
469
  let outcomeResolve!: (o: CommitOutcome) => void;
459
470
  const outcomePromise = new Promise<CommitOutcome>((r) => (outcomeResolve = r));
@@ -471,7 +482,7 @@ export function createRouter(deps: RouterDeps): RouterInstance {
471
482
  const { navState, commit } = prepareNavigation(currentUrl, {
472
483
  payload: payloadTree,
473
484
  params,
474
- segmentInfo: existingEntry?.segmentInfo,
485
+ segmentInfo,
475
486
  });
476
487
  return {
477
488
  payload: payloadTree,
@@ -479,7 +490,7 @@ export function createRouter(deps: RouterDeps): RouterInstance {
479
490
  navState,
480
491
  commit,
481
492
  decodePromise: null,
482
- segmentInfo: existingEntry?.segmentInfo ?? null,
493
+ segmentInfo,
483
494
  skippedSegments: null,
484
495
  };
485
496
  },
@@ -492,16 +503,46 @@ export function createRouter(deps: RouterDeps): RouterInstance {
492
503
  runWhenIdle: (task: () => void) => lifecycle.runWhenIdle(task),
493
504
  settleHandoffs: () => lifecycle.settleHandoffs(),
494
505
  invalidatePath(path: string): void {
495
- historyStack.delete(path);
506
+ const hasSearch = path.includes('?');
507
+ if (hasSearch) {
508
+ historyStack.delete(path);
509
+ } else {
510
+ // TIM-1465: evict all query variants sharing this pathname
511
+ historyStack.deleteByPathname(path);
512
+ }
496
513
  prefetchCache.invalidateUrl(path);
514
+ // TIM-1466: suppress segment/slot reuse so the next navigation
515
+ // gets a full render instead of reusing stale shared layouts.
516
+ // Uses invalidateReuse() instead of clear() to preserve treePaths
517
+ // for interception scoping — a modal mounted on the current tree
518
+ // must still be navigable (codex on #1167).
519
+ segmentCache.invalidateReuse();
497
520
  },
498
521
  evictStaleCaches(): void {
499
522
  prefetchCache.clearReady();
500
- historyStack.clearExcept(deps.getCurrentUrl());
523
+ historyStack.clear();
524
+ },
525
+ hydrate(page) {
526
+ // Where hydration seeds the current page: the same prepare-and-commit
527
+ // every navigation goes through, so the segment cache, the committed
528
+ // segment metadata and the history entry are written by one commit
529
+ // rather than by bootstrap reaching into each (TIM-1541). Committed
530
+ // here, at once — see `RouterInstance.hydrate` for why not on React's
531
+ // hydration commit.
532
+ //
533
+ // Replaying this entry on a Back re-renders `payload` and republishes
534
+ // `params`. Both came out of one split of one decoded payload root
535
+ // (rsc-stream.ts), so the entry cannot replay with another route's
536
+ // params (TIM-1037, TIM-1285).
537
+ const { navState, commit } = prepareNavigation(deps.getCurrentUrl(), {
538
+ payload: page.payload,
539
+ params: page.params,
540
+ segmentInfo: page.segmentInfo,
541
+ });
542
+ commit();
543
+ return navState;
501
544
  },
502
- initSegmentCache: (segments: SegmentInfo[]) => updateSegmentCache(segments),
503
545
  segmentCache,
504
546
  prefetchCache,
505
- historyStack,
506
547
  };
507
548
  }
@@ -18,11 +18,7 @@ import {
18
18
  RSC_KEY_PARAM,
19
19
  } from '../shared/rsc-cache-key.ts';
20
20
  import type { RscManifest, RscManifestEntry } from '../shared/rsc-manifest.ts';
21
- import {
22
- splitPayloadRoot,
23
- readPublishedParams,
24
- type PublishedParams,
25
- } from '../shared/payload-root.ts';
21
+ import { splitPayloadRoot, type PublishedParams } from '../shared/payload-root.ts';
26
22
  import { normalizeBasePath, stripBasePath, withBasePath } from '../shared/base-path.ts';
27
23
  import { rscPayloadPath } from '../shared/rsc-payload-path.ts';
28
24
  import type { SegmentInfo, StateTree } from '../shared/segment-info.ts';
@@ -401,7 +397,8 @@ export class VersionSkewError extends Error {
401
397
  /**
402
398
  * Thrown when the server returns an error for an RSC payload request.
403
399
  * The server sends X-Timber-Error header and a JSON body instead of a
404
- * broken RSC stream for any RenderError (4xx or 5xx). Caught in
400
+ * RSC stream it cannot answer in-SPA (a deny that needs a fallback page, a
401
+ * broken first read). Caught in
405
402
  * navigate() to trigger a hard navigation so the server can render
406
403
  * the error page as HTML.
407
404
  *
@@ -487,9 +484,12 @@ function trackStreamCompletion(body: ReadableStream<Uint8Array>): {
487
484
  // ─── Fetch ───────────────────────────────────────────────────────
488
485
 
489
486
  /**
490
- * Fetch an RSC payload from the server. If a decodeRsc function is provided,
491
- * the response is decoded into a React element tree via createFromFetch.
492
- * Otherwise, the raw response text is returned (test mode).
487
+ * Fetch an RSC payload from the server and start decoding it.
488
+ *
489
+ * Every response is classified here, once: a version-skew reload, a 204 or
490
+ * 3xx redirect, a server error envelope and a non-RSC body are each turned
491
+ * into their error before `decodeRsc` sees the body. There is no second
492
+ * path — tests decode real Flight bytes through the same code (TIM-1541).
493
493
  */
494
494
  export async function fetchRscPayload(
495
495
  url: string,
@@ -518,156 +518,118 @@ export async function fetchRscPayload(
518
518
  // during that window would otherwise still hit the network with an
519
519
  // already-aborted signal.
520
520
  signal?.throwIfAborted();
521
- if (deps.decodeRsc) {
522
- // Production path: use createFromFetch for streaming RSC decoding.
523
- // createFromFetch takes a Promise<Response> and progressively parses
524
- // the RSC Flight stream as chunks arrive.
525
- //
526
- // Intercept the response to read segment metadata before createFromFetch
527
- // consumes the body. Reading headers does NOT consume the body stream.
528
- const fetchPromise = deps.fetch(rscUrl, { headers, redirect: 'manual', signal });
529
- let segmentInfo: SegmentInfo[] | null = null;
530
- let skippedSegments: string[] | null = null;
531
- // Track when the full RSC body stream is consumed (not just shell).
532
- // Initialized to resolved for bodyless responses; overwritten when
533
- // the response has a body.
534
- let streamDone: Promise<void> = Promise.resolve();
535
-
536
- const wrappedPromise = fetchPromise.then((response) => {
537
- // Version skew detection (TIM-446): if the server signals a reload,
538
- // throw VersionSkewError so the caller (router navigate) can trigger
539
- // a full page reload.
540
- if (checkReloadSignal(response)) {
541
- throw new VersionSkewError();
542
- }
543
- // Detect server-side redirects. The server returns 204 + X-Timber-Redirect
544
- // for RSC payload requests instead of a raw 302, because fetch with
545
- // redirect: "manual" turns 302s into opaque redirects (status 0, null body)
546
- // which crashes createFromFetch when it tries to read the body stream.
547
- const redirectLocation =
548
- response.headers.get('X-Timber-Redirect') ||
549
- (response.status >= 300 && response.status < 400 ? response.headers.get('Location') : null);
550
- if (redirectLocation) {
551
- throw new RedirectError(redirectLocation);
552
- }
553
- // Detect server error responses. The server returns X-Timber-Error header
554
- // with a JSON body instead of a broken RSC stream for any RenderError
555
- // (4xx or 5xx). Hard-navigate so the server renders the error page as HTML.
556
- // See design/10-error-handling.md §"Error Page Rendering for Client Navigation"
557
- if (response.headers.get(TIMBER_ERROR_HEADER) === '1') {
558
- throw new ServerErrorResponse(response.status, url);
559
- }
560
- // Content-Type guard: reject non-RSC responses before createFromFetch
561
- // tries to parse the body as Flight data.
562
- // In static mode, accept octet-stream/text/plain/absent content-type
563
- // (static hosts serve .rsc files with these), but still reject 404s
564
- // and text/html (missing .rsc file → host returns 404 page or SPA
565
- // HTML fallback). See TIM-1231, TIM-1243, TIM-1247.
566
- if (staticMode) {
567
- const contentType = response.headers.get('content-type');
568
- if (
569
- !response.ok ||
570
- (contentType && contentType.split(';')[0].trim().toLowerCase() === 'text/html')
571
- ) {
572
- response.body?.cancel();
573
- throw new NonRscResponse(url);
574
- }
575
- } else {
576
- if (!isRscContentType(response.headers.get('content-type'))) {
577
- response.body?.cancel();
578
- throw new NonRscResponse(url);
579
- }
521
+ // createFromFetch takes a Promise<Response> and progressively parses
522
+ // the RSC Flight stream as chunks arrive.
523
+ //
524
+ // Intercept the response to read segment metadata before createFromFetch
525
+ // consumes the body. Reading headers does NOT consume the body stream.
526
+ const fetchPromise = deps.fetch(rscUrl, { headers, redirect: 'manual', signal });
527
+ let segmentInfo: SegmentInfo[] | null = null;
528
+ let skippedSegments: string[] | null = null;
529
+ // Track when the full RSC body stream is consumed (not just shell).
530
+ // Initialized to resolved for bodyless responses; overwritten when
531
+ // the response has a body.
532
+ let streamDone: Promise<void> = Promise.resolve();
533
+
534
+ const wrappedPromise = fetchPromise.then((response) => {
535
+ // Version skew detection (TIM-446): if the server signals a reload,
536
+ // throw VersionSkewError so the caller (router navigate) can trigger
537
+ // a full page reload.
538
+ if (checkReloadSignal(response)) {
539
+ throw new VersionSkewError();
540
+ }
541
+ // Detect server-side redirects. The server returns 204 + X-Timber-Redirect
542
+ // for RSC payload requests instead of a raw 302, because fetch with
543
+ // redirect: "manual" turns 302s into opaque redirects (status 0, null body)
544
+ // which crashes createFromFetch when it tries to read the body stream.
545
+ const redirectLocation =
546
+ response.headers.get('X-Timber-Redirect') ||
547
+ (response.status >= 300 && response.status < 400 ? response.headers.get('Location') : null);
548
+ if (redirectLocation) {
549
+ throw new RedirectError(redirectLocation);
550
+ }
551
+ // Detect server error responses. The server returns X-Timber-Error header
552
+ // with a JSON body instead of an RSC stream it cannot answer in-SPA.
553
+ // Hard-navigate so the server renders the error page as HTML.
554
+ // See design/10-error-handling.md §"Error Page Rendering for Client Navigation"
555
+ if (response.headers.get(TIMBER_ERROR_HEADER) === '1') {
556
+ throw new ServerErrorResponse(response.status, url);
557
+ }
558
+ // Content-Type guard: reject non-RSC responses before createFromFetch
559
+ // tries to parse the body as Flight data.
560
+ // In static mode, accept octet-stream/text/plain/absent content-type
561
+ // (static hosts serve .rsc files with these), but still reject 404s
562
+ // and text/html (missing .rsc file → host returns 404 page or SPA
563
+ // HTML fallback). See TIM-1231, TIM-1243, TIM-1247.
564
+ if (staticMode) {
565
+ const contentType = response.headers.get('content-type');
566
+ if (
567
+ !response.ok ||
568
+ (contentType && contentType.split(';')[0].trim().toLowerCase() === 'text/html')
569
+ ) {
570
+ response.body?.cancel();
571
+ throw new NonRscResponse(url);
580
572
  }
581
- // Metadata (<title>/<meta>/<link>) now rides the RSC Flight payload
582
- // as React elements — React 19 Float handles them. See TIM-1151.
583
- segmentInfo = extractSegmentInfo(response);
584
- skippedSegments = extractSkippedSegments(response);
585
-
586
- // Wrap the body to track full stream consumption. createFromFetch's
587
- // thenable resolves when the root model (shell) arrives, but we need
588
- // to know when ALL chunks are read — that is what the navigation
589
- // promise resolving means, and what keeps the pending store set.
590
- if (response.body) {
591
- const tracked = trackStreamCompletion(response.body);
592
- streamDone = tracked.done;
593
- streamDone.catch(() => {}); // prevent unhandled rejection
594
- return new Response(tracked.body, {
595
- headers: response.headers,
596
- status: response.status,
597
- });
573
+ } else {
574
+ if (!isRscContentType(response.headers.get('content-type'))) {
575
+ response.body?.cancel();
576
+ throw new NonRscResponse(url);
598
577
  }
599
- return response;
600
- });
601
- // Await headers so segmentInfo is populated.
602
- await wrappedPromise;
603
- // Start decoding but do NOT await — return the in-progress thenable.
604
- // React can render a Flight thenable directly: it suspends on unresolved
605
- // parts and progressively renders as chunks arrive, spreading work across
606
- // frames instead of blocking the main thread in one burst.
607
- const root = deps.decodeRsc(wrappedPromise);
608
- // One split, one root: the tree and the params it was rendered with come
609
- // apart here and nowhere else, so nothing downstream has to keep them
610
- // paired by hand.
611
- const { tree: payload, params } = splitPayloadRoot(root);
612
- // Combine stream completion with payload error propagation.
613
- // streamDone marks the end of the RSC stream, which is what the
614
- // navigation promise resolves on. payloadError propagates
615
- // decode failures (stale client references, Flight parse errors)
616
- // so the router's catch block can trigger recovery (stale reload).
617
- const payloadError = new Promise<void>((_, reject) => {
618
- Promise.resolve(root).then(() => {}, reject);
619
- });
620
- payloadError.catch(() => {});
621
- const decodePromise = Promise.race([streamDone, payloadError]);
622
- // Mark handled, like payloadError above. The same rejection reaches the
623
- // router through `payload`, and a navigation that recovers by hard
624
- // navigating (version skew, non-RSC response) hangs deliberately and never
625
- // awaits this one — leaving it to surface as an unhandled rejection.
626
- decodePromise.catch(() => {});
627
- return {
628
- payload,
629
- params,
630
- decodePromise,
631
- segmentInfo,
632
- skippedSegments,
633
- };
634
- }
635
- // Test/fallback path: return raw text
636
- const response = await deps.fetch(rscUrl, { headers, redirect: 'manual', signal });
637
- // Check for redirect in test path too
638
- if (response.status >= 300 && response.status < 400) {
639
- const location = response.headers.get('Location');
640
- if (location) {
641
- throw new RedirectError(location);
642
- }
643
- }
644
- // Server error guard (same as production path above).
645
- if (response.headers.get(TIMBER_ERROR_HEADER) === '1') {
646
- throw new ServerErrorResponse(response.status, url);
647
- }
648
- // Content-Type guard (same as production path above). See TIM-1231, TIM-1243, TIM-1247.
649
- if (staticMode) {
650
- const fallbackContentType = response.headers.get('content-type');
651
- if (
652
- !response.ok ||
653
- (fallbackContentType &&
654
- fallbackContentType.split(';')[0].trim().toLowerCase() === 'text/html')
655
- ) {
656
- response.body?.cancel();
657
- throw new NonRscResponse(url);
658
578
  }
659
- } else {
660
- if (!isRscContentType(response.headers.get('content-type'))) {
661
- response.body?.cancel();
662
- throw new NonRscResponse(url);
579
+ // Metadata (<title>/<meta>/<link>) now rides the RSC Flight payload
580
+ // as React elements — React 19 Float handles them. See TIM-1151.
581
+ segmentInfo = extractSegmentInfo(response);
582
+ skippedSegments = extractSkippedSegments(response);
583
+
584
+ // Wrap the body to track full stream consumption. createFromFetch's
585
+ // thenable resolves when the root model (shell) arrives, but we need
586
+ // to know when ALL chunks are read — that is what the navigation
587
+ // promise resolving means, and what keeps the pending store set.
588
+ if (response.body) {
589
+ const tracked = trackStreamCompletion(response.body);
590
+ streamDone = tracked.done;
591
+ streamDone.catch(() => {}); // prevent unhandled rejection
592
+ return new Response(tracked.body, {
593
+ headers: response.headers,
594
+ status: response.status,
595
+ });
663
596
  }
664
- }
665
- // Test/fallback path: raw text carries no payload root, so no params.
597
+ return response;
598
+ });
599
+ // Await headers so segmentInfo is populated.
600
+ await wrappedPromise;
601
+ // Start decoding but do NOT await — return the in-progress thenable.
602
+ // React can render a Flight thenable directly: it suspends on unresolved
603
+ // parts and progressively renders as chunks arrive, spreading work across
604
+ // frames instead of blocking the main thread in one burst.
605
+ const root = deps.decodeRsc(wrappedPromise);
606
+ // One split, one root: the tree and the params it was rendered with come
607
+ // apart here and nowhere else, so nothing downstream has to keep them
608
+ // paired by hand.
609
+ const { tree: payload, params } = splitPayloadRoot(root);
610
+ // Settles once the stream has ended AND the root has decoded, and rejects
611
+ // as soon as either fails. streamDone marks the end of the RSC stream,
612
+ // which is what the navigation promise resolves on; the root's rejection
613
+ // carries decode failures (stale client references, Flight parse errors)
614
+ // so the router's catch block can trigger recovery (stale reload).
615
+ //
616
+ // Not a race between the two: a payload that arrives whole — a small
617
+ // static `.rsc` file — ends its stream before Flight rejects the root it
618
+ // could not decode, and a race resolved on the stream, so the failure
619
+ // never reached recovery and the skew was never recorded (TIM-1541).
620
+ const rootDecoded = Promise.resolve(root).then(() => {});
621
+ rootDecoded.catch(() => {});
622
+ const decodePromise = Promise.all([streamDone, rootDecoded]).then(() => {});
623
+ // Mark handled, like rootDecoded above. The same rejection reaches the
624
+ // router through `payload`, and a navigation that recovers by hard
625
+ // navigating (version skew, non-RSC response) hangs deliberately and never
626
+ // awaits this one — leaving it to surface as an unhandled rejection.
627
+ decodePromise.catch(() => {});
666
628
  return {
667
- payload: await response.text(),
668
- params: readPublishedParams(undefined),
669
- decodePromise: null,
670
- segmentInfo: extractSegmentInfo(response),
671
- skippedSegments: extractSkippedSegments(response),
629
+ payload,
630
+ params,
631
+ decodePromise,
632
+ segmentInfo,
633
+ skippedSegments,
672
634
  };
673
635
  }
@@ -74,6 +74,16 @@ export interface SegmentNode {
74
74
  */
75
75
  export class SegmentCache {
76
76
  private root: SegmentNode | undefined;
77
+ /**
78
+ * Monotonic counter. Bumped by `invalidateReuse()` (TIM-1466).
79
+ * `serializeStateTree` suppresses `segments` and `slots` when
80
+ * `reuseInvalidatedAt > reuseRestoredAt`. Only `restoreReuse()`
81
+ * advances `reuseRestoredAt` — `set()` does not, because a
82
+ * pre-invalidation navigation's commit thunk would lift suppression
83
+ * before a post-invalidation request has been sent (codex on #1167).
84
+ */
85
+ private reuseInvalidatedAt = 0;
86
+ private reuseRestoredAt = 0;
77
87
 
78
88
  get(segment: string): SegmentNode | undefined {
79
89
  if (segment === '/' || segment === this.root?.segment) {
@@ -90,6 +100,33 @@ export class SegmentCache {
90
100
 
91
101
  clear(): void {
92
102
  this.root = undefined;
103
+ this.reuseRestoredAt = this.reuseInvalidatedAt;
104
+ }
105
+
106
+ /**
107
+ * Suppress segment and slot reuse without destroying the tree
108
+ * structure (TIM-1466). `serializeStateTree` sends empty
109
+ * `segments`/`slots` (forcing a full render) while `treePaths`
110
+ * still reflects the mounted tree for interception scoping.
111
+ * Call `restoreReuse()` once post-invalidation data has been
112
+ * fetched and committed.
113
+ */
114
+ invalidateReuse(): void {
115
+ this.reuseInvalidatedAt++;
116
+ }
117
+
118
+ /**
119
+ * Lift reuse suppression. Called after the segment cache has been
120
+ * rebuilt from a response that was fetched with suppression active
121
+ * (i.e. the server sent a full render). Safe to call idempotently.
122
+ */
123
+ restoreReuse(): void {
124
+ this.reuseRestoredAt = this.reuseInvalidatedAt;
125
+ }
126
+
127
+ /** Whether segment/slot reuse is currently suppressed (TIM-1466). */
128
+ isReuseSuppressed(): boolean {
129
+ return this.reuseInvalidatedAt > this.reuseRestoredAt;
93
130
  }
94
131
 
95
132
  /**
@@ -116,9 +153,12 @@ export class SegmentCache {
116
153
  const segments: string[] = [];
117
154
  const slots: string[] = [];
118
155
  const treePaths: string[] = [];
156
+ const suppressed = this.reuseInvalidatedAt > this.reuseRestoredAt;
119
157
  if (this.root) {
120
- collectSyncSegments(this.root, segments, mergeableFilter);
121
- collectSyncSlots(this.root, slots);
158
+ if (!suppressed) {
159
+ collectSyncSegments(this.root, segments, mergeableFilter);
160
+ collectSyncSlots(this.root, slots);
161
+ }
122
162
  collectTreePaths(this.root, treePaths);
123
163
  }
124
164
  const tree: StateTree = { segments };
@@ -324,6 +364,27 @@ function prefetchMapKey(key: PrefetchKey): string {
324
364
  return `${key.from.length}:${key.from}:${key.scope.length}:${key.scope}:${key.url}`;
325
365
  }
326
366
 
367
+ /**
368
+ * Extract the destination URL from a length-prefixed map key.
369
+ * Key format: `${from.length}:${from}:${scope.length}:${scope}:${url}`
370
+ */
371
+ function extractUrlFromMapKey(mapKey: string): string | null {
372
+ // Parse from-length prefix
373
+ const firstColon = mapKey.indexOf(':');
374
+ if (firstColon === -1) return null;
375
+ const fromLen = parseInt(mapKey.slice(0, firstColon), 10);
376
+ if (Number.isNaN(fromLen)) return null;
377
+ // Skip past `from.length:from:`
378
+ const scopeStart = firstColon + 1 + fromLen + 1;
379
+ // Parse scope-length prefix
380
+ const secondColon = mapKey.indexOf(':', scopeStart);
381
+ if (secondColon === -1) return null;
382
+ const scopeLen = parseInt(mapKey.slice(scopeStart, secondColon), 10);
383
+ if (Number.isNaN(scopeLen)) return null;
384
+ // Skip past `scope.length:scope:` — remainder is the URL
385
+ return mapKey.slice(secondColon + 1 + scopeLen + 1);
386
+ }
387
+
327
388
  /**
328
389
  * The `scope` half of a `PrefetchKey`, derived from the state tree that will
329
390
  * actually be sent. Taking it from the header value rather than re-walking the
@@ -334,8 +395,12 @@ export function prefetchScopeOf(stateTree: StateTree | undefined): string {
334
395
  }
335
396
 
336
397
  /** Outcome of a prefetch flight — discriminated so NonRscResponse is a
337
- * success (stores a negative entry) rather than a rejection. */
338
- export type FlightOutcome = { kind: 'ready'; result: PrefetchResult } | { kind: 'non-route' };
398
+ * success (stores a negative entry) rather than a rejection.
399
+ * `startGen` is the eviction generation at flight start — joiners that
400
+ * started after a gen bump must reject pre-invalidation results (TIM-1464). */
401
+ export type FlightOutcome =
402
+ | { kind: 'ready'; result: PrefetchResult; startGen: number }
403
+ | { kind: 'non-route'; startGen: number };
339
404
 
340
405
  /**
341
406
  * Short-lived cache for hover-triggered prefetches. Entries expire after
@@ -410,16 +475,19 @@ export class PrefetchCache {
410
475
  // Capture the eviction generation before the flight starts. If an
411
476
  // action evicts caches while this flight is in progress, the gen
412
477
  // will have bumped and the stale result is discarded (TIM-1476).
478
+ // The starting gen is also included in the outcome so joiners
479
+ // that started after a gen bump can detect pre-invalidation
480
+ // results (TIM-1464).
413
481
  const genAtStart = this.evictionGen;
414
482
  return this.flights.do(prefetchMapKey(key), async (signal) => {
415
483
  try {
416
484
  const result = await doFetch(signal);
417
485
  if (!signal.aborted && this.evictionGen === genAtStart) this.set(key, result);
418
- return { kind: 'ready' as const, result };
486
+ return { kind: 'ready' as const, result, startGen: genAtStart };
419
487
  } catch (err) {
420
488
  if (isNonRoute(err)) {
421
489
  if (!signal.aborted && this.evictionGen === genAtStart) this.setNegative(key);
422
- return { kind: 'non-route' as const };
490
+ return { kind: 'non-route' as const, startGen: genAtStart };
423
491
  }
424
492
  throw err;
425
493
  }
@@ -455,16 +523,47 @@ export class PrefetchCache {
455
523
  }
456
524
 
457
525
  /**
458
- * Invalidate all entries whose destination URL matches (TIM-1454).
459
- * Used by `revalidatePath` on a non-current route so the next navigation
460
- * fetches fresh data instead of replaying a stale prefetch.
526
+ * Invalidate entries whose destination URL matches (TIM-1454).
527
+ *
528
+ * When `url` has no search string, evicts all entries sharing that
529
+ * pathname — `/products` evicts `/products?page=1` too, because
530
+ * `revalidatePath('/products')` invalidates the route regardless of
531
+ * query (TIM-1465).
532
+ *
533
+ * When `url` has a search string, only the exact URL is evicted.
534
+ *
535
+ * Also bumps the eviction generation so in-flight singleflight
536
+ * flights for the invalidated URL(s) discard their results on
537
+ * completion (TIM-1464).
461
538
  */
462
539
  invalidateUrl(url: string): void {
540
+ const hasSearch = url.includes('?');
541
+ const pathname = hasSearch ? null : url;
542
+
463
543
  for (const [mapKey] of this.entries) {
464
- if (mapKey.endsWith(':' + url)) {
465
- this.entries.delete(mapKey);
544
+ // The map key is `${from.length}:${from}:${scope.length}:${scope}:${url}`.
545
+ // The destination URL is the suffix after the last unescaped colon pair.
546
+ if (hasSearch) {
547
+ if (mapKey.endsWith(':' + url)) {
548
+ this.entries.delete(mapKey);
549
+ }
550
+ } else {
551
+ // Extract the URL suffix — everything after the scope block.
552
+ // Match any entry whose URL pathname matches.
553
+ const entryUrl = extractUrlFromMapKey(mapKey);
554
+ if (entryUrl !== null) {
555
+ const qIndex = entryUrl.indexOf('?');
556
+ const entryPathname = qIndex === -1 ? entryUrl : entryUrl.slice(0, qIndex);
557
+ if (entryPathname === pathname) {
558
+ this.entries.delete(mapKey);
559
+ }
560
+ }
466
561
  }
467
562
  }
563
+
564
+ // Fence in-flight flights so a prefetch that started before
565
+ // invalidation cannot store its stale result (TIM-1464).
566
+ this.evictionGen++;
468
567
  }
469
568
 
470
569
  /**