@timber-js/app 0.2.0-alpha.186 → 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 (314) hide show
  1. package/dist/_chunks/{actions-C-Rw9vPc.js → actions-35jnMdeJ.js} +4 -3
  2. package/dist/_chunks/{actions-C-Rw9vPc.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-mFweg276.js → als-slots-BEEIPKYm.js} +3 -4
  6. package/dist/_chunks/{als-slots-mFweg276.js.map → als-slots-BEEIPKYm.js.map} +1 -1
  7. package/dist/_chunks/{cache-api-Cd0VZ_Pd.js → cache-api-DjNrIWRR.js} +7 -13
  8. package/dist/_chunks/cache-api-DjNrIWRR.js.map +1 -0
  9. package/dist/_chunks/cli-check-CpmN7Nh-.js +256 -0
  10. package/dist/_chunks/cli-check-CpmN7Nh-.js.map +1 -0
  11. package/dist/_chunks/cli-schema-sync-CGMp_Psg.js +298 -0
  12. package/dist/_chunks/cli-schema-sync-CGMp_Psg.js.map +1 -0
  13. package/dist/_chunks/{cloudflare-Cs0uZXea.js → cloudflare-CGP6BZKO.js} +4 -3
  14. package/dist/_chunks/{cloudflare-Cs0uZXea.js.map → cloudflare-CGP6BZKO.js.map} +1 -1
  15. package/dist/_chunks/convention-lint-kXsgc_-7.js +784 -0
  16. package/dist/_chunks/convention-lint-kXsgc_-7.js.map +1 -0
  17. package/dist/_chunks/{error-boundary-DpYRI_I1.js → error-boundary-D-ODYX41.js} +25 -2
  18. package/dist/_chunks/error-boundary-D-ODYX41.js.map +1 -0
  19. package/dist/_chunks/file-cache-DmX7OqZP.js +454 -0
  20. package/dist/_chunks/file-cache-DmX7OqZP.js.map +1 -0
  21. package/dist/_chunks/{logger-N7e5auP0.js → logger-D8xJZXIN.js} +27 -40
  22. package/dist/_chunks/logger-D8xJZXIN.js.map +1 -0
  23. package/dist/_chunks/mdx-file-C005ay-P.js +25 -0
  24. package/dist/_chunks/mdx-file-C005ay-P.js.map +1 -0
  25. package/dist/_chunks/{plugin-context-DEGLSJs3.js → plugin-context-rCinWLiE.js} +7 -2
  26. package/dist/_chunks/{plugin-context-DEGLSJs3.js.map → plugin-context-rCinWLiE.js.map} +1 -1
  27. package/dist/_chunks/purge-store-Byr8XOjU.js +14 -0
  28. package/dist/_chunks/purge-store-Byr8XOjU.js.map +1 -0
  29. package/dist/_chunks/{resolve-schema-Dz3fcFUo.js → resolve-schema-5ma5pp1b.js} +2 -2
  30. package/dist/_chunks/{resolve-schema-Dz3fcFUo.js.map → resolve-schema-5ma5pp1b.js.map} +1 -1
  31. package/dist/_chunks/rsc-media-type-DRqE_lD_.js +46 -0
  32. package/dist/_chunks/rsc-media-type-DRqE_lD_.js.map +1 -0
  33. package/dist/_chunks/{cli-schema-sync-CKgHC2MB.js → scanner-C8b0Gcw3.js} +5 -298
  34. package/dist/_chunks/scanner-C8b0Gcw3.js.map +1 -0
  35. package/dist/_chunks/schema-bridge-Cc2Gngu1.js +199 -0
  36. package/dist/_chunks/schema-bridge-Cc2Gngu1.js.map +1 -0
  37. package/dist/_chunks/segment-classify-C539Pa2O.js.map +1 -1
  38. package/dist/_chunks/{param-value-C8TNYchQ.js → segment-context-CjOlyB8Y.js} +33 -2
  39. package/dist/_chunks/segment-context-CjOlyB8Y.js.map +1 -0
  40. package/dist/_chunks/{use-query-states-DFvWd-EA.js → use-query-states-BbU5Ge1V.js} +74 -26
  41. package/dist/_chunks/use-query-states-BbU5Ge1V.js.map +1 -0
  42. package/dist/_chunks/{navigation-root-B29qg0_T.js → use-segment-params-C4r4BD9T.js} +129 -5
  43. package/dist/_chunks/use-segment-params-C4r4BD9T.js.map +1 -0
  44. package/dist/_chunks/walkers-RzN6AFjr.js +141 -0
  45. package/dist/_chunks/walkers-RzN6AFjr.js.map +1 -0
  46. package/dist/adapters/cloudflare-dev.js +1 -1
  47. package/dist/adapters/cloudflare-kv-cache.js +1 -1
  48. package/dist/adapters/cloudflare.d.ts.map +1 -1
  49. package/dist/adapters/cloudflare.js +1 -1
  50. package/dist/adapters/compress-module.d.ts +12 -0
  51. package/dist/adapters/compress-module.d.ts.map +1 -1
  52. package/dist/adapters/nitro.js +54 -2
  53. package/dist/adapters/nitro.js.map +1 -1
  54. package/dist/cache/index.js +1 -1
  55. package/dist/cdn/cloudflare-purge.js +30 -0
  56. package/dist/cdn/cloudflare-purge.js.map +1 -0
  57. package/dist/cdn/fastly-purge.js +33 -0
  58. package/dist/cdn/fastly-purge.js.map +1 -0
  59. package/dist/cdn/index.js +94 -0
  60. package/dist/cdn/index.js.map +1 -0
  61. package/dist/cdn/workers-cache-purge.js +35 -0
  62. package/dist/cdn/workers-cache-purge.js.map +1 -0
  63. package/dist/cli-check.d.ts +153 -0
  64. package/dist/cli-check.d.ts.map +1 -0
  65. package/dist/cli.d.ts +34 -8
  66. package/dist/cli.d.ts.map +1 -1
  67. package/dist/cli.js +46 -21
  68. package/dist/cli.js.map +1 -1
  69. package/dist/client/browser-entry/action-dispatch.d.ts.map +1 -1
  70. package/dist/client/browser-entry/index.d.ts +1 -1
  71. package/dist/client/browser-entry/index.d.ts.map +1 -1
  72. package/dist/client/error-boundary.d.ts +6 -0
  73. package/dist/client/error-boundary.d.ts.map +1 -1
  74. package/dist/client/error-boundary.js +1 -1
  75. package/dist/client/index.d.ts +1 -0
  76. package/dist/client/index.d.ts.map +1 -1
  77. package/dist/client/index.js +27 -33
  78. package/dist/client/index.js.map +1 -1
  79. package/dist/client/internal.js +21 -14
  80. package/dist/client/internal.js.map +1 -1
  81. package/dist/client/link.d.ts +1 -7
  82. package/dist/client/link.d.ts.map +1 -1
  83. package/dist/client/navigation-commit.d.ts +16 -0
  84. package/dist/client/navigation-commit.d.ts.map +1 -1
  85. package/dist/client/router-pipeline.d.ts.map +1 -1
  86. package/dist/client/router.d.ts.map +1 -1
  87. package/dist/client/rsc-fetch.d.ts +9 -1
  88. package/dist/client/rsc-fetch.d.ts.map +1 -1
  89. package/dist/client/segment-cache.d.ts +8 -0
  90. package/dist/client/segment-cache.d.ts.map +1 -1
  91. package/dist/client/use-query-states.d.ts +9 -3
  92. package/dist/client/use-query-states.d.ts.map +1 -1
  93. package/dist/codec.js +1 -1
  94. package/dist/cookies/define-cookie.d.ts.map +1 -1
  95. package/dist/cookies/index.js +2 -2
  96. package/dist/cookies/index.js.map +1 -1
  97. package/dist/index.d.ts +4 -1
  98. package/dist/index.d.ts.map +1 -1
  99. package/dist/index.js +142 -477
  100. package/dist/index.js.map +1 -1
  101. package/dist/params/index.js +1 -1
  102. package/dist/plugin-context.d.ts +15 -0
  103. package/dist/plugin-context.d.ts.map +1 -1
  104. package/dist/plugins/routing.d.ts +0 -9
  105. package/dist/plugins/routing.d.ts.map +1 -1
  106. package/dist/plugins/shims.d.ts.map +1 -1
  107. package/dist/plugins/static-build.d.ts +24 -0
  108. package/dist/plugins/static-build.d.ts.map +1 -1
  109. package/dist/routing/codegen-shared.d.ts +3 -44
  110. package/dist/routing/codegen-shared.d.ts.map +1 -1
  111. package/dist/routing/codegen-types.d.ts +10 -31
  112. package/dist/routing/codegen-types.d.ts.map +1 -1
  113. package/dist/routing/codegen-write.d.ts +51 -0
  114. package/dist/routing/codegen-write.d.ts.map +1 -0
  115. package/dist/routing/codegen.d.ts.map +1 -1
  116. package/dist/routing/convention-lint.d.ts +18 -4
  117. package/dist/routing/convention-lint.d.ts.map +1 -1
  118. package/dist/routing/export-detect.d.ts +16 -0
  119. package/dist/routing/export-detect.d.ts.map +1 -1
  120. package/dist/routing/index.js +3 -2
  121. package/dist/routing/link-codegen.d.ts +19 -4
  122. package/dist/routing/link-codegen.d.ts.map +1 -1
  123. package/dist/routing/manifest-codegen.d.ts +1 -7
  124. package/dist/routing/manifest-codegen.d.ts.map +1 -1
  125. package/dist/routing/types.d.ts +0 -6
  126. package/dist/routing/types.d.ts.map +1 -1
  127. package/dist/schema-bridge.d.ts +60 -9
  128. package/dist/schema-bridge.d.ts.map +1 -1
  129. package/dist/search-params/define.d.ts +62 -8
  130. package/dist/search-params/define.d.ts.map +1 -1
  131. package/dist/search-params/index.d.ts +0 -1
  132. package/dist/search-params/index.d.ts.map +1 -1
  133. package/dist/search-params/index.js +66 -29
  134. package/dist/search-params/index.js.map +1 -1
  135. package/dist/search-params/parse-total.d.ts +70 -0
  136. package/dist/search-params/parse-total.d.ts.map +1 -0
  137. package/dist/search-params/wrappers.d.ts +26 -3
  138. package/dist/search-params/wrappers.d.ts.map +1 -1
  139. package/dist/server/access-gate.d.ts +19 -8
  140. package/dist/server/access-gate.d.ts.map +1 -1
  141. package/dist/server/action-handler.d.ts.map +1 -1
  142. package/dist/server/als-registry.d.ts +16 -0
  143. package/dist/server/als-registry.d.ts.map +1 -1
  144. package/dist/server/compress.d.ts.map +1 -1
  145. package/dist/server/deny-boundary.d.ts +148 -15
  146. package/dist/server/deny-boundary.d.ts.map +1 -1
  147. package/dist/server/deny-renderer.d.ts +2 -2
  148. package/dist/server/deny-renderer.d.ts.map +1 -1
  149. package/dist/server/error-boundary-wrapper.d.ts +85 -15
  150. package/dist/server/error-boundary-wrapper.d.ts.map +1 -1
  151. package/dist/server/index.d.ts +0 -1
  152. package/dist/server/index.d.ts.map +1 -1
  153. package/dist/server/index.js +3 -2
  154. package/dist/server/index.js.map +1 -1
  155. package/dist/server/internal.d.ts +3 -1
  156. package/dist/server/internal.d.ts.map +1 -1
  157. package/dist/server/internal.js +343 -230
  158. package/dist/server/internal.js.map +1 -1
  159. package/dist/server/metadata-collector.d.ts +52 -0
  160. package/dist/server/metadata-collector.d.ts.map +1 -0
  161. package/dist/server/param-coercion.d.ts +12 -5
  162. package/dist/server/param-coercion.d.ts.map +1 -1
  163. package/dist/server/pipeline-helpers.d.ts.map +1 -1
  164. package/dist/server/pipeline-outcome.d.ts.map +1 -1
  165. package/dist/server/pipeline-phases.d.ts.map +1 -1
  166. package/dist/server/primitives.d.ts +23 -0
  167. package/dist/server/primitives.d.ts.map +1 -1
  168. package/dist/server/route-element-builder.d.ts +1 -12
  169. package/dist/server/route-element-builder.d.ts.map +1 -1
  170. package/dist/server/rsc-cache-key-guard.d.ts.map +1 -1
  171. package/dist/server/rsc-entry/deny-fallback.d.ts.map +1 -1
  172. package/dist/server/rsc-entry/error-renderer.d.ts +1 -1
  173. package/dist/server/rsc-entry/error-renderer.d.ts.map +1 -1
  174. package/dist/server/rsc-entry/helpers.d.ts +0 -7
  175. package/dist/server/rsc-entry/helpers.d.ts.map +1 -1
  176. package/dist/server/rsc-entry/index.d.ts +0 -1
  177. package/dist/server/rsc-entry/index.d.ts.map +1 -1
  178. package/dist/server/rsc-entry/render-route.d.ts.map +1 -1
  179. package/dist/server/rsc-entry/revalidate-renderer.d.ts.map +1 -1
  180. package/dist/server/rsc-entry/rsc-payload.d.ts +1 -3
  181. package/dist/server/rsc-entry/rsc-payload.d.ts.map +1 -1
  182. package/dist/server/rsc-entry/rsc-stream.d.ts +12 -0
  183. package/dist/server/rsc-entry/rsc-stream.d.ts.map +1 -1
  184. package/dist/server/rsc-entry/ssr-renderer.d.ts +0 -2
  185. package/dist/server/rsc-entry/ssr-renderer.d.ts.map +1 -1
  186. package/dist/server/slot-resolver.d.ts.map +1 -1
  187. package/dist/server/ssr-bridge-types.d.ts +11 -0
  188. package/dist/server/ssr-bridge-types.d.ts.map +1 -1
  189. package/dist/server/ssr-entry.d.ts +0 -1
  190. package/dist/server/ssr-entry.d.ts.map +1 -1
  191. package/dist/server/static-generator.d.ts.map +1 -1
  192. package/dist/server/status-code-resolver.d.ts +8 -1
  193. package/dist/server/status-code-resolver.d.ts.map +1 -1
  194. package/dist/server/tree-builder.d.ts +28 -36
  195. package/dist/server/tree-builder.d.ts.map +1 -1
  196. package/dist/server/types.d.ts +12 -7
  197. package/dist/server/types.d.ts.map +1 -1
  198. package/dist/server/utils/element-type.d.ts +40 -0
  199. package/dist/server/utils/element-type.d.ts.map +1 -0
  200. package/dist/shared/rsc-media-type.d.ts +40 -0
  201. package/dist/shared/rsc-media-type.d.ts.map +1 -0
  202. package/docs/api/30-api-server.mdx +1 -1
  203. package/docs/api/33-api-search-params.mdx +38 -16
  204. package/docs/api/35-api-typescript.mdx +3 -3
  205. package/docs/api/36-cli.mdx +34 -7
  206. package/docs/learn/00-introduction.mdx +1 -1
  207. package/docs/learn/02-pages-and-layouts.mdx +1 -1
  208. package/docs/learn/05-typed-params.mdx +8 -8
  209. package/docs/learn/07-typed-routes.mdx +12 -7
  210. package/docs/learn/11-error-handling.mdx +16 -0
  211. package/docs/more/01-advanced-routing.mdx +1 -1
  212. package/docs/more/03-coming-from-nextjs.mdx +2 -2
  213. package/docs/more/50-ai-agent-instructions.mdx +6 -4
  214. package/package.json +8 -5
  215. package/src/adapters/cloudflare.ts +4 -1
  216. package/src/adapters/compress-module.ts +79 -1
  217. package/src/cli-check.ts +458 -0
  218. package/src/cli.ts +59 -24
  219. package/src/client/browser-entry/action-dispatch.ts +2 -1
  220. package/src/client/browser-entry/index.ts +0 -5
  221. package/src/client/error-boundary.tsx +65 -1
  222. package/src/client/index.ts +14 -3
  223. package/src/client/link.tsx +65 -64
  224. package/src/client/navigation-commit.ts +27 -4
  225. package/src/client/params-context.ts +4 -4
  226. package/src/client/router-pipeline.ts +4 -0
  227. package/src/client/router.ts +1 -0
  228. package/src/client/rsc-fetch.ts +14 -4
  229. package/src/client/segment-cache.ts +8 -0
  230. package/src/client/use-query-states.ts +102 -39
  231. package/src/cookies/define-cookie.ts +6 -1
  232. package/src/index.ts +20 -3
  233. package/src/plugin-context.ts +26 -0
  234. package/src/plugins/routing.ts +84 -146
  235. package/src/plugins/shims.ts +0 -1
  236. package/src/plugins/static-build.ts +78 -24
  237. package/src/routing/codegen-shared.ts +3 -79
  238. package/src/routing/codegen-types.ts +10 -31
  239. package/src/routing/codegen-write.ts +139 -0
  240. package/src/routing/codegen.ts +56 -182
  241. package/src/routing/convention-lint.ts +139 -40
  242. package/src/routing/export-detect.ts +151 -7
  243. package/src/routing/link-codegen.ts +32 -65
  244. package/src/routing/manifest-codegen.ts +1 -59
  245. package/src/routing/scanner.ts +3 -3
  246. package/src/routing/types.ts +0 -6
  247. package/src/schema-bridge.ts +180 -58
  248. package/src/search-params/define.ts +102 -37
  249. package/src/search-params/index.ts +0 -1
  250. package/src/search-params/parse-total.ts +78 -0
  251. package/src/search-params/wrappers.ts +60 -11
  252. package/src/server/access-gate.tsx +60 -40
  253. package/src/server/action-handler.ts +1 -4
  254. package/src/server/als-registry.ts +16 -0
  255. package/src/server/compress.ts +9 -1
  256. package/src/server/deny-boundary.ts +269 -41
  257. package/src/server/deny-renderer.ts +32 -21
  258. package/src/server/error-boundary-wrapper.ts +166 -79
  259. package/src/server/index.ts +1 -3
  260. package/src/server/internal.ts +2 -2
  261. package/src/server/metadata-collector.ts +115 -0
  262. package/src/server/param-coercion.ts +13 -61
  263. package/src/server/pipeline-helpers.ts +2 -2
  264. package/src/server/pipeline-outcome.ts +2 -1
  265. package/src/server/pipeline-phases.ts +9 -9
  266. package/src/server/primitives.ts +25 -0
  267. package/src/server/route-element-builder.ts +167 -170
  268. package/src/server/rsc-cache-key-guard.ts +2 -8
  269. package/src/server/rsc-entry/deny-fallback.ts +3 -2
  270. package/src/server/rsc-entry/error-renderer.ts +31 -11
  271. package/src/server/rsc-entry/helpers.ts +2 -12
  272. package/src/server/rsc-entry/index.ts +0 -5
  273. package/src/server/rsc-entry/render-route.ts +14 -11
  274. package/src/server/rsc-entry/revalidate-renderer.ts +2 -1
  275. package/src/server/rsc-entry/rsc-payload.ts +104 -20
  276. package/src/server/rsc-entry/rsc-stream.ts +25 -2
  277. package/src/server/rsc-entry/ssr-renderer.ts +28 -16
  278. package/src/server/slot-resolver.ts +10 -2
  279. package/src/server/ssr-bridge-types.ts +12 -0
  280. package/src/server/ssr-entry.ts +8 -6
  281. package/src/server/static-generator.ts +3 -2
  282. package/src/server/status-code-resolver.ts +28 -11
  283. package/src/server/tree-builder.ts +35 -218
  284. package/src/server/types.ts +12 -7
  285. package/src/server/utils/element-type.ts +72 -0
  286. package/src/shared/rsc-media-type.ts +43 -0
  287. package/dist/_chunks/cache-api-Cd0VZ_Pd.js.map +0 -1
  288. package/dist/_chunks/cli-schema-sync-CKgHC2MB.js.map +0 -1
  289. package/dist/_chunks/error-boundary-DpYRI_I1.js.map +0 -1
  290. package/dist/_chunks/logger-N7e5auP0.js.map +0 -1
  291. package/dist/_chunks/navigation-root-B29qg0_T.js.map +0 -1
  292. package/dist/_chunks/param-value-C8TNYchQ.js.map +0 -1
  293. package/dist/_chunks/registry-DbJPKoBp.js +0 -20
  294. package/dist/_chunks/registry-DbJPKoBp.js.map +0 -1
  295. package/dist/_chunks/schema-bridge-DT_Tn0Xf.js +0 -119
  296. package/dist/_chunks/schema-bridge-DT_Tn0Xf.js.map +0 -1
  297. package/dist/_chunks/segment-context-ZDnXDkbz.js +0 -34
  298. package/dist/_chunks/segment-context-ZDnXDkbz.js.map +0 -1
  299. package/dist/_chunks/use-query-states-DFvWd-EA.js.map +0 -1
  300. package/dist/_chunks/use-segment-params-ClyUNq4d.js +0 -128
  301. package/dist/_chunks/use-segment-params-ClyUNq4d.js.map +0 -1
  302. package/dist/_chunks/walkers-BhhwI9TD.js +0 -936
  303. package/dist/_chunks/walkers-BhhwI9TD.js.map +0 -1
  304. package/dist/search-params/registry.d.ts +0 -20
  305. package/dist/search-params/registry.d.ts.map +0 -1
  306. package/dist/segment-params/define.d.ts +0 -83
  307. package/dist/segment-params/define.d.ts.map +0 -1
  308. package/dist/segment-params/index.d.ts +0 -3
  309. package/dist/segment-params/index.d.ts.map +0 -1
  310. package/dist/segment-params/index.js +0 -70
  311. package/dist/segment-params/index.js.map +0 -1
  312. package/src/search-params/registry.ts +0 -31
  313. package/src/segment-params/define.ts +0 -226
  314. package/src/segment-params/index.ts +0 -9
