@timber-js/app 0.2.0-alpha.185 → 0.2.0-alpha.186

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 (418) hide show
  1. package/dist/_chunks/{actions-O_LsyCE4.js → actions-C-Rw9vPc.js} +3 -3
  2. package/dist/_chunks/{actions-O_LsyCE4.js.map → actions-C-Rw9vPc.js.map} +1 -1
  3. package/dist/_chunks/als-slots-mFweg276.js +27 -0
  4. package/dist/_chunks/als-slots-mFweg276.js.map +1 -0
  5. package/dist/_chunks/base-path-DaQrzbez.js +80 -0
  6. package/dist/_chunks/base-path-DaQrzbez.js.map +1 -0
  7. package/dist/_chunks/build-manifest-DWppEdLB.js +86 -0
  8. package/dist/_chunks/build-manifest-DWppEdLB.js.map +1 -0
  9. package/dist/_chunks/build-output-helper-C3DrfzZR.js +369 -0
  10. package/dist/_chunks/build-output-helper-C3DrfzZR.js.map +1 -0
  11. package/dist/_chunks/{cache-api-B-lhk9p4.js → cache-api-Cd0VZ_Pd.js} +3 -3
  12. package/dist/_chunks/{cache-api-B-lhk9p4.js.map → cache-api-Cd0VZ_Pd.js.map} +1 -1
  13. package/dist/_chunks/cli-schema-sync-CKgHC2MB.js +1857 -0
  14. package/dist/_chunks/cli-schema-sync-CKgHC2MB.js.map +1 -0
  15. package/dist/_chunks/{cloudflare-CnT5Lr7U.js → cloudflare-Cs0uZXea.js} +89 -12
  16. package/dist/_chunks/{cloudflare-CnT5Lr7U.js.map → cloudflare-Cs0uZXea.js.map} +1 -1
  17. package/dist/_chunks/error-boundary-DpYRI_I1.js +330 -0
  18. package/dist/_chunks/error-boundary-DpYRI_I1.js.map +1 -0
  19. package/dist/_chunks/{fast-hash-D6hIVt1Y.js → fast-hash-C-tVYrLb.js} +6 -1
  20. package/dist/_chunks/fast-hash-C-tVYrLb.js.map +1 -0
  21. package/dist/_chunks/fs-identity-D7vb6GdI.js +208 -0
  22. package/dist/_chunks/fs-identity-D7vb6GdI.js.map +1 -0
  23. package/dist/_chunks/{logger-AWfuX-KJ.js → logger-N7e5auP0.js} +9 -13
  24. package/dist/_chunks/logger-N7e5auP0.js.map +1 -0
  25. package/dist/_chunks/navigation-root-B29qg0_T.js +274 -0
  26. package/dist/_chunks/navigation-root-B29qg0_T.js.map +1 -0
  27. package/dist/_chunks/param-value-C8TNYchQ.js +66 -0
  28. package/dist/_chunks/param-value-C8TNYchQ.js.map +1 -0
  29. package/dist/_chunks/{plugin-context---kTF5v8.js → plugin-context-DEGLSJs3.js} +2 -1
  30. package/dist/_chunks/plugin-context-DEGLSJs3.js.map +1 -0
  31. package/dist/_chunks/{router-ref-CtmF-aPv.js → router-ref-DuYuV_0Q.js} +2 -2
  32. package/dist/_chunks/router-ref-DuYuV_0Q.js.map +1 -0
  33. package/dist/_chunks/rsc-cache-key-DD0fl_-s.js +243 -0
  34. package/dist/_chunks/rsc-cache-key-DD0fl_-s.js.map +1 -0
  35. package/dist/_chunks/rsc-error-envelope-tT5PJs4q.js +40 -0
  36. package/dist/_chunks/rsc-error-envelope-tT5PJs4q.js.map +1 -0
  37. package/dist/_chunks/rsc-payload-path-B_LBodc2.js +48 -0
  38. package/dist/_chunks/rsc-payload-path-B_LBodc2.js.map +1 -0
  39. package/dist/_chunks/{segment-classify-Byy425ng.js → segment-classify-C539Pa2O.js} +23 -2
  40. package/dist/_chunks/{segment-classify-Byy425ng.js.map → segment-classify-C539Pa2O.js.map} +1 -1
  41. package/dist/_chunks/{canonicalize-DQHyFClh.js → segment-keys-BawYuNFO.js} +61 -2
  42. package/dist/_chunks/segment-keys-BawYuNFO.js.map +1 -0
  43. package/dist/_chunks/slot-params-BCTmZkQB.js +76 -0
  44. package/dist/_chunks/slot-params-BCTmZkQB.js.map +1 -0
  45. package/dist/_chunks/{ssr-data-BOWsq18U.js → ssr-data-14MXm7Pj.js} +17 -2
  46. package/dist/_chunks/ssr-data-14MXm7Pj.js.map +1 -0
  47. package/dist/_chunks/use-segment-params-ClyUNq4d.js +128 -0
  48. package/dist/_chunks/use-segment-params-ClyUNq4d.js.map +1 -0
  49. package/dist/_chunks/{walkers-_6zKFlch.js → walkers-BhhwI9TD.js} +21 -87
  50. package/dist/_chunks/walkers-BhhwI9TD.js.map +1 -0
  51. package/dist/adapters/build-output-helper.d.ts +37 -0
  52. package/dist/adapters/build-output-helper.d.ts.map +1 -1
  53. package/dist/adapters/cloudflare-dev.js +1 -1
  54. package/dist/adapters/cloudflare-kv-cache.js +1 -1
  55. package/dist/adapters/cloudflare.d.ts.map +1 -1
  56. package/dist/adapters/cloudflare.js +1 -1
  57. package/dist/adapters/fs-identity.d.ts +64 -0
  58. package/dist/adapters/fs-identity.d.ts.map +1 -0
  59. package/dist/adapters/nitro.d.ts.map +1 -1
  60. package/dist/adapters/nitro.js +2 -3
  61. package/dist/adapters/nitro.js.map +1 -1
  62. package/dist/adapters/shared.d.ts +57 -1
  63. package/dist/adapters/shared.d.ts.map +1 -1
  64. package/dist/adapters/types.d.ts +13 -1
  65. package/dist/adapters/types.d.ts.map +1 -1
  66. package/dist/cache/fast-hash.d.ts +5 -0
  67. package/dist/cache/fast-hash.d.ts.map +1 -1
  68. package/dist/cache/index.js +1 -1
  69. package/dist/cli.js +2 -2
  70. package/dist/client/browser-entry/action-dispatch.d.ts.map +1 -1
  71. package/dist/client/browser-entry/hydrate.d.ts +20 -9
  72. package/dist/client/browser-entry/hydrate.d.ts.map +1 -1
  73. package/dist/client/browser-entry/post-hydration.d.ts +4 -1
  74. package/dist/client/browser-entry/post-hydration.d.ts.map +1 -1
  75. package/dist/client/browser-entry/router-init.d.ts +3 -0
  76. package/dist/client/browser-entry/router-init.d.ts.map +1 -1
  77. package/dist/client/browser-entry/rsc-stream.d.ts +8 -0
  78. package/dist/client/browser-entry/rsc-stream.d.ts.map +1 -1
  79. package/dist/client/error-boundary.d.ts +1 -0
  80. package/dist/client/error-boundary.d.ts.map +1 -1
  81. package/dist/client/error-boundary.js +1 -162
  82. package/dist/client/history.d.ts +13 -6
  83. package/dist/client/history.d.ts.map +1 -1
  84. package/dist/client/index.js +18 -11
  85. package/dist/client/index.js.map +1 -1
  86. package/dist/client/internal.d.ts +4 -3
  87. package/dist/client/internal.d.ts.map +1 -1
  88. package/dist/client/internal.js +865 -306
  89. package/dist/client/internal.js.map +1 -1
  90. package/dist/client/link.d.ts.map +1 -1
  91. package/dist/client/navigation-api.d.ts +2 -2
  92. package/dist/client/navigation-commit.d.ts +71 -0
  93. package/dist/client/navigation-commit.d.ts.map +1 -0
  94. package/dist/client/navigation-context.d.ts +37 -20
  95. package/dist/client/navigation-context.d.ts.map +1 -1
  96. package/dist/client/navigation-root.d.ts +63 -23
  97. package/dist/client/navigation-root.d.ts.map +1 -1
  98. package/dist/client/params-context.d.ts +81 -0
  99. package/dist/client/params-context.d.ts.map +1 -0
  100. package/dist/client/router-effects.d.ts +129 -0
  101. package/dist/client/router-effects.d.ts.map +1 -0
  102. package/dist/client/router-lifecycle.d.ts +55 -0
  103. package/dist/client/router-lifecycle.d.ts.map +1 -0
  104. package/dist/client/router-pipeline.d.ts +84 -0
  105. package/dist/client/router-pipeline.d.ts.map +1 -0
  106. package/dist/client/router-ref.d.ts +1 -1
  107. package/dist/client/router-ref.d.ts.map +1 -1
  108. package/dist/client/router-skew.d.ts +21 -0
  109. package/dist/client/router-skew.d.ts.map +1 -0
  110. package/dist/client/router-types.d.ts +207 -0
  111. package/dist/client/router-types.d.ts.map +1 -0
  112. package/dist/client/router.d.ts +1 -182
  113. package/dist/client/router.d.ts.map +1 -1
  114. package/dist/client/rsc-fetch.d.ts +38 -25
  115. package/dist/client/rsc-fetch.d.ts.map +1 -1
  116. package/dist/client/segment-cache.d.ts +79 -34
  117. package/dist/client/segment-cache.d.ts.map +1 -1
  118. package/dist/client/ssr-data.d.ts +6 -0
  119. package/dist/client/ssr-data.d.ts.map +1 -1
  120. package/dist/client/stale-client.d.ts +69 -0
  121. package/dist/client/stale-client.d.ts.map +1 -0
  122. package/dist/client/state.d.ts +10 -1
  123. package/dist/client/state.d.ts.map +1 -1
  124. package/dist/client/top-loader.d.ts +2 -1
  125. package/dist/client/top-loader.d.ts.map +1 -1
  126. package/dist/client/unload-guard.d.ts +5 -0
  127. package/dist/client/unload-guard.d.ts.map +1 -1
  128. package/dist/client/use-cookie.d.ts.map +1 -1
  129. package/dist/client/use-router.d.ts +1 -1
  130. package/dist/client/use-segment-params.d.ts +18 -8
  131. package/dist/client/use-segment-params.d.ts.map +1 -1
  132. package/dist/cookies/index.js +3 -3
  133. package/dist/cookies/index.js.map +1 -1
  134. package/dist/index.d.ts.map +1 -1
  135. package/dist/index.js +906 -321
  136. package/dist/index.js.map +1 -1
  137. package/dist/plugin-context.d.ts +31 -0
  138. package/dist/plugin-context.d.ts.map +1 -1
  139. package/dist/plugins/adapter-build.d.ts.map +1 -1
  140. package/dist/plugins/build-manifest.d.ts +1 -1
  141. package/dist/plugins/build-manifest.d.ts.map +1 -1
  142. package/dist/plugins/entries.d.ts.map +1 -1
  143. package/dist/plugins/fonts.d.ts.map +1 -1
  144. package/dist/plugins/request-dep/analysis.d.ts +106 -0
  145. package/dist/plugins/request-dep/analysis.d.ts.map +1 -0
  146. package/dist/plugins/request-dep/index.d.ts +75 -0
  147. package/dist/plugins/request-dep/index.d.ts.map +1 -0
  148. package/dist/plugins/request-dep/origin.d.ts +61 -0
  149. package/dist/plugins/request-dep/origin.d.ts.map +1 -0
  150. package/dist/plugins/routing.d.ts +9 -0
  151. package/dist/plugins/routing.d.ts.map +1 -1
  152. package/dist/plugins/static-build.d.ts +136 -8
  153. package/dist/plugins/static-build.d.ts.map +1 -1
  154. package/dist/routing/codegen-types.d.ts +18 -0
  155. package/dist/routing/codegen-types.d.ts.map +1 -1
  156. package/dist/routing/codegen.d.ts.map +1 -1
  157. package/dist/routing/collision-probe.d.ts +104 -0
  158. package/dist/routing/collision-probe.d.ts.map +1 -0
  159. package/dist/routing/collision-spaces.d.ts +26 -0
  160. package/dist/routing/collision-spaces.d.ts.map +1 -0
  161. package/dist/routing/index.js +3 -3
  162. package/dist/routing/interception.d.ts +235 -6
  163. package/dist/routing/interception.d.ts.map +1 -1
  164. package/dist/routing/manifest-codegen.d.ts.map +1 -1
  165. package/dist/routing/scanner.d.ts.map +1 -1
  166. package/dist/routing/segment-classify.d.ts +33 -0
  167. package/dist/routing/segment-classify.d.ts.map +1 -1
  168. package/dist/routing/segment-keys.d.ts +95 -0
  169. package/dist/routing/segment-keys.d.ts.map +1 -0
  170. package/dist/routing/slot-placement.d.ts +48 -0
  171. package/dist/routing/slot-placement.d.ts.map +1 -0
  172. package/dist/routing/walkers.d.ts +6 -0
  173. package/dist/routing/walkers.d.ts.map +1 -1
  174. package/dist/search-params/define.d.ts +0 -6
  175. package/dist/search-params/define.d.ts.map +1 -1
  176. package/dist/search-params/index.js +176 -2
  177. package/dist/search-params/index.js.map +1 -1
  178. package/dist/segment-params/define.d.ts +0 -6
  179. package/dist/segment-params/define.d.ts.map +1 -1
  180. package/dist/segment-params/index.js +69 -1
  181. package/dist/segment-params/index.js.map +1 -0
  182. package/dist/server/actions.d.ts +6 -3
  183. package/dist/server/actions.d.ts.map +1 -1
  184. package/dist/server/als-registry.d.ts +2 -1
  185. package/dist/server/als-registry.d.ts.map +1 -1
  186. package/dist/server/build-manifest.d.ts +13 -1
  187. package/dist/server/build-manifest.d.ts.map +1 -1
  188. package/dist/server/chain-url-parts.d.ts +82 -0
  189. package/dist/server/chain-url-parts.d.ts.map +1 -0
  190. package/dist/server/children-interception.d.ts +49 -0
  191. package/dist/server/children-interception.d.ts.map +1 -0
  192. package/dist/server/default-status-page.d.ts +45 -0
  193. package/dist/server/default-status-page.d.ts.map +1 -0
  194. package/dist/server/deny-renderer.d.ts +14 -3
  195. package/dist/server/deny-renderer.d.ts.map +1 -1
  196. package/dist/server/head-response.d.ts +35 -0
  197. package/dist/server/head-response.d.ts.map +1 -0
  198. package/dist/server/index.js +2 -2
  199. package/dist/server/internal.js +393 -212
  200. package/dist/server/internal.js.map +1 -1
  201. package/dist/server/param-coercion.d.ts +10 -1
  202. package/dist/server/param-coercion.d.ts.map +1 -1
  203. package/dist/server/pipeline-helpers.d.ts +0 -18
  204. package/dist/server/pipeline-helpers.d.ts.map +1 -1
  205. package/dist/server/pipeline-interception.d.ts +59 -9
  206. package/dist/server/pipeline-interception.d.ts.map +1 -1
  207. package/dist/server/pipeline-outcome.d.ts.map +1 -1
  208. package/dist/server/pipeline-phases.d.ts.map +1 -1
  209. package/dist/server/pipeline.d.ts +11 -0
  210. package/dist/server/pipeline.d.ts.map +1 -1
  211. package/dist/server/publish-params.d.ts +33 -0
  212. package/dist/server/publish-params.d.ts.map +1 -0
  213. package/dist/server/request-context.d.ts +48 -0
  214. package/dist/server/request-context.d.ts.map +1 -1
  215. package/dist/server/route-element-builder.d.ts +32 -2
  216. package/dist/server/route-element-builder.d.ts.map +1 -1
  217. package/dist/server/route-handler.d.ts.map +1 -1
  218. package/dist/server/route-matcher.d.ts +13 -0
  219. package/dist/server/route-matcher.d.ts.map +1 -1
  220. package/dist/server/rsc-cache-key-guard.d.ts +78 -0
  221. package/dist/server/rsc-cache-key-guard.d.ts.map +1 -0
  222. package/dist/server/rsc-entry/api-handler.d.ts.map +1 -1
  223. package/dist/server/rsc-entry/error-renderer.d.ts +14 -2
  224. package/dist/server/rsc-entry/error-renderer.d.ts.map +1 -1
  225. package/dist/server/rsc-entry/helpers.d.ts +2 -10
  226. package/dist/server/rsc-entry/helpers.d.ts.map +1 -1
  227. package/dist/server/rsc-entry/index.d.ts.map +1 -1
  228. package/dist/server/rsc-entry/render-route.d.ts.map +1 -1
  229. package/dist/server/rsc-entry/rsc-payload.d.ts +3 -2
  230. package/dist/server/rsc-entry/rsc-payload.d.ts.map +1 -1
  231. package/dist/server/rsc-entry/rsc-stream.d.ts +3 -2
  232. package/dist/server/rsc-entry/rsc-stream.d.ts.map +1 -1
  233. package/dist/server/rsc-entry/ssr-renderer.d.ts +2 -0
  234. package/dist/server/rsc-entry/ssr-renderer.d.ts.map +1 -1
  235. package/dist/server/skippable-prefix.d.ts +23 -0
  236. package/dist/server/skippable-prefix.d.ts.map +1 -0
  237. package/dist/server/slot-interception.d.ts +80 -0
  238. package/dist/server/slot-interception.d.ts.map +1 -0
  239. package/dist/server/slot-resolver.d.ts +70 -8
  240. package/dist/server/slot-resolver.d.ts.map +1 -1
  241. package/dist/server/ssr-bridge-types.d.ts +6 -0
  242. package/dist/server/ssr-bridge-types.d.ts.map +1 -1
  243. package/dist/server/ssr-entry.d.ts.map +1 -1
  244. package/dist/server/ssr-wrappers.d.ts +24 -13
  245. package/dist/server/ssr-wrappers.d.ts.map +1 -1
  246. package/dist/server/state-tree-diff.d.ts +6 -27
  247. package/dist/server/state-tree-diff.d.ts.map +1 -1
  248. package/dist/server/static-generator.d.ts +6 -0
  249. package/dist/server/static-generator.d.ts.map +1 -1
  250. package/dist/server/static-not-found.d.ts +52 -0
  251. package/dist/server/static-not-found.d.ts.map +1 -0
  252. package/dist/shared/als-slots.d.ts +41 -0
  253. package/dist/shared/als-slots.d.ts.map +1 -0
  254. package/dist/shared/base-path.d.ts +56 -0
  255. package/dist/shared/base-path.d.ts.map +1 -0
  256. package/dist/shared/param-value.d.ts +80 -0
  257. package/dist/shared/param-value.d.ts.map +1 -0
  258. package/dist/shared/payload-root.d.ts +108 -0
  259. package/dist/shared/payload-root.d.ts.map +1 -0
  260. package/dist/shared/rsc-cache-key.d.ts +163 -0
  261. package/dist/shared/rsc-cache-key.d.ts.map +1 -0
  262. package/dist/shared/rsc-error-envelope.d.ts +25 -0
  263. package/dist/shared/rsc-error-envelope.d.ts.map +1 -0
  264. package/dist/shared/rsc-manifest.d.ts +28 -0
  265. package/dist/shared/rsc-manifest.d.ts.map +1 -0
  266. package/dist/shared/rsc-payload-path.d.ts +42 -0
  267. package/dist/shared/rsc-payload-path.d.ts.map +1 -0
  268. package/dist/shared/segment-info.d.ts +59 -0
  269. package/dist/shared/segment-info.d.ts.map +1 -0
  270. package/dist/shared/slot-params.d.ts +51 -0
  271. package/dist/shared/slot-params.d.ts.map +1 -0
  272. package/dist/shared/static-platform-files.d.ts +71 -0
  273. package/dist/shared/static-platform-files.d.ts.map +1 -0
  274. package/docs/api/31-api-client.mdx +4 -2
  275. package/docs/learn/02-pages-and-layouts.mdx +1 -1
  276. package/docs/learn/11-error-handling.mdx +2 -0
  277. package/docs/learn/12-client-navigation.mdx +2 -2
  278. package/docs/learn/14-deploying.mdx +23 -0
  279. package/docs/more/01-advanced-routing.mdx +172 -2
  280. package/docs/more/04-metadata-and-fonts.mdx +1 -1
  281. package/package.json +2 -2
  282. package/src/adapters/build-output-helper.ts +116 -3
  283. package/src/adapters/cloudflare.ts +86 -11
  284. package/src/adapters/fs-identity.ts +156 -0
  285. package/src/adapters/nitro.ts +6 -6
  286. package/src/adapters/shared.ts +196 -21
  287. package/src/adapters/types.ts +13 -1
  288. package/src/cache/fast-hash.ts +5 -0
  289. package/src/client/browser-entry/action-dispatch.ts +9 -4
  290. package/src/client/browser-entry/hydrate.ts +40 -29
  291. package/src/client/browser-entry/index.ts +15 -1
  292. package/src/client/browser-entry/post-hydration.ts +10 -9
  293. package/src/client/browser-entry/router-init.ts +104 -48
  294. package/src/client/browser-entry/rsc-stream.ts +11 -2
  295. package/src/client/error-boundary.tsx +20 -15
  296. package/src/client/history.ts +13 -6
  297. package/src/client/internal.ts +4 -3
  298. package/src/client/link.tsx +55 -8
  299. package/src/client/navigation-api.ts +3 -3
  300. package/src/client/navigation-commit.ts +174 -0
  301. package/src/client/navigation-context.ts +40 -67
  302. package/src/client/navigation-root.tsx +326 -101
  303. package/src/client/params-context.ts +192 -0
  304. package/src/client/router-effects.ts +231 -0
  305. package/src/client/router-lifecycle.ts +288 -0
  306. package/src/client/router-pipeline.ts +389 -0
  307. package/src/client/router-ref.ts +1 -1
  308. package/src/client/router-skew.ts +30 -0
  309. package/src/client/router-types.ts +230 -0
  310. package/src/client/router.ts +242 -707
  311. package/src/client/rsc-fetch.ts +169 -102
  312. package/src/client/segment-cache.ts +121 -44
  313. package/src/client/ssr-data.ts +6 -0
  314. package/src/client/stale-client.ts +191 -0
  315. package/src/client/state.ts +14 -1
  316. package/src/client/top-loader.tsx +2 -1
  317. package/src/client/unload-guard.ts +34 -2
  318. package/src/client/use-cookie.ts +9 -2
  319. package/src/client/use-router.ts +1 -1
  320. package/src/client/use-segment-params.ts +41 -18
  321. package/src/index.ts +5 -1
  322. package/src/plugin-context.ts +32 -0
  323. package/src/plugins/adapter-build.ts +4 -0
  324. package/src/plugins/build-manifest.ts +34 -13
  325. package/src/plugins/entries.ts +4 -0
  326. package/src/plugins/fonts.ts +11 -8
  327. package/src/plugins/request-dep/analysis.ts +447 -0
  328. package/src/plugins/request-dep/index.ts +376 -0
  329. package/src/plugins/request-dep/origin.ts +310 -0
  330. package/src/plugins/routing.ts +59 -2
  331. package/src/plugins/static-build.ts +315 -73
  332. package/src/routing/codegen-types.ts +18 -0
  333. package/src/routing/codegen.ts +50 -10
  334. package/src/routing/collision-probe.ts +438 -0
  335. package/src/routing/collision-spaces.ts +281 -0
  336. package/src/routing/interception.ts +896 -50
  337. package/src/routing/manifest-codegen.ts +5 -3
  338. package/src/routing/scanner.ts +114 -89
  339. package/src/routing/segment-classify.ts +57 -0
  340. package/src/routing/segment-keys.ts +146 -0
  341. package/src/routing/slot-placement.ts +97 -0
  342. package/src/routing/walkers.ts +8 -0
  343. package/src/search-params/define.ts +6 -29
  344. package/src/segment-params/define.ts +4 -27
  345. package/src/server/action-handler.ts +3 -1
  346. package/src/server/actions.ts +6 -3
  347. package/src/server/als-registry.ts +2 -1
  348. package/src/server/build-manifest.ts +14 -1
  349. package/src/server/chain-url-parts.ts +163 -0
  350. package/src/server/children-interception.ts +115 -0
  351. package/src/server/default-status-page.ts +127 -0
  352. package/src/server/deny-renderer.ts +81 -20
  353. package/src/server/head-response.ts +54 -0
  354. package/src/server/param-coercion.ts +48 -18
  355. package/src/server/pipeline-helpers.ts +0 -45
  356. package/src/server/pipeline-interception.ts +81 -32
  357. package/src/server/pipeline-outcome.ts +2 -4
  358. package/src/server/pipeline-phases.ts +68 -16
  359. package/src/server/pipeline.ts +57 -10
  360. package/src/server/prebuilt-runtime.ts +4 -4
  361. package/src/server/publish-params.ts +42 -0
  362. package/src/server/request-context.ts +97 -18
  363. package/src/server/route-element-builder.ts +89 -98
  364. package/src/server/route-handler.ts +8 -8
  365. package/src/server/route-matcher.ts +21 -9
  366. package/src/server/rsc-cache-key-guard.ts +170 -0
  367. package/src/server/rsc-entry/api-handler.ts +6 -8
  368. package/src/server/rsc-entry/error-renderer.ts +64 -25
  369. package/src/server/rsc-entry/helpers.ts +31 -16
  370. package/src/server/rsc-entry/index.ts +36 -28
  371. package/src/server/rsc-entry/render-route.ts +9 -4
  372. package/src/server/rsc-entry/revalidate-renderer.ts +1 -1
  373. package/src/server/rsc-entry/rsc-payload.ts +14 -25
  374. package/src/server/rsc-entry/rsc-stream.ts +4 -3
  375. package/src/server/rsc-entry/ssr-renderer.ts +13 -9
  376. package/src/server/skippable-prefix.ts +92 -0
  377. package/src/server/slot-interception.ts +168 -0
  378. package/src/server/slot-resolver.ts +253 -156
  379. package/src/server/ssr-bridge-types.ts +6 -0
  380. package/src/server/ssr-entry.ts +12 -4
  381. package/src/server/ssr-wrappers.tsx +33 -32
  382. package/src/server/state-tree-diff.ts +14 -55
  383. package/src/server/static-generator.ts +65 -27
  384. package/src/server/static-not-found.ts +134 -0
  385. package/src/shared/als-slots.ts +72 -0
  386. package/src/shared/base-path.ts +80 -0
  387. package/src/shared/param-value.ts +244 -0
  388. package/src/shared/payload-root.ts +151 -0
  389. package/src/shared/rsc-cache-key.ts +269 -0
  390. package/src/shared/rsc-error-envelope.ts +31 -0
  391. package/src/shared/rsc-manifest.ts +29 -0
  392. package/src/shared/rsc-payload-path.ts +45 -0
  393. package/src/shared/segment-info.ts +60 -0
  394. package/src/shared/slot-params.ts +111 -0
  395. package/src/shared/static-platform-files.ts +75 -0
  396. package/dist/_chunks/build-output-helper-DOGYFb_X.js +0 -196
  397. package/dist/_chunks/build-output-helper-DOGYFb_X.js.map +0 -1
  398. package/dist/_chunks/canonicalize-DQHyFClh.js.map +0 -1
  399. package/dist/_chunks/cli-schema-sync-BTWEKJXo.js +0 -668
  400. package/dist/_chunks/cli-schema-sync-BTWEKJXo.js.map +0 -1
  401. package/dist/_chunks/define-COtkxMRT.js +0 -191
  402. package/dist/_chunks/define-COtkxMRT.js.map +0 -1
  403. package/dist/_chunks/define-c4au4I9R.js +0 -82
  404. package/dist/_chunks/define-c4au4I9R.js.map +0 -1
  405. package/dist/_chunks/fast-hash-D6hIVt1Y.js.map +0 -1
  406. package/dist/_chunks/logger-AWfuX-KJ.js.map +0 -1
  407. package/dist/_chunks/navigation-root-bUDZLw74.js +0 -124
  408. package/dist/_chunks/navigation-root-bUDZLw74.js.map +0 -1
  409. package/dist/_chunks/plugin-context---kTF5v8.js.map +0 -1
  410. package/dist/_chunks/router-ref-CtmF-aPv.js.map +0 -1
  411. package/dist/_chunks/ssr-data-BOWsq18U.js.map +0 -1
  412. package/dist/_chunks/use-segment-params-D1CzlgBo.js +0 -151
  413. package/dist/_chunks/use-segment-params-D1CzlgBo.js.map +0 -1
  414. package/dist/_chunks/walkers-_6zKFlch.js.map +0 -1
  415. package/dist/client/error-boundary.js.map +0 -1
  416. package/dist/plugins/request-dep.d.ts +0 -32
  417. package/dist/plugins/request-dep.d.ts.map +0 -1
  418. package/src/plugins/request-dep.ts +0 -499
