ts-procedures 8.5.0 → 9.0.0

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 (635) hide show
  1. package/README.md +166 -101
  2. package/agent_config/claude-code/.claude-plugin/plugin.json +1 -1
  3. package/agent_config/claude-code/agents/ts-procedures-architect.md +11 -10
  4. package/agent_config/claude-code/skills/ts-procedures/SKILL.md +25 -12
  5. package/agent_config/claude-code/skills/ts-procedures/anti-patterns.md +10 -12
  6. package/agent_config/claude-code/skills/ts-procedures/api-reference.md +141 -45
  7. package/agent_config/claude-code/skills/ts-procedures/checklist.md +7 -6
  8. package/agent_config/claude-code/skills/ts-procedures/patterns.md +45 -6
  9. package/agent_config/claude-code/skills/ts-procedures/templates/client.md +1 -1
  10. package/agent_config/claude-code/skills/ts-procedures/templates/hono.md +1 -1
  11. package/agent_config/copilot/copilot-instructions.md +50 -33
  12. package/agent_config/cursor/cursorrules +50 -33
  13. package/build/adapters/astro/astro-context.js.map +1 -0
  14. package/build/adapters/astro/create-handler.js.map +1 -0
  15. package/build/adapters/astro/index.js.map +1 -0
  16. package/build/{implementations/http → adapters}/astro/index.test.js +1 -1
  17. package/build/adapters/astro/index.test.js.map +1 -0
  18. package/build/adapters/astro/rewrite-request.js.map +1 -0
  19. package/build/adapters/hono/envelope-parity.test.js +98 -0
  20. package/build/adapters/hono/envelope-parity.test.js.map +1 -0
  21. package/build/{implementations/http → adapters}/hono/handlers/http-stream.d.ts +1 -1
  22. package/build/adapters/hono/handlers/http-stream.js +55 -0
  23. package/build/adapters/hono/handlers/http-stream.js.map +1 -0
  24. package/build/{implementations/http → adapters}/hono/handlers/http-stream.test.js +1 -1
  25. package/build/adapters/hono/handlers/http-stream.test.js.map +1 -0
  26. package/build/{implementations/http → adapters}/hono/handlers/http.d.ts +1 -1
  27. package/build/adapters/hono/handlers/http.js +50 -0
  28. package/build/adapters/hono/handlers/http.js.map +1 -0
  29. package/build/{implementations/http → adapters}/hono/handlers/http.test.js +1 -1
  30. package/build/adapters/hono/handlers/http.test.js.map +1 -0
  31. package/build/{implementations/http → adapters}/hono/handlers/rpc.d.ts +2 -2
  32. package/build/adapters/hono/handlers/rpc.js +23 -0
  33. package/build/adapters/hono/handlers/rpc.js.map +1 -0
  34. package/build/{implementations/http → adapters}/hono/handlers/rpc.test.js +1 -1
  35. package/build/adapters/hono/handlers/rpc.test.js.map +1 -0
  36. package/build/adapters/hono/handlers/stream.d.ts +12 -0
  37. package/build/adapters/hono/handlers/stream.js +89 -0
  38. package/build/adapters/hono/handlers/stream.js.map +1 -0
  39. package/build/{implementations/http → adapters}/hono/handlers/stream.test.js +3 -2
  40. package/build/adapters/hono/handlers/stream.test.js.map +1 -0
  41. package/build/{implementations/http → adapters}/hono/index.d.ts +24 -12
  42. package/build/{implementations/http → adapters}/hono/index.js +19 -8
  43. package/build/adapters/hono/index.js.map +1 -0
  44. package/build/{implementations/http → adapters}/hono/index.test.js +2 -4
  45. package/build/adapters/hono/index.test.js.map +1 -0
  46. package/build/{implementations/http → adapters/hono}/on-request-error.test.js +2 -2
  47. package/build/adapters/hono/on-request-error.test.js.map +1 -0
  48. package/build/adapters/hono/request.d.ts +7 -0
  49. package/build/adapters/hono/request.js +22 -0
  50. package/build/adapters/hono/request.js.map +1 -0
  51. package/build/{implementations/http → adapters/hono}/route-errors.test.js +4 -4
  52. package/build/adapters/hono/route-errors.test.js.map +1 -0
  53. package/build/adapters/hono/types.d.ts +55 -0
  54. package/build/adapters/hono/types.js +19 -0
  55. package/build/adapters/hono/types.js.map +1 -0
  56. package/build/client/freeze.test.js +39 -0
  57. package/build/client/freeze.test.js.map +1 -0
  58. package/build/client/typed-error-dispatch.test.js +2 -2
  59. package/build/client/typed-error-dispatch.test.js.map +1 -1
  60. package/build/codegen/__fixtures__/make-envelope.d.ts +1 -1
  61. package/build/codegen/bin/cli.d.ts +5 -0
  62. package/build/codegen/bin/cli.js +139 -182
  63. package/build/codegen/bin/cli.js.map +1 -1
  64. package/build/codegen/bin/cli.test.js +12 -2
  65. package/build/codegen/bin/cli.test.js.map +1 -1
  66. package/build/codegen/bin/flag-specs.d.ts +9 -0
  67. package/build/codegen/bin/flag-specs.js +33 -31
  68. package/build/codegen/bin/flag-specs.js.map +1 -1
  69. package/build/codegen/bin/flag-specs.test.js +14 -1
  70. package/build/codegen/bin/flag-specs.test.js.map +1 -1
  71. package/build/codegen/collect-models.d.ts +1 -1
  72. package/build/codegen/emit/api-route.d.ts +8 -0
  73. package/build/codegen/emit/api-route.js +156 -0
  74. package/build/codegen/emit/api-route.js.map +1 -0
  75. package/build/codegen/emit/context.d.ts +30 -0
  76. package/build/codegen/emit/context.js +2 -0
  77. package/build/codegen/emit/context.js.map +1 -0
  78. package/build/codegen/emit/declarations.d.ts +24 -0
  79. package/build/codegen/emit/declarations.js +48 -0
  80. package/build/codegen/emit/declarations.js.map +1 -0
  81. package/build/codegen/emit/format-types.d.ts +61 -0
  82. package/build/codegen/emit/format-types.js +188 -0
  83. package/build/codegen/emit/format-types.js.map +1 -0
  84. package/build/codegen/emit/http-stream-route.d.ts +7 -0
  85. package/build/codegen/emit/http-stream-route.js +138 -0
  86. package/build/codegen/emit/http-stream-route.js.map +1 -0
  87. package/build/codegen/emit/route-shared.d.ts +35 -0
  88. package/build/codegen/emit/route-shared.js +88 -0
  89. package/build/codegen/emit/route-shared.js.map +1 -0
  90. package/build/codegen/emit/rpc-route.d.ts +7 -0
  91. package/build/codegen/emit/rpc-route.js +37 -0
  92. package/build/codegen/emit/rpc-route.js.map +1 -0
  93. package/build/codegen/emit/scope-file.d.ts +39 -0
  94. package/build/codegen/emit/scope-file.js +166 -0
  95. package/build/codegen/emit/scope-file.js.map +1 -0
  96. package/build/codegen/emit/stream-route.d.ts +7 -0
  97. package/build/codegen/emit/stream-route.js +62 -0
  98. package/build/codegen/emit/stream-route.js.map +1 -0
  99. package/build/codegen/emit-errors.d.ts +1 -1
  100. package/build/codegen/emit-errors.integration.test.js +1 -1
  101. package/build/codegen/emit-errors.integration.test.js.map +1 -1
  102. package/build/codegen/emit-index.js +13 -0
  103. package/build/codegen/emit-index.js.map +1 -1
  104. package/build/codegen/emit-index.test.js +25 -0
  105. package/build/codegen/emit-index.test.js.map +1 -1
  106. package/build/codegen/emit-scope.d.ts +13 -30
  107. package/build/codegen/emit-scope.js +15 -807
  108. package/build/codegen/emit-scope.js.map +1 -1
  109. package/build/codegen/emit-scope.test.js +86 -4
  110. package/build/codegen/emit-scope.test.js.map +1 -1
  111. package/build/codegen/goldens.test.js +69 -0
  112. package/build/codegen/goldens.test.js.map +1 -0
  113. package/build/codegen/group-routes.d.ts +1 -1
  114. package/build/codegen/pipeline.d.ts +1 -1
  115. package/build/codegen/resolve-envelope.d.ts +1 -1
  116. package/build/codegen/targets/_shared/error-schemas.d.ts +1 -1
  117. package/build/codegen/targets/_shared/route-slots.d.ts +1 -1
  118. package/build/codegen/targets/_shared/target-run.d.ts +1 -1
  119. package/build/codegen/targets/kotlin/emit-route-kotlin.d.ts +1 -1
  120. package/build/codegen/targets/swift/emit-route-swift.d.ts +1 -1
  121. package/build/core/create-http-stream.d.ts +50 -0
  122. package/build/core/create-http-stream.js +108 -0
  123. package/build/core/create-http-stream.js.map +1 -0
  124. package/build/{create-http-stream.test.js → core/create-http-stream.test.js} +1 -1
  125. package/build/core/create-http-stream.test.js.map +1 -0
  126. package/build/core/create-http.d.ts +51 -0
  127. package/build/core/create-http.js +65 -0
  128. package/build/core/create-http.js.map +1 -0
  129. package/build/{create-http.test.js → core/create-http.test.js} +13 -4
  130. package/build/core/create-http.test.js.map +1 -0
  131. package/build/core/create-stream.d.ts +26 -0
  132. package/build/core/create-stream.js +80 -0
  133. package/build/core/create-stream.js.map +1 -0
  134. package/build/{create-stream.test.js → core/create-stream.test.js} +23 -28
  135. package/build/core/create-stream.test.js.map +1 -0
  136. package/build/core/create.d.ts +22 -0
  137. package/build/core/create.js +71 -0
  138. package/build/core/create.js.map +1 -0
  139. package/build/{create.test.js → core/create.test.js} +25 -46
  140. package/build/core/create.test.js.map +1 -0
  141. package/build/core/definition-site.d.ts +24 -0
  142. package/build/{stack-utils.js → core/definition-site.js} +20 -20
  143. package/build/core/definition-site.js.map +1 -0
  144. package/build/{stack-utils.test.js → core/definition-site.test.js} +12 -3
  145. package/build/core/definition-site.test.js.map +1 -0
  146. package/build/{errors.d.ts → core/errors.d.ts} +19 -8
  147. package/build/{errors.js → core/errors.js} +21 -26
  148. package/build/core/errors.js.map +1 -0
  149. package/build/core/errors.test.js.map +1 -0
  150. package/build/core/factory-options.test.js +82 -0
  151. package/build/core/factory-options.test.js.map +1 -0
  152. package/build/core/http-route.d.ts +13 -0
  153. package/build/core/http-route.js +54 -0
  154. package/build/core/http-route.js.map +1 -0
  155. package/build/core/internal.d.ts +72 -0
  156. package/build/core/internal.js +128 -0
  157. package/build/core/internal.js.map +1 -0
  158. package/build/{migration.test.js → core/migration.test.js} +17 -1
  159. package/build/core/migration.test.js.map +1 -0
  160. package/build/core/procedures.d.ts +143 -0
  161. package/build/core/procedures.js +64 -0
  162. package/build/core/procedures.js.map +1 -0
  163. package/build/{index.test.js → core/procedures.test.js} +14 -11
  164. package/build/core/procedures.test.js.map +1 -0
  165. package/build/core/types.d.ts +182 -0
  166. package/build/{schema → core}/types.js.map +1 -1
  167. package/build/exports.d.ts +31 -11
  168. package/build/exports.js +23 -8
  169. package/build/exports.js.map +1 -1
  170. package/build/schema/adapter.d.ts +35 -0
  171. package/build/schema/adapter.js +13 -0
  172. package/build/schema/adapter.js.map +1 -0
  173. package/build/schema/adapter.test.js +53 -0
  174. package/build/schema/adapter.test.js.map +1 -0
  175. package/build/schema/compile.d.ts +37 -0
  176. package/build/schema/compile.js +38 -0
  177. package/build/schema/compile.js.map +1 -0
  178. package/build/schema/compile.test.js +78 -0
  179. package/build/schema/compile.test.js.map +1 -0
  180. package/build/schema/compute-schema.d.ts +47 -37
  181. package/build/schema/compute-schema.js +86 -29
  182. package/build/schema/compute-schema.js.map +1 -1
  183. package/build/schema/compute-schema.test.js +158 -40
  184. package/build/schema/compute-schema.test.js.map +1 -1
  185. package/build/schema/json-schema.d.ts +17 -0
  186. package/build/schema/json-schema.js +2 -0
  187. package/build/schema/json-schema.js.map +1 -0
  188. package/build/schema/typebox.d.ts +11 -0
  189. package/build/schema/typebox.js +24 -0
  190. package/build/schema/typebox.js.map +1 -0
  191. package/build/schema/typebox.test.js +34 -0
  192. package/build/schema/typebox.test.js.map +1 -0
  193. package/build/server/context.d.ts +8 -0
  194. package/build/server/context.js +7 -0
  195. package/build/server/context.js.map +1 -0
  196. package/build/server/context.test.js +16 -0
  197. package/build/server/context.test.js.map +1 -0
  198. package/build/{doc-envelope.d.ts → server/doc-envelope.d.ts} +1 -1
  199. package/build/server/doc-envelope.js.map +1 -0
  200. package/build/server/doc-envelope.test.d.ts +1 -0
  201. package/build/server/doc-envelope.test.js.map +1 -0
  202. package/build/{implementations/http → server}/doc-registry.d.ts +7 -2
  203. package/build/{implementations/http → server}/doc-registry.js +9 -5
  204. package/build/server/doc-registry.js.map +1 -0
  205. package/build/server/doc-registry.test.d.ts +1 -0
  206. package/build/{implementations/http → server}/doc-registry.test.js +27 -24
  207. package/build/server/doc-registry.test.js.map +1 -0
  208. package/build/server/docs/docs.test.d.ts +1 -0
  209. package/build/server/docs/docs.test.js +237 -0
  210. package/build/server/docs/docs.test.js.map +1 -0
  211. package/build/{implementations/http/hono → server}/docs/http-doc.d.ts +2 -2
  212. package/build/{implementations/http/hono → server}/docs/http-doc.js +1 -1
  213. package/build/server/docs/http-doc.js.map +1 -0
  214. package/build/{implementations/http/hono → server}/docs/http-stream-doc.d.ts +2 -2
  215. package/build/{implementations/http/hono → server}/docs/http-stream-doc.js +1 -1
  216. package/build/server/docs/http-stream-doc.js.map +1 -0
  217. package/build/{implementations/http/hono → server}/docs/rpc-doc.d.ts +2 -2
  218. package/build/{implementations/http/hono → server}/docs/rpc-doc.js +1 -1
  219. package/build/server/docs/rpc-doc.js.map +1 -0
  220. package/build/{implementations/http/hono → server}/docs/stream-doc.d.ts +2 -2
  221. package/build/{implementations/http/hono → server}/docs/stream-doc.js +1 -1
  222. package/build/server/docs/stream-doc.js.map +1 -0
  223. package/build/server/errors/dispatch.d.ts +96 -0
  224. package/build/{implementations/http/error-dispatch.js → server/errors/dispatch.js} +20 -10
  225. package/build/server/errors/dispatch.js.map +1 -0
  226. package/build/server/errors/dispatch.test.d.ts +1 -0
  227. package/build/server/errors/dispatch.test.js +418 -0
  228. package/build/server/errors/dispatch.test.js.map +1 -0
  229. package/build/{implementations/http/error-taxonomy.d.ts → server/errors/taxonomy.d.ts} +8 -17
  230. package/build/{implementations/http/error-taxonomy.js → server/errors/taxonomy.js} +6 -15
  231. package/build/server/errors/taxonomy.js.map +1 -0
  232. package/build/server/errors/taxonomy.test.d.ts +1 -0
  233. package/build/{implementations/http/error-taxonomy.test.js → server/errors/taxonomy.test.js} +45 -39
  234. package/build/server/errors/taxonomy.test.js.map +1 -0
  235. package/build/server/index.d.ts +29 -0
  236. package/build/server/index.js +27 -0
  237. package/build/server/index.js.map +1 -0
  238. package/build/server/no-framework-imports.test.d.ts +1 -0
  239. package/build/server/no-framework-imports.test.js +40 -0
  240. package/build/server/no-framework-imports.test.js.map +1 -0
  241. package/build/{implementations/http/hono/path.d.ts → server/paths.d.ts} +2 -3
  242. package/build/{implementations/http/hono/path.js → server/paths.js} +1 -1
  243. package/build/server/paths.js.map +1 -0
  244. package/build/server/paths.test.d.ts +1 -0
  245. package/build/server/paths.test.js +111 -0
  246. package/build/server/paths.test.js.map +1 -0
  247. package/build/server/request/params.d.ts +29 -0
  248. package/build/server/request/params.js +43 -0
  249. package/build/server/request/params.js.map +1 -0
  250. package/build/server/request/params.test.d.ts +1 -0
  251. package/build/server/request/params.test.js +91 -0
  252. package/build/server/request/params.test.js.map +1 -0
  253. package/build/server/request/query.d.ts +9 -0
  254. package/build/server/request/query.js +22 -0
  255. package/build/server/request/query.js.map +1 -0
  256. package/build/server/request/query.test.d.ts +1 -0
  257. package/build/server/request/query.test.js +60 -0
  258. package/build/server/request/query.test.js.map +1 -0
  259. package/build/server/sse.d.ts +70 -0
  260. package/build/server/sse.js +94 -0
  261. package/build/server/sse.js.map +1 -0
  262. package/build/server/sse.test.d.ts +1 -0
  263. package/build/server/sse.test.js +98 -0
  264. package/build/server/sse.test.js.map +1 -0
  265. package/build/{implementations → server}/types.d.ts +17 -15
  266. package/build/{implementations → server}/types.js.map +1 -1
  267. package/docs/astro-adapter.md +8 -9
  268. package/docs/client-and-codegen.md +4 -4
  269. package/docs/client-error-handling.md +92 -5
  270. package/docs/codegen-kotlin.md +2 -3
  271. package/docs/codegen-swift.md +1 -2
  272. package/docs/core.md +135 -54
  273. package/docs/http-integrations.md +83 -6
  274. package/docs/migration-v8-to-v9.md +192 -0
  275. package/docs/plans/2026-06-09-v9-rewrite.md +130 -0
  276. package/docs/specs/2026-06-09-v9-rewrite-design.md +221 -0
  277. package/docs/streaming.md +12 -0
  278. package/package.json +23 -47
  279. package/src/{implementations/http → adapters}/astro/index.test.ts +2 -2
  280. package/src/adapters/hono/__fixtures__/parity-envelope.json +389 -0
  281. package/src/adapters/hono/envelope-parity.test.ts +126 -0
  282. package/src/{implementations/http → adapters}/hono/handlers/http-stream.test.ts +1 -1
  283. package/src/adapters/hono/handlers/http-stream.ts +73 -0
  284. package/src/{implementations/http → adapters}/hono/handlers/http.test.ts +1 -1
  285. package/src/adapters/hono/handlers/http.ts +70 -0
  286. package/src/{implementations/http → adapters}/hono/handlers/rpc.test.ts +2 -2
  287. package/src/adapters/hono/handlers/rpc.ts +39 -0
  288. package/src/{implementations/http → adapters}/hono/handlers/stream.test.ts +4 -3
  289. package/src/{implementations/http → adapters}/hono/handlers/stream.ts +19 -92
  290. package/src/{implementations/http → adapters}/hono/index.test.ts +14 -16
  291. package/src/{implementations/http → adapters}/hono/index.ts +35 -30
  292. package/src/{implementations/http → adapters/hono}/on-request-error.test.ts +3 -3
  293. package/src/adapters/hono/request.ts +28 -0
  294. package/src/{implementations/http → adapters/hono}/route-errors.test.ts +5 -5
  295. package/src/{implementations/http → adapters}/hono/types.ts +43 -20
  296. package/src/client/freeze.test.ts +41 -0
  297. package/src/client/typed-error-dispatch.test.ts +3 -3
  298. package/src/codegen/__fixtures__/make-envelope.ts +1 -1
  299. package/src/codegen/__fixtures__/models-envelope.json +310 -0
  300. package/src/codegen/__fixtures__/users-envelope.json +9 -0
  301. package/src/codegen/__goldens__/MANIFEST.json +85 -0
  302. package/src/codegen/__goldens__/kotlin-default--models/Billing.kt +112 -0
  303. package/src/codegen/__goldens__/kotlin-default--models/BillingReports.kt +26 -0
  304. package/src/codegen/__goldens__/kotlin-default--models/Orders.kt +88 -0
  305. package/src/codegen/__goldens__/kotlin-default--users/Users.kt +189 -0
  306. package/src/codegen/__goldens__/swift-default--models/Billing.swift +97 -0
  307. package/src/codegen/__goldens__/swift-default--models/BillingReports.swift +20 -0
  308. package/src/codegen/__goldens__/swift-default--models/Orders.swift +81 -0
  309. package/src/codegen/__goldens__/swift-default--users/Users.swift +204 -0
  310. package/src/codegen/__goldens__/ts-default--models/_client.ts +1319 -0
  311. package/src/codegen/__goldens__/ts-default--models/_errors.ts +90 -0
  312. package/src/codegen/__goldens__/ts-default--models/_models.ts +10 -0
  313. package/src/codegen/__goldens__/ts-default--models/_types.ts +502 -0
  314. package/src/codegen/__goldens__/ts-default--models/billing-reports.ts +29 -0
  315. package/src/codegen/__goldens__/ts-default--models/billing.ts +67 -0
  316. package/src/codegen/__goldens__/ts-default--models/index.ts +48 -0
  317. package/src/codegen/__goldens__/ts-default--models/orders.ts +80 -0
  318. package/src/codegen/__goldens__/ts-default--users/_client.ts +1319 -0
  319. package/src/codegen/__goldens__/ts-default--users/_errors.ts +90 -0
  320. package/src/codegen/__goldens__/ts-default--users/_types.ts +502 -0
  321. package/src/codegen/__goldens__/ts-default--users/index.ts +38 -0
  322. package/src/codegen/__goldens__/ts-default--users/users.ts +169 -0
  323. package/src/codegen/__goldens__/ts-external-runtime--models/_errors.ts +90 -0
  324. package/src/codegen/__goldens__/ts-external-runtime--models/_models.ts +10 -0
  325. package/src/codegen/__goldens__/ts-external-runtime--models/billing-reports.ts +29 -0
  326. package/src/codegen/__goldens__/ts-external-runtime--models/billing.ts +67 -0
  327. package/src/codegen/__goldens__/ts-external-runtime--models/index.ts +48 -0
  328. package/src/codegen/__goldens__/ts-external-runtime--models/orders.ts +80 -0
  329. package/src/codegen/__goldens__/ts-external-runtime--users/_errors.ts +90 -0
  330. package/src/codegen/__goldens__/ts-external-runtime--users/index.ts +38 -0
  331. package/src/codegen/__goldens__/ts-external-runtime--users/users.ts +169 -0
  332. package/src/codegen/__goldens__/ts-flat--models/_client.ts +1319 -0
  333. package/src/codegen/__goldens__/ts-flat--models/_errors.ts +87 -0
  334. package/src/codegen/__goldens__/ts-flat--models/_models.ts +10 -0
  335. package/src/codegen/__goldens__/ts-flat--models/_types.ts +502 -0
  336. package/src/codegen/__goldens__/ts-flat--models/billing-reports.ts +28 -0
  337. package/src/codegen/__goldens__/ts-flat--models/billing.ts +51 -0
  338. package/src/codegen/__goldens__/ts-flat--models/index.ts +42 -0
  339. package/src/codegen/__goldens__/ts-flat--models/orders.ts +73 -0
  340. package/src/codegen/__goldens__/ts-flat--users/_client.ts +1319 -0
  341. package/src/codegen/__goldens__/ts-flat--users/_errors.ts +87 -0
  342. package/src/codegen/__goldens__/ts-flat--users/_types.ts +502 -0
  343. package/src/codegen/__goldens__/ts-flat--users/index.ts +34 -0
  344. package/src/codegen/__goldens__/ts-flat--users/users.ts +126 -0
  345. package/src/codegen/__goldens__/ts-no-share-models--models/_client.ts +1319 -0
  346. package/src/codegen/__goldens__/ts-no-share-models--models/_errors.ts +90 -0
  347. package/src/codegen/__goldens__/ts-no-share-models--models/_types.ts +502 -0
  348. package/src/codegen/__goldens__/ts-no-share-models--models/billing-reports.ts +29 -0
  349. package/src/codegen/__goldens__/ts-no-share-models--models/billing.ts +111 -0
  350. package/src/codegen/__goldens__/ts-no-share-models--models/index.ts +48 -0
  351. package/src/codegen/__goldens__/ts-no-share-models--models/orders.ts +112 -0
  352. package/src/codegen/__goldens__/ts-no-share-models--users/_client.ts +1319 -0
  353. package/src/codegen/__goldens__/ts-no-share-models--users/_errors.ts +90 -0
  354. package/src/codegen/__goldens__/ts-no-share-models--users/_types.ts +502 -0
  355. package/src/codegen/__goldens__/ts-no-share-models--users/index.ts +38 -0
  356. package/src/codegen/__goldens__/ts-no-share-models--users/users.ts +169 -0
  357. package/src/codegen/__goldens__/ts-shared-models-module--models/_client.ts +1319 -0
  358. package/src/codegen/__goldens__/ts-shared-models-module--models/_errors.ts +90 -0
  359. package/src/codegen/__goldens__/ts-shared-models-module--models/_models.ts +7 -0
  360. package/src/codegen/__goldens__/ts-shared-models-module--models/_types.ts +502 -0
  361. package/src/codegen/__goldens__/ts-shared-models-module--models/billing-reports.ts +29 -0
  362. package/src/codegen/__goldens__/ts-shared-models-module--models/billing.ts +67 -0
  363. package/src/codegen/__goldens__/ts-shared-models-module--models/index.ts +48 -0
  364. package/src/codegen/__goldens__/ts-shared-models-module--models/orders.ts +80 -0
  365. package/src/codegen/bin/cli.test.ts +13 -2
  366. package/src/codegen/bin/cli.ts +181 -144
  367. package/src/codegen/bin/flag-specs.test.ts +16 -1
  368. package/src/codegen/bin/flag-specs.ts +43 -31
  369. package/src/codegen/bundle-size.test.ts +1 -1
  370. package/src/codegen/collect-models.ts +1 -1
  371. package/src/codegen/e2e.test.ts +1 -1
  372. package/src/codegen/emit/api-route.ts +184 -0
  373. package/src/codegen/emit/context.ts +32 -0
  374. package/src/codegen/emit/declarations.ts +49 -0
  375. package/src/codegen/emit/format-types.ts +232 -0
  376. package/src/codegen/emit/http-stream-route.ts +162 -0
  377. package/src/codegen/emit/route-shared.ts +102 -0
  378. package/src/codegen/emit/rpc-route.ts +49 -0
  379. package/src/codegen/emit/scope-file.ts +226 -0
  380. package/src/codegen/emit/stream-route.ts +81 -0
  381. package/src/codegen/emit-errors.integration.test.ts +2 -2
  382. package/src/codegen/emit-errors.test.ts +1 -1
  383. package/src/codegen/emit-errors.ts +1 -1
  384. package/src/codegen/emit-index.test.ts +34 -0
  385. package/src/codegen/emit-index.ts +19 -0
  386. package/src/codegen/emit-scope.test.ts +96 -6
  387. package/src/codegen/emit-scope.ts +15 -1003
  388. package/src/codegen/goldens.test.ts +89 -0
  389. package/src/codegen/group-routes.test.ts +1 -1
  390. package/src/codegen/group-routes.ts +1 -1
  391. package/src/codegen/pipeline.test.ts +1 -1
  392. package/src/codegen/pipeline.ts +1 -1
  393. package/src/codegen/resolve-envelope.test.ts +1 -1
  394. package/src/codegen/resolve-envelope.ts +1 -1
  395. package/src/codegen/targets/_shared/error-schemas.test.ts +1 -1
  396. package/src/codegen/targets/_shared/error-schemas.ts +1 -1
  397. package/src/codegen/targets/_shared/route-slots.test.ts +1 -1
  398. package/src/codegen/targets/_shared/route-slots.ts +1 -1
  399. package/src/codegen/targets/_shared/target-run.ts +1 -1
  400. package/src/codegen/targets/kotlin/__fixtures__/users-golden.kt +6 -0
  401. package/src/codegen/targets/kotlin/emit-route-kotlin.test.ts +1 -1
  402. package/src/codegen/targets/kotlin/emit-route-kotlin.ts +1 -1
  403. package/src/codegen/targets/kotlin/emit-scope-kotlin.test.ts +1 -1
  404. package/src/codegen/targets/swift/__fixtures__/users-golden.swift +6 -0
  405. package/src/codegen/targets/swift/access-level.test.ts +1 -1
  406. package/src/codegen/targets/swift/emit-route-swift.test.ts +1 -1
  407. package/src/codegen/targets/swift/emit-route-swift.ts +1 -1
  408. package/src/codegen/targets/swift/emit-scope-swift.test.ts +1 -1
  409. package/src/codegen/targets/ts/shared-models.test.ts +1 -1
  410. package/src/{create-http-stream.test.ts → core/create-http-stream.test.ts} +1 -1
  411. package/src/core/create-http-stream.ts +207 -0
  412. package/src/{create-http.test.ts → core/create-http.test.ts} +15 -4
  413. package/src/core/create-http.ts +126 -0
  414. package/src/{create-stream.test.ts → core/create-stream.test.ts} +28 -31
  415. package/src/core/create-stream.ts +142 -0
  416. package/src/{create.test.ts → core/create.test.ts} +25 -57
  417. package/src/core/create.ts +121 -0
  418. package/src/{stack-utils.test.ts → core/definition-site.test.ts} +14 -3
  419. package/src/{stack-utils.ts → core/definition-site.ts} +20 -23
  420. package/src/{errors.test.ts → core/errors.test.ts} +1 -1
  421. package/src/{errors.ts → core/errors.ts} +30 -28
  422. package/src/core/factory-options.test.ts +112 -0
  423. package/src/core/http-route.ts +73 -0
  424. package/src/core/internal.ts +203 -0
  425. package/src/{migration.test.ts → core/migration.test.ts} +23 -1
  426. package/src/{index.test.ts → core/procedures.test.ts} +13 -11
  427. package/src/core/procedures.ts +75 -0
  428. package/src/core/types.ts +195 -0
  429. package/src/exports.ts +60 -11
  430. package/src/schema/adapter.test.ts +58 -0
  431. package/src/schema/adapter.ts +45 -0
  432. package/src/schema/compile.test.ts +95 -0
  433. package/src/schema/compile.ts +64 -0
  434. package/src/schema/compute-schema.test.ts +222 -41
  435. package/src/schema/compute-schema.ts +145 -71
  436. package/src/schema/json-schema.ts +21 -0
  437. package/src/schema/typebox.test.ts +40 -0
  438. package/src/schema/typebox.ts +27 -0
  439. package/src/server/context.test.ts +22 -0
  440. package/src/server/context.ts +18 -0
  441. package/src/{doc-envelope.test.ts → server/doc-envelope.test.ts} +2 -2
  442. package/src/{doc-envelope.ts → server/doc-envelope.ts} +1 -1
  443. package/src/{implementations/http → server}/doc-registry.test.ts +32 -26
  444. package/src/{implementations/http → server}/doc-registry.ts +11 -7
  445. package/src/server/docs/docs.test.ts +287 -0
  446. package/src/{implementations/http/hono → server}/docs/http-doc.ts +3 -3
  447. package/src/{implementations/http/hono → server}/docs/http-stream-doc.ts +3 -3
  448. package/src/{implementations/http/hono → server}/docs/rpc-doc.ts +3 -3
  449. package/src/{implementations/http/hono → server}/docs/stream-doc.ts +3 -3
  450. package/src/server/errors/dispatch.test.ts +450 -0
  451. package/src/server/errors/dispatch.ts +189 -0
  452. package/src/{implementations/http/error-taxonomy.test.ts → server/errors/taxonomy.test.ts} +45 -39
  453. package/src/{implementations/http/error-taxonomy.ts → server/errors/taxonomy.ts} +8 -17
  454. package/src/server/index.ts +29 -0
  455. package/src/server/no-framework-imports.test.ts +43 -0
  456. package/src/server/paths.test.ts +141 -0
  457. package/src/{implementations/http/hono/path.ts → server/paths.ts} +2 -13
  458. package/src/server/request/params.test.ts +143 -0
  459. package/src/server/request/params.ts +68 -0
  460. package/src/server/request/query.test.ts +70 -0
  461. package/src/server/request/query.ts +24 -0
  462. package/src/server/sse.test.ts +113 -0
  463. package/src/server/sse.ts +117 -0
  464. package/src/{implementations → server}/types.ts +17 -16
  465. package/build/create-http-stream.d.ts +0 -58
  466. package/build/create-http-stream.js +0 -122
  467. package/build/create-http-stream.js.map +0 -1
  468. package/build/create-http-stream.test.js.map +0 -1
  469. package/build/create-http.d.ts +0 -49
  470. package/build/create-http.js +0 -108
  471. package/build/create-http.js.map +0 -1
  472. package/build/create-http.test.js.map +0 -1
  473. package/build/create-stream.d.ts +0 -35
  474. package/build/create-stream.js +0 -123
  475. package/build/create-stream.js.map +0 -1
  476. package/build/create-stream.test.js.map +0 -1
  477. package/build/create.d.ts +0 -28
  478. package/build/create.js +0 -82
  479. package/build/create.js.map +0 -1
  480. package/build/create.test.js.map +0 -1
  481. package/build/doc-envelope.js.map +0 -1
  482. package/build/doc-envelope.test.js.map +0 -1
  483. package/build/errors.js.map +0 -1
  484. package/build/errors.test.js.map +0 -1
  485. package/build/implementations/http/astro/astro-context.js.map +0 -1
  486. package/build/implementations/http/astro/create-handler.js.map +0 -1
  487. package/build/implementations/http/astro/index.js.map +0 -1
  488. package/build/implementations/http/astro/index.test.js.map +0 -1
  489. package/build/implementations/http/astro/rewrite-request.js.map +0 -1
  490. package/build/implementations/http/doc-registry.js.map +0 -1
  491. package/build/implementations/http/doc-registry.test.js.map +0 -1
  492. package/build/implementations/http/error-dispatch.d.ts +0 -76
  493. package/build/implementations/http/error-dispatch.js.map +0 -1
  494. package/build/implementations/http/error-dispatch.test.js +0 -254
  495. package/build/implementations/http/error-dispatch.test.js.map +0 -1
  496. package/build/implementations/http/error-taxonomy.js.map +0 -1
  497. package/build/implementations/http/error-taxonomy.test.js.map +0 -1
  498. package/build/implementations/http/hono/docs/http-doc.js.map +0 -1
  499. package/build/implementations/http/hono/docs/http-stream-doc.js.map +0 -1
  500. package/build/implementations/http/hono/docs/rpc-doc.js.map +0 -1
  501. package/build/implementations/http/hono/docs/stream-doc.js.map +0 -1
  502. package/build/implementations/http/hono/handlers/http-stream.js +0 -123
  503. package/build/implementations/http/hono/handlers/http-stream.js.map +0 -1
  504. package/build/implementations/http/hono/handlers/http-stream.test.js.map +0 -1
  505. package/build/implementations/http/hono/handlers/http.js +0 -110
  506. package/build/implementations/http/hono/handlers/http.js.map +0 -1
  507. package/build/implementations/http/hono/handlers/http.test.js.map +0 -1
  508. package/build/implementations/http/hono/handlers/rpc.js +0 -32
  509. package/build/implementations/http/hono/handlers/rpc.js.map +0 -1
  510. package/build/implementations/http/hono/handlers/rpc.test.js.map +0 -1
  511. package/build/implementations/http/hono/handlers/stream.d.ts +0 -23
  512. package/build/implementations/http/hono/handlers/stream.js +0 -147
  513. package/build/implementations/http/hono/handlers/stream.js.map +0 -1
  514. package/build/implementations/http/hono/handlers/stream.test.js.map +0 -1
  515. package/build/implementations/http/hono/index.js.map +0 -1
  516. package/build/implementations/http/hono/index.test.js.map +0 -1
  517. package/build/implementations/http/hono/path.js.map +0 -1
  518. package/build/implementations/http/hono/path.test.js +0 -83
  519. package/build/implementations/http/hono/path.test.js.map +0 -1
  520. package/build/implementations/http/hono/types.d.ts +0 -51
  521. package/build/implementations/http/hono/types.js.map +0 -1
  522. package/build/implementations/http/on-request-error.test.js.map +0 -1
  523. package/build/implementations/http/route-errors.test.js.map +0 -1
  524. package/build/index.d.ts +0 -175
  525. package/build/index.js +0 -47
  526. package/build/index.js.map +0 -1
  527. package/build/index.test.js.map +0 -1
  528. package/build/migration.test.js.map +0 -1
  529. package/build/schema/extract-json-schema.d.ts +0 -2
  530. package/build/schema/extract-json-schema.js +0 -12
  531. package/build/schema/extract-json-schema.js.map +0 -1
  532. package/build/schema/extract-json-schema.test.js +0 -23
  533. package/build/schema/extract-json-schema.test.js.map +0 -1
  534. package/build/schema/parser.d.ts +0 -36
  535. package/build/schema/parser.js +0 -210
  536. package/build/schema/parser.js.map +0 -1
  537. package/build/schema/parser.test.js +0 -120
  538. package/build/schema/parser.test.js.map +0 -1
  539. package/build/schema/resolve-schema-lib.d.ts +0 -12
  540. package/build/schema/resolve-schema-lib.js +0 -11
  541. package/build/schema/resolve-schema-lib.js.map +0 -1
  542. package/build/schema/resolve-schema-lib.test.js +0 -17
  543. package/build/schema/resolve-schema-lib.test.js.map +0 -1
  544. package/build/schema/types.d.ts +0 -8
  545. package/build/schema/types.js +0 -2
  546. package/build/stack-utils.d.ts +0 -25
  547. package/build/stack-utils.js.map +0 -1
  548. package/build/stack-utils.test.js.map +0 -1
  549. package/build/types.d.ts +0 -142
  550. package/build/types.js +0 -2
  551. package/build/types.js.map +0 -1
  552. package/docs/decisions/2026-06-02-monorepo-split-evaluation.md +0 -80
  553. package/docs/handoffs/ajsc-named-type-collision.md +0 -134
  554. package/docs/handoffs/ajsc-named-type-support.md +0 -181
  555. package/docs/handoffs/shared-models-auto-resolve-response.md +0 -181
  556. package/docs/npm-workspaces-migration-plan.md +0 -611
  557. package/docs/superpowers/plans/2026-04-24-doc-registry-simplification.md +0 -886
  558. package/docs/superpowers/plans/2026-04-24-kotlin-codegen-target.md +0 -1265
  559. package/docs/superpowers/plans/2026-04-25-ajsc-v7-kotlin-polish.md +0 -1993
  560. package/docs/superpowers/plans/2026-04-29-safe-result-api.md +0 -2293
  561. package/docs/superpowers/plans/2026-05-07-astro-adapter.md +0 -1391
  562. package/docs/superpowers/plans/2026-05-08-create-http.md +0 -3355
  563. package/docs/superpowers/plans/2026-05-08-hono-app-builder-convergence.md +0 -3365
  564. package/docs/superpowers/plans/2026-06-05-dx-feedback-round.md +0 -1292
  565. package/docs/superpowers/plans/2026-06-06-shared-models-convention-and-diagnostics.md +0 -659
  566. package/docs/superpowers/specs/2026-04-24-kotlin-swift-codegen-design.md +0 -401
  567. package/docs/superpowers/specs/2026-04-25-ajsc-v7-kotlin-polish-design.md +0 -314
  568. package/docs/superpowers/specs/2026-04-25-swift-codegen-design.md +0 -264
  569. package/docs/superpowers/specs/2026-04-29-safe-result-api-design.md +0 -324
  570. package/docs/superpowers/specs/2026-05-07-astro-adapter-design.md +0 -252
  571. package/docs/superpowers/specs/2026-05-08-create-http-design.md +0 -409
  572. package/docs/superpowers/specs/2026-05-08-hono-app-builder-convergence-design.md +0 -411
  573. package/docs/superpowers/specs/2026-06-05-dx-feedback-round-design.md +0 -285
  574. package/src/create-http-stream.ts +0 -191
  575. package/src/create-http.ts +0 -210
  576. package/src/create-stream.ts +0 -228
  577. package/src/create.ts +0 -172
  578. package/src/implementations/http/README.md +0 -390
  579. package/src/implementations/http/error-dispatch.test.ts +0 -283
  580. package/src/implementations/http/error-dispatch.ts +0 -176
  581. package/src/implementations/http/hono/handlers/http-stream.ts +0 -152
  582. package/src/implementations/http/hono/handlers/http.ts +0 -145
  583. package/src/implementations/http/hono/handlers/rpc.ts +0 -54
  584. package/src/implementations/http/hono/path.test.ts +0 -96
  585. package/src/index.ts +0 -101
  586. package/src/schema/extract-json-schema.test.ts +0 -25
  587. package/src/schema/extract-json-schema.ts +0 -15
  588. package/src/schema/parser.test.ts +0 -182
  589. package/src/schema/parser.ts +0 -265
  590. package/src/schema/resolve-schema-lib.test.ts +0 -19
  591. package/src/schema/resolve-schema-lib.ts +0 -29
  592. package/src/schema/types.ts +0 -20
  593. package/src/types.ts +0 -133
  594. /package/build/{implementations/http → adapters}/astro/astro-context.d.ts +0 -0
  595. /package/build/{implementations/http → adapters}/astro/astro-context.js +0 -0
  596. /package/build/{implementations/http → adapters}/astro/create-handler.d.ts +0 -0
  597. /package/build/{implementations/http → adapters}/astro/create-handler.js +0 -0
  598. /package/build/{implementations/http → adapters}/astro/index.d.ts +0 -0
  599. /package/build/{implementations/http → adapters}/astro/index.js +0 -0
  600. /package/build/{implementations/http → adapters}/astro/index.test.d.ts +0 -0
  601. /package/build/{implementations/http → adapters}/astro/rewrite-request.d.ts +0 -0
  602. /package/build/{implementations/http → adapters}/astro/rewrite-request.js +0 -0
  603. /package/build/{create-http-stream.test.d.ts → adapters/hono/envelope-parity.test.d.ts} +0 -0
  604. /package/build/{implementations/http → adapters}/hono/handlers/http-stream.test.d.ts +0 -0
  605. /package/build/{implementations/http → adapters}/hono/handlers/http.test.d.ts +0 -0
  606. /package/build/{implementations/http → adapters}/hono/handlers/rpc.test.d.ts +0 -0
  607. /package/build/{implementations/http → adapters}/hono/handlers/stream.test.d.ts +0 -0
  608. /package/build/{implementations/http → adapters}/hono/index.test.d.ts +0 -0
  609. /package/build/{implementations/http → adapters/hono}/on-request-error.test.d.ts +0 -0
  610. /package/build/{implementations/http → adapters/hono}/route-errors.test.d.ts +0 -0
  611. /package/build/{create-http.test.d.ts → client/freeze.test.d.ts} +0 -0
  612. /package/build/{create-stream.test.d.ts → codegen/goldens.test.d.ts} +0 -0
  613. /package/build/{create.test.d.ts → core/create-http-stream.test.d.ts} +0 -0
  614. /package/build/{doc-envelope.test.d.ts → core/create-http.test.d.ts} +0 -0
  615. /package/build/{errors.test.d.ts → core/create-stream.test.d.ts} +0 -0
  616. /package/build/{implementations/http/doc-registry.test.d.ts → core/create.test.d.ts} +0 -0
  617. /package/build/{implementations/http/error-dispatch.test.d.ts → core/definition-site.test.d.ts} +0 -0
  618. /package/build/{implementations/http/error-taxonomy.test.d.ts → core/errors.test.d.ts} +0 -0
  619. /package/build/{errors.test.js → core/errors.test.js} +0 -0
  620. /package/build/{implementations/http/hono/path.test.d.ts → core/factory-options.test.d.ts} +0 -0
  621. /package/build/{migration.test.d.ts → core/migration.test.d.ts} +0 -0
  622. /package/build/{index.test.d.ts → core/procedures.test.d.ts} +0 -0
  623. /package/build/{implementations/http/hono → core}/types.js +0 -0
  624. /package/build/schema/{extract-json-schema.test.d.ts → adapter.test.d.ts} +0 -0
  625. /package/build/schema/{parser.test.d.ts → compile.test.d.ts} +0 -0
  626. /package/build/schema/{resolve-schema-lib.test.d.ts → typebox.test.d.ts} +0 -0
  627. /package/build/{stack-utils.test.d.ts → server/context.test.d.ts} +0 -0
  628. /package/build/{doc-envelope.js → server/doc-envelope.js} +0 -0
  629. /package/build/{doc-envelope.test.js → server/doc-envelope.test.js} +0 -0
  630. /package/build/{implementations → server}/types.js +0 -0
  631. /package/src/{implementations/http → adapters}/astro/README.md +0 -0
  632. /package/src/{implementations/http → adapters}/astro/astro-context.ts +0 -0
  633. /package/src/{implementations/http → adapters}/astro/create-handler.ts +0 -0
  634. /package/src/{implementations/http → adapters}/astro/index.ts +0 -0
  635. /package/src/{implementations/http → adapters}/astro/rewrite-request.ts +0 -0
