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

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 (570) hide show
  1. package/dist/_chunks/{actions-O_LsyCE4.js → actions-35jnMdeJ.js} +4 -3
  2. package/dist/_chunks/{actions-O_LsyCE4.js.map → actions-35jnMdeJ.js.map} +1 -1
  3. package/dist/_chunks/als-registry-C6kcfprT.js +41 -0
  4. package/dist/_chunks/als-registry-C6kcfprT.js.map +1 -0
  5. package/dist/_chunks/als-slots-BEEIPKYm.js +26 -0
  6. package/dist/_chunks/als-slots-BEEIPKYm.js.map +1 -0
  7. package/dist/_chunks/base-path-DaQrzbez.js +80 -0
  8. package/dist/_chunks/base-path-DaQrzbez.js.map +1 -0
  9. package/dist/_chunks/build-manifest-DWppEdLB.js +86 -0
  10. package/dist/_chunks/build-manifest-DWppEdLB.js.map +1 -0
  11. package/dist/_chunks/build-output-helper-C3DrfzZR.js +369 -0
  12. package/dist/_chunks/build-output-helper-C3DrfzZR.js.map +1 -0
  13. package/dist/_chunks/{cache-api-B-lhk9p4.js → cache-api-DjNrIWRR.js} +8 -14
  14. package/dist/_chunks/cache-api-DjNrIWRR.js.map +1 -0
  15. package/dist/_chunks/cli-check-CpmN7Nh-.js +256 -0
  16. package/dist/_chunks/cli-check-CpmN7Nh-.js.map +1 -0
  17. package/dist/_chunks/cli-schema-sync-CGMp_Psg.js +298 -0
  18. package/dist/_chunks/cli-schema-sync-CGMp_Psg.js.map +1 -0
  19. package/dist/_chunks/{cloudflare-CnT5Lr7U.js → cloudflare-CGP6BZKO.js} +92 -14
  20. package/dist/_chunks/{cloudflare-CnT5Lr7U.js.map → cloudflare-CGP6BZKO.js.map} +1 -1
  21. package/dist/_chunks/convention-lint-kXsgc_-7.js +784 -0
  22. package/dist/_chunks/convention-lint-kXsgc_-7.js.map +1 -0
  23. package/dist/_chunks/error-boundary-D-ODYX41.js +353 -0
  24. package/dist/_chunks/error-boundary-D-ODYX41.js.map +1 -0
  25. package/dist/_chunks/{fast-hash-D6hIVt1Y.js → fast-hash-C-tVYrLb.js} +6 -1
  26. package/dist/_chunks/fast-hash-C-tVYrLb.js.map +1 -0
  27. package/dist/_chunks/file-cache-DmX7OqZP.js +454 -0
  28. package/dist/_chunks/file-cache-DmX7OqZP.js.map +1 -0
  29. package/dist/_chunks/fs-identity-D7vb6GdI.js +208 -0
  30. package/dist/_chunks/fs-identity-D7vb6GdI.js.map +1 -0
  31. package/dist/_chunks/{logger-AWfuX-KJ.js → logger-D8xJZXIN.js} +34 -51
  32. package/dist/_chunks/logger-D8xJZXIN.js.map +1 -0
  33. package/dist/_chunks/mdx-file-C005ay-P.js +25 -0
  34. package/dist/_chunks/mdx-file-C005ay-P.js.map +1 -0
  35. package/dist/_chunks/{plugin-context---kTF5v8.js → plugin-context-rCinWLiE.js} +8 -2
  36. package/dist/_chunks/plugin-context-rCinWLiE.js.map +1 -0
  37. package/dist/_chunks/purge-store-Byr8XOjU.js +14 -0
  38. package/dist/_chunks/purge-store-Byr8XOjU.js.map +1 -0
  39. package/dist/_chunks/{resolve-schema-Dz3fcFUo.js → resolve-schema-5ma5pp1b.js} +2 -2
  40. package/dist/_chunks/{resolve-schema-Dz3fcFUo.js.map → resolve-schema-5ma5pp1b.js.map} +1 -1
  41. package/dist/_chunks/{router-ref-CtmF-aPv.js → router-ref-DuYuV_0Q.js} +2 -2
  42. package/dist/_chunks/router-ref-DuYuV_0Q.js.map +1 -0
  43. package/dist/_chunks/rsc-cache-key-DD0fl_-s.js +243 -0
  44. package/dist/_chunks/rsc-cache-key-DD0fl_-s.js.map +1 -0
  45. package/dist/_chunks/rsc-error-envelope-tT5PJs4q.js +40 -0
  46. package/dist/_chunks/rsc-error-envelope-tT5PJs4q.js.map +1 -0
  47. package/dist/_chunks/rsc-media-type-DRqE_lD_.js +46 -0
  48. package/dist/_chunks/rsc-media-type-DRqE_lD_.js.map +1 -0
  49. package/dist/_chunks/rsc-payload-path-B_LBodc2.js +48 -0
  50. package/dist/_chunks/rsc-payload-path-B_LBodc2.js.map +1 -0
  51. package/dist/_chunks/scanner-C8b0Gcw3.js +1564 -0
  52. package/dist/_chunks/scanner-C8b0Gcw3.js.map +1 -0
  53. package/dist/_chunks/schema-bridge-Cc2Gngu1.js +199 -0
  54. package/dist/_chunks/schema-bridge-Cc2Gngu1.js.map +1 -0
  55. package/dist/_chunks/{segment-classify-Byy425ng.js → segment-classify-C539Pa2O.js} +23 -2
  56. package/dist/_chunks/segment-classify-C539Pa2O.js.map +1 -0
  57. package/dist/_chunks/segment-context-CjOlyB8Y.js +97 -0
  58. package/dist/_chunks/segment-context-CjOlyB8Y.js.map +1 -0
  59. package/dist/_chunks/{canonicalize-DQHyFClh.js → segment-keys-BawYuNFO.js} +61 -2
  60. package/dist/_chunks/segment-keys-BawYuNFO.js.map +1 -0
  61. package/dist/_chunks/slot-params-BCTmZkQB.js +76 -0
  62. package/dist/_chunks/slot-params-BCTmZkQB.js.map +1 -0
  63. package/dist/_chunks/{ssr-data-BOWsq18U.js → ssr-data-14MXm7Pj.js} +17 -2
  64. package/dist/_chunks/ssr-data-14MXm7Pj.js.map +1 -0
  65. package/dist/_chunks/{use-query-states-DFvWd-EA.js → use-query-states-BbU5Ge1V.js} +74 -26
  66. package/dist/_chunks/use-query-states-BbU5Ge1V.js.map +1 -0
  67. package/dist/_chunks/use-segment-params-C4r4BD9T.js +398 -0
  68. package/dist/_chunks/use-segment-params-C4r4BD9T.js.map +1 -0
  69. package/dist/_chunks/walkers-RzN6AFjr.js +141 -0
  70. package/dist/_chunks/walkers-RzN6AFjr.js.map +1 -0
  71. package/dist/adapters/build-output-helper.d.ts +37 -0
  72. package/dist/adapters/build-output-helper.d.ts.map +1 -1
  73. package/dist/adapters/cloudflare-dev.js +1 -1
  74. package/dist/adapters/cloudflare-kv-cache.js +1 -1
  75. package/dist/adapters/cloudflare.d.ts.map +1 -1
  76. package/dist/adapters/cloudflare.js +1 -1
  77. package/dist/adapters/compress-module.d.ts +12 -0
  78. package/dist/adapters/compress-module.d.ts.map +1 -1
  79. package/dist/adapters/fs-identity.d.ts +64 -0
  80. package/dist/adapters/fs-identity.d.ts.map +1 -0
  81. package/dist/adapters/nitro.d.ts.map +1 -1
  82. package/dist/adapters/nitro.js +56 -5
  83. package/dist/adapters/nitro.js.map +1 -1
  84. package/dist/adapters/shared.d.ts +57 -1
  85. package/dist/adapters/shared.d.ts.map +1 -1
  86. package/dist/adapters/types.d.ts +13 -1
  87. package/dist/adapters/types.d.ts.map +1 -1
  88. package/dist/cache/fast-hash.d.ts +5 -0
  89. package/dist/cache/fast-hash.d.ts.map +1 -1
  90. package/dist/cache/index.js +1 -1
  91. package/dist/cdn/cloudflare-purge.js +30 -0
  92. package/dist/cdn/cloudflare-purge.js.map +1 -0
  93. package/dist/cdn/fastly-purge.js +33 -0
  94. package/dist/cdn/fastly-purge.js.map +1 -0
  95. package/dist/cdn/index.js +94 -0
  96. package/dist/cdn/index.js.map +1 -0
  97. package/dist/cdn/workers-cache-purge.js +35 -0
  98. package/dist/cdn/workers-cache-purge.js.map +1 -0
  99. package/dist/cli-check.d.ts +153 -0
  100. package/dist/cli-check.d.ts.map +1 -0
  101. package/dist/cli.d.ts +34 -8
  102. package/dist/cli.d.ts.map +1 -1
  103. package/dist/cli.js +46 -21
  104. package/dist/cli.js.map +1 -1
  105. package/dist/client/browser-entry/action-dispatch.d.ts.map +1 -1
  106. package/dist/client/browser-entry/hydrate.d.ts +20 -9
  107. package/dist/client/browser-entry/hydrate.d.ts.map +1 -1
  108. package/dist/client/browser-entry/index.d.ts +1 -1
  109. package/dist/client/browser-entry/index.d.ts.map +1 -1
  110. package/dist/client/browser-entry/post-hydration.d.ts +4 -1
  111. package/dist/client/browser-entry/post-hydration.d.ts.map +1 -1
  112. package/dist/client/browser-entry/router-init.d.ts +3 -0
  113. package/dist/client/browser-entry/router-init.d.ts.map +1 -1
  114. package/dist/client/browser-entry/rsc-stream.d.ts +8 -0
  115. package/dist/client/browser-entry/rsc-stream.d.ts.map +1 -1
  116. package/dist/client/error-boundary.d.ts +7 -0
  117. package/dist/client/error-boundary.d.ts.map +1 -1
  118. package/dist/client/error-boundary.js +1 -162
  119. package/dist/client/history.d.ts +13 -6
  120. package/dist/client/history.d.ts.map +1 -1
  121. package/dist/client/index.d.ts +1 -0
  122. package/dist/client/index.d.ts.map +1 -1
  123. package/dist/client/index.js +42 -41
  124. package/dist/client/index.js.map +1 -1
  125. package/dist/client/internal.d.ts +4 -3
  126. package/dist/client/internal.d.ts.map +1 -1
  127. package/dist/client/internal.js +877 -311
  128. package/dist/client/internal.js.map +1 -1
  129. package/dist/client/link.d.ts +1 -7
  130. package/dist/client/link.d.ts.map +1 -1
  131. package/dist/client/navigation-api.d.ts +2 -2
  132. package/dist/client/navigation-commit.d.ts +87 -0
  133. package/dist/client/navigation-commit.d.ts.map +1 -0
  134. package/dist/client/navigation-context.d.ts +37 -20
  135. package/dist/client/navigation-context.d.ts.map +1 -1
  136. package/dist/client/navigation-root.d.ts +63 -23
  137. package/dist/client/navigation-root.d.ts.map +1 -1
  138. package/dist/client/params-context.d.ts +81 -0
  139. package/dist/client/params-context.d.ts.map +1 -0
  140. package/dist/client/router-effects.d.ts +129 -0
  141. package/dist/client/router-effects.d.ts.map +1 -0
  142. package/dist/client/router-lifecycle.d.ts +55 -0
  143. package/dist/client/router-lifecycle.d.ts.map +1 -0
  144. package/dist/client/router-pipeline.d.ts +84 -0
  145. package/dist/client/router-pipeline.d.ts.map +1 -0
  146. package/dist/client/router-ref.d.ts +1 -1
  147. package/dist/client/router-ref.d.ts.map +1 -1
  148. package/dist/client/router-skew.d.ts +21 -0
  149. package/dist/client/router-skew.d.ts.map +1 -0
  150. package/dist/client/router-types.d.ts +207 -0
  151. package/dist/client/router-types.d.ts.map +1 -0
  152. package/dist/client/router.d.ts +1 -182
  153. package/dist/client/router.d.ts.map +1 -1
  154. package/dist/client/rsc-fetch.d.ts +47 -26
  155. package/dist/client/rsc-fetch.d.ts.map +1 -1
  156. package/dist/client/segment-cache.d.ts +87 -34
  157. package/dist/client/segment-cache.d.ts.map +1 -1
  158. package/dist/client/ssr-data.d.ts +6 -0
  159. package/dist/client/ssr-data.d.ts.map +1 -1
  160. package/dist/client/stale-client.d.ts +69 -0
  161. package/dist/client/stale-client.d.ts.map +1 -0
  162. package/dist/client/state.d.ts +10 -1
  163. package/dist/client/state.d.ts.map +1 -1
  164. package/dist/client/top-loader.d.ts +2 -1
  165. package/dist/client/top-loader.d.ts.map +1 -1
  166. package/dist/client/unload-guard.d.ts +5 -0
  167. package/dist/client/unload-guard.d.ts.map +1 -1
  168. package/dist/client/use-cookie.d.ts.map +1 -1
  169. package/dist/client/use-query-states.d.ts +9 -3
  170. package/dist/client/use-query-states.d.ts.map +1 -1
  171. package/dist/client/use-router.d.ts +1 -1
  172. package/dist/client/use-segment-params.d.ts +18 -8
  173. package/dist/client/use-segment-params.d.ts.map +1 -1
  174. package/dist/codec.js +1 -1
  175. package/dist/cookies/define-cookie.d.ts.map +1 -1
  176. package/dist/cookies/index.js +5 -5
  177. package/dist/cookies/index.js.map +1 -1
  178. package/dist/index.d.ts +4 -1
  179. package/dist/index.d.ts.map +1 -1
  180. package/dist/index.js +1009 -759
  181. package/dist/index.js.map +1 -1
  182. package/dist/params/index.js +1 -1
  183. package/dist/plugin-context.d.ts +46 -0
  184. package/dist/plugin-context.d.ts.map +1 -1
  185. package/dist/plugins/adapter-build.d.ts.map +1 -1
  186. package/dist/plugins/build-manifest.d.ts +1 -1
  187. package/dist/plugins/build-manifest.d.ts.map +1 -1
  188. package/dist/plugins/entries.d.ts.map +1 -1
  189. package/dist/plugins/fonts.d.ts.map +1 -1
  190. package/dist/plugins/request-dep/analysis.d.ts +106 -0
  191. package/dist/plugins/request-dep/analysis.d.ts.map +1 -0
  192. package/dist/plugins/request-dep/index.d.ts +75 -0
  193. package/dist/plugins/request-dep/index.d.ts.map +1 -0
  194. package/dist/plugins/request-dep/origin.d.ts +61 -0
  195. package/dist/plugins/request-dep/origin.d.ts.map +1 -0
  196. package/dist/plugins/routing.d.ts.map +1 -1
  197. package/dist/plugins/shims.d.ts.map +1 -1
  198. package/dist/plugins/static-build.d.ts +160 -8
  199. package/dist/plugins/static-build.d.ts.map +1 -1
  200. package/dist/routing/codegen-shared.d.ts +3 -44
  201. package/dist/routing/codegen-shared.d.ts.map +1 -1
  202. package/dist/routing/codegen-types.d.ts +28 -31
  203. package/dist/routing/codegen-types.d.ts.map +1 -1
  204. package/dist/routing/codegen-write.d.ts +51 -0
  205. package/dist/routing/codegen-write.d.ts.map +1 -0
  206. package/dist/routing/codegen.d.ts.map +1 -1
  207. package/dist/routing/collision-probe.d.ts +104 -0
  208. package/dist/routing/collision-probe.d.ts.map +1 -0
  209. package/dist/routing/collision-spaces.d.ts +26 -0
  210. package/dist/routing/collision-spaces.d.ts.map +1 -0
  211. package/dist/routing/convention-lint.d.ts +18 -4
  212. package/dist/routing/convention-lint.d.ts.map +1 -1
  213. package/dist/routing/export-detect.d.ts +16 -0
  214. package/dist/routing/export-detect.d.ts.map +1 -1
  215. package/dist/routing/index.js +4 -3
  216. package/dist/routing/interception.d.ts +235 -6
  217. package/dist/routing/interception.d.ts.map +1 -1
  218. package/dist/routing/link-codegen.d.ts +19 -4
  219. package/dist/routing/link-codegen.d.ts.map +1 -1
  220. package/dist/routing/manifest-codegen.d.ts +1 -7
  221. package/dist/routing/manifest-codegen.d.ts.map +1 -1
  222. package/dist/routing/scanner.d.ts.map +1 -1
  223. package/dist/routing/segment-classify.d.ts +33 -0
  224. package/dist/routing/segment-classify.d.ts.map +1 -1
  225. package/dist/routing/segment-keys.d.ts +95 -0
  226. package/dist/routing/segment-keys.d.ts.map +1 -0
  227. package/dist/routing/slot-placement.d.ts +48 -0
  228. package/dist/routing/slot-placement.d.ts.map +1 -0
  229. package/dist/routing/types.d.ts +0 -6
  230. package/dist/routing/types.d.ts.map +1 -1
  231. package/dist/routing/walkers.d.ts +6 -0
  232. package/dist/routing/walkers.d.ts.map +1 -1
  233. package/dist/schema-bridge.d.ts +60 -9
  234. package/dist/schema-bridge.d.ts.map +1 -1
  235. package/dist/search-params/define.d.ts +62 -14
  236. package/dist/search-params/define.d.ts.map +1 -1
  237. package/dist/search-params/index.d.ts +0 -1
  238. package/dist/search-params/index.d.ts.map +1 -1
  239. package/dist/search-params/index.js +221 -10
  240. package/dist/search-params/index.js.map +1 -1
  241. package/dist/search-params/parse-total.d.ts +70 -0
  242. package/dist/search-params/parse-total.d.ts.map +1 -0
  243. package/dist/search-params/wrappers.d.ts +26 -3
  244. package/dist/search-params/wrappers.d.ts.map +1 -1
  245. package/dist/server/access-gate.d.ts +19 -8
  246. package/dist/server/access-gate.d.ts.map +1 -1
  247. package/dist/server/action-handler.d.ts.map +1 -1
  248. package/dist/server/actions.d.ts +6 -3
  249. package/dist/server/actions.d.ts.map +1 -1
  250. package/dist/server/als-registry.d.ts +18 -1
  251. package/dist/server/als-registry.d.ts.map +1 -1
  252. package/dist/server/build-manifest.d.ts +13 -1
  253. package/dist/server/build-manifest.d.ts.map +1 -1
  254. package/dist/server/chain-url-parts.d.ts +82 -0
  255. package/dist/server/chain-url-parts.d.ts.map +1 -0
  256. package/dist/server/children-interception.d.ts +49 -0
  257. package/dist/server/children-interception.d.ts.map +1 -0
  258. package/dist/server/compress.d.ts.map +1 -1
  259. package/dist/server/default-status-page.d.ts +45 -0
  260. package/dist/server/default-status-page.d.ts.map +1 -0
  261. package/dist/server/deny-boundary.d.ts +148 -15
  262. package/dist/server/deny-boundary.d.ts.map +1 -1
  263. package/dist/server/deny-renderer.d.ts +15 -4
  264. package/dist/server/deny-renderer.d.ts.map +1 -1
  265. package/dist/server/error-boundary-wrapper.d.ts +85 -15
  266. package/dist/server/error-boundary-wrapper.d.ts.map +1 -1
  267. package/dist/server/head-response.d.ts +35 -0
  268. package/dist/server/head-response.d.ts.map +1 -0
  269. package/dist/server/index.d.ts +0 -1
  270. package/dist/server/index.d.ts.map +1 -1
  271. package/dist/server/index.js +3 -2
  272. package/dist/server/index.js.map +1 -1
  273. package/dist/server/internal.d.ts +3 -1
  274. package/dist/server/internal.d.ts.map +1 -1
  275. package/dist/server/internal.js +712 -418
  276. package/dist/server/internal.js.map +1 -1
  277. package/dist/server/metadata-collector.d.ts +52 -0
  278. package/dist/server/metadata-collector.d.ts.map +1 -0
  279. package/dist/server/param-coercion.d.ts +22 -6
  280. package/dist/server/param-coercion.d.ts.map +1 -1
  281. package/dist/server/pipeline-helpers.d.ts +0 -18
  282. package/dist/server/pipeline-helpers.d.ts.map +1 -1
  283. package/dist/server/pipeline-interception.d.ts +59 -9
  284. package/dist/server/pipeline-interception.d.ts.map +1 -1
  285. package/dist/server/pipeline-outcome.d.ts.map +1 -1
  286. package/dist/server/pipeline-phases.d.ts.map +1 -1
  287. package/dist/server/pipeline.d.ts +11 -0
  288. package/dist/server/pipeline.d.ts.map +1 -1
  289. package/dist/server/primitives.d.ts +23 -0
  290. package/dist/server/primitives.d.ts.map +1 -1
  291. package/dist/server/publish-params.d.ts +33 -0
  292. package/dist/server/publish-params.d.ts.map +1 -0
  293. package/dist/server/request-context.d.ts +48 -0
  294. package/dist/server/request-context.d.ts.map +1 -1
  295. package/dist/server/route-element-builder.d.ts +22 -3
  296. package/dist/server/route-element-builder.d.ts.map +1 -1
  297. package/dist/server/route-handler.d.ts.map +1 -1
  298. package/dist/server/route-matcher.d.ts +13 -0
  299. package/dist/server/route-matcher.d.ts.map +1 -1
  300. package/dist/server/rsc-cache-key-guard.d.ts +78 -0
  301. package/dist/server/rsc-cache-key-guard.d.ts.map +1 -0
  302. package/dist/server/rsc-entry/api-handler.d.ts.map +1 -1
  303. package/dist/server/rsc-entry/deny-fallback.d.ts.map +1 -1
  304. package/dist/server/rsc-entry/error-renderer.d.ts +15 -3
  305. package/dist/server/rsc-entry/error-renderer.d.ts.map +1 -1
  306. package/dist/server/rsc-entry/helpers.d.ts +2 -17
  307. package/dist/server/rsc-entry/helpers.d.ts.map +1 -1
  308. package/dist/server/rsc-entry/index.d.ts +0 -1
  309. package/dist/server/rsc-entry/index.d.ts.map +1 -1
  310. package/dist/server/rsc-entry/render-route.d.ts.map +1 -1
  311. package/dist/server/rsc-entry/revalidate-renderer.d.ts.map +1 -1
  312. package/dist/server/rsc-entry/rsc-payload.d.ts +1 -2
  313. package/dist/server/rsc-entry/rsc-payload.d.ts.map +1 -1
  314. package/dist/server/rsc-entry/rsc-stream.d.ts +15 -2
  315. package/dist/server/rsc-entry/rsc-stream.d.ts.map +1 -1
  316. package/dist/server/rsc-entry/ssr-renderer.d.ts.map +1 -1
  317. package/dist/server/skippable-prefix.d.ts +23 -0
  318. package/dist/server/skippable-prefix.d.ts.map +1 -0
  319. package/dist/server/slot-interception.d.ts +80 -0
  320. package/dist/server/slot-interception.d.ts.map +1 -0
  321. package/dist/server/slot-resolver.d.ts +70 -8
  322. package/dist/server/slot-resolver.d.ts.map +1 -1
  323. package/dist/server/ssr-bridge-types.d.ts +17 -0
  324. package/dist/server/ssr-bridge-types.d.ts.map +1 -1
  325. package/dist/server/ssr-entry.d.ts +0 -1
  326. package/dist/server/ssr-entry.d.ts.map +1 -1
  327. package/dist/server/ssr-wrappers.d.ts +24 -13
  328. package/dist/server/ssr-wrappers.d.ts.map +1 -1
  329. package/dist/server/state-tree-diff.d.ts +6 -27
  330. package/dist/server/state-tree-diff.d.ts.map +1 -1
  331. package/dist/server/static-generator.d.ts +6 -0
  332. package/dist/server/static-generator.d.ts.map +1 -1
  333. package/dist/server/static-not-found.d.ts +52 -0
  334. package/dist/server/static-not-found.d.ts.map +1 -0
  335. package/dist/server/status-code-resolver.d.ts +8 -1
  336. package/dist/server/status-code-resolver.d.ts.map +1 -1
  337. package/dist/server/tree-builder.d.ts +28 -36
  338. package/dist/server/tree-builder.d.ts.map +1 -1
  339. package/dist/server/types.d.ts +12 -7
  340. package/dist/server/types.d.ts.map +1 -1
  341. package/dist/server/utils/element-type.d.ts +40 -0
  342. package/dist/server/utils/element-type.d.ts.map +1 -0
  343. package/dist/shared/als-slots.d.ts +41 -0
  344. package/dist/shared/als-slots.d.ts.map +1 -0
  345. package/dist/shared/base-path.d.ts +56 -0
  346. package/dist/shared/base-path.d.ts.map +1 -0
  347. package/dist/shared/param-value.d.ts +80 -0
  348. package/dist/shared/param-value.d.ts.map +1 -0
  349. package/dist/shared/payload-root.d.ts +108 -0
  350. package/dist/shared/payload-root.d.ts.map +1 -0
  351. package/dist/shared/rsc-cache-key.d.ts +163 -0
  352. package/dist/shared/rsc-cache-key.d.ts.map +1 -0
  353. package/dist/shared/rsc-error-envelope.d.ts +25 -0
  354. package/dist/shared/rsc-error-envelope.d.ts.map +1 -0
  355. package/dist/shared/rsc-manifest.d.ts +28 -0
  356. package/dist/shared/rsc-manifest.d.ts.map +1 -0
  357. package/dist/shared/rsc-media-type.d.ts +40 -0
  358. package/dist/shared/rsc-media-type.d.ts.map +1 -0
  359. package/dist/shared/rsc-payload-path.d.ts +42 -0
  360. package/dist/shared/rsc-payload-path.d.ts.map +1 -0
  361. package/dist/shared/segment-info.d.ts +59 -0
  362. package/dist/shared/segment-info.d.ts.map +1 -0
  363. package/dist/shared/slot-params.d.ts +51 -0
  364. package/dist/shared/slot-params.d.ts.map +1 -0
  365. package/dist/shared/static-platform-files.d.ts +71 -0
  366. package/dist/shared/static-platform-files.d.ts.map +1 -0
  367. package/docs/api/30-api-server.mdx +1 -1
  368. package/docs/api/31-api-client.mdx +4 -2
  369. package/docs/api/33-api-search-params.mdx +38 -16
  370. package/docs/api/35-api-typescript.mdx +3 -3
  371. package/docs/api/36-cli.mdx +34 -7
  372. package/docs/learn/00-introduction.mdx +1 -1
  373. package/docs/learn/02-pages-and-layouts.mdx +2 -2
  374. package/docs/learn/05-typed-params.mdx +8 -8
  375. package/docs/learn/07-typed-routes.mdx +12 -7
  376. package/docs/learn/11-error-handling.mdx +18 -0
  377. package/docs/learn/12-client-navigation.mdx +2 -2
  378. package/docs/learn/14-deploying.mdx +23 -0
  379. package/docs/more/01-advanced-routing.mdx +173 -3
  380. package/docs/more/03-coming-from-nextjs.mdx +2 -2
  381. package/docs/more/04-metadata-and-fonts.mdx +1 -1
  382. package/docs/more/50-ai-agent-instructions.mdx +6 -4
  383. package/package.json +9 -6
  384. package/src/adapters/build-output-helper.ts +116 -3
  385. package/src/adapters/cloudflare.ts +90 -12
  386. package/src/adapters/compress-module.ts +79 -1
  387. package/src/adapters/fs-identity.ts +156 -0
  388. package/src/adapters/nitro.ts +6 -6
  389. package/src/adapters/shared.ts +196 -21
  390. package/src/adapters/types.ts +13 -1
  391. package/src/cache/fast-hash.ts +5 -0
  392. package/src/cli-check.ts +458 -0
  393. package/src/cli.ts +59 -24
  394. package/src/client/browser-entry/action-dispatch.ts +11 -5
  395. package/src/client/browser-entry/hydrate.ts +40 -29
  396. package/src/client/browser-entry/index.ts +15 -6
  397. package/src/client/browser-entry/post-hydration.ts +10 -9
  398. package/src/client/browser-entry/router-init.ts +104 -48
  399. package/src/client/browser-entry/rsc-stream.ts +11 -2
  400. package/src/client/error-boundary.tsx +85 -16
  401. package/src/client/history.ts +13 -6
  402. package/src/client/index.ts +14 -3
  403. package/src/client/internal.ts +4 -3
  404. package/src/client/link.tsx +120 -72
  405. package/src/client/navigation-api.ts +3 -3
  406. package/src/client/navigation-commit.ts +197 -0
  407. package/src/client/navigation-context.ts +40 -67
  408. package/src/client/navigation-root.tsx +326 -101
  409. package/src/client/params-context.ts +192 -0
  410. package/src/client/router-effects.ts +231 -0
  411. package/src/client/router-lifecycle.ts +288 -0
  412. package/src/client/router-pipeline.ts +393 -0
  413. package/src/client/router-ref.ts +1 -1
  414. package/src/client/router-skew.ts +30 -0
  415. package/src/client/router-types.ts +230 -0
  416. package/src/client/router.ts +243 -707
  417. package/src/client/rsc-fetch.ts +183 -106
  418. package/src/client/segment-cache.ts +129 -44
  419. package/src/client/ssr-data.ts +6 -0
  420. package/src/client/stale-client.ts +191 -0
  421. package/src/client/state.ts +14 -1
  422. package/src/client/top-loader.tsx +2 -1
  423. package/src/client/unload-guard.ts +34 -2
  424. package/src/client/use-cookie.ts +9 -2
  425. package/src/client/use-query-states.ts +102 -39
  426. package/src/client/use-router.ts +1 -1
  427. package/src/client/use-segment-params.ts +41 -18
  428. package/src/cookies/define-cookie.ts +6 -1
  429. package/src/index.ts +25 -4
  430. package/src/plugin-context.ts +58 -0
  431. package/src/plugins/adapter-build.ts +4 -0
  432. package/src/plugins/build-manifest.ts +34 -13
  433. package/src/plugins/entries.ts +4 -0
  434. package/src/plugins/fonts.ts +11 -8
  435. package/src/plugins/request-dep/analysis.ts +447 -0
  436. package/src/plugins/request-dep/index.ts +376 -0
  437. package/src/plugins/request-dep/origin.ts +310 -0
  438. package/src/plugins/routing.ts +97 -102
  439. package/src/plugins/shims.ts +0 -1
  440. package/src/plugins/static-build.ts +391 -95
  441. package/src/routing/codegen-shared.ts +3 -79
  442. package/src/routing/codegen-types.ts +28 -31
  443. package/src/routing/codegen-write.ts +139 -0
  444. package/src/routing/codegen.ts +86 -172
  445. package/src/routing/collision-probe.ts +438 -0
  446. package/src/routing/collision-spaces.ts +281 -0
  447. package/src/routing/convention-lint.ts +139 -40
  448. package/src/routing/export-detect.ts +151 -7
  449. package/src/routing/interception.ts +896 -50
  450. package/src/routing/link-codegen.ts +32 -65
  451. package/src/routing/manifest-codegen.ts +6 -62
  452. package/src/routing/scanner.ts +117 -92
  453. package/src/routing/segment-classify.ts +57 -0
  454. package/src/routing/segment-keys.ts +146 -0
  455. package/src/routing/slot-placement.ts +97 -0
  456. package/src/routing/types.ts +0 -6
  457. package/src/routing/walkers.ts +8 -0
  458. package/src/schema-bridge.ts +180 -58
  459. package/src/search-params/define.ts +108 -66
  460. package/src/search-params/index.ts +0 -1
  461. package/src/search-params/parse-total.ts +78 -0
  462. package/src/search-params/wrappers.ts +60 -11
  463. package/src/server/access-gate.tsx +60 -40
  464. package/src/server/action-handler.ts +4 -5
  465. package/src/server/actions.ts +6 -3
  466. package/src/server/als-registry.ts +18 -1
  467. package/src/server/build-manifest.ts +14 -1
  468. package/src/server/chain-url-parts.ts +163 -0
  469. package/src/server/children-interception.ts +115 -0
  470. package/src/server/compress.ts +9 -1
  471. package/src/server/default-status-page.ts +127 -0
  472. package/src/server/deny-boundary.ts +269 -41
  473. package/src/server/deny-renderer.ts +111 -39
  474. package/src/server/error-boundary-wrapper.ts +166 -79
  475. package/src/server/head-response.ts +54 -0
  476. package/src/server/index.ts +1 -3
  477. package/src/server/internal.ts +2 -2
  478. package/src/server/metadata-collector.ts +115 -0
  479. package/src/server/param-coercion.ts +51 -69
  480. package/src/server/pipeline-helpers.ts +2 -47
  481. package/src/server/pipeline-interception.ts +81 -32
  482. package/src/server/pipeline-outcome.ts +4 -5
  483. package/src/server/pipeline-phases.ts +77 -25
  484. package/src/server/pipeline.ts +57 -10
  485. package/src/server/prebuilt-runtime.ts +4 -4
  486. package/src/server/primitives.ts +25 -0
  487. package/src/server/publish-params.ts +42 -0
  488. package/src/server/request-context.ts +97 -18
  489. package/src/server/route-element-builder.ts +241 -253
  490. package/src/server/route-handler.ts +8 -8
  491. package/src/server/route-matcher.ts +21 -9
  492. package/src/server/rsc-cache-key-guard.ts +164 -0
  493. package/src/server/rsc-entry/api-handler.ts +6 -8
  494. package/src/server/rsc-entry/deny-fallback.ts +3 -2
  495. package/src/server/rsc-entry/error-renderer.ts +94 -35
  496. package/src/server/rsc-entry/helpers.ts +33 -28
  497. package/src/server/rsc-entry/index.ts +36 -33
  498. package/src/server/rsc-entry/render-route.ts +18 -10
  499. package/src/server/rsc-entry/revalidate-renderer.ts +3 -2
  500. package/src/server/rsc-entry/rsc-payload.ts +113 -40
  501. package/src/server/rsc-entry/rsc-stream.ts +29 -5
  502. package/src/server/rsc-entry/ssr-renderer.ts +38 -22
  503. package/src/server/skippable-prefix.ts +92 -0
  504. package/src/server/slot-interception.ts +168 -0
  505. package/src/server/slot-resolver.ts +263 -158
  506. package/src/server/ssr-bridge-types.ts +18 -0
  507. package/src/server/ssr-entry.ts +20 -10
  508. package/src/server/ssr-wrappers.tsx +33 -32
  509. package/src/server/state-tree-diff.ts +14 -55
  510. package/src/server/static-generator.ts +68 -29
  511. package/src/server/static-not-found.ts +134 -0
  512. package/src/server/status-code-resolver.ts +28 -11
  513. package/src/server/tree-builder.ts +35 -218
  514. package/src/server/types.ts +12 -7
  515. package/src/server/utils/element-type.ts +72 -0
  516. package/src/shared/als-slots.ts +72 -0
  517. package/src/shared/base-path.ts +80 -0
  518. package/src/shared/param-value.ts +244 -0
  519. package/src/shared/payload-root.ts +151 -0
  520. package/src/shared/rsc-cache-key.ts +269 -0
  521. package/src/shared/rsc-error-envelope.ts +31 -0
  522. package/src/shared/rsc-manifest.ts +29 -0
  523. package/src/shared/rsc-media-type.ts +43 -0
  524. package/src/shared/rsc-payload-path.ts +45 -0
  525. package/src/shared/segment-info.ts +60 -0
  526. package/src/shared/slot-params.ts +111 -0
  527. package/src/shared/static-platform-files.ts +75 -0
  528. package/dist/_chunks/build-output-helper-DOGYFb_X.js +0 -196
  529. package/dist/_chunks/build-output-helper-DOGYFb_X.js.map +0 -1
  530. package/dist/_chunks/cache-api-B-lhk9p4.js.map +0 -1
  531. package/dist/_chunks/canonicalize-DQHyFClh.js.map +0 -1
  532. package/dist/_chunks/cli-schema-sync-BTWEKJXo.js +0 -668
  533. package/dist/_chunks/cli-schema-sync-BTWEKJXo.js.map +0 -1
  534. package/dist/_chunks/define-COtkxMRT.js +0 -191
  535. package/dist/_chunks/define-COtkxMRT.js.map +0 -1
  536. package/dist/_chunks/define-c4au4I9R.js +0 -82
  537. package/dist/_chunks/define-c4au4I9R.js.map +0 -1
  538. package/dist/_chunks/fast-hash-D6hIVt1Y.js.map +0 -1
  539. package/dist/_chunks/logger-AWfuX-KJ.js.map +0 -1
  540. package/dist/_chunks/navigation-root-bUDZLw74.js +0 -124
  541. package/dist/_chunks/navigation-root-bUDZLw74.js.map +0 -1
  542. package/dist/_chunks/plugin-context---kTF5v8.js.map +0 -1
  543. package/dist/_chunks/registry-DbJPKoBp.js +0 -20
  544. package/dist/_chunks/registry-DbJPKoBp.js.map +0 -1
  545. package/dist/_chunks/router-ref-CtmF-aPv.js.map +0 -1
  546. package/dist/_chunks/schema-bridge-DT_Tn0Xf.js +0 -119
  547. package/dist/_chunks/schema-bridge-DT_Tn0Xf.js.map +0 -1
  548. package/dist/_chunks/segment-classify-Byy425ng.js.map +0 -1
  549. package/dist/_chunks/segment-context-ZDnXDkbz.js +0 -34
  550. package/dist/_chunks/segment-context-ZDnXDkbz.js.map +0 -1
  551. package/dist/_chunks/ssr-data-BOWsq18U.js.map +0 -1
  552. package/dist/_chunks/use-query-states-DFvWd-EA.js.map +0 -1
  553. package/dist/_chunks/use-segment-params-D1CzlgBo.js +0 -151
  554. package/dist/_chunks/use-segment-params-D1CzlgBo.js.map +0 -1
  555. package/dist/_chunks/walkers-_6zKFlch.js +0 -1002
  556. package/dist/_chunks/walkers-_6zKFlch.js.map +0 -1
  557. package/dist/client/error-boundary.js.map +0 -1
  558. package/dist/plugins/request-dep.d.ts +0 -32
  559. package/dist/plugins/request-dep.d.ts.map +0 -1
  560. package/dist/search-params/registry.d.ts +0 -20
  561. package/dist/search-params/registry.d.ts.map +0 -1
  562. package/dist/segment-params/define.d.ts +0 -89
  563. package/dist/segment-params/define.d.ts.map +0 -1
  564. package/dist/segment-params/index.d.ts +0 -3
  565. package/dist/segment-params/index.d.ts.map +0 -1
  566. package/dist/segment-params/index.js +0 -2
  567. package/src/plugins/request-dep.ts +0 -499
  568. package/src/search-params/registry.ts +0 -31
  569. package/src/segment-params/define.ts +0 -249
  570. package/src/segment-params/index.ts +0 -9