@@ -1 +1 @@
1
- {"version":3,"file":"nitro.js","names":[],"sources":["../../src/adapters/compress-module.ts","../../src/adapters/nitro.ts"],"sourcesContent":["// Compiles server/compress.ts into a standalone ESM module (_compress.mjs)\n// for self-hosted deployments. The adapter writes this to the build output\n// during buildOutput(). It's imported by the Nitro entry and preview server\n// at runtime.\n//\n// By compiling the actual TypeScript source rather than maintaining a\n// hand-written string template, any fix to server/compress.ts automatically\n// flows to the production module. See design/18-build-system.md (\"entry\n// modules are real TypeScript files, not codegen strings\").\n\nimport { transformWithOxc } from 'vite';\n// Vite's ?raw import embeds the file as a string at build time — works\n// in both dev (Vite resolves the TS source) and the published package\n// (the string is inlined into the compiled adapter entry).\nimport compressSource from '../server/compress.ts?raw';\n\n/**\n * Compile server/compress.ts into a standalone ESM module.\n *\n * Uses Vite's OXC transform to strip TypeScript types from the\n * embedded source. The result is written to `_compress.mjs` during\n * buildOutput.\n *\n * @internal Exported for testing.\n */\nexport async function generateCompressModule(): Promise<string> {\n const result = await transformWithOxc(compressSource, 'compress.ts', {\n lang: 'ts',\n });\n\n return (\n '// Generated by @timber-js/app — compiled from server/compress.ts.\\n' +\n '// Do not edit — this file is regenerated on each build.\\n' +\n result.code\n );\n}\n","// Nitro adapter — multi-platform deployment\n//\n// Covers everything except Cloudflare Workers: Node.js, Bun, Vercel,\n// Netlify, AWS Lambda, Deno Deploy, Azure Functions. Nitro handles\n// compression, graceful shutdown, static file serving, and platform quirks.\n// See design/11-platform.md and design/25-production-deployments.md.\n\nimport { writeFile, readFile, cp, glob } from 'node:fs/promises';\nimport { execFile } from 'node:child_process';\nimport { join, relative, dirname, basename } from 'node:path';\nimport type { TimberPlatformAdapter, TimberConfig } from './types';\nimport { generateCompressModule } from './compress-module.js';\nimport { IMMUTABLE_CACHE, generateHeadersFile } from './shared.js';\nimport {\n runSharedBuildSteps,\n buildStaticDirectoryOutput,\n findUnhashedStaticFiles,\n} from './build-output-helper.js';\n\n// ─── Presets ─────────────────────────────────────────────────────────────────\n\n/**\n * Supported Nitro deployment presets.\n *\n * Each preset maps to a Nitro deployment target. The adapter generates\n * the appropriate configuration and entry point for the selected platform.\n */\nexport type NitroPreset =\n | 'vercel'\n | 'vercel-edge'\n | 'netlify'\n | 'netlify-edge'\n | 'aws-lambda'\n | 'deno-deploy'\n | 'azure-functions'\n | 'node-server'\n | 'bun';\n\n/** Preset-specific Nitro configuration. */\ninterface PresetConfig {\n /** Nitro preset name passed to the Nitro build. */\n nitroPreset: string;\n /** Output directory name within the build dir. */\n outputDir: string;\n /**\n * Path to the server bundle directory relative to outputDir.\n * Nitro places the bundled server entry + _chunks/ here.\n * Most presets use `server`; Vercel uses `functions/__server.func`.\n */\n serverBundleDir: string;\n /** Whether the runtime supports waitUntil. */\n supportsWaitUntil: boolean;\n /** Whether the runtime supports application-level 103 Early Hints. */\n supportsEarlyHints: boolean;\n /** Value for TIMBER_RUNTIME env var. See design/25-production-deployments.md. */\n runtimeName: string;\n /** Additional nitro.config fields for this preset. */\n extraConfig?: Record<string, unknown>;\n}\n\nconst PRESET_CONFIGS: Record<NitroPreset, PresetConfig> = {\n 'vercel': {\n nitroPreset: 'vercel',\n outputDir: '.vercel/output',\n serverBundleDir: 'functions/__server.func',\n supportsWaitUntil: true,\n supportsEarlyHints: false,\n runtimeName: 'vercel',\n extraConfig: { vercel: { functions: { maxDuration: 30 } } },\n },\n 'vercel-edge': {\n nitroPreset: 'vercel-edge',\n outputDir: '.vercel/output',\n serverBundleDir: 'functions/__server.func',\n supportsWaitUntil: true,\n supportsEarlyHints: false,\n runtimeName: 'vercel-edge',\n },\n 'netlify': {\n nitroPreset: 'netlify',\n outputDir: '.netlify/functions-internal',\n serverBundleDir: 'server',\n supportsWaitUntil: false,\n supportsEarlyHints: false,\n runtimeName: 'netlify',\n },\n 'netlify-edge': {\n nitroPreset: 'netlify-edge',\n outputDir: '.netlify/edge-functions',\n serverBundleDir: 'server',\n supportsWaitUntil: true,\n supportsEarlyHints: false,\n runtimeName: 'netlify-edge',\n },\n 'aws-lambda': {\n nitroPreset: 'aws-lambda',\n outputDir: '.output',\n serverBundleDir: 'server',\n supportsWaitUntil: false,\n supportsEarlyHints: false,\n runtimeName: 'aws-lambda',\n },\n 'deno-deploy': {\n nitroPreset: 'deno-deploy',\n outputDir: '.output',\n serverBundleDir: 'server',\n supportsWaitUntil: true,\n supportsEarlyHints: false,\n runtimeName: 'deno-deploy',\n },\n 'azure-functions': {\n nitroPreset: 'azure-functions',\n outputDir: '.output',\n serverBundleDir: 'server',\n supportsWaitUntil: false,\n supportsEarlyHints: false,\n runtimeName: 'azure-functions',\n },\n 'node-server': {\n nitroPreset: 'node-server',\n outputDir: '.output',\n serverBundleDir: 'server',\n supportsWaitUntil: true,\n // Disabled by default: most node-server deployments sit behind a\n // reverse proxy (nginx, caddy, traefik) that doesn't support 103\n // Early Hints over HTTP/1.1. Sending 103 causes nginx to intermittently\n // treat the response as an error and retry after proxy_connect_timeout\n // (~5s stalls on ~23% of requests). Link headers on the 200 response\n // are the safe fallback — Cloudflare and other CDNs convert them to\n // 103 automatically at the edge (over HTTP/2+ to the browser).\n supportsEarlyHints: false,\n runtimeName: 'node-server',\n },\n 'bun': {\n nitroPreset: 'bun',\n outputDir: '.output',\n serverBundleDir: 'server',\n supportsWaitUntil: true,\n // Disabled for same reason as node-server — reverse proxies choke on 103.\n // Link headers on the 200 response are converted to 103 by CDNs.\n supportsEarlyHints: false,\n runtimeName: 'bun',\n },\n};\n\n// ─── Options ─────────────────────────────────────────────────────────────────\n\n/**\n * Static deploy target for `output: 'static'` mode.\n *\n * Controls platform-specific files written alongside the static output:\n * - `'github-pages'` — writes `.nojekyll` to skip Jekyll processing\n * - `'generic'` — no extra files (default)\n */\nexport type StaticTarget = 'github-pages' | 'generic';\n\n/** Options for the Nitro adapter. */\nexport interface NitroAdapterOptions {\n /**\n * Deployment preset. Determines the target platform.\n * @default 'node-server'\n */\n preset?: NitroPreset;\n\n /**\n * Enable application-level gzip compression for HTML and RSC responses.\n *\n * When `true` (default), the origin compresses responses using the Web\n * `CompressionStream` API. This is useful for self-hosted deployments\n * where no reverse proxy or CDN handles compression.\n *\n * Set to `false` when deploying behind a reverse proxy (nginx, caddy)\n * or CDN (Cloudflare, Fastly, Vercel) that compresses at the edge.\n * Disabling origin compression saves CPU on the Node.js event loop —\n * compressing 1MB+ streaming HTML responses takes 10-15ms of main\n * thread time per request, directly reducing throughput under load.\n *\n * @default true\n */\n compress?: boolean;\n\n /**\n * Static deploy target for `output: 'static'` mode. Controls\n * platform-specific files (e.g. `.nojekyll` for GitHub Pages).\n * @default 'generic'\n */\n staticTarget?: StaticTarget;\n\n /**\n * Additional Nitro configuration to merge into the generated config.\n * Overrides default values for the selected preset.\n */\n nitroConfig?: Record<string, unknown>;\n}\n\n// ─── Adapter ─────────────────────────────────────────────────────────────────\n\n/**\n * Create a Nitro-based adapter for multi-platform deployment.\n *\n * Nitro abstracts deployment targets — the same timber.js app can deploy\n * to Vercel, Netlify, AWS, Deno Deploy, or Azure by changing the preset.\n *\n * @example\n * ```ts\n * import { nitro } from '@timber-js/app/adapters/nitro'\n *\n * export default {\n * output: 'server',\n * adapter: nitro({ preset: 'vercel' }),\n * }\n * ```\n */\nexport function nitro(options: NitroAdapterOptions = {}): TimberPlatformAdapter {\n const preset = options.preset ?? 'node-server';\n const compress = options.compress ?? true;\n const presetConfig = PRESET_CONFIGS[preset];\n return {\n name: `nitro-${preset}`,\n\n async buildOutput(config: TimberConfig, buildDir: string) {\n const outDir = join(buildDir, 'nitro');\n\n // ── Static mode: plain directory output ────────────────────────\n // No Nitro server — just pre-rendered HTML + client assets.\n if (config.output === 'static' && config.staticPagesDir) {\n await buildStaticDirectoryOutput(config, buildDir, outDir, 'public');\n const publicDir = join(outDir, 'public');\n\n // Write _headers for cache control + extensionless Content-Type.\n // Netlify supports the _headers convention just like Cloudflare.\n const unhashedPaths = await findUnhashedStaticFiles(publicDir);\n await writeFile(\n join(publicDir, '_headers'),\n generateHeadersFile(unhashedPaths, config.staticContentTypes)\n );\n\n // Platform-specific files for static deploy targets.\n const target = options.staticTarget ?? 'generic';\n if (target === 'github-pages') {\n await writeFile(join(publicDir, '.nojekyll'), '');\n // GitHub Pages doesn't support _redirects files. Warn if any\n // redirects were generated — those routes will 404 on GH Pages.\n try {\n const redirects = await readFile(join(publicDir, '_redirects'), 'utf-8');\n if (redirects.trim().length > 0) {\n console.warn(\n `[timber] Warning: GitHub Pages does not support _redirects files. ` +\n `Redirect rules from middleware/proxy will not work on this platform.`\n );\n }\n } catch {\n // No _redirects file — nothing to warn about\n }\n }\n\n return;\n }\n\n // Steps 1–5: shared across all adapters (mkdir, copy client/rsc/ssr,\n // write _headers, write manifest-init).\n await runSharedBuildSteps({\n config,\n buildDir,\n outDir,\n publicDirName: 'public',\n });\n\n // Write runtime helper modules used by the preview server.\n // See design/25-production-deployments.md — self-hosted deployments\n // need application-level compression (Cloudflare handles it at the edge).\n await writeFile(join(outDir, '_compress.mjs'), await generateCompressModule());\n\n // Web→Node response bridge with backpressure. See TIM-1154.\n await writeFile(join(outDir, '_send-response.mjs'), generateSendResponseModule());\n\n // Prepend the manifest assignment directly into the RSC entry so\n // globalThis.__TIMBER_BUILD_MANIFEST__ is set before any module reads it.\n // This must be top-level code, not an import, because rollup tree-shakes\n // side-effect-only globalThis assignments from imported modules.\n if (config.manifestInit) {\n const rscEntry = join(outDir, 'rsc', 'index.js');\n const rscContent = await readFile(rscEntry, 'utf-8');\n await writeFile(rscEntry, `${config.manifestInit}\\n${rscContent}`);\n }\n\n // Generate the Nitro entry point (imports from ./rsc/ within nitro dir)\n const entry = generateNitroEntry(buildDir, outDir, preset, compress);\n await writeFile(join(outDir, 'entry.ts'), entry);\n\n // Run the Nitro build to produce a production-ready server bundle.\n // The output goes to dist/nitro/.output/server/index.mjs (for node-server preset).\n // Config is passed programmatically — no nitro.config.ts file needed.\n const { serverDir } = await runNitroBuild(outDir, preset, options.nitroConfig);\n\n // Copy prebuilt flight payloads from the canonical build source\n // (buildDir/prebuilt, cleaned by the prebuilt plugin each build) into\n // the Nitro output. The RSC entry resolves `../prebuilt/` relative to\n // its bundled chunk (<serverDir>/_chunks/), so prebuilt/ must live at\n // <serverDir>/prebuilt/. serverDir is the resolved path from Nitro\n // (honors user overrides via nitroConfig.output.serverDir).\n const prebuiltSrc = join(buildDir, 'prebuilt');\n await cp(prebuiltSrc, join(serverDir, 'prebuilt'), {\n recursive: true,\n }).catch((e: NodeJS.ErrnoException) => {\n if (e.code !== 'ENOENT') throw e;\n });\n\n // Vercel with functionRules clones __server.func to per-route .func\n // directories during the Nitro build. Copy prebuilt into those clones\n // so requests routed to them also resolve ../prebuilt correctly.\n if (preset === 'vercel' || preset === 'vercel-edge') {\n const functionsDir = dirname(serverDir);\n const baseFuncName = basename(serverDir);\n await copyPrebuiltToVercelFuncClones(functionsDir, prebuiltSrc, baseFuncName);\n }\n },\n\n // Only presets that produce a locally-runnable server get preview().\n // Serverless presets (vercel, netlify, aws-lambda, etc.) have no\n // local runtime — Vite's built-in preview is the fallback.\n preview: LOCALLY_PREVIEWABLE.has(preset)\n ? async (_config: TimberConfig, buildDir: string) => {\n // Generate a standalone preview server that uses Node's built-in\n // HTTP server. The Nitro entry.ts can't be run directly because\n // it imports h3 (a Nitro dependency not available at runtime).\n const previewScript = generatePreviewScript(buildDir, preset);\n const scriptPath = join(buildDir, 'nitro', '_preview-server.mjs');\n await writeFile(scriptPath, previewScript);\n\n const command = preset === 'bun' ? 'bun' : 'node';\n await spawnNitroPreview(command, [scriptPath], join(buildDir, 'nitro'));\n }\n : undefined,\n };\n}\n\n// ─── Entry Generation ────────────────────────────────────────────────────────\n\n/** @internal Exported for testing. */\nexport function generateNitroEntry(\n buildDir: string,\n outDir: string,\n preset: NitroPreset,\n compress = true\n): string {\n // The RSC entry is the main request handler — it exports the fetch handler as default.\n // rsc/ is copied into the nitro dir so the import is local.\n // The manifest init is prepended to rsc/index.js before the nitro build,\n // so globalThis.__TIMBER_BUILD_MANIFEST__ is set before any code reads it.\n const serverEntryRelative = './rsc/index.js';\n const runtimeName = PRESET_CONFIGS[preset].runtimeName;\n const earlyHints = PRESET_CONFIGS[preset].supportsEarlyHints;\n const supportsWaitUntil = PRESET_CONFIGS[preset].supportsWaitUntil;\n\n // On node-server and bun, wrap the handler with ALS so the pipeline\n // can send 103 Early Hints via res.writeEarlyHints(). Other presets\n // either don't support 103 or handle it at the CDN level.\n //\n // For presets that support waitUntil, bridge h3's event.waitUntil()\n // to timber's waitUntil() primitive via the ALS bridge.\n // See design/11-platform.md §\"waitUntil()\".\n let handlerCall: string;\n if (earlyHints && supportsWaitUntil) {\n handlerCall = ` const nodeRes = event.node?.res\n const earlyHintsSender = (typeof nodeRes?.writeEarlyHints === 'function')\n ? (links) => { try { nodeRes.writeEarlyHints({ link: links }) } catch {} }\n : undefined\n\n const waitUntilFn = (typeof event.waitUntil === 'function')\n ? (p) => event.waitUntil(p)\n : undefined\n\n const callHandler = () => handler(webRequest)\n let wrappedHandler = earlyHintsSender\n ? () => runWithEarlyHintsSender(earlyHintsSender, callHandler)\n : callHandler\n const webResponse = waitUntilFn\n ? await runWithWaitUntil(waitUntilFn, wrappedHandler)\n : await wrappedHandler()`;\n } else if (earlyHints) {\n handlerCall = ` const nodeRes = event.node?.res\n const earlyHintsSender = (typeof nodeRes?.writeEarlyHints === 'function')\n ? (links) => { try { nodeRes.writeEarlyHints({ link: links }) } catch {} }\n : undefined\n\n const webResponse = earlyHintsSender\n ? await runWithEarlyHintsSender(earlyHintsSender, () => handler(webRequest))\n : await handler(webRequest)`;\n } else if (supportsWaitUntil) {\n handlerCall = ` const waitUntilFn = (typeof event.waitUntil === 'function')\n ? (p) => event.waitUntil(p)\n : undefined\n\n const webResponse = waitUntilFn\n ? await runWithWaitUntil(waitUntilFn, () => handler(webRequest))\n : await handler(webRequest)`;\n } else {\n handlerCall = ` const webResponse = await handler(webRequest)`;\n }\n\n // Import runWithWaitUntil only when the preset supports it.\n const waitUntilImport = supportsWaitUntil ? ', runWithWaitUntil' : '';\n\n const compressImport = compress ? \"import { compressResponse } from './_compress.mjs'\\n\" : '';\n const compressCall = compress ? 'compressResponse(webRequest, webResponse)' : 'webResponse';\n\n return `// Generated by @timber-js/app/adapters/nitro\n// Do not edit — this file is regenerated on each build.\n\nimport handler, { runWithEarlyHintsSender${waitUntilImport} } from '${serverEntryRelative}'\n${compressImport}\n// Set TIMBER_RUNTIME for instrumentation.ts conditional SDK initialization.\n// See design/25-production-deployments.md §\"TIMBER_RUNTIME\".\nprocess.env.TIMBER_RUNTIME = '${runtimeName}'\n\n// Nitro's index.mjs wraps this import with defineLazyEventHandler, which\n// already handles the event handler protocol. We export a plain async\n// function — no defineEventHandler wrapper needed.\n// Nitro bundles h3 into its internal _libs/ directory but doesn't make\n// it available as a bare specifier in the output's node_modules.\nexport default async function timberHandler(event) {\n // h3 v2: event.req is the Web Request\n const webRequest = event.req\n${handlerCall}\n return ${compressCall}\n}\n`;\n}\n\n// ─── Preview ─────────────────────────────────────────────────────────────────\n\n/** Presets that produce a locally-runnable server entry. */\nconst LOCALLY_PREVIEWABLE = new Set<NitroPreset>(['node-server', 'bun']);\n\n/**\n * Generate a standalone preview server script that uses Node's built-in\n * HTTP server. This bypasses Nitro entirely — the Nitro entry.ts imports\n * h3 which isn't available outside a Nitro build. For local preview we\n * just need to serve static files and route requests to the RSC handler.\n *\n * @internal Exported for testing.\n */\nexport function generatePreviewScript(buildDir: string, preset: NitroPreset): string {\n const rscEntryRelative = relative(join(buildDir, 'nitro'), join(buildDir, 'rsc', 'index.js'));\n const rscEntry = rscEntryRelative.startsWith('.') ? rscEntryRelative : './' + rscEntryRelative;\n const publicDir = './public';\n const manifestInitPath = './_timber-manifest-init.js';\n const runtimeName = PRESET_CONFIGS[preset].runtimeName;\n\n return `// Generated by @timber-js/app — standalone preview server.\n// Uses Node's built-in HTTP server to serve static assets and route\n// dynamic requests through the RSC handler. No Nitro/h3 dependency.\n\nimport { createServer } from 'node:http';\nimport { readFile, stat } from 'node:fs/promises';\nimport { join, extname } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { existsSync } from 'node:fs';\n\n// Set runtime before importing the handler.\nprocess.env.TIMBER_RUNTIME = '${runtimeName}';\n\n// Load the build manifest if it exists.\nconst __dirname = fileURLToPath(new URL('.', import.meta.url));\nconst manifestPath = join(__dirname, '${manifestInitPath}');\nif (existsSync(manifestPath)) {\n await import('${manifestInitPath}');\n}\n\n// Import the RSC handler (default export is the fetch-like handler).\nconst { default: handler, runWithEarlyHintsSender } = await import('${rscEntry}');\n\n// Import compression helper for self-hosted response compression.\nconst { compressResponse } = await import('./_compress.mjs');\n\n// Web→Node response bridge with backpressure (TIM-1154).\nconst { sendNodeResponse } = await import('./_send-response.mjs');\n\nconst MIME_TYPES = {\n '.html': 'text/html',\n '.js': 'application/javascript',\n '.mjs': 'application/javascript',\n '.css': 'text/css',\n '.json': 'application/json',\n '.png': 'image/png',\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.gif': 'image/gif',\n '.svg': 'image/svg+xml',\n '.ico': 'image/x-icon',\n '.woff': 'font/woff',\n '.woff2': 'font/woff2',\n '.ttf': 'font/ttf',\n '.otf': 'font/otf',\n '.webp': 'image/webp',\n '.avif': 'image/avif',\n '.webm': 'video/webm',\n '.mp4': 'video/mp4',\n '.txt': 'text/plain',\n '.xml': 'application/xml',\n '.wasm': 'application/wasm',\n};\n\nconst publicDir = join(__dirname, '${publicDir}');\n\n// Port resolution (TIM-842):\n// - Default to 3000\n// - If PORT env var is set, honor it strictly (fail loudly on conflict)\n// - Otherwise auto-bump from 3000 until a free port is found\n// Mirrors the dev server behavior so timber dev/preview/production all\n// behave the same way around port selection.\nconst envPort = process.env.PORT ? parseInt(process.env.PORT, 10) : null;\nconst portIsExplicit = envPort != null && Number.isFinite(envPort) && envPort > 0;\nconst startPort = portIsExplicit ? envPort : 3000;\nconst host = process.env.HOST || 'localhost';\n\n// Set after listenWithBump() resolves so request handlers can build\n// absolute URLs from the actual bound port (which may differ from\n// startPort after an auto-bump).\nlet boundPort = startPort;\n\nconst server = createServer(async (req, res) => {\n // Scheme from the socket's TLS state — a hardcoded http:// breaks CSRF\n // origin comparison and absolute-URL construction when TLS is terminated\n // at this server (TIM-1073; mirrors the dev-server fix in TIM-1067).\n // X-Forwarded-Proto is intentionally not consulted here: on a direct\n // connection it would be attacker-controlled. The CSRF layer honors it\n // separately for reverse-proxy deployments.\n const scheme = req.socket?.encrypted ? 'https' : 'http';\n const url = new URL(req.url || '/', \\`\\${scheme}://\\${host}:\\${boundPort}\\`);\n\n // Try serving static files from the public directory first.\n const filePath = join(publicDir, url.pathname);\n // Prevent path traversal.\n if (filePath.startsWith(publicDir)) {\n try {\n const fileStat = await stat(filePath);\n if (fileStat.isFile()) {\n const ext = extname(filePath);\n const contentType = MIME_TYPES[ext] || 'application/octet-stream';\n const body = await readFile(filePath);\n // Hashed assets get immutable cache, others get short cache.\n const isImmutable =\n url.pathname.startsWith('/assets/') || url.pathname.startsWith('/_timber/fonts/');\n const cacheControl = isImmutable\n ? 'public, max-age=31536000, immutable'\n : 'public, max-age=3600, must-revalidate';\n res.writeHead(200, {\n 'Content-Type': contentType,\n 'Content-Length': body.length,\n 'Cache-Control': cacheControl,\n });\n res.end(body);\n return;\n }\n } catch {\n // File not found — fall through to the RSC handler.\n }\n }\n\n // Convert Node request to Web Request.\n const headers = new Headers();\n for (const [key, value] of Object.entries(req.headers)) {\n if (value) {\n if (Array.isArray(value)) {\n for (const v of value) headers.append(key, v);\n } else {\n headers.set(key, value);\n }\n }\n }\n\n let body = undefined;\n if (req.method !== 'GET' && req.method !== 'HEAD') {\n body = await new Promise((resolve) => {\n const chunks = [];\n req.on('data', (chunk) => chunks.push(chunk));\n req.on('end', () => resolve(Buffer.concat(chunks)));\n });\n }\n\n const webRequest = new Request(url.href, {\n method: req.method,\n headers,\n body,\n duplex: body ? 'half' : undefined,\n });\n\n try {\n // Support 103 Early Hints when available.\n const earlyHintsSender = (typeof res.writeEarlyHints === 'function')\n ? (links) => { try { res.writeEarlyHints({ link: links }); } catch {} }\n : undefined;\n\n const rawResponse = earlyHintsSender && runWithEarlyHintsSender\n ? await runWithEarlyHintsSender(earlyHintsSender, () => handler(webRequest))\n : await handler(webRequest);\n\n // Compress the response for self-hosted deployments.\n const webResponse = compressResponse(webRequest, rawResponse);\n\n // Write the response to Node's ServerResponse. sendNodeResponse handles\n // status, headers (including Set-Cookie splitting via [...headers]),\n // body streaming with backpressure, and client disconnect cleanup.\n // See TIM-1154.\n await sendNodeResponse(res, webResponse);\n } catch (err) {\n console.error('[timber preview] Request error:', err);\n if (!res.headersSent) {\n res.writeHead(500, { 'Content-Type': 'text/plain' });\n }\n res.end('Internal Server Error');\n }\n});\n\nfunction listenWithBump(port, attempt = 0) {\n return new Promise((resolveListen, rejectListen) => {\n const onError = (err) => {\n server.removeListener('listening', onListening);\n if (err && err.code === 'EADDRINUSE' && !portIsExplicit && attempt < 100) {\n if (attempt === 0) {\n console.log(\\` [timber] Port \\${port} in use, trying \\${port + 1}...\\`);\n }\n listenWithBump(port + 1, attempt + 1).then(resolveListen, rejectListen);\n return;\n }\n rejectListen(err);\n };\n const onListening = () => {\n server.removeListener('error', onError);\n resolveListen(port);\n };\n server.once('error', onError);\n server.once('listening', onListening);\n server.listen(port, host);\n });\n}\n\ntry {\n boundPort = await listenWithBump(startPort);\n if (boundPort !== startPort) {\n console.log();\n console.log(\\` [timber] Port \\${startPort} in use, using \\${boundPort}\\`);\n }\n console.log();\n console.log(' ⚡ timber preview server running at:');\n console.log();\n console.log(\\` ➜ http://\\${host}:\\${boundPort}\\`);\n console.log();\n} catch (err) {\n if (err && err.code === 'EADDRINUSE') {\n console.error(\\`[timber] Port \\${startPort} is already in use. \\` +\n 'Set PORT to a free port, or unset PORT to auto-pick.');\n } else {\n console.error('[timber] Failed to start preview server:', err);\n }\n process.exit(1);\n}\n`;\n}\n\n/** Command descriptor for Nitro preview — testable without spawning. */\nexport interface NitroPreviewCommand {\n command: string;\n args: string[];\n cwd: string;\n}\n\n/** @internal Exported for testing. */\nexport function generateNitroPreviewCommand(\n buildDir: string,\n preset: NitroPreset\n): NitroPreviewCommand | null {\n if (!LOCALLY_PREVIEWABLE.has(preset)) return null;\n\n const nitroDir = join(buildDir, 'nitro');\n const entryPath = join(nitroDir, 'entry.ts');\n\n const command = preset === 'bun' ? 'bun' : 'node';\n return {\n command,\n args: [entryPath],\n cwd: nitroDir,\n };\n}\n\n/**\n * Run the Nitro production build using the programmatic API.\n * Uses dynamic import so nitro is only loaded at build time.\n * Externalizes the timber RSC/SSR output — those files are pre-built\n * by timber and have internal references that nitro's bundler can't follow.\n */\ninterface NitroBuildResult {\n /** Resolved output.serverDir — where the server bundle + _chunks/ live. */\n serverDir: string;\n}\n\nasync function runNitroBuild(\n nitroDir: string,\n preset: NitroPreset,\n userConfig?: Record<string, unknown>\n): Promise<NitroBuildResult> {\n const presetConfig = PRESET_CONFIGS[preset];\n const {\n createNitro,\n build: nitroBuild,\n prepare,\n copyPublicAssets,\n } = await import('nitro/builder');\n\n const nitro = await createNitro({\n rootDir: nitroDir,\n preset: presetConfig.nitroPreset,\n // Use renderer.entry so Nitro wraps our handler with its server runtime\n // (HTTP server, static file serving, graceful shutdown, etc.).\n // Using `entry` directly would bypass the Nitro server runtime.\n renderer: { handler: join(nitroDir, 'entry.ts') },\n output: { dir: join(nitroDir, presetConfig.outputDir) },\n routeRules: {\n '/assets/**': { headers: { 'Cache-Control': IMMUTABLE_CACHE } },\n '/_timber/fonts/**': { headers: { 'Cache-Control': IMMUTABLE_CACHE } },\n },\n // Don't bundle the timber RSC/SSR build output — it has its own\n // internal file references that nitro's bundler can't follow.\n // Mark them as external so rollup leaves the imports as-is.\n rollupConfig: {\n external: [/\\.\\.\\/rsc\\//],\n },\n ...presetConfig.extraConfig,\n ...userConfig,\n });\n\n // Read the resolved serverDir AFTER config merging — userConfig may\n // override output.dir or output.serverDir. Nitro appends a trailing\n // slash during resolution; strip it for join() compatibility.\n const raw = nitro.options.output.serverDir;\n const serverDir = raw.endsWith('/') ? raw.slice(0, -1) : raw;\n\n await prepare(nitro);\n await copyPublicAssets(nitro);\n await nitroBuild(nitro);\n await nitro.close();\n\n return { serverDir };\n}\n\n/** Spawn a Nitro preview process and pipe stdio. */\nfunction spawnNitroPreview(command: string, args: string[], cwd: string): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n const child = execFile(command, args, { cwd }, (err) => {\n if (err) reject(err);\n else resolve();\n });\n child.stdout?.pipe(process.stdout);\n child.stderr?.pipe(process.stderr);\n });\n}\n\n// ─── Send Response Module ───────────────────────────────────────────────────\n\n/**\n * Generate a standalone ESM module that exports sendNodeResponse.\n *\n * Mirrors srvx/node's sendNodeResponse: status + headers via writeHead,\n * body streaming with backpressure (waits for drain on write() === false),\n * and client disconnect cleanup. Written to `_send-response.mjs` during\n * buildOutput for use by the preview server script. See TIM-1154.\n *\n * @internal Exported for testing.\n */\nexport function generateSendResponseModule(): string {\n return `// Generated by @timber-js/app — Web→Node response bridge.\n// Do not edit — this file is regenerated on each build.\n// Mirrors srvx/node's sendNodeResponse with backpressure support.\n\nexport function sendNodeResponse(nodeRes, webRes) {\n if (!webRes) {\n nodeRes.statusCode = 500;\n return new Promise((resolve) => nodeRes.end(resolve));\n }\n const rawHeaders = [...webRes.headers];\n const writeHeaders = rawHeaders.flat();\n if (!nodeRes.headersSent) {\n if (nodeRes.req?.httpVersion === '2.0') {\n nodeRes.writeHead(webRes.status, writeHeaders);\n } else {\n nodeRes.writeHead(webRes.status, webRes.statusText, writeHeaders);\n }\n }\n if (!webRes.body) {\n return new Promise((resolve) => nodeRes.end(resolve));\n }\n // Stream with backpressure: pause reading when the kernel buffer is\n // full (write() returns false) and resume on 'drain'.\n if (nodeRes.destroyed) {\n webRes.body.cancel();\n return;\n }\n const reader = webRes.body.getReader();\n function streamCancel(error) {\n reader.cancel(error).catch(() => {});\n if (error) nodeRes.destroy(error);\n }\n function streamHandle({ done, value }) {\n try {\n if (done) {\n nodeRes.end();\n } else if (nodeRes.write(value)) {\n reader.read().then(streamHandle, streamCancel);\n } else {\n nodeRes.once('drain', () => reader.read().then(streamHandle, streamCancel));\n }\n } catch (error) {\n streamCancel(error instanceof Error ? error : undefined);\n }\n }\n nodeRes.on('close', streamCancel);\n nodeRes.on('error', streamCancel);\n reader.read().then(streamHandle, streamCancel);\n return reader.closed.catch(streamCancel).finally(() => {\n nodeRes.off('close', streamCancel);\n nodeRes.off('error', streamCancel);\n });\n}\n`;\n}\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\n/**\n * Get the preset configuration for a given preset name.\n * @internal Exported for testing.\n */\nexport function getPresetConfig(preset: NitroPreset): PresetConfig {\n return PRESET_CONFIGS[preset];\n}\n\n/**\n * Resolve the destination path for prebuilt flight payloads in the Nitro\n * output directory. The RSC entry resolves `../prebuilt/` relative to its\n * bundled chunk location (`<serverBundleDir>/_chunks/`), so prebuilt/ must\n * live at `<serverBundleDir>/prebuilt/`.\n *\n * The serverBundleDir varies by preset — Nitro's Vercel preset uses\n * `functions/__server.func` while most others use `server`.\n *\n * @internal Exported for testing.\n */\nexport function resolvePrebuiltOutputPath(outDir: string, preset: NitroPreset): string {\n const presetConfig = PRESET_CONFIGS[preset];\n return join(outDir, presetConfig.outputDir, presetConfig.serverBundleDir, 'prebuilt');\n}\n\n/**\n * Copy prebuilt payloads into cloned Vercel function directories.\n *\n * When users configure `vercel.functionRules`, Nitro's compiled hook clones\n * the base __server.func to per-route .func directories. These clones are\n * created during `runNitroBuild()` — before our post-build prebuilt copy.\n * Each clone resolves `../prebuilt` independently, so they each need a copy.\n *\n * @internal Exported for testing.\n */\nexport async function copyPrebuiltToVercelFuncClones(\n functionsDir: string,\n prebuiltSrc: string,\n baseFuncName: string\n): Promise<void> {\n const funcDirs: string[] = [];\n try {\n for await (const match of glob('**/*.func', { cwd: functionsDir })) {\n if (!match.endsWith(baseFuncName)) {\n funcDirs.push(join(functionsDir, match));\n }\n }\n } catch {\n return;\n }\n await Promise.all(\n funcDirs.map((dir) =>\n cp(prebuiltSrc, join(dir, 'prebuilt'), { recursive: true }).catch(\n (e: NodeJS.ErrnoException) => {\n if (e.code !== 'ENOENT') throw e;\n }\n )\n )\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"nitro.js","names":[],"sources":["../../src/adapters/compress-module.ts","../../src/adapters/nitro.ts"],"sourcesContent":["// Compiles server/compress.ts into a standalone ESM module (_compress.mjs)\n// for self-hosted deployments. The adapter writes this to the build output\n// during buildOutput(). It's imported by the Nitro entry and preview server\n// at runtime.\n//\n// By compiling the actual TypeScript source rather than maintaining a\n// hand-written string template, any fix to server/compress.ts automatically\n// flows to the production module. See design/18-build-system.md (\"entry\n// modules are real TypeScript files, not codegen strings\").\n\nimport { transformWithOxc } from 'vite';\n// Vite's ?raw import embeds the file as a string at build time — works\n// in both dev (Vite resolves the TS source) and the published package\n// (the string is inlined into the compiled adapter entry).\nimport compressSource from '../server/compress.ts?raw';\n\n/**\n * Compile server/compress.ts into a standalone ESM module.\n *\n * Uses Vite's OXC transform to strip TypeScript types from the\n * embedded source. The result is written to `_compress.mjs` during\n * buildOutput.\n *\n * @internal Exported for testing.\n */\nexport async function generateCompressModule(): Promise<string> {\n const result = await transformWithOxc(compressSource, 'compress.ts', {\n lang: 'ts',\n });\n\n return (\n '// Generated by @timber-js/app — compiled from server/compress.ts.\\n' +\n '// Do not edit — this file is regenerated on each build.\\n' +\n result.code\n );\n}\n","// Nitro adapter — multi-platform deployment\n//\n// Covers everything except Cloudflare Workers: Node.js, Bun, Vercel,\n// Netlify, AWS Lambda, Deno Deploy, Azure Functions. Nitro handles\n// compression, graceful shutdown, static file serving, and platform quirks.\n// See design/11-platform.md and design/25-production-deployments.md.\n\nimport { writeFile, readFile, cp, glob } from 'node:fs/promises';\nimport { execFile } from 'node:child_process';\nimport { join, relative, dirname, basename } from 'node:path';\nimport type { TimberPlatformAdapter, TimberConfig } from './types';\nimport { generateCompressModule } from './compress-module.js';\nimport { adapterBase, IMMUTABLE_CACHE, generateHeadersFile } from './shared.js';\nimport {\n runSharedBuildSteps,\n buildStaticDirectoryOutput,\n findUnhashedStaticFiles,\n writeHeadersFile,\n} from './build-output-helper.js';\n\n// ─── Presets ─────────────────────────────────────────────────────────────────\n\n/**\n * Supported Nitro deployment presets.\n *\n * Each preset maps to a Nitro deployment target. The adapter generates\n * the appropriate configuration and entry point for the selected platform.\n */\nexport type NitroPreset =\n | 'vercel'\n | 'vercel-edge'\n | 'netlify'\n | 'netlify-edge'\n | 'aws-lambda'\n | 'deno-deploy'\n | 'azure-functions'\n | 'node-server'\n | 'bun';\n\n/** Preset-specific Nitro configuration. */\ninterface PresetConfig {\n /** Nitro preset name passed to the Nitro build. */\n nitroPreset: string;\n /** Output directory name within the build dir. */\n outputDir: string;\n /**\n * Path to the server bundle directory relative to outputDir.\n * Nitro places the bundled server entry + _chunks/ here.\n * Most presets use `server`; Vercel uses `functions/__server.func`.\n */\n serverBundleDir: string;\n /** Whether the runtime supports waitUntil. */\n supportsWaitUntil: boolean;\n /** Whether the runtime supports application-level 103 Early Hints. */\n supportsEarlyHints: boolean;\n /** Value for TIMBER_RUNTIME env var. See design/25-production-deployments.md. */\n runtimeName: string;\n /** Additional nitro.config fields for this preset. */\n extraConfig?: Record<string, unknown>;\n}\n\nconst PRESET_CONFIGS: Record<NitroPreset, PresetConfig> = {\n 'vercel': {\n nitroPreset: 'vercel',\n outputDir: '.vercel/output',\n serverBundleDir: 'functions/__server.func',\n supportsWaitUntil: true,\n supportsEarlyHints: false,\n runtimeName: 'vercel',\n extraConfig: { vercel: { functions: { maxDuration: 30 } } },\n },\n 'vercel-edge': {\n nitroPreset: 'vercel-edge',\n outputDir: '.vercel/output',\n serverBundleDir: 'functions/__server.func',\n supportsWaitUntil: true,\n supportsEarlyHints: false,\n runtimeName: 'vercel-edge',\n },\n 'netlify': {\n nitroPreset: 'netlify',\n outputDir: '.netlify/functions-internal',\n serverBundleDir: 'server',\n supportsWaitUntil: false,\n supportsEarlyHints: false,\n runtimeName: 'netlify',\n },\n 'netlify-edge': {\n nitroPreset: 'netlify-edge',\n outputDir: '.netlify/edge-functions',\n serverBundleDir: 'server',\n supportsWaitUntil: true,\n supportsEarlyHints: false,\n runtimeName: 'netlify-edge',\n },\n 'aws-lambda': {\n nitroPreset: 'aws-lambda',\n outputDir: '.output',\n serverBundleDir: 'server',\n supportsWaitUntil: false,\n supportsEarlyHints: false,\n runtimeName: 'aws-lambda',\n },\n 'deno-deploy': {\n nitroPreset: 'deno-deploy',\n outputDir: '.output',\n serverBundleDir: 'server',\n supportsWaitUntil: true,\n supportsEarlyHints: false,\n runtimeName: 'deno-deploy',\n },\n 'azure-functions': {\n nitroPreset: 'azure-functions',\n outputDir: '.output',\n serverBundleDir: 'server',\n supportsWaitUntil: false,\n supportsEarlyHints: false,\n runtimeName: 'azure-functions',\n },\n 'node-server': {\n nitroPreset: 'node-server',\n outputDir: '.output',\n serverBundleDir: 'server',\n supportsWaitUntil: true,\n // Disabled by default: most node-server deployments sit behind a\n // reverse proxy (nginx, caddy, traefik) that doesn't support 103\n // Early Hints over HTTP/1.1. Sending 103 causes nginx to intermittently\n // treat the response as an error and retry after proxy_connect_timeout\n // (~5s stalls on ~23% of requests). Link headers on the 200 response\n // are the safe fallback — Cloudflare and other CDNs convert them to\n // 103 automatically at the edge (over HTTP/2+ to the browser).\n supportsEarlyHints: false,\n runtimeName: 'node-server',\n },\n 'bun': {\n nitroPreset: 'bun',\n outputDir: '.output',\n serverBundleDir: 'server',\n supportsWaitUntil: true,\n // Disabled for same reason as node-server — reverse proxies choke on 103.\n // Link headers on the 200 response are converted to 103 by CDNs.\n supportsEarlyHints: false,\n runtimeName: 'bun',\n },\n};\n\n// ─── Options ─────────────────────────────────────────────────────────────────\n\n/**\n * Static deploy target for `output: 'static'` mode.\n *\n * Controls platform-specific files written alongside the static output:\n * - `'github-pages'` — writes `.nojekyll` to skip Jekyll processing\n * - `'generic'` — no extra files (default)\n */\nexport type StaticTarget = 'github-pages' | 'generic';\n\n/** Options for the Nitro adapter. */\nexport interface NitroAdapterOptions {\n /**\n * Deployment preset. Determines the target platform.\n * @default 'node-server'\n */\n preset?: NitroPreset;\n\n /**\n * Enable application-level gzip compression for HTML and RSC responses.\n *\n * When `true` (default), the origin compresses responses using the Web\n * `CompressionStream` API. This is useful for self-hosted deployments\n * where no reverse proxy or CDN handles compression.\n *\n * Set to `false` when deploying behind a reverse proxy (nginx, caddy)\n * or CDN (Cloudflare, Fastly, Vercel) that compresses at the edge.\n * Disabling origin compression saves CPU on the Node.js event loop —\n * compressing 1MB+ streaming HTML responses takes 10-15ms of main\n * thread time per request, directly reducing throughput under load.\n *\n * @default true\n */\n compress?: boolean;\n\n /**\n * Static deploy target for `output: 'static'` mode. Controls\n * platform-specific files (e.g. `.nojekyll` for GitHub Pages).\n * @default 'generic'\n */\n staticTarget?: StaticTarget;\n\n /**\n * Additional Nitro configuration to merge into the generated config.\n * Overrides default values for the selected preset.\n */\n nitroConfig?: Record<string, unknown>;\n}\n\n// ─── Adapter ─────────────────────────────────────────────────────────────────\n\n/**\n * Create a Nitro-based adapter for multi-platform deployment.\n *\n * Nitro abstracts deployment targets — the same timber.js app can deploy\n * to Vercel, Netlify, AWS, Deno Deploy, or Azure by changing the preset.\n *\n * @example\n * ```ts\n * import { nitro } from '@timber-js/app/adapters/nitro'\n *\n * export default {\n * output: 'server',\n * adapter: nitro({ preset: 'vercel' }),\n * }\n * ```\n */\nexport function nitro(options: NitroAdapterOptions = {}): TimberPlatformAdapter {\n const preset = options.preset ?? 'node-server';\n const compress = options.compress ?? true;\n const presetConfig = PRESET_CONFIGS[preset];\n return {\n name: `nitro-${preset}`,\n\n async buildOutput(config: TimberConfig, buildDir: string) {\n const outDir = join(buildDir, 'nitro');\n\n // ── Static mode: plain directory output ────────────────────────\n // No Nitro server — just pre-rendered HTML + client assets.\n if (config.output === 'static' && config.staticPagesDir) {\n await buildStaticDirectoryOutput(config, buildDir, outDir, 'public');\n const publicDir = join(outDir, 'public');\n\n // Write _headers for cache control + extensionless Content-Type.\n // Netlify supports the _headers convention just like Cloudflare.\n await writeHeadersFile(publicDir, config, await findUnhashedStaticFiles(publicDir));\n\n // Platform-specific files for static deploy targets.\n const target = options.staticTarget ?? 'generic';\n if (target === 'github-pages') {\n // Deploy-from-a-branch runs Jekyll, which drops `_`-prefixed paths —\n // every `_rsc/` payload. Written unconditionally: GitHub reads only\n // that the file exists, never its contents.\n await writeFile(join(publicDir, '.nojekyll'), '');\n // GitHub Pages doesn't support _redirects files. Warn if any\n // redirects were generated — those routes will 404 on GH Pages.\n try {\n const redirects = await readFile(join(publicDir, '_redirects'), 'utf-8');\n if (redirects.trim().length > 0) {\n console.warn(\n `[timber] Warning: GitHub Pages does not support _redirects files. ` +\n `Redirect rules from middleware/proxy will not work on this platform.`\n );\n }\n } catch {\n // No _redirects file — nothing to warn about\n }\n }\n\n return;\n }\n\n // Steps 1–5: shared across all adapters (mkdir, copy client/rsc/ssr,\n // write _headers, write manifest-init).\n await runSharedBuildSteps({\n config,\n buildDir,\n outDir,\n publicDirName: 'public',\n });\n\n // Write runtime helper modules used by the preview server.\n // See design/25-production-deployments.md — self-hosted deployments\n // need application-level compression (Cloudflare handles it at the edge).\n await writeFile(join(outDir, '_compress.mjs'), await generateCompressModule());\n\n // Web→Node response bridge with backpressure. See TIM-1154.\n await writeFile(join(outDir, '_send-response.mjs'), generateSendResponseModule());\n\n // Prepend the manifest assignment directly into the RSC entry so\n // globalThis.__TIMBER_BUILD_MANIFEST__ is set before any module reads it.\n // This must be top-level code, not an import, because rollup tree-shakes\n // side-effect-only globalThis assignments from imported modules.\n if (config.manifestInit) {\n const rscEntry = join(outDir, 'rsc', 'index.js');\n const rscContent = await readFile(rscEntry, 'utf-8');\n await writeFile(rscEntry, `${config.manifestInit}\\n${rscContent}`);\n }\n\n // Generate the Nitro entry point (imports from ./rsc/ within nitro dir)\n const entry = generateNitroEntry(buildDir, outDir, preset, compress);\n await writeFile(join(outDir, 'entry.ts'), entry);\n\n // Run the Nitro build to produce a production-ready server bundle.\n // The output goes to dist/nitro/.output/server/index.mjs (for node-server preset).\n // Config is passed programmatically — no nitro.config.ts file needed.\n const { serverDir } = await runNitroBuild(outDir, preset, options.nitroConfig);\n\n // Copy prebuilt flight payloads from the canonical build source\n // (buildDir/prebuilt, cleaned by the prebuilt plugin each build) into\n // the Nitro output. The RSC entry resolves `../prebuilt/` relative to\n // its bundled chunk (<serverDir>/_chunks/), so prebuilt/ must live at\n // <serverDir>/prebuilt/. serverDir is the resolved path from Nitro\n // (honors user overrides via nitroConfig.output.serverDir).\n const prebuiltSrc = join(buildDir, 'prebuilt');\n await cp(prebuiltSrc, join(serverDir, 'prebuilt'), {\n recursive: true,\n }).catch((e: NodeJS.ErrnoException) => {\n if (e.code !== 'ENOENT') throw e;\n });\n\n // Vercel with functionRules clones __server.func to per-route .func\n // directories during the Nitro build. Copy prebuilt into those clones\n // so requests routed to them also resolve ../prebuilt correctly.\n if (preset === 'vercel' || preset === 'vercel-edge') {\n const functionsDir = dirname(serverDir);\n const baseFuncName = basename(serverDir);\n await copyPrebuiltToVercelFuncClones(functionsDir, prebuiltSrc, baseFuncName);\n }\n },\n\n // Only presets that produce a locally-runnable server get preview().\n // Serverless presets (vercel, netlify, aws-lambda, etc.) have no\n // local runtime — Vite's built-in preview is the fallback.\n preview: LOCALLY_PREVIEWABLE.has(preset)\n ? async (_config: TimberConfig, buildDir: string) => {\n // Generate a standalone preview server that uses Node's built-in\n // HTTP server. The Nitro entry.ts can't be run directly because\n // it imports h3 (a Nitro dependency not available at runtime).\n const previewScript = generatePreviewScript(buildDir, preset);\n const scriptPath = join(buildDir, 'nitro', '_preview-server.mjs');\n await writeFile(scriptPath, previewScript);\n\n const command = preset === 'bun' ? 'bun' : 'node';\n await spawnNitroPreview(command, [scriptPath], join(buildDir, 'nitro'));\n }\n : undefined,\n };\n}\n\n// ─── Entry Generation ────────────────────────────────────────────────────────\n\n/** @internal Exported for testing. */\nexport function generateNitroEntry(\n buildDir: string,\n outDir: string,\n preset: NitroPreset,\n compress = true\n): string {\n // The RSC entry is the main request handler — it exports the fetch handler as default.\n // rsc/ is copied into the nitro dir so the import is local.\n // The manifest init is prepended to rsc/index.js before the nitro build,\n // so globalThis.__TIMBER_BUILD_MANIFEST__ is set before any code reads it.\n const serverEntryRelative = './rsc/index.js';\n const runtimeName = PRESET_CONFIGS[preset].runtimeName;\n const earlyHints = PRESET_CONFIGS[preset].supportsEarlyHints;\n const supportsWaitUntil = PRESET_CONFIGS[preset].supportsWaitUntil;\n\n // On node-server and bun, wrap the handler with ALS so the pipeline\n // can send 103 Early Hints via res.writeEarlyHints(). Other presets\n // either don't support 103 or handle it at the CDN level.\n //\n // For presets that support waitUntil, bridge h3's event.waitUntil()\n // to timber's waitUntil() primitive via the ALS bridge.\n // See design/11-platform.md §\"waitUntil()\".\n let handlerCall: string;\n if (earlyHints && supportsWaitUntil) {\n handlerCall = ` const nodeRes = event.node?.res\n const earlyHintsSender = (typeof nodeRes?.writeEarlyHints === 'function')\n ? (links) => { try { nodeRes.writeEarlyHints({ link: links }) } catch {} }\n : undefined\n\n const waitUntilFn = (typeof event.waitUntil === 'function')\n ? (p) => event.waitUntil(p)\n : undefined\n\n const callHandler = () => handler(webRequest)\n let wrappedHandler = earlyHintsSender\n ? () => runWithEarlyHintsSender(earlyHintsSender, callHandler)\n : callHandler\n const webResponse = waitUntilFn\n ? await runWithWaitUntil(waitUntilFn, wrappedHandler)\n : await wrappedHandler()`;\n } else if (earlyHints) {\n handlerCall = ` const nodeRes = event.node?.res\n const earlyHintsSender = (typeof nodeRes?.writeEarlyHints === 'function')\n ? (links) => { try { nodeRes.writeEarlyHints({ link: links }) } catch {} }\n : undefined\n\n const webResponse = earlyHintsSender\n ? await runWithEarlyHintsSender(earlyHintsSender, () => handler(webRequest))\n : await handler(webRequest)`;\n } else if (supportsWaitUntil) {\n handlerCall = ` const waitUntilFn = (typeof event.waitUntil === 'function')\n ? (p) => event.waitUntil(p)\n : undefined\n\n const webResponse = waitUntilFn\n ? await runWithWaitUntil(waitUntilFn, () => handler(webRequest))\n : await handler(webRequest)`;\n } else {\n handlerCall = ` const webResponse = await handler(webRequest)`;\n }\n\n // Import runWithWaitUntil only when the preset supports it.\n const waitUntilImport = supportsWaitUntil ? ', runWithWaitUntil' : '';\n\n const compressImport = compress ? \"import { compressResponse } from './_compress.mjs'\\n\" : '';\n const compressCall = compress ? 'compressResponse(webRequest, webResponse)' : 'webResponse';\n\n return `// Generated by @timber-js/app/adapters/nitro\n// Do not edit — this file is regenerated on each build.\n\nimport handler, { runWithEarlyHintsSender${waitUntilImport} } from '${serverEntryRelative}'\n${compressImport}\n// Set TIMBER_RUNTIME for instrumentation.ts conditional SDK initialization.\n// See design/25-production-deployments.md §\"TIMBER_RUNTIME\".\nprocess.env.TIMBER_RUNTIME = '${runtimeName}'\n\n// Nitro's index.mjs wraps this import with defineLazyEventHandler, which\n// already handles the event handler protocol. We export a plain async\n// function — no defineEventHandler wrapper needed.\n// Nitro bundles h3 into its internal _libs/ directory but doesn't make\n// it available as a bare specifier in the output's node_modules.\nexport default async function timberHandler(event) {\n // h3 v2: event.req is the Web Request\n const webRequest = event.req\n${handlerCall}\n return ${compressCall}\n}\n`;\n}\n\n// ─── Preview ─────────────────────────────────────────────────────────────────\n\n/** Presets that produce a locally-runnable server entry. */\nconst LOCALLY_PREVIEWABLE = new Set<NitroPreset>(['node-server', 'bun']);\n\n/**\n * Generate a standalone preview server script that uses Node's built-in\n * HTTP server. This bypasses Nitro entirely — the Nitro entry.ts imports\n * h3 which isn't available outside a Nitro build. For local preview we\n * just need to serve static files and route requests to the RSC handler.\n *\n * @internal Exported for testing.\n */\nexport function generatePreviewScript(buildDir: string, preset: NitroPreset): string {\n const rscEntryRelative = relative(join(buildDir, 'nitro'), join(buildDir, 'rsc', 'index.js'));\n const rscEntry = rscEntryRelative.startsWith('.') ? rscEntryRelative : './' + rscEntryRelative;\n const publicDir = './public';\n const manifestInitPath = './_timber-manifest-init.js';\n const runtimeName = PRESET_CONFIGS[preset].runtimeName;\n\n return `// Generated by @timber-js/app — standalone preview server.\n// Uses Node's built-in HTTP server to serve static assets and route\n// dynamic requests through the RSC handler. No Nitro/h3 dependency.\n\nimport { createServer } from 'node:http';\nimport { readFile, stat } from 'node:fs/promises';\nimport { join, extname } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { existsSync } from 'node:fs';\n\n// Set runtime before importing the handler.\nprocess.env.TIMBER_RUNTIME = '${runtimeName}';\n\n// Load the build manifest if it exists.\nconst __dirname = fileURLToPath(new URL('.', import.meta.url));\nconst manifestPath = join(__dirname, '${manifestInitPath}');\nif (existsSync(manifestPath)) {\n await import('${manifestInitPath}');\n}\n\n// Import the RSC handler (default export is the fetch-like handler).\nconst { default: handler, runWithEarlyHintsSender } = await import('${rscEntry}');\n\n// Import compression helper for self-hosted response compression.\nconst { compressResponse } = await import('./_compress.mjs');\n\n// Web→Node response bridge with backpressure (TIM-1154).\nconst { sendNodeResponse } = await import('./_send-response.mjs');\n\nconst MIME_TYPES = {\n '.html': 'text/html',\n '.js': 'application/javascript',\n '.mjs': 'application/javascript',\n '.css': 'text/css',\n '.json': 'application/json',\n '.png': 'image/png',\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.gif': 'image/gif',\n '.svg': 'image/svg+xml',\n '.ico': 'image/x-icon',\n '.woff': 'font/woff',\n '.woff2': 'font/woff2',\n '.ttf': 'font/ttf',\n '.otf': 'font/otf',\n '.webp': 'image/webp',\n '.avif': 'image/avif',\n '.webm': 'video/webm',\n '.mp4': 'video/mp4',\n '.txt': 'text/plain',\n '.xml': 'application/xml',\n '.wasm': 'application/wasm',\n};\n\nconst publicDir = join(__dirname, '${publicDir}');\n\n// Port resolution (TIM-842):\n// - Default to 3000\n// - If PORT env var is set, honor it strictly (fail loudly on conflict)\n// - Otherwise auto-bump from 3000 until a free port is found\n// Mirrors the dev server behavior so timber dev/preview/production all\n// behave the same way around port selection.\nconst envPort = process.env.PORT ? parseInt(process.env.PORT, 10) : null;\nconst portIsExplicit = envPort != null && Number.isFinite(envPort) && envPort > 0;\nconst startPort = portIsExplicit ? envPort : 3000;\nconst host = process.env.HOST || 'localhost';\n\n// Set after listenWithBump() resolves so request handlers can build\n// absolute URLs from the actual bound port (which may differ from\n// startPort after an auto-bump).\nlet boundPort = startPort;\n\nconst server = createServer(async (req, res) => {\n // Scheme from the socket's TLS state — a hardcoded http:// breaks CSRF\n // origin comparison and absolute-URL construction when TLS is terminated\n // at this server (TIM-1073; mirrors the dev-server fix in TIM-1067).\n // X-Forwarded-Proto is intentionally not consulted here: on a direct\n // connection it would be attacker-controlled. The CSRF layer honors it\n // separately for reverse-proxy deployments.\n const scheme = req.socket?.encrypted ? 'https' : 'http';\n const url = new URL(req.url || '/', \\`\\${scheme}://\\${host}:\\${boundPort}\\`);\n\n // Try serving static files from the public directory first.\n const filePath = join(publicDir, url.pathname);\n // Prevent path traversal.\n if (filePath.startsWith(publicDir)) {\n try {\n const fileStat = await stat(filePath);\n if (fileStat.isFile()) {\n const ext = extname(filePath);\n const contentType = MIME_TYPES[ext] || 'application/octet-stream';\n const body = await readFile(filePath);\n // Hashed assets get immutable cache, others get short cache.\n const isImmutable =\n url.pathname.startsWith('/assets/') || url.pathname.startsWith('/_timber/fonts/');\n const cacheControl = isImmutable\n ? 'public, max-age=31536000, immutable'\n : 'public, max-age=3600, must-revalidate';\n res.writeHead(200, {\n 'Content-Type': contentType,\n 'Content-Length': body.length,\n 'Cache-Control': cacheControl,\n });\n res.end(body);\n return;\n }\n } catch {\n // File not found — fall through to the RSC handler.\n }\n }\n\n // Convert Node request to Web Request.\n const headers = new Headers();\n for (const [key, value] of Object.entries(req.headers)) {\n if (value) {\n if (Array.isArray(value)) {\n for (const v of value) headers.append(key, v);\n } else {\n headers.set(key, value);\n }\n }\n }\n\n let body = undefined;\n if (req.method !== 'GET' && req.method !== 'HEAD') {\n body = await new Promise((resolve) => {\n const chunks = [];\n req.on('data', (chunk) => chunks.push(chunk));\n req.on('end', () => resolve(Buffer.concat(chunks)));\n });\n }\n\n const webRequest = new Request(url.href, {\n method: req.method,\n headers,\n body,\n duplex: body ? 'half' : undefined,\n });\n\n try {\n // Support 103 Early Hints when available.\n const earlyHintsSender = (typeof res.writeEarlyHints === 'function')\n ? (links) => { try { res.writeEarlyHints({ link: links }); } catch {} }\n : undefined;\n\n const rawResponse = earlyHintsSender && runWithEarlyHintsSender\n ? await runWithEarlyHintsSender(earlyHintsSender, () => handler(webRequest))\n : await handler(webRequest);\n\n // Compress the response for self-hosted deployments.\n const webResponse = compressResponse(webRequest, rawResponse);\n\n // Write the response to Node's ServerResponse. sendNodeResponse handles\n // status, headers (including Set-Cookie splitting via [...headers]),\n // body streaming with backpressure, and client disconnect cleanup.\n // See TIM-1154.\n await sendNodeResponse(res, webResponse);\n } catch (err) {\n console.error('[timber preview] Request error:', err);\n if (!res.headersSent) {\n res.writeHead(500, { 'Content-Type': 'text/plain' });\n }\n res.end('Internal Server Error');\n }\n});\n\nfunction listenWithBump(port, attempt = 0) {\n return new Promise((resolveListen, rejectListen) => {\n const onError = (err) => {\n server.removeListener('listening', onListening);\n if (err && err.code === 'EADDRINUSE' && !portIsExplicit && attempt < 100) {\n if (attempt === 0) {\n console.log(\\` [timber] Port \\${port} in use, trying \\${port + 1}...\\`);\n }\n listenWithBump(port + 1, attempt + 1).then(resolveListen, rejectListen);\n return;\n }\n rejectListen(err);\n };\n const onListening = () => {\n server.removeListener('error', onError);\n resolveListen(port);\n };\n server.once('error', onError);\n server.once('listening', onListening);\n server.listen(port, host);\n });\n}\n\ntry {\n boundPort = await listenWithBump(startPort);\n if (boundPort !== startPort) {\n console.log();\n console.log(\\` [timber] Port \\${startPort} in use, using \\${boundPort}\\`);\n }\n console.log();\n console.log(' ⚡ timber preview server running at:');\n console.log();\n console.log(\\` ➜ http://\\${host}:\\${boundPort}\\`);\n console.log();\n} catch (err) {\n if (err && err.code === 'EADDRINUSE') {\n console.error(\\`[timber] Port \\${startPort} is already in use. \\` +\n 'Set PORT to a free port, or unset PORT to auto-pick.');\n } else {\n console.error('[timber] Failed to start preview server:', err);\n }\n process.exit(1);\n}\n`;\n}\n\n/** Command descriptor for Nitro preview — testable without spawning. */\nexport interface NitroPreviewCommand {\n command: string;\n args: string[];\n cwd: string;\n}\n\n/** @internal Exported for testing. */\nexport function generateNitroPreviewCommand(\n buildDir: string,\n preset: NitroPreset\n): NitroPreviewCommand | null {\n if (!LOCALLY_PREVIEWABLE.has(preset)) return null;\n\n const nitroDir = join(buildDir, 'nitro');\n const entryPath = join(nitroDir, 'entry.ts');\n\n const command = preset === 'bun' ? 'bun' : 'node';\n return {\n command,\n args: [entryPath],\n cwd: nitroDir,\n };\n}\n\n/**\n * Run the Nitro production build using the programmatic API.\n * Uses dynamic import so nitro is only loaded at build time.\n * Externalizes the timber RSC/SSR output — those files are pre-built\n * by timber and have internal references that nitro's bundler can't follow.\n */\ninterface NitroBuildResult {\n /** Resolved output.serverDir — where the server bundle + _chunks/ live. */\n serverDir: string;\n}\n\nasync function runNitroBuild(\n nitroDir: string,\n preset: NitroPreset,\n userConfig?: Record<string, unknown>\n): Promise<NitroBuildResult> {\n const presetConfig = PRESET_CONFIGS[preset];\n const {\n createNitro,\n build: nitroBuild,\n prepare,\n copyPublicAssets,\n } = await import('nitro/builder');\n\n const nitro = await createNitro({\n rootDir: nitroDir,\n preset: presetConfig.nitroPreset,\n // Use renderer.entry so Nitro wraps our handler with its server runtime\n // (HTTP server, static file serving, graceful shutdown, etc.).\n // Using `entry` directly would bypass the Nitro server runtime.\n renderer: { handler: join(nitroDir, 'entry.ts') },\n output: { dir: join(nitroDir, presetConfig.outputDir) },\n routeRules: {\n '/assets/**': { headers: { 'Cache-Control': IMMUTABLE_CACHE } },\n '/_timber/fonts/**': { headers: { 'Cache-Control': IMMUTABLE_CACHE } },\n },\n // Don't bundle the timber RSC/SSR build output — it has its own\n // internal file references that nitro's bundler can't follow.\n // Mark them as external so rollup leaves the imports as-is.\n rollupConfig: {\n external: [/\\.\\.\\/rsc\\//],\n },\n ...presetConfig.extraConfig,\n ...userConfig,\n });\n\n // Read the resolved serverDir AFTER config merging — userConfig may\n // override output.dir or output.serverDir. Nitro appends a trailing\n // slash during resolution; strip it for join() compatibility.\n const raw = nitro.options.output.serverDir;\n const serverDir = raw.endsWith('/') ? raw.slice(0, -1) : raw;\n\n await prepare(nitro);\n await copyPublicAssets(nitro);\n await nitroBuild(nitro);\n await nitro.close();\n\n return { serverDir };\n}\n\n/** Spawn a Nitro preview process and pipe stdio. */\nfunction spawnNitroPreview(command: string, args: string[], cwd: string): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n const child = execFile(command, args, { cwd }, (err) => {\n if (err) reject(err);\n else resolve();\n });\n child.stdout?.pipe(process.stdout);\n child.stderr?.pipe(process.stderr);\n });\n}\n\n// ─── Send Response Module ───────────────────────────────────────────────────\n\n/**\n * Generate a standalone ESM module that exports sendNodeResponse.\n *\n * Mirrors srvx/node's sendNodeResponse: status + headers via writeHead,\n * body streaming with backpressure (waits for drain on write() === false),\n * and client disconnect cleanup. Written to `_send-response.mjs` during\n * buildOutput for use by the preview server script. See TIM-1154.\n *\n * @internal Exported for testing.\n */\nexport function generateSendResponseModule(): string {\n return `// Generated by @timber-js/app — Web→Node response bridge.\n// Do not edit — this file is regenerated on each build.\n// Mirrors srvx/node's sendNodeResponse with backpressure support.\n\nexport function sendNodeResponse(nodeRes, webRes) {\n if (!webRes) {\n nodeRes.statusCode = 500;\n return new Promise((resolve) => nodeRes.end(resolve));\n }\n const rawHeaders = [...webRes.headers];\n const writeHeaders = rawHeaders.flat();\n if (!nodeRes.headersSent) {\n if (nodeRes.req?.httpVersion === '2.0') {\n nodeRes.writeHead(webRes.status, writeHeaders);\n } else {\n nodeRes.writeHead(webRes.status, webRes.statusText, writeHeaders);\n }\n }\n if (!webRes.body) {\n return new Promise((resolve) => nodeRes.end(resolve));\n }\n // Stream with backpressure: pause reading when the kernel buffer is\n // full (write() returns false) and resume on 'drain'.\n if (nodeRes.destroyed) {\n webRes.body.cancel();\n return;\n }\n const reader = webRes.body.getReader();\n function streamCancel(error) {\n reader.cancel(error).catch(() => {});\n if (error) nodeRes.destroy(error);\n }\n function streamHandle({ done, value }) {\n try {\n if (done) {\n nodeRes.end();\n } else if (nodeRes.write(value)) {\n reader.read().then(streamHandle, streamCancel);\n } else {\n nodeRes.once('drain', () => reader.read().then(streamHandle, streamCancel));\n }\n } catch (error) {\n streamCancel(error instanceof Error ? error : undefined);\n }\n }\n nodeRes.on('close', streamCancel);\n nodeRes.on('error', streamCancel);\n reader.read().then(streamHandle, streamCancel);\n return reader.closed.catch(streamCancel).finally(() => {\n nodeRes.off('close', streamCancel);\n nodeRes.off('error', streamCancel);\n });\n}\n`;\n}\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\n/**\n * Get the preset configuration for a given preset name.\n * @internal Exported for testing.\n */\nexport function getPresetConfig(preset: NitroPreset): PresetConfig {\n return PRESET_CONFIGS[preset];\n}\n\n/**\n * Resolve the destination path for prebuilt flight payloads in the Nitro\n * output directory. The RSC entry resolves `../prebuilt/` relative to its\n * bundled chunk location (`<serverBundleDir>/_chunks/`), so prebuilt/ must\n * live at `<serverBundleDir>/prebuilt/`.\n *\n * The serverBundleDir varies by preset — Nitro's Vercel preset uses\n * `functions/__server.func` while most others use `server`.\n *\n * @internal Exported for testing.\n */\nexport function resolvePrebuiltOutputPath(outDir: string, preset: NitroPreset): string {\n const presetConfig = PRESET_CONFIGS[preset];\n return join(outDir, presetConfig.outputDir, presetConfig.serverBundleDir, 'prebuilt');\n}\n\n/**\n * Copy prebuilt payloads into cloned Vercel function directories.\n *\n * When users configure `vercel.functionRules`, Nitro's compiled hook clones\n * the base __server.func to per-route .func directories. These clones are\n * created during `runNitroBuild()` — before our post-build prebuilt copy.\n * Each clone resolves `../prebuilt` independently, so they each need a copy.\n *\n * @internal Exported for testing.\n */\nexport async function copyPrebuiltToVercelFuncClones(\n functionsDir: string,\n prebuiltSrc: string,\n baseFuncName: string\n): Promise<void> {\n const funcDirs: string[] = [];\n try {\n for await (const match of glob('**/*.func', { cwd: functionsDir })) {\n if (!match.endsWith(baseFuncName)) {\n funcDirs.push(join(functionsDir, match));\n }\n }\n } catch {\n return;\n }\n await Promise.all(\n funcDirs.map((dir) =>\n cp(prebuiltSrc, join(dir, 'prebuilt'), { recursive: true }).catch(\n (e: NodeJS.ErrnoException) => {\n if (e.code !== 'ENOENT') throw e;\n }\n )\n )\n );\n}\n"],"mappings":"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"}
@@ -1,6 +1,62 @@
1
+ /**
2
+ * The deployment base an adapter should write platform rules against.
3
+ *
4
+ * `config.base` is optional on `TimberConfig` because adapters can be invoked
5
+ * by callers that predate it (a hand-written adapter, a test harness). Falling
6
+ * back to `'/'` reproduces the pre-TIM-1274 output exactly, which is correct
7
+ * for the only deployments those callers can have: a build under a non-root
8
+ * base always goes through `plugins/adapter-build.ts`, which populates it.
9
+ *
10
+ * Typed structurally rather than as `TimberConfig` so this module keeps no
11
+ * import of `./types.js` at runtime.
12
+ */
13
+ export declare function adapterBase(config: {
14
+ base?: string;
15
+ }): string;
1
16
  /** Cache-Control value for hashed (immutable) assets. */
2
17
  export declare const IMMUTABLE_CACHE = "public, max-age=31536000, immutable";
3
18
  /** Cache-Control value for unhashed static assets. */
4
19
  export declare const STATIC_CACHE = "public, max-age=3600, must-revalidate";
5
- export declare function generateHeadersFile(unhashedPaths?: string[], contentTypes?: Record<string, string>): string;
20
+ export interface HeadersFileOptions {
21
+ /**
22
+ * The deployment base, already normalized by `normalizeBasePath()` —
23
+ * leading and trailing slash, `'/'` at the domain root.
24
+ *
25
+ * Required rather than defaulted: a caller that forgets it would emit
26
+ * root-relative rules that match nothing, which is the bug this closes.
27
+ */
28
+ base: string;
29
+ /** Unhashed static files (route-space paths) needing per-file cache rules. */
30
+ unhashedPaths?: string[];
31
+ /** Route-space path → Content-Type, for extensionless API route outputs. */
32
+ contentTypes?: Record<string, string>;
33
+ /**
34
+ * Verbatim contents of the user's `public/_headers`, appended after the
35
+ * generated rules. See `mergeUserHeaders` for why it goes last.
36
+ */
37
+ userRules?: string;
38
+ }
39
+ /**
40
+ * Count the rules in a `_headers` file — a rule is a path line, which is any
41
+ * line that is neither blank, a comment, nor an indented header.
42
+ *
43
+ * @internal Exported for testing.
44
+ */
45
+ export declare function countHeaderRules(content: string): number;
46
+ /** One `_headers` block: a path pattern and the headers it sets. */
47
+ export interface HeaderRule {
48
+ pattern: string;
49
+ headers: Array<{
50
+ name: string;
51
+ value: string;
52
+ }>;
53
+ }
54
+ /**
55
+ * Parse a `_headers` file. A non-indented, non-comment line opens a rule; the
56
+ * indented `Name: value` lines beneath it belong to that rule.
57
+ *
58
+ * @internal Exported for testing.
59
+ */
60
+ export declare function parseHeaderRules(content: string): HeaderRule[];
61
+ export declare function generateHeadersFile({ base, unhashedPaths, contentTypes, userRules, }: HeadersFileOptions): string;
6
62
  //# sourceMappingURL=shared.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../src/adapters/shared.ts"],"names":[],"mappings":"AAaA,yDAAyD;AACzD,eAAO,MAAM,eAAe,wCAAwC,CAAC;AAErE,sDAAsD;AACtD,eAAO,MAAM,YAAY,0CAA0C,CAAC;AAuBpE,wBAAgB,mBAAmB,CACjC,aAAa,CAAC,EAAE,MAAM,EAAE,EACxB,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GACpC,MAAM,CAuDR"}
1
+ {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../src/adapters/shared.ts"],"names":[],"mappings":"AAcA;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAE7D;AAID,yDAAyD;AACzD,eAAO,MAAM,eAAe,wCAAwC,CAAC;AAErE,sDAAsD;AACtD,eAAO,MAAM,YAAY,0CAA0C,CAAC;AAoCpE,MAAM,WAAW,kBAAkB;IACjC;;;;;;OAMG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,8EAA8E;IAC9E,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,4EAA4E;IAC5E,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAIxD;AAED,oEAAoE;AACpE,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACjD;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,EAAE,CAmB9D;AAmBD,wBAAgB,mBAAmB,CAAC,EAClC,IAAI,EACJ,aAAa,EACb,YAAY,EACZ,SAAS,GACV,EAAE,kBAAkB,GAAG,MAAM,CAkH7B"}
@@ -5,6 +5,18 @@
5
5
  export interface TimberConfig {
6
6
  /** Absolute path to the project root (Vite root). */
7
7
  root?: string;
8
+ /**
9
+ * The resolved deployment base, normalized by `normalizeBasePath()` —
10
+ * leading and trailing slash, `'/'` at the domain root.
11
+ *
12
+ * Adapters write platform files (`_headers`, `_redirects`) whose rules the
13
+ * host matches against the *request* path, so those rules live in deployed-
14
+ * URL space rather than route space and need this prefix (TIM-1274).
15
+ *
16
+ * Populated by `plugins/adapter-build.ts` from `ctx.base`, the single copy
17
+ * lifted in TIM-1261. Adapters must not re-derive it from `config.base`.
18
+ */
19
+ base?: string;
8
20
  output: 'server' | 'static';
9
21
  clientJavascriptDisabled?: boolean;
10
22
  /**
@@ -17,7 +29,7 @@ export interface TimberConfig {
17
29
  * Absolute path to the directory containing pre-rendered static HTML,
18
30
  * RSC flight files, and API route outputs. Present only when
19
31
  * output: 'static' and the static generation pass completed.
20
- * Layout: path/index.html, _rsc/path.rsc, api-route-path
32
+ * Layout: path/index.html, _rsc/path/index.rsc, api-route-path
21
33
  */
22
34
  staticPagesDir?: string;
23
35
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/adapters/types.ts"],"names":[],"mappings":"AAMA;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,qDAAqD;IACrD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC5B,wBAAwB,CAAC,EAAE,OAAO,CAAC;IACnC;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC7C;AAED;;;;;;GAMG;AACH,MAAM,WAAW,qBAAqB;IACpC,8DAA8D;IAC9D,IAAI,EAAE,MAAM,CAAC;IAEb;;;OAGG;IACH,WAAW,CAAC,MAAM,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEnE;;;OAGG;IACH,OAAO,CAAC,CAAC,MAAM,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEhE;;;;;;;;;OASG;IACH,cAAc,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;CACxC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/adapters/types.ts"],"names":[],"mappings":"AAMA;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,qDAAqD;IACrD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;;;;;;OAUG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC5B,wBAAwB,CAAC,EAAE,OAAO,CAAC;IACnC;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC7C;AAED;;;;;;GAMG;AACH,MAAM,WAAW,qBAAqB;IACpC,8DAA8D;IAC9D,IAAI,EAAE,MAAM,CAAC;IAEb;;;OAGG;IACH,WAAW,CAAC,MAAM,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEnE;;;OAGG;IACH,OAAO,CAAC,CAAC,MAAM,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEhE;;;;;;;;;OASG;IACH,cAAc,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;CACxC"}
@@ -9,6 +9,11 @@
9
9
  * Uses BigInt for 64-bit arithmetic — supported in all modern runtimes
10
10
  * including Cloudflare Workers. No node:crypto dependency.
11
11
  *
12
+ * Server-side only. The `_rsc` payload cache key deliberately does NOT use
13
+ * this — the origin compares that value, which makes it a check rather than
14
+ * a cache key, and FNV is solvable for a chosen digest. See
15
+ * `shared/rsc-cache-key.ts`.
16
+ *
12
17
  * See TIM-370.
13
18
  */
14
19
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"fast-hash.d.ts","sourceRoot":"","sources":["../../src/cache/fast-hash.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAOH;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAO/C"}
1
+ {"version":3,"file":"fast-hash.d.ts","sourceRoot":"","sources":["../../src/cache/fast-hash.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAOH;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAO/C"}
@@ -1,3 +1,3 @@
1
- import { _ as setDefaultSingleflightTimeout, f as MemoryCacheHandler, g as setCacheHandler, h as getCacheHandler, m as RedisCacheHandler, p as TagAwareCacheHandler, t as cache } from "../_chunks/cache-api-B-lhk9p4.js";
1
+ import { _ as setDefaultSingleflightTimeout, f as MemoryCacheHandler, g as setCacheHandler, h as getCacheHandler, m as RedisCacheHandler, p as TagAwareCacheHandler, t as cache } from "../_chunks/cache-api-Cd0VZ_Pd.js";
2
2
  import { t as estimateByteSize } from "../_chunks/sizeof-lXffQ1Vr.js";
3
3
  export { MemoryCacheHandler, RedisCacheHandler, TagAwareCacheHandler, cache, estimateByteSize, getCacheHandler, setCacheHandler, setDefaultSingleflightTimeout };
package/dist/cli.js CHANGED
@@ -79,7 +79,7 @@ function resolvePreviewStrategy(adapter) {
79
79
  * Vite preview. See TIM-1066.
80
80
  */
81
81
  async function runPreview(options, _deps) {
82
- const { loadTimberConfigFile, resolveBuildDir } = await import("./_chunks/plugin-context---kTF5v8.js").then((n) => n.i);
82
+ const { loadTimberConfigFile, resolveBuildDir } = await import("./_chunks/plugin-context-DEGLSJs3.js").then((n) => n.i);
83
83
  const root = process.cwd();
84
84
  const config = loadTimberConfigFile(root);
85
85
  const adapter = config?.adapter;
@@ -116,7 +116,7 @@ async function runCheck(options) {
116
116
  async function runSchema(args) {
117
117
  const subcommand = args[0];
118
118
  if (subcommand !== "sync") throw new Error(`Unknown schema subcommand: ${subcommand ?? "(none)"}. Usage: timber schema sync`);
119
- const { runSchemaSync, defaultCodecForSegment } = await import("./_chunks/cli-schema-sync-BTWEKJXo.js").then((n) => n.t);
119
+ const { runSchemaSync, defaultCodecForSegment } = await import("./_chunks/cli-schema-sync-CKgHC2MB.js").then((n) => n.t);
120
120
  const result = runSchemaSync(process.cwd());
121
121
  if (result.filesystemSegments.length === 0) {
122
122
  console.log("[timber] No dynamic segments found in app/.");
@@ -1 +1 @@
1
- {"version":3,"file":"action-dispatch.d.ts","sourceRoot":"","sources":["../../../src/client/browser-entry/action-dispatch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAMH,wBAAgB,kBAAkB,IAAI,IAAI,CAuGzC"}
1
+ {"version":3,"file":"action-dispatch.d.ts","sourceRoot":"","sources":["../../../src/client/browser-entry/action-dispatch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAOH,wBAAgB,kBAAkB,IAAI,IAAI,CA2GzC"}
@@ -6,13 +6,17 @@
6
6
  * 2. No RSC payload: deferred root creation via installDeferredNavigation
7
7
  *
8
8
  * Pre-hydration ordering contract (MUST execute in this order):
9
- * 1. initRouter() — creates the global router so useRouter()
10
- * works during render
11
- * 2. setCurrentParams() — populates params snapshot so
12
- * + setNavigationState() useSegmentParams() and usePathname()
13
- * return correct values during hydration
14
- * 3. hydrateRoot() — synchronously executes component render
15
- * functions that depend on steps 1-2
9
+ * 1. initRouter() — creates the global router so useRouter()
10
+ * works during render
11
+ * 2. setNavigationState() — populates pathname/search so usePathname()
12
+ * returns correct values during hydration
13
+ * 3. hydrateRoot() — synchronously executes component render
14
+ * functions that depend on steps 1-2
15
+ *
16
+ * Params are not part of this sequence. They arrive on the payload root and
17
+ * are published by `PayloadRoot` at the top of the tree, so
18
+ * `useSegmentParams()` is correct during hydration without anything having to
19
+ * run before it (TIM-1294, TIM-1297).
16
20
  *
17
21
  * See design/19-client-navigation.md §"NavigationContext"
18
22
  */
@@ -27,8 +31,15 @@ interface HydrateOptions {
27
31
  };
28
32
  }
29
33
  /**
30
- * Run the pre-hydration sequence: read server-embedded params and
31
- * set navigation state. Must be called AFTER initRouter().
34
+ * Run the pre-hydration sequence: set navigation state from the address bar.
35
+ * Must be called AFTER initRouter().
36
+ *
37
+ * Params are no longer seeded here. They used to arrive on two inline scripts
38
+ * that had to execute before `hydrateRoot()`, because hydration synchronously
39
+ * renders components that read them. They now ride the payload root, and the
40
+ * provider that publishes them renders before its own descendants — so the
41
+ * ordering the step existed to guarantee is now a property of the tree rather
42
+ * than of the bootstrap sequence (TIM-1294, TIM-1297).
32
43
  */
33
44
  export declare function runPreHydration(): void;
34
45
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"hydrate.d.ts","sourceRoot":"","sources":["../../../src/client/browser-entry/hydrate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAcH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAExD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAwCvD,UAAU,cAAc;IACtB,qDAAqD;IACrD,SAAS,EAAE,eAAe,GAAG,IAAI,CAAC;IAClC,gDAAgD;IAChD,MAAM,EAAE;QAAE,SAAS,CAAC,EAAE,eAAe,CAAA;KAAE,CAAC;CACzC;AAED;;;GAGG;AACH,wBAAgB,eAAe,IAAI,IAAI,CAiBtC;AAED;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,cAAc,GAAG,IAAI,CAyEtE"}
1
+ {"version":3,"file":"hydrate.d.ts","sourceRoot":"","sources":["../../../src/client/browser-entry/hydrate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAeH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAExD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAwCvD,UAAU,cAAc;IACtB,qDAAqD;IACrD,SAAS,EAAE,eAAe,GAAG,IAAI,CAAC;IAClC,gDAAgD;IAChD,MAAM,EAAE;QAAE,SAAS,CAAC,EAAE,eAAe,CAAA;KAAE,CAAC;CACzC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,IAAI,IAAI,CAKtC;AAED;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,cAAc,GAAG,IAAI,CAoFtE"}
@@ -12,15 +12,18 @@
12
12
  */
13
13
  import type { RouterInstance } from '#client-internal';
14
14
  import type { NavigationApiController } from '../navigation-api.js';
15
+ import type { ParamsSource } from '../../shared/payload-root.js';
15
16
  interface PostHydrationOptions {
16
17
  router: RouterInstance;
17
18
  navApiController: NavigationApiController | null;
18
19
  /** Decoded RSC element from initial SSR (null if no RSC payload) */
19
20
  initialElement: unknown;
21
+ /** The params that payload published, from the same decode. */
22
+ initialParams: ParamsSource;
20
23
  }
21
24
  /**
22
25
  * Wire up post-hydration event handlers and state.
23
26
  */
24
- export declare function setupPostHydration({ router, navApiController, initialElement, }: PostHydrationOptions): void;
27
+ export declare function setupPostHydration({ router, navApiController, initialElement, initialParams, }: PostHydrationOptions): void;
25
28
  export {};
26
29
  //# sourceMappingURL=post-hydration.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"post-hydration.d.ts","sourceRoot":"","sources":["../../../src/client/browser-entry/post-hydration.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAGpE,UAAU,oBAAoB;IAC5B,MAAM,EAAE,cAAc,CAAC;IACvB,gBAAgB,EAAE,uBAAuB,GAAG,IAAI,CAAC;IACjD,oEAAoE;IACpE,cAAc,EAAE,OAAO,CAAC;CACzB;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,MAAM,EACN,gBAAgB,EAChB,cAAc,GACf,EAAE,oBAAoB,GAAG,IAAI,CAwF7B"}
1
+ {"version":3,"file":"post-hydration.d.ts","sourceRoot":"","sources":["../../../src/client/browser-entry/post-hydration.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAEpE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAEjE,UAAU,oBAAoB;IAC5B,MAAM,EAAE,cAAc,CAAC;IACvB,gBAAgB,EAAE,uBAAuB,GAAG,IAAI,CAAC;IACjD,oEAAoE;IACpE,cAAc,EAAE,OAAO,CAAC;IACxB,+DAA+D;IAC/D,aAAa,EAAE,YAAY,CAAC;CAC7B;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,MAAM,EACN,gBAAgB,EAChB,cAAc,EACd,aAAa,GACd,EAAE,oBAAoB,GAAG,IAAI,CAqF7B"}
@@ -6,10 +6,13 @@
6
6
  *
7
7
  * See design/19-client-navigation.md §"Navigation API Integration"
8
8
  */
9
+ import { type ParamsSource } from '../../shared/payload-root.js';
9
10
  import type { RouterInstance } from '#client-internal';
10
11
  import { type NavigationApiController } from '../navigation-api.js';
11
12
  export interface RouterInitOptions {
12
13
  initialElement?: unknown;
14
+ /** The params the hydration payload published, beside `initialElement`. */
15
+ initialParams?: ParamsSource;
13
16
  clientSegmentCache?: boolean;
14
17
  }
15
18
  export interface RouterInitResult {
@@ -1 +1 @@
1
- {"version":3,"file":"router-init.d.ts","sourceRoot":"","sources":["../../../src/client/browser-entry/router-init.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,OAAO,KAAK,EAAc,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAUnE,OAAO,EAGL,KAAK,uBAAuB,EAC7B,MAAM,sBAAsB,CAAC;AAG9B,MAAM,WAAW,iBAAiB;IAChC,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,cAAc,CAAC;IACvB,gBAAgB,EAAE,uBAAuB,GAAG,IAAI,CAAC;CAClD;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,gBAAgB,CA+OhF"}
1
+ {"version":3,"file":"router-init.d.ts","sourceRoot":"","sources":["../../../src/client/browser-entry/router-init.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,OAAO,EAAuB,KAAK,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAEtF,OAAO,KAAK,EAAc,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAUnE,OAAO,EAGL,KAAK,uBAAuB,EAC7B,MAAM,sBAAsB,CAAC;AAG9B,MAAM,WAAW,iBAAiB;IAChC,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,2EAA2E;IAC3E,aAAa,CAAC,EAAE,YAAY,CAAC;IAC7B,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,cAAc,CAAC;IACvB,gBAAgB,EAAE,uBAAuB,GAAG,IAAI,CAAC;CAClD;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,gBAAgB,CAmShF"}
@@ -10,9 +10,17 @@
10
10
  *
11
11
  * See design/18-build-system.md §"Entry Files"
12
12
  */
13
+ import { type PublishedParams } from '../../shared/payload-root.js';
13
14
  export interface RscStreamResult {
14
15
  /** The decoded RSC element (thenable/lazy — resolved by React during render) */
15
16
  element: unknown;
17
+ /**
18
+ * The params the same payload published beside that tree. A promise: the
19
+ * inlined Flight stream is still arriving when this runs, and hydration must
20
+ * not wait for it. `PayloadRoot` suspends on it at the position React was
21
+ * going to suspend on `element` anyway (TIM-1297).
22
+ */
23
+ params: Promise<PublishedParams>;
16
24
  }
17
25
  /**
18
26
  * Create the RSC payload stream from server-inlined `__timber_f` chunks.
@@ -1 +1 @@
1
- {"version":3,"file":"rsc-stream.d.ts","sourceRoot":"","sources":["../../../src/client/browser-entry/rsc-stream.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAOH,MAAM,WAAW,eAAe;IAC9B,gFAAgF;IAChF,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,IAAI,eAAe,GAAG,IAAI,CA+H/D"}
1
+ {"version":3,"file":"rsc-stream.d.ts","sourceRoot":"","sources":["../../../src/client/browser-entry/rsc-stream.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,EAAoB,KAAK,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAItF,MAAM,WAAW,eAAe;IAC9B,gFAAgF;IAChF,OAAO,EAAE,OAAO,CAAC;IACjB;;;;;OAKG;IACH,MAAM,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC;CAClC;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,IAAI,eAAe,GAAG,IAAI,CAgI/D"}
@@ -47,6 +47,7 @@ interface TimberErrorBoundaryState {
47
47
  export declare class TimberErrorBoundary extends Component<TimberErrorBoundaryProps, TimberErrorBoundaryState> {
48
48
  constructor(props: TimberErrorBoundaryProps);
49
49
  static getDerivedStateFromError(error: Error): TimberErrorBoundaryState;
50
+ componentDidCatch(error: Error): void;
50
51
  componentDidUpdate(prevProps: TimberErrorBoundaryProps): void;
51
52
  /** Reset the error state so children re-render. */
52
53
  private reset;
@@ -1 +1 @@
1
- {"version":3,"file":"error-boundary.d.ts","sourceRoot":"","sources":["../../src/client/error-boundary.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,SAAS,EAAiB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AA8CjE,MAAM,WAAW,wBAAwB;IACvC,uDAAuD;IACvD,iBAAiB,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,SAAS,CAAC;IACtD;;;;;;;OAOG;IACH,eAAe,CAAC,EAAE,SAAS,CAAC;IAC5B;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,UAAU,wBAAwB;IAChC,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CACrB;AAkBD,qBAAa,mBAAoB,SAAQ,SAAS,CAChD,wBAAwB,EACxB,wBAAwB,CACzB;IACC,YAAY,KAAK,EAAE,wBAAwB,EAG1C;IAED,MAAM,CAAC,wBAAwB,CAAC,KAAK,EAAE,KAAK,GAAG,wBAAwB,CAUtE;IAED,kBAAkB,CAAC,SAAS,EAAE,wBAAwB,GAAG,IAAI,CAO5D;IAED,mDAAmD;IACnD,OAAO,CAAC,KAAK,CAEX;IAEF,MAAM,IAAI,SAAS,CA2IlB;CACF"}
1
+ {"version":3,"file":"error-boundary.d.ts","sourceRoot":"","sources":["../../src/client/error-boundary.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,SAAS,EAAiB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAsCjE,MAAM,WAAW,wBAAwB;IACvC,uDAAuD;IACvD,iBAAiB,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,SAAS,CAAC;IACtD;;;;;;;OAOG;IACH,eAAe,CAAC,EAAE,SAAS,CAAC;IAC5B;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,UAAU,wBAAwB;IAChC,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CACrB;AAkBD,qBAAa,mBAAoB,SAAQ,SAAS,CAChD,wBAAwB,EACxB,wBAAwB,CACzB;IACC,YAAY,KAAK,EAAE,wBAAwB,EAG1C;IAED,MAAM,CAAC,wBAAwB,CAAC,KAAK,EAAE,KAAK,GAAG,wBAAwB,CAUtE;IAED,iBAAiB,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAWpC;IAED,kBAAkB,CAAC,SAAS,EAAE,wBAAwB,GAAG,IAAI,CAO5D;IAED,mDAAmD;IACnD,OAAO,CAAC,KAAK,CAEX;IAEF,MAAM,IAAI,SAAS,CA2IlB;CACF"}