@@ -3,9 +3,9 @@ import {
3
3
  ProcedureError,
4
4
  ProcedureValidationError,
5
5
  ProcedureYieldValidationError,
6
- } from '../../errors.js'
7
- import type { TProcedureRegistration } from '../../index.js'
8
- import * as AJV from 'ajv'
6
+ } from '../../core/errors.js'
7
+ import type { TProcedureRegistration } from '../../core/types.js'
8
+ import { Ajv } from 'ajv'
9
9
  import {
10
10
  defineErrorTaxonomy,
11
11
  resolveErrorResponse,
@@ -13,7 +13,7 @@ import {
13
13
  taxonomyToErrorDocs,
14
14
  defaultErrorSchema,
15
15
  defaultErrorBody,
16
- } from './error-taxonomy.js'
16
+ } from './taxonomy.js'
17
17
 
18
18
  class UseCaseError extends Error {
19
19
  constructor(
@@ -34,7 +34,12 @@ class AuthError extends Error {
34
34
  }
35
35
  }
36
36
 
37
- const fakeProcedure = { name: 'Test', config: {}, handler: async () => {} } as unknown as TProcedureRegistration
37
+ const fakeProcedure = {
38
+ name: 'Test',
39
+ kind: 'rpc',
40
+ config: {},
41
+ handler: async () => {},
42
+ } as unknown as TProcedureRegistration
38
43
 
39
44
  describe('defineErrorTaxonomy', () => {
40
45
  test('validates exactly one discriminator per entry', () => {
@@ -78,8 +83,8 @@ describe('resolveErrorResponse', () => {
78
83
  procedure: fakeProcedure,
79
84
  raw: {},
80
85
  })
81
- expect(resolved?.statusCode).toBe(422)
82
- expect(resolved?.body).toEqual({ name: 'UseCaseError', message: 'external' })
86
+ expect(resolved!.statusCode).toBe(422)
87
+ expect(resolved!.body).toEqual({ name: 'UseCaseError', message: 'external' })
83
88
  })
84
89
 
85
90
  test('match predicate catches 3rd-party errors', () => {
@@ -97,8 +102,8 @@ describe('resolveErrorResponse', () => {
97
102
  procedure: fakeProcedure,
98
103
  raw: {},
99
104
  })
100
- expect(resolved?.statusCode).toBe(409)
101
- expect(resolved?.body).toEqual({ name: 'Conflict' })
105
+ expect(resolved!.statusCode).toBe(409)
106
+ expect(resolved!.body).toEqual({ name: 'Conflict' })
102
107
  })
103
108
 
104
109
  test('default toResponse emits { name, message } from entry key', () => {
@@ -111,7 +116,7 @@ describe('resolveErrorResponse', () => {
111
116
  procedure: fakeProcedure,
112
117
  raw: {},
113
118
  })
114
- expect(resolved?.body).toEqual({ name: 'AuthError', message: 'unauthenticated' })
119
+ expect(resolved!.body).toEqual({ name: 'AuthError', message: 'unauthenticated' })
115
120
  })
116
121
 
117
122
  test('first matching entry wins — subclass declared before base', () => {
@@ -134,8 +139,8 @@ describe('resolveErrorResponse', () => {
134
139
  includeDefaults: false,
135
140
  raw: {},
136
141
  })
137
- expect(resolved?.statusCode).toBe(400)
138
- expect(resolved?.body).toEqual({ name: 'ProcedureValidationError', kind: 'validation' })
142
+ expect(resolved!.statusCode).toBe(400)
143
+ expect(resolved!.body).toEqual({ name: 'ProcedureValidationError', kind: 'validation' })
139
144
  })
140
145
 
141
146
  test('topological sort fixes a subclass that was declared after its base', () => {
@@ -160,8 +165,8 @@ describe('resolveErrorResponse', () => {
160
165
  includeDefaults: false,
161
166
  raw: {},
162
167
  })
163
- expect(resolved?.statusCode).toBe(400)
164
- expect(resolved?.body).toEqual({ name: 'ProcedureValidationError', kind: 'validation' })
168
+ expect(resolved!.statusCode).toBe(400)
169
+ expect(resolved!.body).toEqual({ name: 'ProcedureValidationError', kind: 'validation' })
165
170
  })
166
171
 
167
172
  test('falls through to unknownError when nothing matches', () => {
@@ -178,8 +183,8 @@ describe('resolveErrorResponse', () => {
178
183
  procedure: fakeProcedure,
179
184
  raw: {},
180
185
  })
181
- expect(resolved?.statusCode).toBe(500)
182
- expect(resolved?.body).toEqual({ name: 'InternalServerError' })
186
+ expect(resolved!.statusCode).toBe(500)
187
+ expect(resolved!.body).toEqual({ name: 'InternalServerError' })
183
188
  })
184
189
 
185
190
  test('returns null when nothing matches and no unknownError', () => {
@@ -201,8 +206,8 @@ describe('resolveErrorResponse', () => {
201
206
  procedure: fakeProcedure,
202
207
  raw: {},
203
208
  })
204
- expect(resolved?.statusCode).toBe(400)
205
- expect((resolved?.body as any).name).toBe('ProcedureValidationError')
209
+ expect(resolved!.statusCode).toBe(400)
210
+ expect((resolved!.body as any).name).toBe('ProcedureValidationError')
206
211
  })
207
212
 
208
213
  test('default taxonomy catches ProcedureYieldValidationError with status 500', () => {
@@ -211,8 +216,8 @@ describe('resolveErrorResponse', () => {
211
216
  procedure: fakeProcedure,
212
217
  raw: {},
213
218
  })
214
- expect(resolved?.statusCode).toBe(500)
215
- expect((resolved?.body as any).name).toBe('ProcedureYieldValidationError')
219
+ expect(resolved!.statusCode).toBe(500)
220
+ expect((resolved!.body as any).name).toBe('ProcedureYieldValidationError')
216
221
  })
217
222
 
218
223
  test('includeDefaults: false disables the default taxonomy', () => {
@@ -238,8 +243,8 @@ describe('resolveErrorResponse', () => {
238
243
  procedure: fakeProcedure,
239
244
  raw: {},
240
245
  })
241
- expect(resolved?.statusCode).toBe(418)
242
- expect(resolved?.body).toEqual({ name: 'ProcedureValidationError', overridden: true })
246
+ expect(resolved!.statusCode).toBe(418)
247
+ expect(resolved!.body).toEqual({ name: 'ProcedureValidationError', overridden: true })
243
248
  })
244
249
 
245
250
  test('onCatch is awaited via runOnCatch', async () => {
@@ -300,15 +305,15 @@ describe('resolveErrorResponse', () => {
300
305
  err: new AuthError('forbidden'),
301
306
  userTaxonomy: taxonomy,
302
307
  procedure: fakeProcedure,
303
- raw: { marker: 'hono-context' },
308
+ raw: { marker: 'adapter-context' },
304
309
  })
305
310
  await resolved!.runOnCatch()
306
311
  expect(received.procedure).toBe(fakeProcedure)
307
312
  expect(received.key).toBe('AuthError')
308
- expect(received.raw).toEqual({ marker: 'hono-context' })
313
+ expect(received.raw).toEqual({ marker: 'adapter-context' })
309
314
  })
310
315
 
311
- test('defaultErrorTaxonomy exposes all four framework error mappings', () => {
316
+ test('defaultErrorTaxonomy exposes the framework error mappings', () => {
312
317
  expect(defaultErrorTaxonomy.ProcedureValidationError.statusCode).toBe(400)
313
318
  expect(defaultErrorTaxonomy.ProcedureYieldValidationError.statusCode).toBe(500)
314
319
  expect(defaultErrorTaxonomy.ProcedureError.statusCode).toBe(500)
@@ -335,8 +340,8 @@ describe('resolveErrorResponse', () => {
335
340
  procedure: fakeProcedure,
336
341
  raw: {},
337
342
  })
338
- expect(resolved?.statusCode).toBe(422)
339
- expect(resolved?.body).toEqual({ name: 'UseCaseError', message: 'public' })
343
+ expect(resolved!.statusCode).toBe(422)
344
+ expect(resolved!.body).toEqual({ name: 'UseCaseError', message: 'public' })
340
345
  })
341
346
 
342
347
  test('wrapped ProcedureError falls through default taxonomy to unknownError', () => {
@@ -353,8 +358,8 @@ describe('resolveErrorResponse', () => {
353
358
  procedure: fakeProcedure,
354
359
  raw: {},
355
360
  })
356
- expect(resolved?.statusCode).toBe(500)
357
- expect(resolved?.body).toEqual({ name: 'InternalServerError', type: 'TypeError' })
361
+ expect(resolved!.statusCode).toBe(500)
362
+ expect(resolved!.body).toEqual({ name: 'InternalServerError', type: 'TypeError' })
358
363
  })
359
364
 
360
365
  test('direct ProcedureError (no cause) still caught by default entry', () => {
@@ -364,8 +369,8 @@ describe('resolveErrorResponse', () => {
364
369
  procedure: fakeProcedure,
365
370
  raw: {},
366
371
  })
367
- expect(resolved?.statusCode).toBe(500)
368
- expect((resolved?.body as any).name).toBe('ProcedureError')
372
+ expect(resolved!.statusCode).toBe(500)
373
+ expect((resolved!.body as any).name).toBe('ProcedureError')
369
374
  })
370
375
 
371
376
  test('auto-injects name when toResponse omits it', () => {
@@ -383,7 +388,7 @@ describe('resolveErrorResponse', () => {
383
388
  procedure: fakeProcedure,
384
389
  raw: {},
385
390
  })
386
- expect(resolved?.body).toEqual({ name: 'UseCaseError', message: 'ext', detail: 'int' })
391
+ expect(resolved!.body).toEqual({ name: 'UseCaseError', message: 'ext', detail: 'int' })
387
392
  })
388
393
 
389
394
  test('preserves explicit name in toResponse output', () => {
@@ -400,7 +405,7 @@ describe('resolveErrorResponse', () => {
400
405
  procedure: fakeProcedure,
401
406
  raw: {},
402
407
  })
403
- expect((resolved?.body as any).name).toBe('CustomAlias')
408
+ expect((resolved!.body as any).name).toBe('CustomAlias')
404
409
  })
405
410
 
406
411
  test('defineErrorTaxonomy topologically sorts class entries (subclass before base)', () => {
@@ -427,7 +432,7 @@ describe('resolveErrorResponse', () => {
427
432
  procedure: fakeProcedure,
428
433
  raw: {},
429
434
  })
430
- expect(resolved?.statusCode).toBe(400)
435
+ expect(resolved!.statusCode).toBe(400)
431
436
  })
432
437
 
433
438
  test('topological sort preserves declared order for unrelated classes', () => {
@@ -448,8 +453,9 @@ describe('taxonomyToErrorDocs', () => {
448
453
  })
449
454
  const docs = taxonomyToErrorDocs(taxonomy)
450
455
  const auth = docs.find((d) => d.name === 'AuthError')
451
- expect(auth?.statusCode).toBe(401)
452
- expect(auth?.schema).toEqual({
456
+ expect(auth).toBeDefined()
457
+ expect(auth!.statusCode).toBe(401)
458
+ expect(auth!.schema).toEqual({
453
459
  type: 'object',
454
460
  properties: {
455
461
  name: { type: 'string', const: 'AuthError' },
@@ -469,7 +475,7 @@ describe('taxonomyToErrorDocs', () => {
469
475
  })
470
476
  const docs = taxonomyToErrorDocs(taxonomy)
471
477
  const useCase = docs.find((d) => d.name === 'UseCaseError')
472
- expect(useCase?.schema).toBeUndefined()
478
+ expect(useCase!.schema).toBeUndefined()
473
479
  })
474
480
 
475
481
  test('preserves an explicit schema untouched', () => {
@@ -490,7 +496,7 @@ describe('taxonomyToErrorDocs', () => {
490
496
  })
491
497
  const docs = taxonomyToErrorDocs(taxonomy)
492
498
  const useCase = docs.find((d) => d.name === 'UseCaseError')
493
- expect(useCase?.schema).toBe(explicitSchema)
499
+ expect(useCase!.schema).toBe(explicitSchema)
494
500
  })
495
501
  })
496
502
 
@@ -529,7 +535,7 @@ describe('defaultErrorSchema', () => {
529
535
  // against the schema codegen turns into the client error class. If either side
530
536
  // changes shape, this fails before consumers see a mismatch.
531
537
  describe('defaultErrorBody / defaultErrorSchema invariant', () => {
532
- const ajv = new AJV.Ajv()
538
+ const ajv = new Ajv()
533
539
 
534
540
  test('default body validates against the synthesized schema', () => {
535
541
  const schema = defaultErrorSchema('AuthError', { class: AuthError, statusCode: 401 })
@@ -2,12 +2,12 @@ import {
2
2
  ProcedureError,
3
3
  ProcedureValidationError,
4
4
  ProcedureYieldValidationError,
5
- } from '../../errors.js'
6
- import type { TProcedureRegistration, TStreamProcedureRegistration, THttpProcedureRegistration, THttpStreamProcedureRegistration } from '../../index.js'
5
+ } from '../../core/errors.js'
6
+ import type { AnyProcedureRegistration } from '../../core/types.js'
7
7
  import type { ErrorDoc } from '../types.js'
8
8
 
9
9
  /** Any procedure registration — accepted by taxonomy callbacks. */
10
- export type TAnyProcedureRegistration = TProcedureRegistration | TStreamProcedureRegistration | THttpProcedureRegistration<any> | THttpStreamProcedureRegistration<any>
10
+ export type TAnyProcedureRegistration = AnyProcedureRegistration<any, any>
11
11
 
12
12
  /**
13
13
  * An entry in an {@link ErrorTaxonomy}. Describes how to recognize a thrown
@@ -35,7 +35,7 @@ export type ErrorTaxonomyEntry<TError = any, TBody = unknown> = {
35
35
  toResponse?: (err: TError, meta: { key: string }) => TBody
36
36
  /**
37
37
  * Side effect on catch (logging, metrics). Awaited before the response is sent.
38
- * `raw` is the framework-specific request context (e.g., Hono `Context`) —
38
+ * `raw` is the server-adapter-specific request context (e.g., Hono `Context`) —
39
39
  * cast as needed.
40
40
  */
41
41
  onCatch?: (
@@ -222,24 +222,15 @@ export function defaultErrorBody(key: string, err: unknown): { name: string; mes
222
222
  * Synthesizes the response-body JSON Schema for a taxonomy entry that ships
223
223
  * neither an explicit `schema` nor a custom `toResponse`.
224
224
  *
225
- * The common case for `defineErrorTaxonomy` is `{ class, statusCode }` only.
226
- * For those entries the default `toResponse` (see `resolveErrorResponse`) emits
227
- * exactly `{ name: <key>, message }`. Without a schema, `taxonomyToErrorDocs`
228
- * produced a schema-less `ErrorDoc`, and codegen (`emit-errors.ts`) only emits a
229
- * typed client error class for docs that carry a schema — so these entries
230
- * silently fell back to the untyped `ClientHttpError`, while framework errors
231
- * (which ship schemas) worked. That mismatch was confusing.
232
- *
233
- * By describing the default envelope here, the entry becomes self-describing and
234
- * codegen emits a typed client error class with zero ceremony from the consumer.
235
- *
236
225
  * Rules:
237
- * - Entry has an explicit `schema` → caller keeps it (this is not consulted).
226
+ * - Entry has an explicit `schema` → caller keeps it.
238
227
  * - Entry has a custom `toResponse` but no `schema` → returns `undefined`; the
239
228
  * body shape is unknown and we never guess it.
240
229
  * - Entry has neither → returns the `{ name: const <key>, message }` schema that
241
230
  * describes {@link defaultErrorBody} — the exact body the runtime serializes.
242
- * (An invariant test keeps the two from drifting.)
231
+ * (An invariant test keeps the two from drifting.) This makes bare
232
+ * `{ class, statusCode }` entries self-describing so codegen emits a typed
233
+ * client error class with zero ceremony from the consumer.
243
234
  */
244
235
  export function defaultErrorSchema(
245
236
  key: string,
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Transport-agnostic HTTP server layer.
3
+ *
4
+ * Everything a server adapter (Hono, Fastify, Express, ...) needs to serve the
5
+ * four procedure kinds without each adapter re-implementing the framework:
6
+ * route docs, error taxonomy + dispatch, request channel extraction, SSE
7
+ * metadata, path resolution, and the doc registry. Adapters are thin glue:
8
+ * read the request via their framework, call into this layer, write the
9
+ * response via their framework.
10
+ *
11
+ * No framework imports are allowed in this subtree (enforced by test).
12
+ */
13
+ export * from './types.js'
14
+ export * from './paths.js'
15
+ export * from './context.js'
16
+ export * from './sse.js'
17
+ export { BODY_METHODS, defaultSuccessStatus, extractReqChannels } from './request/params.js'
18
+ export type { RequestSource } from './request/params.js'
19
+ export { parseQueryNative, extractQuery } from './request/query.js'
20
+ export type { QueryParser } from './request/query.js'
21
+ export * from './errors/taxonomy.js'
22
+ export * from './errors/dispatch.js'
23
+ export { buildRpcRouteDoc } from './docs/rpc-doc.js'
24
+ export { buildStreamRouteDoc } from './docs/stream-doc.js'
25
+ export { buildHttpRouteDoc } from './docs/http-doc.js'
26
+ export { buildHttpStreamRouteDoc } from './docs/http-stream-doc.js'
27
+ export { DocRegistry } from './doc-registry.js'
28
+ export { writeDocEnvelope } from './doc-envelope.js'
29
+ export type { DocEnvelopeSource } from './doc-envelope.js'
@@ -0,0 +1,43 @@
1
+ import { readFile, readdir } from 'node:fs/promises'
2
+ import { dirname, join } from 'node:path'
3
+ import { fileURLToPath } from 'node:url'
4
+ import { describe, expect, it } from 'vitest'
5
+
6
+ /**
7
+ * Enforces the transport-agnostic guarantee: nothing under src/server may
8
+ * import a web framework. Adapters (src/adapters/*) are the only place
9
+ * framework imports belong.
10
+ */
11
+ const FRAMEWORKS = ['hono', 'astro', 'express', 'fastify']
12
+ const IMPORT_RE = new RegExp(
13
+ `(?:from\\s+|import\\s*\\()\\s*['"](?:${FRAMEWORKS.join('|')})(?:/|['"])`,
14
+ )
15
+
16
+ async function collectSourceFiles(dir: string): Promise<string[]> {
17
+ const entries = await readdir(dir, { withFileTypes: true })
18
+ const files: string[] = []
19
+ for (const entry of entries) {
20
+ const full = join(dir, entry.name)
21
+ if (entry.isDirectory()) {
22
+ files.push(...(await collectSourceFiles(full)))
23
+ } else if (entry.name.endsWith('.ts') && !entry.name.endsWith('.test.ts')) {
24
+ files.push(full)
25
+ }
26
+ }
27
+ return files
28
+ }
29
+
30
+ describe('server layer is framework-free', () => {
31
+ it('no file under src/server imports hono/astro/express/fastify', async () => {
32
+ const serverDir = dirname(fileURLToPath(import.meta.url))
33
+ const files = await collectSourceFiles(serverDir)
34
+ expect(files.length).toBeGreaterThan(10)
35
+
36
+ const offenders: string[] = []
37
+ for (const file of files) {
38
+ const content = await readFile(file, 'utf-8')
39
+ if (IMPORT_RE.test(content)) offenders.push(file)
40
+ }
41
+ expect(offenders, 'framework imports found in server layer').toEqual([])
42
+ })
43
+ })
@@ -0,0 +1,141 @@
1
+ import { describe, expect, test } from 'vitest'
2
+ import type { AnyProcedureRegistration, ProcedureKind } from '../core/types.js'
3
+ import { makeRoutePath, resolveFullPath } from './paths.js'
4
+
5
+ function rpcLike(
6
+ kind: 'rpc' | 'rpc-stream',
7
+ name: string,
8
+ scope: string | string[],
9
+ version: number,
10
+ ): AnyProcedureRegistration<any, any> {
11
+ return {
12
+ name,
13
+ kind,
14
+ config: { scope, version },
15
+ handler: async () => {},
16
+ } as unknown as AnyProcedureRegistration<any, any>
17
+ }
18
+
19
+ function httpLike(
20
+ kind: 'http' | 'http-stream',
21
+ name: string,
22
+ path: string,
23
+ ): AnyProcedureRegistration<any, any> {
24
+ return {
25
+ name,
26
+ kind,
27
+ config: { path, method: 'get' },
28
+ handler: async () => {},
29
+ } as unknown as AnyProcedureRegistration<any, any>
30
+ }
31
+
32
+ describe('makeRoutePath', () => {
33
+ describe('rpc / rpc-stream kinds', () => {
34
+ test('builds /{kebab(scope)}/{kebab(name)}/{version}', () => {
35
+ const path = makeRoutePath({
36
+ procedure: rpcLike('rpc', 'GetUser', 'userAccounts', 1),
37
+ prefix: undefined,
38
+ })
39
+ expect(path).toBe('/user-accounts/get-user/1')
40
+ })
41
+
42
+ test.each(['rpc', 'rpc-stream'] as ProcedureKind[])(
43
+ '%s kind uses the rpc path shape',
44
+ (kind) => {
45
+ const path = makeRoutePath({
46
+ procedure: rpcLike(kind as 'rpc' | 'rpc-stream', 'Echo', 'echo', 2),
47
+ prefix: undefined,
48
+ })
49
+ expect(path).toBe('/echo/echo/2')
50
+ },
51
+ )
52
+
53
+ test('prefix without a leading slash is normalized', () => {
54
+ const path = makeRoutePath({
55
+ procedure: rpcLike('rpc', 'GetUser', 'users', 1),
56
+ prefix: 'api',
57
+ })
58
+ expect(path).toBe('/api/users/get-user/1')
59
+ })
60
+
61
+ test('prefix with a leading slash is kept as-is', () => {
62
+ const path = makeRoutePath({
63
+ procedure: rpcLike('rpc', 'GetUser', 'users', 1),
64
+ prefix: '/api',
65
+ })
66
+ expect(path).toBe('/api/users/get-user/1')
67
+ })
68
+
69
+ test('empty prefix adds nothing', () => {
70
+ const path = makeRoutePath({
71
+ procedure: rpcLike('rpc', 'GetUser', 'users', 1),
72
+ prefix: '',
73
+ })
74
+ expect(path).toBe('/users/get-user/1')
75
+ })
76
+
77
+ test('array scope joins segments with "/" (each kebab-cased)', () => {
78
+ const path = makeRoutePath({
79
+ procedure: rpcLike('rpc-stream', 'WatchOrders', ['admin', 'orderEvents'], 3),
80
+ prefix: '/api',
81
+ })
82
+ expect(path).toBe('/api/admin/order-events/watch-orders/3')
83
+ })
84
+ })
85
+
86
+ describe('http / http-stream kinds', () => {
87
+ test('http kind resolves prefix + config.path', () => {
88
+ const path = makeRoutePath({
89
+ procedure: httpLike('http', 'GetUser', '/users/:id'),
90
+ prefix: '/api',
91
+ })
92
+ expect(path).toBe('/api/users/:id')
93
+ })
94
+
95
+ test('http-stream kind resolves prefix + config.path', () => {
96
+ const path = makeRoutePath({
97
+ procedure: httpLike('http-stream', 'Tail', '/streams/logs'),
98
+ prefix: 'api',
99
+ })
100
+ expect(path).toBe('/api/streams/logs')
101
+ })
102
+
103
+ test('path is used as-given when there is no prefix', () => {
104
+ const path = makeRoutePath({
105
+ procedure: httpLike('http', 'GetUser', '/users/:id'),
106
+ prefix: undefined,
107
+ })
108
+ expect(path).toBe('/users/:id')
109
+ })
110
+ })
111
+ })
112
+
113
+ describe('resolveFullPath', () => {
114
+ test('path without a leading slash gets one', () => {
115
+ expect(resolveFullPath('users')).toBe('/users')
116
+ })
117
+
118
+ test('path with a leading slash is unchanged', () => {
119
+ expect(resolveFullPath('/users')).toBe('/users')
120
+ })
121
+
122
+ test('prefix without a leading slash is normalized', () => {
123
+ expect(resolveFullPath('/users', 'api')).toBe('/api/users')
124
+ })
125
+
126
+ test('prefix with a leading slash is kept', () => {
127
+ expect(resolveFullPath('/users', '/api')).toBe('/api/users')
128
+ })
129
+
130
+ test('both unslashed → single slashes everywhere', () => {
131
+ expect(resolveFullPath('users', 'api')).toBe('/api/users')
132
+ })
133
+
134
+ test('empty prefix adds nothing', () => {
135
+ expect(resolveFullPath('/users', '')).toBe('/users')
136
+ })
137
+
138
+ test('undefined prefix adds nothing', () => {
139
+ expect(resolveFullPath('users', undefined)).toBe('/users')
140
+ })
141
+ })
@@ -1,17 +1,6 @@
1
1
  import { kebabCase } from 'es-toolkit/string'
2
2
  import { castArray } from 'es-toolkit/compat'
3
- import type {
4
- TProcedureRegistration,
5
- TStreamProcedureRegistration,
6
- THttpProcedureRegistration,
7
- THttpStreamProcedureRegistration,
8
- } from '../../../types.js'
9
-
10
- export type AnyProcedureRegistration =
11
- | TProcedureRegistration
12
- | TStreamProcedureRegistration
13
- | THttpProcedureRegistration<any>
14
- | THttpStreamProcedureRegistration<any>
3
+ import type { AnyProcedureRegistration } from '../core/types.js'
15
4
 
16
5
  function normalizePrefix(prefix?: string): string {
17
6
  if (!prefix) return ''
@@ -28,7 +17,7 @@ export function makeRoutePath({
28
17
  procedure,
29
18
  prefix,
30
19
  }: {
31
- procedure: AnyProcedureRegistration
20
+ procedure: AnyProcedureRegistration<any, any>
32
21
  prefix?: string
33
22
  }): string {
34
23
  const normalizedPrefix = normalizePrefix(prefix)
@@ -0,0 +1,143 @@
1
+ import { describe, expect, test } from 'vitest'
2
+ import type { HttpMethod } from '../types.js'
3
+ import type { RequestSource } from './params.js'
4
+ import { BODY_METHODS, defaultSuccessStatus, extractReqChannels } from './params.js'
5
+ import { parseQueryNative } from './query.js'
6
+
7
+ function makeSource(overrides?: Partial<RequestSource>): RequestSource {
8
+ return {
9
+ pathParams: () => ({ id: '42' }),
10
+ url: () => 'http://localhost/users/42?limit=10&tag=a&tag=b',
11
+ json: async () => ({ name: 'Ada' }),
12
+ headers: () => ({ 'x-api-key': 'secret' }),
13
+ ...overrides,
14
+ }
15
+ }
16
+
17
+ describe('extractReqChannels', () => {
18
+ test('extracts only the declared channels', async () => {
19
+ const params = await extractReqChannels(
20
+ makeSource(),
21
+ 'get',
22
+ { query: {} },
23
+ parseQueryNative,
24
+ )
25
+ expect(Object.keys(params)).toEqual(['query'])
26
+ expect(params.query).toEqual({ limit: '10', tag: ['a', 'b'] })
27
+ })
28
+
29
+ test('extracts pathParams channel', async () => {
30
+ const params = await extractReqChannels(
31
+ makeSource(),
32
+ 'get',
33
+ { pathParams: {} },
34
+ parseQueryNative,
35
+ )
36
+ expect(params).toEqual({ pathParams: { id: '42' } })
37
+ })
38
+
39
+ test('extracts headers channel (lowercased keys come from the source)', async () => {
40
+ const params = await extractReqChannels(
41
+ makeSource(),
42
+ 'get',
43
+ { headers: {} },
44
+ parseQueryNative,
45
+ )
46
+ expect(params).toEqual({ headers: { 'x-api-key': 'secret' } })
47
+ })
48
+
49
+ test('extracts all four channels for a body-carrying method', async () => {
50
+ const params = await extractReqChannels(
51
+ makeSource(),
52
+ 'post',
53
+ { pathParams: {}, query: {}, body: {}, headers: {} },
54
+ parseQueryNative,
55
+ )
56
+ expect(params).toEqual({
57
+ pathParams: { id: '42' },
58
+ query: { limit: '10', tag: ['a', 'b'] },
59
+ body: { name: 'Ada' },
60
+ headers: { 'x-api-key': 'secret' },
61
+ })
62
+ })
63
+
64
+ test.each(['post', 'put', 'patch'] as HttpMethod[])(
65
+ 'parses the body for %s',
66
+ async (method) => {
67
+ const params = await extractReqChannels(makeSource(), method, { body: {} }, parseQueryNative)
68
+ expect(params.body).toEqual({ name: 'Ada' })
69
+ },
70
+ )
71
+
72
+ test.each(['get', 'head', 'delete'] as HttpMethod[])(
73
+ 'skips the body for %s (never calls source.json)',
74
+ async (method) => {
75
+ let jsonCalled = false
76
+ const source = makeSource({
77
+ json: async () => {
78
+ jsonCalled = true
79
+ return {}
80
+ },
81
+ })
82
+ const params = await extractReqChannels(source, method, { body: {} }, parseQueryNative)
83
+ expect(jsonCalled).toBe(false)
84
+ expect('body' in params).toBe(false)
85
+ },
86
+ )
87
+
88
+ test('JSON parse failure yields {} so validation reports the missing fields', async () => {
89
+ const source = makeSource({
90
+ json: async () => {
91
+ throw new SyntaxError('Unexpected token')
92
+ },
93
+ })
94
+ const params = await extractReqChannels(source, 'post', { body: {} }, parseQueryNative)
95
+ expect(params.body).toEqual({})
96
+ })
97
+
98
+ test('unknown channel is set to undefined (key present, no extraction)', async () => {
99
+ const params = await extractReqChannels(
100
+ makeSource(),
101
+ 'get',
102
+ { cookies: {} },
103
+ parseQueryNative,
104
+ )
105
+ expect('cookies' in params).toBe(true)
106
+ expect(params.cookies).toBeUndefined()
107
+ })
108
+
109
+ test('empty schema extracts nothing', async () => {
110
+ const params = await extractReqChannels(makeSource(), 'post', {}, parseQueryNative)
111
+ expect(params).toEqual({})
112
+ })
113
+
114
+ test('custom query parser is used for the query channel', async () => {
115
+ const params = await extractReqChannels(
116
+ makeSource(),
117
+ 'get',
118
+ { query: {} },
119
+ (raw) => ({ raw }),
120
+ )
121
+ expect(params.query).toEqual({ raw: 'limit=10&tag=a&tag=b' })
122
+ })
123
+ })
124
+
125
+ describe('BODY_METHODS', () => {
126
+ test('contains exactly post, put, patch', () => {
127
+ expect(BODY_METHODS).toEqual(['post', 'put', 'patch'])
128
+ })
129
+ })
130
+
131
+ describe('defaultSuccessStatus', () => {
132
+ test('post → 201', () => {
133
+ expect(defaultSuccessStatus('post')).toBe(201)
134
+ })
135
+
136
+ test('delete → 204', () => {
137
+ expect(defaultSuccessStatus('delete')).toBe(204)
138
+ })
139
+
140
+ test.each(['get', 'put', 'patch', 'head'] as HttpMethod[])('%s → 200', (method) => {
141
+ expect(defaultSuccessStatus(method)).toBe(200)
142
+ })
143
+ })