@@ -14,35 +14,13 @@ import { useQueryStates as clientUseQueryStates } from '../client/use-query-stat
14
14
  import { fromSchema, isStandardSchema, isCodec } from '../schema-bridge.js';
15
15
  import type { StandardSchemaV1 } from '../schema-bridge.js';
16
16
  import type { Codec } from '../codec.js';
17
-
18
- // Server-only reference for .get() — avoids pulling server ALS into client bundles.
19
- // In client environments, .get() throws before reaching this code path.
20
- //
21
- // IMPORTANT: This is set eagerly via _setGetSearchParamsFn() at server startup
22
- // (called from request-context.ts module initialization). It must NOT use
23
- // dynamic `await import()` at call time because the async microtask from the
24
- // dynamic import loses AsyncLocalStorage context in React's RSC Flight renderer,
25
- // breaking getSearchParams() in parallel slot pages. See TIM-523.
26
- let _getSearchParamsFn: (() => URLSearchParams) | undefined;
27
-
28
- /**
29
- * Register the getSearchParams function. Called once at module load time
30
- * from request-context.ts to avoid dynamic import at call time.
31
- * @internal
32
- */
33
- export function _setGetSearchParamsFn(fn: () => URLSearchParams): void {
34
- _getSearchParamsFn = fn;
35
- }
36
-
37
- function getSearchParamsFromAls(): URLSearchParams {
38
- if (!_getSearchParamsFn) {
39
- throw new Error(
40
- '[timber] searchParams.get() is only available on the server. ' +
41
- 'Use searchParams.useQueryStates() on the client.'
42
- );
43
- }
44
- return _getSearchParamsFn();
45
- }
17
+ // Server-only reference for .get() — avoids pulling server ALS into client
18
+ // bundles. In client environments, .get() throws before reaching this code
19
+ // path. The slot lives in its own leaf module so that `request-context.ts`
20
+ // can register the getter without importing this file, which would drag
21
+ // `nuqs` into every server entry (TIM-1298). See shared/als-slots.ts.
22
+ import { getSearchParamsFromAls } from '../shared/als-slots.js';
23
+ import { parseTotal } from './parse-total.js';
46
24
 