@@ -1,226 +0,0 @@
1
- /**
2
- * defineSegmentParams — factory for typed route param coercion.
3
- *
4
- * Creates a ParamsDefinition that coerces raw string params from the
5
- * URL into typed values. Used by exporting from params.ts (segment-level)
6
- * convention file.
7
- *
8
- * Reuses the shared Codec<T> protocol with Standard Schema auto-detection,
9
- * same pattern as defineSearchParams. Runtime constraints are stricter:
10
- * - serialize must return string (not null — path segments can't be omitted)
11
- * - parse throwing → 404 (invalid param value)
12
- *
13
- * Design doc: design/07a-route-params-triage.md
14
- */
15
-
16
- import type { Codec } from '../codec.js';
17
- import { type StandardSchemaV1, resolveCodecOrSchema } from '../schema-bridge.js';
18
- import { useSegmentParams as clientUseSegmentParams } from '../client/use-segment-params.js';
19
- // Server-only ALS reference for .get(), eagerly registered at server startup
20
- // from a leaf module so this file stays out of the server graph. Same slot
21
- // mechanism as search-params. See shared/als-slots.ts, TIM-523, TIM-1298.
22
- import { getSegmentParamsFromAls } from '../shared/als-slots.js';
23
-
24
- // ---------------------------------------------------------------------------
25
- // Types
26
- // ---------------------------------------------------------------------------
27
-
28
- /** Infer the output type from a Codec or StandardSchemaV1. */
29
- export type InferParamField<V> =
30
- V extends Codec<infer T> ? T : V extends StandardSchemaV1<infer T> ? T : never;
31
-
32
- /** Acceptable field value for defineParams: a Codec or a Standard Schema. */
33
- export type ParamField<T = unknown> = Codec<T> | StandardSchemaV1<T>;
34
-
35
- export type { StandardSchemaV1 };
36
-
37
- /**
38
- * A typed route params definition.
39
- *
40
- * Returned by defineParams(). Provides parse (string → typed) and
41
- * serialize (typed → string) for each declared param.
42
- */
43
- export interface ParamsDefinition<T extends Record<string, unknown>> {
44
- /** Parse raw string params into typed values. Throws on invalid values. */
45
- parse(raw: Record<string, string | string[]>): T;
46
-
47
- /** Serialize typed values back to strings for URL construction. */
48
- serialize(values: T): Record<string, string>;
49
-
50
- /**
51
- * Get typed segment params from the current request context (ALS).
52
- *
53
- * Server-only, sync.
54
- *
55
- * ```ts
56
- * // app/products/[id]/params.ts
57
- * export const segmentParams = defineSegmentParams({ id: z.coerce.number() })
58
- *
59
- * // app/products/[id]/page.tsx
60
- * import { segmentParams } from './params'
61
- * export default function Page() {
62
- * const { id } = segmentParams.get() // id: number
63
- * }
64
- * ```
65
- */
66
- get(): T;
67
-
68
- /**
69
- * Client hook for accessing typed segment params reactively.
70
- *
71
- * ```tsx
72
- * 'use client'
73
- * import { segmentParams } from './params'
74
- * export function ProductHeader() {
75
- * const { id } = segmentParams.useSegmentParams()
76
- * }
77
- * ```
78
- */
79
- useSegmentParams(): T;
80
-
81
- /** Read-only codec map. */
82
- codecs: { [K in keyof T]: Codec<T[K]> };
83
- }
84
-
85
- // ---------------------------------------------------------------------------
86
- // Internal helpers
87
- // ---------------------------------------------------------------------------
88
-
89
- /**
90
- * Validate that no codec's serialize returns null.
91
- * Route params are structural — they must produce a valid path segment.
92
- */
93
- function validateSerialize(codecMap: Record<string, Codec<unknown>>): void {
94
- for (const [key, codec] of Object.entries(codecMap)) {
95
- // Test serialize with a sample parsed value to check for null
96
- // We can't exhaustively test, but we can check that serialize(parse("test"))
97
- // doesn't return null for a basic input.
98
- try {
99
- const testValue = codec.parse('test');
100
- const serialized = codec.serialize(testValue);
101
- if (serialized === null) {
102
- throw new Error(
103
- `[timber] defineSegmentParams: field '${key}' codec.serialize() returned null.\n` +
104
- ` Route params are path segments — they cannot be omitted.\n` +
105
- ` Ensure serialize() always returns a string.`
106
- );
107
- }
108
- } catch (e) {
109
- // parse('test') may throw for strict codecs (e.g., number-only).
110
- // That's fine — it means the codec validates. We only care about
111
- // serialize returning null, which we can't test without a valid value.
112
- if (e instanceof Error && e.message.includes('returned null')) {
113
- throw e;
114
- }
115
- }
116
- }
117
- }
118
-
119
- // ---------------------------------------------------------------------------
120
- // Factory
121
- // ---------------------------------------------------------------------------
122
-
123
- /**
124
- * Create a ParamsDefinition from a map of codecs and/or Standard Schema objects.
125
- *
126
- * ```ts
127
- * // app/products/[id]/layout.tsx
128
- * import { defineSegmentParams } from '@timber-js/app/segment-params'
129
- * import { z } from 'zod/v4'
130
- *
131
- * export const segmentParams = defineSegmentParams({
132
- * id: z.coerce.number().int().positive(),
133
- * })
134
- * ```
135
- */
136
- export function defineSegmentParams<C extends Record<string, ParamField>>(
137
- codecs: C
138
- ): ParamsDefinition<{ [K in keyof C]: InferParamField<C[K]> }> {
139
- type T = { [K in keyof C]: InferParamField<C[K]> };
140
-
141
- const resolvedCodecs: Record<string, Codec<unknown>> = {};
142
-
143
- for (const [key, value] of Object.entries(codecs)) {
144
- resolvedCodecs[key] = resolveCodecOrSchema(key, value, 'param');
145
- }
146
-
147
- // Validate that serialize doesn't return null
148
- validateSerialize(resolvedCodecs);
149
-
150
- // ---- parse ----
151
- function parse(raw: Record<string, string | string[]>): T {
152
- const result: Record<string, unknown> = {};
153
-
154
- for (const [key, codec] of Object.entries(resolvedCodecs)) {
155
- const rawValue = raw[key];
156
- // Skip params not present in the URL at this route depth.
157
- // This supports TIM-834 inheritance: a parent params.ts can declare
158
- // codecs for descendant segments. At shallower depths those params
159
- // don't exist yet — skip them instead of passing undefined to the codec.
160
- if (rawValue === undefined) continue;
161
- result[key] = codec.parse(rawValue);
162
- }
163
-
164
- return result as T;
165
- }
166
-
167
- // ---- serialize ----
168
- function serialize(values: T): Record<string, string> {
169
- const result: Record<string, string> = {};
170
-
171
- for (const [key, codec] of Object.entries(resolvedCodecs)) {
172
- const serialized = codec.serialize(values[key as keyof T] as unknown);
173
- if (serialized === null) {
174
- throw new Error(
175
- `[timber] params.serialize: field '${key}' serialized to null. ` +
176
- `Route params must produce a valid path segment.`
177
- );
178
- }
179
- result[key] = serialized;
180
- }
181
-
182
- return result;
183
- }
184
-
185
- // ---- get ----
186
- // ALS-backed: reads segment params from the current request context.
187
- // Server-only, sync.
188
- //
189
- // The pipeline already coerces params via coerceSegmentParams() which
190
- // calls parse() and stores typed values in ALS via setSegmentParams().
191
- // We return those directly instead of re-parsing, because codecs may
192
- // not be idempotent (e.g., a codec that only accepts raw strings would
193
- // throw if given an already-parsed value). See TIM-574.
194
- function get(): T {
195
- if (typeof window !== 'undefined') {
196
- throw new Error(
197
- '[timber] segmentParams.get() is server-only. ' +
198
- 'Use segmentParams.useSegmentParams() on the client.'
199
- );
200
- }
201
- const params = getSegmentParamsFromAls();
202
- // params are already coerced by the pipeline — return as-is.
203
- return params as unknown as T;
204
- }
205
-
206
- // ---- useSegmentParams ----
207
- // Client hook that reads typed params from the navigation context.
208
- // Delegates to the existing useSegmentParams hook from use-segment-params.ts.
209
- function useSegmentParams(): T {
210
- const raw = clientUseSegmentParams();
211
- // Params are already coerced by the server pipeline before being sent
212
- // to the client — return as-is. Re-parsing would break non-idempotent
213
- // codecs (e.g., z.coerce.number() on an already-parsed number). See TIM-574.
214
- return raw as unknown as T;
215
- }
216
-
217
- const definition: ParamsDefinition<T> = {
218
- parse,
219
- serialize,
220
- get,
221
- useSegmentParams,
222
- codecs: resolvedCodecs as { [K in keyof T]: Codec<T[K]> },
223
- };
224
-
225
- return definition;
226
- }
@@ -1,9 +0,0 @@
1
- // @timber-js/app/segment-params — Typed route param coercion
2
- //
3
- // This is the import path for defineSegmentParams and related types.
4
- // For search params, import from @timber-js/app/search-params instead.
5
- //
6
- // See design/07-routing.md §"params.ts Convention File"
7
-
8
- export type { ParamsDefinition, InferParamField, ParamField } from './define.js';
9
- export { defineSegmentParams } from './define.js';