47
25
  // ---------------------------------------------------------------------------
48
26
  // Types
@@ -51,13 +29,43 @@ function getSearchParamsFromAls(): URLSearchParams {
51
29
  /**
52
30
  * A codec that converts between URL string values and typed values.
53
31
  *
54
- * nuqs parsers implement this interface natively — no adapter needed.
32
+ * `parse` receives the RAW url value: `string | string[] | undefined`.
33
+ * A codec must be TOTAL over that domain — return a default rather than
34
+ * throwing on an absent or repeated param.
35
+ *
36
+ * A codec whose own `parse` covers only a PRESENT, SINGLE-VALUED param
37
+ * may publish `parseServerSide` instead, and timber calls that. nuqs
38
+ * parsers do exactly this, which is how they become total here: absent →
39
+ * `null` (or their `withDefault` value), repeated → the first value, and
40
+ * a throw from the inner parse → `null`. See
41
+ * design/23-search-params.md §'nuqs parsers, made total',
42
+ * `parse-total.ts`, and tests/nuqs-codec-boundary.test.ts (TIM-1350).
43
+ *
55
44
  * Standard Schema objects (Zod, Valibot, ArkType) are auto-detected
56
- * by defineSearchParams and wrapped via fromSchema.
45
+ * by defineSearchParams and wrapped via fromSchema; those ARE total
46
+ * through `parse` and are called that way.
57
47
  */
58
48
  export interface SearchParamCodec<T> extends Codec<T> {
59
49
  /** Optional URL key alias, set by withUrlKey(). */
60
50
  urlKey?: string;
51
+ /**
52
+ * Optional TOTAL entry point over `string | string[] | undefined`,
53
+ * preferred over `parse` wherever timber invokes a codec. Declared here
54
+ * so the protocol is typed rather than duck-checked at each wrapper:
55
+ * anything that reconstructs a codec has to carry it, and a wrapper
56
+ * cannot carry a property the interface does not admit.
57
+ *
58
+ * It returns `T`, not `T | null`. This is the entry point timber calls,
59
+ * so whatever it returns IS the field's type — admitting a `null` the
60
+ * field type did not carry would let `SearchParamCodec<string>` produce
61
+ * `null` for an absent param under a non-nullable annotation. A codec
62
+ * whose absent-case answer is `null` declares that in `T`, exactly as a
63
+ * bare nuqs parser does: `parseAsString` is a `SearchParamCodec<string |
64
+ * null>` here, never a `SearchParamCodec<string>`.
65
+ *
66
+ * nuqs parser builders satisfy this. See parse-total.ts.
67
+ */
68
+ parseServerSide?(value: string | string[] | undefined): T;
61
69
  }
62
70
 
63
71
  /** A codec with a URL key alias attached via withUrlKey(). */
@@ -116,7 +124,7 @@ export interface SearchParamsDefinition<T extends Record<string, unknown>> {
116
124
  *
117
125
  * ```tsx
118
126
  * // app/products/page.tsx
119
- * import { searchParams } from './params'
127
+ * import { searchParams } from './search-params'
120
128
  * export default function Page() {
121
129
  * const { page, category } = searchParams.get()
122
130
  * }
@@ -135,15 +143,21 @@ export interface SearchParamsDefinition<T extends Record<string, unknown>> {
135
143
  /** Pick a subset of keys. Preserves codecs and aliases. */
136
144
  pick<K extends keyof T & string>(...keys: K[]): SearchParamsDefinition<Pick<T, K>>;
137
145
 
138
- /** Serialize values to a query string (no leading '?'), omitting defaults. */
139
- serialize(values: Partial<T>): string;
146
+ /**
147
+ * Serialize values to a query string (no leading '?'), omitting defaults
148
+ * and applying `withUrlKey` aliases. This is the value to pass to
149
+ * `<Link searchParams={...}>`.
150
+ *
151
+ * Returns a **string**, not a `URLSearchParams`. A `<Link>` prop crosses
152
+ * the RSC Flight boundary, and `URLSearchParams` is iterable — React
153
+ * serializes it as an entries array, which arrives as `[['pg','2'], …]`
154
+ * and renders as `?0=pg&0=2`. A string survives intact.
155
+ */
156
+ buildSearchParams(values: Partial<T>): string;
140
157
 
141
158
  /** Build a full path with query string, omitting defaults. */
142
159
  href(pathname: string, values: Partial<T>): string;
143
160
 
144
- /** Build a URLSearchParams instance, omitting defaults. */
145
- toSearchParams(values: Partial<T>): URLSearchParams;
146
-
147
161
  /** Read-only codec map for spreading into .extend(). */
148
162
  codecs: { [K in keyof T]: SearchParamCodec<T[K]> };
149
163
 
@@ -182,6 +196,21 @@ type InferSchemaInput<V> = V extends { '~standard': { types?: infer TS } }
182
196
  /**
183
197
  * Infer the output type from either a SearchParamCodec or a StandardSchemaV1.
184
198
  *
199
+ * A codec publishing `parseServerSide` is read through THAT signature, not
200
+ * through `parse` — it is the entry point timber actually calls, and it is
201
+ * the one that tells the truth about absent input. A bare `parseAsString`
202
+ * declares `parse(value: string): string` but answers `null` for a missing
203
+ * param, so the field is `string | null`; `parseAsInteger.withDefault(1)`
204
+ * narrows its own `parseServerSide` to `NonNullable<number>` and the field
205
+ * stays `number`. Reading `parse` instead produced a non-nullable type for
206
+ * a nullable field (TIM-1350).
207
+ *
208
+ * The match is structural, not nuqs-specific: any codec declaring that
209
+ * signature opts into being read through it, which is exactly the contract
210
+ * `parseTotal` applies at runtime. The two must stay in step — a type
211
+ * inferred from `parse` while the runtime calls `parseServerSide` is the
212
+ * lie this branch exists to remove.
213
+ *
185
214
  * Schemas whose input type rejects `undefined` (e.g. bare `z.string()`, whose
186
215
  * input is `string`) are implicitly optional: the URL might not contain the
187
216
  * param, and fromSchema returns `undefined` when the schema rejects absent
@@ -194,8 +223,11 @@ type InferSchemaInput<V> = V extends { '~standard': { types?: infer TS } }
194
223
  * keeps its narrow output type even though absent input yields `undefined`
195
224
  * at runtime. Add `.default()` to coerce schemas for accurate types.
196
225
  */
197
- export type InferField<V> =
198
- V extends SearchParamCodec<infer T>
226
+ export type InferField<V> = V extends {
227
+ parseServerSide(value: string | string[] | undefined): infer R;
228
+ }
229
+ ? R
230
+ : V extends SearchParamCodec<infer T>
199
231
  ? T
200
232
  : V extends StandardSchemaV1<infer T>
201
233
  ? undefined extends InferSchemaInput<V>
@@ -233,13 +265,36 @@ function normalizeRaw(
233
265
  * default-omission: when serialize(value) === serialize(parse(undefined)),
234
266
  * the field is omitted from the URL.
235
267
  *
268
+ * Goes through `parseTotal` for the same reason request-time parsing does:
269
+ * a nuqs parser's own `parse` throws on absent input, and this is the
270
+ * absent case by construction.
271
+ *
272
+ * **A codec with no value for an absent param has no default to omit**,
273
+ * and `null` is returned rather than `serialize(null)`. Serializing it
274
+ * makes the "no value" case collide with a real one: `parseAsInteger`
275
+ * serializes `null` as the string `'null'`, so `buildSearchParams({ q:
276
+ * 'null' })` would silently drop a legitimate value (before TIM-1350 the
277
+ * absent parse was `undefined` and the swallowed input was the string
278
+ * `'undefined'` — same defect, a less likely input). Nothing is lost:
279
+ * `buildSearchParams` already skips a field whose `serialize` returns
280
+ * `null`, so a codec that encodes "no value" as an omission behaves
281
+ * identically, and one that encodes it as a real query value now writes
282
+ * it instead of dropping it.
283
+ *
236
284
  * Codecs are documented to return a default rather than throw, but a
237
285
  * hand-written codec that throws on absent input must not turn definition
238
- * into a crash — treat its default as null (nothing to omit).
286
+ * into a crash — treat its default as null (nothing to omit). `serialize`
287
+ * is inside the try for the same reason.
288
+ *
289
+ * Typed `SearchParamCodec<unknown>` rather than generic on purpose: the
290
+ * absent-input value is whatever the codec's total entry point returns,
291
+ * which for a nuqs parser is `T | null` while its `serialize` declares
292
+ * `T`. Widening to `unknown` states that honestly instead of casting.
239
293
  */
240
- function getDefaultSerialized<T>(codec: SearchParamCodec<T>): string | null {
294
+ function getDefaultSerialized(codec: SearchParamCodec<unknown>): string | null {
241
295
  try {
242
- return codec.serialize(codec.parse(undefined));
296
+ const absent = parseTotal(codec, undefined);
297
+ return absent === null || absent === undefined ? null : codec.serialize(absent);
243
298
  } catch {
244
299
  return null;
245
300
  }
@@ -405,7 +460,7 @@ function buildDefinition<T extends Record<string, unknown>>(
405
460
  for (const prop of Object.keys(codecMap)) {
406
461
  const urlKey = getUrlKey(prop);
407
462
  const rawValue = normalized[urlKey];
408
- result[prop] = (codecMap[prop as keyof T] as SearchParamCodec<unknown>).parse(rawValue);
463
+ result[prop] = parseTotal(codecMap[prop as keyof T] as SearchParamCodec<unknown>, rawValue);
409
464
  }
410
465
 
411
466
  return result as T;
@@ -429,8 +484,14 @@ function buildDefinition<T extends Record<string, unknown>>(
429
484
  return parseSync(raw);
430
485
  }
431
486
 
432
- // ---- serialize ----
433
- function serialize(values: Partial<T>): string {
487
+ // ---- buildSearchParams ----
488
+ //
489
+ // Returns a query string. It used to have a URLSearchParams-returning
490
+ // sibling (`toSearchParams`) that `<Link>` consumed; that shape cannot
491
+ // cross the RSC Flight boundary (see the interface docstring), and having
492
+ // two methods produce the same query two ways was a drift waiting to
493
+ // happen. One method now.
494
+ function buildSearchParams(values: Partial<T>): string {
434
495
  const parts: string[] = [];
435
496
 
436
497
  for (const prop of Object.keys(codecMap)) {
@@ -450,28 +511,10 @@ function buildDefinition<T extends Record<string, unknown>>(
450
511
 
451
512
  // ---- href ----
452
513
  function href(pathname: string, values: Partial<T>): string {
453
- const qs = serialize(values);
514
+ const qs = buildSearchParams(values);
454
515
  return qs ? `${pathname}?${qs}` : pathname;
455
516
  }
456
517
 
457
- // ---- toSearchParams ----
458
- function toSearchParams(values: Partial<T>): URLSearchParams {
459
- const usp = new URLSearchParams();
460
-
461
- for (const prop of Object.keys(codecMap)) {
462
- if (!(prop in values)) continue;
463
- const codec = codecMap[prop as keyof T] as SearchParamCodec<unknown>;
464
- const serialized = codec.serialize(values[prop as keyof T] as unknown);
465
-
466
- if (serialized === defaultSerialized[prop]) continue;
467
- if (serialized === null) continue;
468
-
469
- usp.set(getUrlKey(prop), serialized);
470
- }
471
-
472
- return usp;
473
- }
474
-
475
518
  // ---- extend ----
476
519
  function extend<U extends Record<string, SearchParamCodec<unknown> | StandardSchemaV1<unknown>>>(
477
520
  newCodecs: U
@@ -565,9 +608,8 @@ function buildDefinition<T extends Record<string, unknown>>(
565
608
  useQueryStates,
566
609
  extend,
567
610
  pick,
568
- serialize,
569
611
  href,
570
- toSearchParams,
612
+ buildSearchParams,
571
613
  codecs: codecMap,
572
614
  urlKeys: Object.freeze({ ...urlKeys }),
573
615
  };
@@ -25,4 +25,3 @@ export { defineSearchParams } from './define.js';
25
25
  export { withDefault, withUrlKey } from './wrappers.js';
26
26
 
27
27
  // Runtime registry (route-scoped useQueryStates)
28
- export { registerSearchParams, getSearchParamsDefinition } from './registry.js';
@@ -0,0 +1,78 @@
1
+ /**
2
+ * parseTotal — invoke a codec over the FULL raw domain.
3
+ *
4
+ * `SearchParamCodec.parse` is documented to be total over
5
+ * `string | string[] | undefined`, because that is exactly what a URL
6
+ * hands it: a param can be absent (`undefined`) or repeated (`string[]`).
7
+ * Timber's own codecs and the Standard Schema bridges honour that.
8
+ *
9
+ * nuqs parsers do not. Their `parse` expects a **present scalar string** —
10
+ * nuqs checks presence itself before ever calling it — so `parseAsBoolean`
11
+ * and `parseAsIsoDate` threw a render-phase 500 on an absent param and
12
+ * `parseAsString` returned an array on a repeated one (TIM-1350).
13
+ *
14
+ * nuqs ships the missing adapter: every parser builder exposes
15
+ * `parseServerSide(value: string | string[] | undefined)`, which maps
16
+ * absent → `null` (or the parser's `withDefault` value), takes the FIRST
17
+ * entry of a repeated param (matching `URLSearchParams.get()`), and wraps
18
+ * the inner `parse` so a throw becomes `null`. That is precisely timber's
19
+ * domain, so we call it in preference to `parse` rather than hand-rolling
20
+ * a second normalization that could disagree with the client hook.
21
+ *
22
+ * Feature detection, not an instanceof check: any codec MAY publish
23
+ * `parseServerSide` to declare "this is my total entry point" — it is an
24
+ * optional member of `SearchParamCodec` — and a codec that does not is
25
+ * assumed already total and called through `parse`. Timber codecs and
26
+ * schema bridges take the second branch untouched; several of them rely on
27
+ * `parse(undefined)` to produce their default.
28
+ *
29
+ * **Search params only.** Segment params (`server/param-coercion.ts`) call
30
+ * `codec.parse` directly and must keep doing so: their domain is a value
31
+ * the router matched, never absent, and a codec that REJECTS one is how a
32
+ * route produces a 404. Routing a rejection through nuqs's `safeParse`
33
+ * would turn that 404 into a silent `null` param. The two domains differ
34
+ * in what "no value" means, not just in plumbing. Cookies are a third
35
+ * domain (`cookies/define-cookie.ts`) and are likewise untouched.
36
+ *
37
+ * `parseServerSide` carries a `@deprecated` tag in nuqs (it steers users to
38
+ * loaders, which timber does not use). It remains public, typed and
39
+ * exercised; `tests/nuqs-codec-boundary.test.ts` asserts totality for every
40
+ * parser through timber's own API, so a nuqs release that drops it fails
41
+ * loudly rather than silently reinstating the 500s.
42
+ *
43
+ * Design doc: design/23-search-params.md §"nuqs parsers, made total"
44
+ */
45
+
46
+ import type { Codec } from '../codec.js';
47
+
48
+ /**
49
+ * A codec that publishes a total entry point over the raw URL domain.
50
+ *
51
+ * The return is `T`, not `T | null`: this is the entry point timber calls,
52
+ * so whatever it answers IS the field's type. A `null` for an absent param
53
+ * belongs in `T` — a bare nuqs parser is a codec of `string | null`, and
54
+ * `parseAsInteger.withDefault(1)` is a codec of `number`, because nuqs
55
+ * narrows its own `parseServerSide` return to `NonNullable<T>`. Declaring
56
+ * `T | null` here would let a codec annotated `SearchParamCodec<string>`
57
+ * hand back `null` under a non-nullable type (TIM-1350 review).
58
+ */
59
+ export interface TotalCodec<T> {
60
+ parseServerSide(value: string | string[] | undefined): T;
61
+ }
62
+
63
+ function hasParseServerSide<T>(codec: Codec<T>): codec is Codec<T> & TotalCodec<T> {
64
+ return typeof (codec as Partial<TotalCodec<T>>).parseServerSide === 'function';
65
+ }
66
+
67
+ /**
68
+ * Parse a raw URL value through a codec, using the codec's total entry
69
+ * point when it publishes one.
70
+ *
71
+ * Returns `T`, from both branches. A `null` for an absent param is part of
72
+ * the codec's own `T` — see TotalCodec above — so this signature does not
73
+ * widen it, and a caller that must handle "no value" (`withDefault`) sees
74
+ * it because `T` carries it.
75
+ */
76
+ export function parseTotal<T>(codec: Codec<T>, raw: string | string[] | undefined): T {
77
+ return hasParseServerSide(codec) ? codec.parseServerSide(raw) : codec.parse(raw);
78
+ }
@@ -15,14 +15,32 @@ import type {
15
15
  SearchParamField,
16
16
  } from './define.js';
17
17
  import { isCodec, isStandardSchema, fromSchema } from '../schema-bridge.js';
18
+ import { parseTotal } from './parse-total.js';
18
19
 
19
20
  // ---------------------------------------------------------------------------
20
21
  // withDefault
21
22
  // ---------------------------------------------------------------------------
22
23
 
23
24
  /**
24
- * Wrap a nullable codec with a default value. When the inner codec returns
25
- * null, the default is used instead. The output type becomes non-nullable.
25
+ * Wrap a nullable codec with a default value. The output type becomes
26
+ * non-nullable, and the wrapper is TOTAL over `string | string[] |
27
+ * undefined` even when the inner codec is not.
28
+ *
29
+ * The default is substituted for `null` — the documented "no value"
30
+ * answer — and for `undefined`, which is what an implicitly-optional
31
+ * Standard Schema field and a bare `parseAsString` produce for an absent
32
+ * param. Substituting only for `null` left a field typed non-nullable
33
+ * `string` holding `undefined` (TIM-1350).
34
+ *
35
+ * The inner codec is invoked through `parseTotal`, so a nuqs parser gets
36
+ * its own normalization (absent → null, repeated → first value, inner
37
+ * throw → null) before either check applies. That is what makes
38
+ * `withDefault(parseAsBoolean, false)` total; the wrapper itself does NOT
39
+ * catch. A throw from a codec is a deliberate signal — `fromSchema` throws
40
+ * on an async schema, and an app codec may `redirect()` or `notFound()` on
41
+ * a value it refuses — and swallowing it would convert a loud failure into
42
+ * a permanently-default field. design/09 §"The SearchParamCodec Protocol":
43
+ * a codec that throws bubbles as a render-phase error, by design.
26
44
  *
27
45
  * Works with any codec — nuqs parsers, custom codecs, fromSchema results.
28
46
  *
@@ -34,20 +52,35 @@ import { isCodec, isStandardSchema, fromSchema } from '../schema-bridge.js';
34
52
  * // page.parse(undefined) → 1 (not null)
35
53
  * // page.parse('5') → 5
36
54
  * ```
55
+ *
56
+ * The signature strips BOTH nullish constituents from the result, not just
57
+ * `null`, because the runtime substitutes for both. An implicitly-optional
58
+ * schema field is a `SearchParamCodec<string | undefined>`, and wrapping it
59
+ * used to yield `SearchParamCodec<string | undefined>` — a field the
60
+ * wrapper guarantees is always present, still typed as maybe-absent.
37
61
  */
38
62
  export function withDefault<T>(
39
- codec: SearchParamCodec<T | null>,
40
- defaultValue: T
41
- ): SearchParamCodec<T> {
42
- return {
43
- parse(value: string | string[] | undefined): T {
44
- const result = codec.parse(value);
45
- return result === null ? defaultValue : result;
63
+ codec: SearchParamCodec<T | null | undefined>,
64
+ defaultValue: NonNullable<T>
65
+ ): SearchParamCodec<NonNullable<T>> {
66
+ // A wrapper that rebuilds a codec from two methods drops everything else
67
+ // the codec carries. `withUrlKey` lost `parseServerSide` that way; this
68
+ // one lost `urlKey`, so `withDefault(withUrlKey(parseAsInteger, 'p'), 1)`
69
+ // silently read `?page=` instead of `?p=`. Carry it explicitly rather
70
+ // than spreading: this wrapper's own `parse` IS the total entry point,
71
+ // and re-publishing the inner `parseServerSide` would let `parseTotal`
72
+ // reach past it and skip the default entirely.
73
+ const wrapped: SearchParamCodec<NonNullable<T>> = {
74
+ parse(value: string | string[] | undefined): NonNullable<T> {
75
+ const result = parseTotal(codec, value);
76
+ return result === null || result === undefined ? defaultValue : result;
46
77
  },
47
- serialize(value: T): string | null {
78
+ serialize(value: NonNullable<T>): string | null {
48
79
  return codec.serialize(value);
49
80
  },
50
81
  };
82
+ if (codec.urlKey !== undefined) wrapped.urlKey = codec.urlKey;
83
+ return wrapped;
51
84
  }
52
85
 
53
86
  // ---------------------------------------------------------------------------
@@ -93,9 +126,25 @@ export function withUrlKey<F extends SearchParamField>(
93
126
  : isStandardSchema(codecOrSchema)
94
127
  ? (fromSchema(codecOrSchema) as SearchParamCodec<T>)
95
128
  : (codecOrSchema as SearchParamCodec<T>);
96
- return {
129
+ // Carry the total entry point, bound like the other two methods. A
130
+ // codec's totality lives in a THIRD property, `parseServerSide` (see
131
+ // parse-total.ts); picking only `parse`/`serialize` silently produced a
132
+ // non-total alias, so `withUrlKey(parseAsBoolean, 'b')` threw on an
133
+ // absent param while the bare parser did not (TIM-1350).
134
+ //
135
+ // Bound explicitly rather than spread: a spread copies own enumerable
136
+ // properties only, so a class-based codec — whose `parse` and
137
+ // `serialize` this wrapper already reaches through the prototype —
138
+ // would have kept its methods and lost exactly the one that makes it
139
+ // total. Three properties, one rule, no dependence on how the inner
140
+ // codec was constructed.
141
+ const wrapped: SearchParamCodecWithUrlKey<T> = {
97
142
  parse: codec.parse.bind(codec),
98
143
  serialize: codec.serialize.bind(codec),
99
144
  urlKey,
100
145
  };
146
+ if (typeof codec.parseServerSide === 'function') {
147
+ wrapped.parseServerSide = codec.parseServerSide.bind(codec);
148
+ }
149
+ return wrapped;
101
150
  }
@@ -18,7 +18,7 @@ import type { AccessGateProps, SlotAccessGateProps } from './tree-builder.js';
18
18
  import { withSpan, setSpanAttribute } from './tracing.js';
19
19
  import { isDebug } from './debug.js';
20
20
  import type { DenyPageEntry } from './deny-boundary.js';
21
- import { renderMatchingDenyPage, setDenyStatus } from './deny-boundary.js';
21
+ import { handleCaughtDeny, setFirstDeniedSegmentIndex } from './deny-boundary.js';
22
22
  import type { ReactNode } from 'react';
23
23
 
24
24
  // ─── AccessGate ─────────────────────────────────────────────────────────────
@@ -26,50 +26,64 @@ import type { ReactNode } from 'react';
26
26
  /**
27
27
  * Framework-injected access gate for segments.
28
28
  *
29
- * When a pre-computed `verdict` prop is provided (from the pre-render pass
30
- * in route-element-builder.ts), AccessGate replays it synchronously — no
31
- * async, no re-execution of access.ts, immune to Suspense timing. The OTEL
32
- * span was already emitted during the pre-render pass.
33
- *
34
- * When no verdict is provided (backward compat with tree-builder.ts),
35
- * AccessGate calls accessFn directly with OTEL instrumentation.
29
+ * Calls accessFn during render — inside `renderToReadableStream`, sharing
30
+ * the same `React.cache` scope as layouts and pages. A `requireUser()`
31
+ * call in access.ts populates the cache; the same call in any layout is
32
+ * a free hit. Access executes exactly once per request. See TIM-1363.
36
33
  *
37
34
  * access.ts is a pure gate — return values are discarded. The layout below
38
35
  * gets the same data by calling the same cached functions (React.cache dedup).
36
+ *
37
+ * On denial the gate renders the matching deny page only when this segment
38
+ * owns it. Because the gate is wrapped OUTSIDE its own layout, that render
39
+ * omits the denying segment's layout — deliberate, and the one place the
40
+ * in-tree path diverges from `renderDenyPage`'s inclusive slice: a layout
41
+ * whose access.ts denied must never run and serialize protected data into
42
+ * the Flight stream (TIM-1074). An ancestor-owned page is hoisted instead,
43
+ * because the gate also sits inside every layout above it and rendering
44
+ * here would wrap the deny page in all of them (TIM-1356).
45
+ *
46
+ * For skipped segments (skippedSegment=true), deny pages are NOT rendered
47
+ * in-tree — the client's cached layout chrome would wrap them incorrectly.
48
+ * Instead, the deny is recorded in ALS and rethrown so the response path
49
+ * can return rscErrorEnvelope for a client hard-navigate.
39
50
  */
40
- export function AccessGate(props: AccessGateProps): ReactNode | Promise<ReactNode> {
41
- const { accessFn, segmentName, verdict, denyPages, children } = props;
51
+ export function AccessGate(props: AccessGateProps): Promise<ReactNode> {
52
+ const {
53
+ accessFn,
54
+ segmentName,
55
+ segmentKey,
56
+ segmentIndex,
57
+ denyPages,
58
+ skippedSegment,
59
+ onSettled,
60
+ children,
61
+ } = props;
42
62
 
43
- // Fast path: replay pre-computed verdict from the pre-render pass.
44
- if (verdict !== undefined) {
45
- if (verdict === 'pass') {
46
- return children;
47
- }
48
- // Render deny page in-tree when possible (same as the fallback path).
49
- if (verdict instanceof DenySignal && denyPages) {
50
- const denyElement = renderMatchingDenyPage(denyPages, verdict.status, verdict.data);
51
- if (denyElement) {
52
- setDenyStatus(verdict.status);
53
- return denyElement;
54
- }
55
- }
56
- throw verdict;
57
- }
58
-
59
- // Primary path: call accessFn directly during render.
60
- // If denyPages is provided, catch DenySignal and render the deny page
61
- // in-tree — no throw reaches React Flight, no second render pass.
62
- return accessGateFallback(accessFn, segmentName, denyPages, children);
63
+ return accessGateFallback(
64
+ accessFn,
65
+ segmentName,
66
+ segmentKey,
67
+ segmentIndex,
68
+ skippedSegment ? undefined : denyPages,
69
+ skippedSegment,
70
+ onSettled,
71
+ children
72
+ );
63
73
  }
64
74
 
65
75
  /**
66
- * Async fallback for AccessGate when no pre-computed verdict is available.
67
- * Calls accessFn with OTEL instrumentation.
76
+ * Calls accessFn during render with OTEL instrumentation.
77
+ * Records the first denied segment index in ALS for post-render consumers.
68
78
  */
69
79
  async function accessGateFallback(
70
80
  accessFn: AccessGateProps['accessFn'],
71
81
  segmentName: AccessGateProps['segmentName'],
82
+ segmentKey: AccessGateProps['segmentKey'],
83
+ segmentIndex: AccessGateProps['segmentIndex'],
72
84
  denyPages: DenyPageEntry[] | undefined,
85
+ skippedSegment: boolean | undefined,
86
+ onSettled: (() => void) | undefined,
73
87
  children: ReactNode
74
88
  ): Promise<ReactNode> {
75
89
  try {
@@ -91,17 +105,23 @@ async function accessGateFallback(
91
105
  }
92
106
  });
93
107
  } catch (error: unknown) {
94
- // Catch DenySignal and render the deny page in-tree.
95
- // No throw reaches React Flight — clean stream, single render pass.
96
- // RedirectSignal and other errors propagate normally.
97
- if (error instanceof DenySignal && denyPages) {
98
- const denyElement = renderMatchingDenyPage(denyPages, error.status, error.data);
99
- if (denyElement) {
100
- setDenyStatus(error.status);
101
- return denyElement;
108
+ if (error instanceof DenySignal) {
109
+ if (segmentIndex !== undefined) {
110
+ setFirstDeniedSegmentIndex(segmentIndex);
111
+ }
112
+
113
+ if (skippedSegment) {
114
+ error._hardNavigate = true;
115
+ throw error;
116
+ }
117
+
118
+ if (denyPages) {
119
+ return handleCaughtDeny(denyPages, error, segmentKey);
102
120
  }
103
121
  }
104
122
  throw error;
123
+ } finally {
124
+ onSettled?.();
105
125
  }
106
126
 
107
127
  return children;
@@ -36,6 +36,7 @@ import {
36
36
  import type { FormFlashData } from './form-flash.js';
37
37
  import { checkVersionSkew, applyReloadHeaders } from './version-skew.js';
38
38
  import { logActionError } from './logger.js';
39
+ import { RSC_CONTENT_TYPE } from '../shared/rsc-media-type.js';
39
40
 
40
41
  // ─── Types ────────────────────────────────────────────────────────────────
41
42
 
@@ -55,10 +56,6 @@ export interface ActionDispatchConfig {
55
56
  sensitiveFields?: SensitiveFieldsOption;
56
57
  }
57
58
 
58
- // ─── Constants ────────────────────────────────────────────────────────────
59
-
60
- const RSC_CONTENT_TYPE = 'text/x-component';
61
-
62
59
  // ─── Detection ────────────────────────────────────────────────────────────
63
60
 
64
61
  /**
@@ -296,7 +293,9 @@ async function handleRscAction(
296
293
  // serialized together so React Flight handles both in one stream.
297
294
  payload = {
298
295
  _action: result.actionResult,
299
- _tree: result.revalidation.element,
296
+ // The payload root, not a bare tree: it carries the revalidated
297
+ // route's params too, and the client publishes them (TIM-1297).
298
+ _tree: result.revalidation.payload,
300
299
  };
301
300
  headers['X-Timber-Revalidation'] = '1';
302
301
  // Metadata (<title>/<meta>/<link>) now rides the revalidation element
@@ -20,10 +20,13 @@ import { withSpan } from './tracing';
20
20
  import { revalidationAls, type RevalidationState } from './als-registry.js';
21
21
  // ─── Types ───────────────────────────────────────────────────────────────
22
22
 
23
- /** Result of rendering a revalidation — element tree before RSC serialization. */
23
+ /** Result of rendering a revalidation — payload root before RSC serialization. */
24
24
  export interface RevalidationResult {
25
- /** React element tree (pre-serialization — passed to renderToReadableStream). */
26
- element: unknown;
25
+ /**
26
+ * The payload root — tree plus the params it rendered with, exactly as an
27
+ * ordinary route payload. The client splits it in `applyRevalidation`.
28
+ */
29
+ payload: unknown;
27
30
  }
28
31
 
29
32
  /** Renderer function that builds a React element tree for a given path. */