ts-procedures 8.6.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 (627) 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-scope.d.ts +13 -30
  103. package/build/codegen/emit-scope.js +15 -844
  104. package/build/codegen/emit-scope.js.map +1 -1
  105. package/build/codegen/goldens.test.js +69 -0
  106. package/build/codegen/goldens.test.js.map +1 -0
  107. package/build/codegen/group-routes.d.ts +1 -1
  108. package/build/codegen/pipeline.d.ts +1 -1
  109. package/build/codegen/resolve-envelope.d.ts +1 -1
  110. package/build/codegen/targets/_shared/error-schemas.d.ts +1 -1
  111. package/build/codegen/targets/_shared/route-slots.d.ts +1 -1
  112. package/build/codegen/targets/_shared/target-run.d.ts +1 -1
  113. package/build/codegen/targets/kotlin/emit-route-kotlin.d.ts +1 -1
  114. package/build/codegen/targets/swift/emit-route-swift.d.ts +1 -1
  115. package/build/core/create-http-stream.d.ts +50 -0
  116. package/build/core/create-http-stream.js +108 -0
  117. package/build/core/create-http-stream.js.map +1 -0
  118. package/build/{create-http-stream.test.js → core/create-http-stream.test.js} +1 -1
  119. package/build/core/create-http-stream.test.js.map +1 -0
  120. package/build/core/create-http.d.ts +51 -0
  121. package/build/core/create-http.js +65 -0
  122. package/build/core/create-http.js.map +1 -0
  123. package/build/{create-http.test.js → core/create-http.test.js} +13 -4
  124. package/build/core/create-http.test.js.map +1 -0
  125. package/build/core/create-stream.d.ts +26 -0
  126. package/build/core/create-stream.js +80 -0
  127. package/build/core/create-stream.js.map +1 -0
  128. package/build/{create-stream.test.js → core/create-stream.test.js} +23 -28
  129. package/build/core/create-stream.test.js.map +1 -0
  130. package/build/core/create.d.ts +22 -0
  131. package/build/core/create.js +71 -0
  132. package/build/core/create.js.map +1 -0
  133. package/build/{create.test.js → core/create.test.js} +25 -46
  134. package/build/core/create.test.js.map +1 -0
  135. package/build/core/definition-site.d.ts +24 -0
  136. package/build/{stack-utils.js → core/definition-site.js} +20 -20
  137. package/build/core/definition-site.js.map +1 -0
  138. package/build/{stack-utils.test.js → core/definition-site.test.js} +12 -3
  139. package/build/core/definition-site.test.js.map +1 -0
  140. package/build/{errors.d.ts → core/errors.d.ts} +19 -8
  141. package/build/{errors.js → core/errors.js} +21 -26
  142. package/build/core/errors.js.map +1 -0
  143. package/build/core/errors.test.js.map +1 -0
  144. package/build/core/factory-options.test.js +82 -0
  145. package/build/core/factory-options.test.js.map +1 -0
  146. package/build/core/http-route.d.ts +13 -0
  147. package/build/core/http-route.js +54 -0
  148. package/build/core/http-route.js.map +1 -0
  149. package/build/core/internal.d.ts +72 -0
  150. package/build/core/internal.js +128 -0
  151. package/build/core/internal.js.map +1 -0
  152. package/build/{migration.test.js → core/migration.test.js} +17 -1
  153. package/build/core/migration.test.js.map +1 -0
  154. package/build/core/procedures.d.ts +143 -0
  155. package/build/core/procedures.js +64 -0
  156. package/build/core/procedures.js.map +1 -0
  157. package/build/{index.test.js → core/procedures.test.js} +14 -11
  158. package/build/core/procedures.test.js.map +1 -0
  159. package/build/core/types.d.ts +182 -0
  160. package/build/{schema → core}/types.js.map +1 -1
  161. package/build/exports.d.ts +31 -11
  162. package/build/exports.js +23 -8
  163. package/build/exports.js.map +1 -1
  164. package/build/schema/adapter.d.ts +35 -0
  165. package/build/schema/adapter.js +13 -0
  166. package/build/schema/adapter.js.map +1 -0
  167. package/build/schema/adapter.test.js +53 -0
  168. package/build/schema/adapter.test.js.map +1 -0
  169. package/build/schema/compile.d.ts +37 -0
  170. package/build/schema/compile.js +38 -0
  171. package/build/schema/compile.js.map +1 -0
  172. package/build/schema/compile.test.js +78 -0
  173. package/build/schema/compile.test.js.map +1 -0
  174. package/build/schema/compute-schema.d.ts +47 -37
  175. package/build/schema/compute-schema.js +86 -29
  176. package/build/schema/compute-schema.js.map +1 -1
  177. package/build/schema/compute-schema.test.js +158 -40
  178. package/build/schema/compute-schema.test.js.map +1 -1
  179. package/build/schema/json-schema.d.ts +17 -0
  180. package/build/schema/json-schema.js +2 -0
  181. package/build/schema/json-schema.js.map +1 -0
  182. package/build/schema/typebox.d.ts +11 -0
  183. package/build/schema/typebox.js +24 -0
  184. package/build/schema/typebox.js.map +1 -0
  185. package/build/schema/typebox.test.js +34 -0
  186. package/build/schema/typebox.test.js.map +1 -0
  187. package/build/server/context.d.ts +8 -0
  188. package/build/server/context.js +7 -0
  189. package/build/server/context.js.map +1 -0
  190. package/build/server/context.test.js +16 -0
  191. package/build/server/context.test.js.map +1 -0
  192. package/build/{doc-envelope.d.ts → server/doc-envelope.d.ts} +1 -1
  193. package/build/server/doc-envelope.js.map +1 -0
  194. package/build/server/doc-envelope.test.d.ts +1 -0
  195. package/build/server/doc-envelope.test.js.map +1 -0
  196. package/build/{implementations/http → server}/doc-registry.d.ts +7 -2
  197. package/build/{implementations/http → server}/doc-registry.js +9 -5
  198. package/build/server/doc-registry.js.map +1 -0
  199. package/build/server/doc-registry.test.d.ts +1 -0
  200. package/build/{implementations/http → server}/doc-registry.test.js +27 -24
  201. package/build/server/doc-registry.test.js.map +1 -0
  202. package/build/server/docs/docs.test.d.ts +1 -0
  203. package/build/server/docs/docs.test.js +237 -0
  204. package/build/server/docs/docs.test.js.map +1 -0
  205. package/build/{implementations/http/hono → server}/docs/http-doc.d.ts +2 -2
  206. package/build/{implementations/http/hono → server}/docs/http-doc.js +1 -1
  207. package/build/server/docs/http-doc.js.map +1 -0
  208. package/build/{implementations/http/hono → server}/docs/http-stream-doc.d.ts +2 -2
  209. package/build/{implementations/http/hono → server}/docs/http-stream-doc.js +1 -1
  210. package/build/server/docs/http-stream-doc.js.map +1 -0
  211. package/build/{implementations/http/hono → server}/docs/rpc-doc.d.ts +2 -2
  212. package/build/{implementations/http/hono → server}/docs/rpc-doc.js +1 -1
  213. package/build/server/docs/rpc-doc.js.map +1 -0
  214. package/build/{implementations/http/hono → server}/docs/stream-doc.d.ts +2 -2
  215. package/build/{implementations/http/hono → server}/docs/stream-doc.js +1 -1
  216. package/build/server/docs/stream-doc.js.map +1 -0
  217. package/build/server/errors/dispatch.d.ts +96 -0
  218. package/build/{implementations/http/error-dispatch.js → server/errors/dispatch.js} +20 -10
  219. package/build/server/errors/dispatch.js.map +1 -0
  220. package/build/server/errors/dispatch.test.d.ts +1 -0
  221. package/build/server/errors/dispatch.test.js +418 -0
  222. package/build/server/errors/dispatch.test.js.map +1 -0
  223. package/build/{implementations/http/error-taxonomy.d.ts → server/errors/taxonomy.d.ts} +8 -17
  224. package/build/{implementations/http/error-taxonomy.js → server/errors/taxonomy.js} +6 -15
  225. package/build/server/errors/taxonomy.js.map +1 -0
  226. package/build/server/errors/taxonomy.test.d.ts +1 -0
  227. package/build/{implementations/http/error-taxonomy.test.js → server/errors/taxonomy.test.js} +45 -39
  228. package/build/server/errors/taxonomy.test.js.map +1 -0
  229. package/build/server/index.d.ts +29 -0
  230. package/build/server/index.js +27 -0
  231. package/build/server/index.js.map +1 -0
  232. package/build/server/no-framework-imports.test.d.ts +1 -0
  233. package/build/server/no-framework-imports.test.js +40 -0
  234. package/build/server/no-framework-imports.test.js.map +1 -0
  235. package/build/{implementations/http/hono/path.d.ts → server/paths.d.ts} +2 -3
  236. package/build/{implementations/http/hono/path.js → server/paths.js} +1 -1
  237. package/build/server/paths.js.map +1 -0
  238. package/build/server/paths.test.d.ts +1 -0
  239. package/build/server/paths.test.js +111 -0
  240. package/build/server/paths.test.js.map +1 -0
  241. package/build/server/request/params.d.ts +29 -0
  242. package/build/server/request/params.js +43 -0
  243. package/build/server/request/params.js.map +1 -0
  244. package/build/server/request/params.test.d.ts +1 -0
  245. package/build/server/request/params.test.js +91 -0
  246. package/build/server/request/params.test.js.map +1 -0
  247. package/build/server/request/query.d.ts +9 -0
  248. package/build/server/request/query.js +22 -0
  249. package/build/server/request/query.js.map +1 -0
  250. package/build/server/request/query.test.d.ts +1 -0
  251. package/build/server/request/query.test.js +60 -0
  252. package/build/server/request/query.test.js.map +1 -0
  253. package/build/server/sse.d.ts +70 -0
  254. package/build/server/sse.js +94 -0
  255. package/build/server/sse.js.map +1 -0
  256. package/build/server/sse.test.d.ts +1 -0
  257. package/build/server/sse.test.js +98 -0
  258. package/build/server/sse.test.js.map +1 -0
  259. package/build/{implementations → server}/types.d.ts +17 -15
  260. package/build/{implementations → server}/types.js.map +1 -1
  261. package/docs/astro-adapter.md +8 -9
  262. package/docs/client-and-codegen.md +4 -4
  263. package/docs/client-error-handling.md +5 -5
  264. package/docs/codegen-kotlin.md +2 -3
  265. package/docs/codegen-swift.md +1 -2
  266. package/docs/core.md +135 -54
  267. package/docs/http-integrations.md +58 -6
  268. package/docs/migration-v8-to-v9.md +192 -0
  269. package/docs/plans/2026-06-09-v9-rewrite.md +130 -0
  270. package/docs/specs/2026-06-09-v9-rewrite-design.md +221 -0
  271. package/docs/streaming.md +12 -0
  272. package/package.json +23 -47
  273. package/src/{implementations/http → adapters}/astro/index.test.ts +2 -2
  274. package/src/adapters/hono/__fixtures__/parity-envelope.json +389 -0
  275. package/src/adapters/hono/envelope-parity.test.ts +126 -0
  276. package/src/{implementations/http → adapters}/hono/handlers/http-stream.test.ts +1 -1
  277. package/src/adapters/hono/handlers/http-stream.ts +73 -0
  278. package/src/{implementations/http → adapters}/hono/handlers/http.test.ts +1 -1
  279. package/src/adapters/hono/handlers/http.ts +70 -0
  280. package/src/{implementations/http → adapters}/hono/handlers/rpc.test.ts +2 -2
  281. package/src/adapters/hono/handlers/rpc.ts +39 -0
  282. package/src/{implementations/http → adapters}/hono/handlers/stream.test.ts +4 -3
  283. package/src/{implementations/http → adapters}/hono/handlers/stream.ts +19 -92
  284. package/src/{implementations/http → adapters}/hono/index.test.ts +14 -16
  285. package/src/{implementations/http → adapters}/hono/index.ts +35 -30
  286. package/src/{implementations/http → adapters/hono}/on-request-error.test.ts +3 -3
  287. package/src/adapters/hono/request.ts +28 -0
  288. package/src/{implementations/http → adapters/hono}/route-errors.test.ts +5 -5
  289. package/src/{implementations/http → adapters}/hono/types.ts +43 -20
  290. package/src/client/freeze.test.ts +41 -0
  291. package/src/client/typed-error-dispatch.test.ts +3 -3
  292. package/src/codegen/__fixtures__/make-envelope.ts +1 -1
  293. package/src/codegen/__fixtures__/models-envelope.json +310 -0
  294. package/src/codegen/__goldens__/MANIFEST.json +85 -0
  295. package/src/codegen/__goldens__/kotlin-default--models/Billing.kt +112 -0
  296. package/src/codegen/__goldens__/kotlin-default--models/BillingReports.kt +26 -0
  297. package/src/codegen/__goldens__/kotlin-default--models/Orders.kt +88 -0
  298. package/src/codegen/__goldens__/kotlin-default--users/Users.kt +189 -0
  299. package/src/codegen/__goldens__/swift-default--models/Billing.swift +97 -0
  300. package/src/codegen/__goldens__/swift-default--models/BillingReports.swift +20 -0
  301. package/src/codegen/__goldens__/swift-default--models/Orders.swift +81 -0
  302. package/src/codegen/__goldens__/swift-default--users/Users.swift +204 -0
  303. package/src/codegen/__goldens__/ts-default--models/_client.ts +1319 -0
  304. package/src/codegen/__goldens__/ts-default--models/_errors.ts +90 -0
  305. package/src/codegen/__goldens__/ts-default--models/_models.ts +10 -0
  306. package/src/codegen/__goldens__/ts-default--models/_types.ts +502 -0
  307. package/src/codegen/__goldens__/ts-default--models/billing-reports.ts +29 -0
  308. package/src/codegen/__goldens__/ts-default--models/billing.ts +67 -0
  309. package/src/codegen/__goldens__/ts-default--models/index.ts +48 -0
  310. package/src/codegen/__goldens__/ts-default--models/orders.ts +80 -0
  311. package/src/codegen/__goldens__/ts-default--users/_client.ts +1319 -0
  312. package/src/codegen/__goldens__/ts-default--users/_errors.ts +90 -0
  313. package/src/codegen/__goldens__/ts-default--users/_types.ts +502 -0
  314. package/src/codegen/__goldens__/ts-default--users/index.ts +38 -0
  315. package/src/codegen/__goldens__/ts-default--users/users.ts +169 -0
  316. package/src/codegen/__goldens__/ts-external-runtime--models/_errors.ts +90 -0
  317. package/src/codegen/__goldens__/ts-external-runtime--models/_models.ts +10 -0
  318. package/src/codegen/__goldens__/ts-external-runtime--models/billing-reports.ts +29 -0
  319. package/src/codegen/__goldens__/ts-external-runtime--models/billing.ts +67 -0
  320. package/src/codegen/__goldens__/ts-external-runtime--models/index.ts +48 -0
  321. package/src/codegen/__goldens__/ts-external-runtime--models/orders.ts +80 -0
  322. package/src/codegen/__goldens__/ts-external-runtime--users/_errors.ts +90 -0
  323. package/src/codegen/__goldens__/ts-external-runtime--users/index.ts +38 -0
  324. package/src/codegen/__goldens__/ts-external-runtime--users/users.ts +169 -0
  325. package/src/codegen/__goldens__/ts-flat--models/_client.ts +1319 -0
  326. package/src/codegen/__goldens__/ts-flat--models/_errors.ts +87 -0
  327. package/src/codegen/__goldens__/ts-flat--models/_models.ts +10 -0
  328. package/src/codegen/__goldens__/ts-flat--models/_types.ts +502 -0
  329. package/src/codegen/__goldens__/ts-flat--models/billing-reports.ts +28 -0
  330. package/src/codegen/__goldens__/ts-flat--models/billing.ts +51 -0
  331. package/src/codegen/__goldens__/ts-flat--models/index.ts +42 -0
  332. package/src/codegen/__goldens__/ts-flat--models/orders.ts +73 -0
  333. package/src/codegen/__goldens__/ts-flat--users/_client.ts +1319 -0
  334. package/src/codegen/__goldens__/ts-flat--users/_errors.ts +87 -0
  335. package/src/codegen/__goldens__/ts-flat--users/_types.ts +502 -0
  336. package/src/codegen/__goldens__/ts-flat--users/index.ts +34 -0
  337. package/src/codegen/__goldens__/ts-flat--users/users.ts +126 -0
  338. package/src/codegen/__goldens__/ts-no-share-models--models/_client.ts +1319 -0
  339. package/src/codegen/__goldens__/ts-no-share-models--models/_errors.ts +90 -0
  340. package/src/codegen/__goldens__/ts-no-share-models--models/_types.ts +502 -0
  341. package/src/codegen/__goldens__/ts-no-share-models--models/billing-reports.ts +29 -0
  342. package/src/codegen/__goldens__/ts-no-share-models--models/billing.ts +111 -0
  343. package/src/codegen/__goldens__/ts-no-share-models--models/index.ts +48 -0
  344. package/src/codegen/__goldens__/ts-no-share-models--models/orders.ts +112 -0
  345. package/src/codegen/__goldens__/ts-no-share-models--users/_client.ts +1319 -0
  346. package/src/codegen/__goldens__/ts-no-share-models--users/_errors.ts +90 -0
  347. package/src/codegen/__goldens__/ts-no-share-models--users/_types.ts +502 -0
  348. package/src/codegen/__goldens__/ts-no-share-models--users/index.ts +38 -0
  349. package/src/codegen/__goldens__/ts-no-share-models--users/users.ts +169 -0
  350. package/src/codegen/__goldens__/ts-shared-models-module--models/_client.ts +1319 -0
  351. package/src/codegen/__goldens__/ts-shared-models-module--models/_errors.ts +90 -0
  352. package/src/codegen/__goldens__/ts-shared-models-module--models/_models.ts +7 -0
  353. package/src/codegen/__goldens__/ts-shared-models-module--models/_types.ts +502 -0
  354. package/src/codegen/__goldens__/ts-shared-models-module--models/billing-reports.ts +29 -0
  355. package/src/codegen/__goldens__/ts-shared-models-module--models/billing.ts +67 -0
  356. package/src/codegen/__goldens__/ts-shared-models-module--models/index.ts +48 -0
  357. package/src/codegen/__goldens__/ts-shared-models-module--models/orders.ts +80 -0
  358. package/src/codegen/bin/cli.test.ts +13 -2
  359. package/src/codegen/bin/cli.ts +181 -144
  360. package/src/codegen/bin/flag-specs.test.ts +16 -1
  361. package/src/codegen/bin/flag-specs.ts +43 -31
  362. package/src/codegen/bundle-size.test.ts +1 -1
  363. package/src/codegen/collect-models.ts +1 -1
  364. package/src/codegen/e2e.test.ts +1 -1
  365. package/src/codegen/emit/api-route.ts +184 -0
  366. package/src/codegen/emit/context.ts +32 -0
  367. package/src/codegen/emit/declarations.ts +49 -0
  368. package/src/codegen/emit/format-types.ts +232 -0
  369. package/src/codegen/emit/http-stream-route.ts +162 -0
  370. package/src/codegen/emit/route-shared.ts +102 -0
  371. package/src/codegen/emit/rpc-route.ts +49 -0
  372. package/src/codegen/emit/scope-file.ts +226 -0
  373. package/src/codegen/emit/stream-route.ts +81 -0
  374. package/src/codegen/emit-errors.integration.test.ts +2 -2
  375. package/src/codegen/emit-errors.test.ts +1 -1
  376. package/src/codegen/emit-errors.ts +1 -1
  377. package/src/codegen/emit-scope.test.ts +2 -2
  378. package/src/codegen/emit-scope.ts +15 -1048
  379. package/src/codegen/goldens.test.ts +89 -0
  380. package/src/codegen/group-routes.test.ts +1 -1
  381. package/src/codegen/group-routes.ts +1 -1
  382. package/src/codegen/pipeline.test.ts +1 -1
  383. package/src/codegen/pipeline.ts +1 -1
  384. package/src/codegen/resolve-envelope.test.ts +1 -1
  385. package/src/codegen/resolve-envelope.ts +1 -1
  386. package/src/codegen/targets/_shared/error-schemas.test.ts +1 -1
  387. package/src/codegen/targets/_shared/error-schemas.ts +1 -1
  388. package/src/codegen/targets/_shared/route-slots.test.ts +1 -1
  389. package/src/codegen/targets/_shared/route-slots.ts +1 -1
  390. package/src/codegen/targets/_shared/target-run.ts +1 -1
  391. package/src/codegen/targets/kotlin/emit-route-kotlin.test.ts +1 -1
  392. package/src/codegen/targets/kotlin/emit-route-kotlin.ts +1 -1
  393. package/src/codegen/targets/kotlin/emit-scope-kotlin.test.ts +1 -1
  394. package/src/codegen/targets/swift/access-level.test.ts +1 -1
  395. package/src/codegen/targets/swift/emit-route-swift.test.ts +1 -1
  396. package/src/codegen/targets/swift/emit-route-swift.ts +1 -1
  397. package/src/codegen/targets/swift/emit-scope-swift.test.ts +1 -1
  398. package/src/codegen/targets/ts/shared-models.test.ts +1 -1
  399. package/src/{create-http-stream.test.ts → core/create-http-stream.test.ts} +1 -1
  400. package/src/core/create-http-stream.ts +207 -0
  401. package/src/{create-http.test.ts → core/create-http.test.ts} +15 -4
  402. package/src/core/create-http.ts +126 -0
  403. package/src/{create-stream.test.ts → core/create-stream.test.ts} +28 -31
  404. package/src/core/create-stream.ts +142 -0
  405. package/src/{create.test.ts → core/create.test.ts} +25 -57
  406. package/src/core/create.ts +121 -0
  407. package/src/{stack-utils.test.ts → core/definition-site.test.ts} +14 -3
  408. package/src/{stack-utils.ts → core/definition-site.ts} +20 -23
  409. package/src/{errors.test.ts → core/errors.test.ts} +1 -1
  410. package/src/{errors.ts → core/errors.ts} +30 -28
  411. package/src/core/factory-options.test.ts +112 -0
  412. package/src/core/http-route.ts +73 -0
  413. package/src/core/internal.ts +203 -0
  414. package/src/{migration.test.ts → core/migration.test.ts} +23 -1
  415. package/src/{index.test.ts → core/procedures.test.ts} +13 -11
  416. package/src/core/procedures.ts +75 -0
  417. package/src/core/types.ts +195 -0
  418. package/src/exports.ts +60 -11
  419. package/src/schema/adapter.test.ts +58 -0
  420. package/src/schema/adapter.ts +45 -0
  421. package/src/schema/compile.test.ts +95 -0
  422. package/src/schema/compile.ts +64 -0
  423. package/src/schema/compute-schema.test.ts +222 -41
  424. package/src/schema/compute-schema.ts +145 -71
  425. package/src/schema/json-schema.ts +21 -0
  426. package/src/schema/typebox.test.ts +40 -0
  427. package/src/schema/typebox.ts +27 -0
  428. package/src/server/context.test.ts +22 -0
  429. package/src/server/context.ts +18 -0
  430. package/src/{doc-envelope.test.ts → server/doc-envelope.test.ts} +2 -2
  431. package/src/{doc-envelope.ts → server/doc-envelope.ts} +1 -1
  432. package/src/{implementations/http → server}/doc-registry.test.ts +32 -26
  433. package/src/{implementations/http → server}/doc-registry.ts +11 -7
  434. package/src/server/docs/docs.test.ts +287 -0
  435. package/src/{implementations/http/hono → server}/docs/http-doc.ts +3 -3
  436. package/src/{implementations/http/hono → server}/docs/http-stream-doc.ts +3 -3
  437. package/src/{implementations/http/hono → server}/docs/rpc-doc.ts +3 -3
  438. package/src/{implementations/http/hono → server}/docs/stream-doc.ts +3 -3
  439. package/src/server/errors/dispatch.test.ts +450 -0
  440. package/src/server/errors/dispatch.ts +189 -0
  441. package/src/{implementations/http/error-taxonomy.test.ts → server/errors/taxonomy.test.ts} +45 -39
  442. package/src/{implementations/http/error-taxonomy.ts → server/errors/taxonomy.ts} +8 -17
  443. package/src/server/index.ts +29 -0
  444. package/src/server/no-framework-imports.test.ts +43 -0
  445. package/src/server/paths.test.ts +141 -0
  446. package/src/{implementations/http/hono/path.ts → server/paths.ts} +2 -13
  447. package/src/server/request/params.test.ts +143 -0
  448. package/src/server/request/params.ts +68 -0
  449. package/src/server/request/query.test.ts +70 -0
  450. package/src/server/request/query.ts +24 -0
  451. package/src/server/sse.test.ts +113 -0
  452. package/src/server/sse.ts +117 -0
  453. package/src/{implementations → server}/types.ts +17 -16
  454. package/build/create-http-stream.d.ts +0 -58
  455. package/build/create-http-stream.js +0 -122
  456. package/build/create-http-stream.js.map +0 -1
  457. package/build/create-http-stream.test.js.map +0 -1
  458. package/build/create-http.d.ts +0 -49
  459. package/build/create-http.js +0 -108
  460. package/build/create-http.js.map +0 -1
  461. package/build/create-http.test.js.map +0 -1
  462. package/build/create-stream.d.ts +0 -35
  463. package/build/create-stream.js +0 -123
  464. package/build/create-stream.js.map +0 -1
  465. package/build/create-stream.test.js.map +0 -1
  466. package/build/create.d.ts +0 -28
  467. package/build/create.js +0 -82
  468. package/build/create.js.map +0 -1
  469. package/build/create.test.js.map +0 -1
  470. package/build/doc-envelope.js.map +0 -1
  471. package/build/doc-envelope.test.js.map +0 -1
  472. package/build/errors.js.map +0 -1
  473. package/build/errors.test.js.map +0 -1
  474. package/build/implementations/http/astro/astro-context.js.map +0 -1
  475. package/build/implementations/http/astro/create-handler.js.map +0 -1
  476. package/build/implementations/http/astro/index.js.map +0 -1
  477. package/build/implementations/http/astro/index.test.js.map +0 -1
  478. package/build/implementations/http/astro/rewrite-request.js.map +0 -1
  479. package/build/implementations/http/doc-registry.js.map +0 -1
  480. package/build/implementations/http/doc-registry.test.js.map +0 -1
  481. package/build/implementations/http/error-dispatch.d.ts +0 -76
  482. package/build/implementations/http/error-dispatch.js.map +0 -1
  483. package/build/implementations/http/error-dispatch.test.js +0 -254
  484. package/build/implementations/http/error-dispatch.test.js.map +0 -1
  485. package/build/implementations/http/error-taxonomy.js.map +0 -1
  486. package/build/implementations/http/error-taxonomy.test.js.map +0 -1
  487. package/build/implementations/http/hono/docs/http-doc.js.map +0 -1
  488. package/build/implementations/http/hono/docs/http-stream-doc.js.map +0 -1
  489. package/build/implementations/http/hono/docs/rpc-doc.js.map +0 -1
  490. package/build/implementations/http/hono/docs/stream-doc.js.map +0 -1
  491. package/build/implementations/http/hono/handlers/http-stream.js +0 -123
  492. package/build/implementations/http/hono/handlers/http-stream.js.map +0 -1
  493. package/build/implementations/http/hono/handlers/http-stream.test.js.map +0 -1
  494. package/build/implementations/http/hono/handlers/http.js +0 -110
  495. package/build/implementations/http/hono/handlers/http.js.map +0 -1
  496. package/build/implementations/http/hono/handlers/http.test.js.map +0 -1
  497. package/build/implementations/http/hono/handlers/rpc.js +0 -32
  498. package/build/implementations/http/hono/handlers/rpc.js.map +0 -1
  499. package/build/implementations/http/hono/handlers/rpc.test.js.map +0 -1
  500. package/build/implementations/http/hono/handlers/stream.d.ts +0 -23
  501. package/build/implementations/http/hono/handlers/stream.js +0 -147
  502. package/build/implementations/http/hono/handlers/stream.js.map +0 -1
  503. package/build/implementations/http/hono/handlers/stream.test.js.map +0 -1
  504. package/build/implementations/http/hono/index.js.map +0 -1
  505. package/build/implementations/http/hono/index.test.js.map +0 -1
  506. package/build/implementations/http/hono/path.js.map +0 -1
  507. package/build/implementations/http/hono/path.test.js +0 -83
  508. package/build/implementations/http/hono/path.test.js.map +0 -1
  509. package/build/implementations/http/hono/types.d.ts +0 -51
  510. package/build/implementations/http/hono/types.js.map +0 -1
  511. package/build/implementations/http/on-request-error.test.js.map +0 -1
  512. package/build/implementations/http/route-errors.test.js.map +0 -1
  513. package/build/index.d.ts +0 -175
  514. package/build/index.js +0 -47
  515. package/build/index.js.map +0 -1
  516. package/build/index.test.js.map +0 -1
  517. package/build/migration.test.js.map +0 -1
  518. package/build/schema/extract-json-schema.d.ts +0 -2
  519. package/build/schema/extract-json-schema.js +0 -12
  520. package/build/schema/extract-json-schema.js.map +0 -1
  521. package/build/schema/extract-json-schema.test.js +0 -23
  522. package/build/schema/extract-json-schema.test.js.map +0 -1
  523. package/build/schema/parser.d.ts +0 -36
  524. package/build/schema/parser.js +0 -210
  525. package/build/schema/parser.js.map +0 -1
  526. package/build/schema/parser.test.js +0 -120
  527. package/build/schema/parser.test.js.map +0 -1
  528. package/build/schema/resolve-schema-lib.d.ts +0 -12
  529. package/build/schema/resolve-schema-lib.js +0 -11
  530. package/build/schema/resolve-schema-lib.js.map +0 -1
  531. package/build/schema/resolve-schema-lib.test.js +0 -17
  532. package/build/schema/resolve-schema-lib.test.js.map +0 -1
  533. package/build/schema/types.d.ts +0 -8
  534. package/build/schema/types.js +0 -2
  535. package/build/stack-utils.d.ts +0 -25
  536. package/build/stack-utils.js.map +0 -1
  537. package/build/stack-utils.test.js.map +0 -1
  538. package/build/types.d.ts +0 -142
  539. package/build/types.js +0 -2
  540. package/build/types.js.map +0 -1
  541. package/docs/decisions/2026-06-02-monorepo-split-evaluation.md +0 -80
  542. package/docs/handoffs/2026-06-08-dx-round2-declines.md +0 -45
  543. package/docs/handoffs/ajsc-named-type-collision.md +0 -134
  544. package/docs/handoffs/ajsc-named-type-support.md +0 -181
  545. package/docs/handoffs/shared-models-auto-resolve-response.md +0 -181
  546. package/docs/npm-workspaces-migration-plan.md +0 -611
  547. package/docs/superpowers/plans/2026-04-24-doc-registry-simplification.md +0 -886
  548. package/docs/superpowers/plans/2026-04-24-kotlin-codegen-target.md +0 -1265
  549. package/docs/superpowers/plans/2026-04-25-ajsc-v7-kotlin-polish.md +0 -1993
  550. package/docs/superpowers/plans/2026-04-29-safe-result-api.md +0 -2293
  551. package/docs/superpowers/plans/2026-05-07-astro-adapter.md +0 -1391
  552. package/docs/superpowers/plans/2026-05-08-create-http.md +0 -3355
  553. package/docs/superpowers/plans/2026-05-08-hono-app-builder-convergence.md +0 -3365
  554. package/docs/superpowers/plans/2026-06-05-dx-feedback-round.md +0 -1292
  555. package/docs/superpowers/plans/2026-06-06-shared-models-convention-and-diagnostics.md +0 -659
  556. package/docs/superpowers/plans/2026-06-08-codegen-dx-surfacing.md +0 -428
  557. package/docs/superpowers/specs/2026-04-24-kotlin-swift-codegen-design.md +0 -401
  558. package/docs/superpowers/specs/2026-04-25-ajsc-v7-kotlin-polish-design.md +0 -314
  559. package/docs/superpowers/specs/2026-04-25-swift-codegen-design.md +0 -264
  560. package/docs/superpowers/specs/2026-04-29-safe-result-api-design.md +0 -324
  561. package/docs/superpowers/specs/2026-05-07-astro-adapter-design.md +0 -252
  562. package/docs/superpowers/specs/2026-05-08-create-http-design.md +0 -409
  563. package/docs/superpowers/specs/2026-05-08-hono-app-builder-convergence-design.md +0 -411
  564. package/docs/superpowers/specs/2026-06-05-dx-feedback-round-design.md +0 -285
  565. package/docs/superpowers/specs/2026-06-08-dx-feedback-round-2-design.md +0 -376
  566. package/src/create-http-stream.ts +0 -191
  567. package/src/create-http.ts +0 -210
  568. package/src/create-stream.ts +0 -228
  569. package/src/create.ts +0 -172
  570. package/src/implementations/http/README.md +0 -390
  571. package/src/implementations/http/error-dispatch.test.ts +0 -283
  572. package/src/implementations/http/error-dispatch.ts +0 -176
  573. package/src/implementations/http/hono/handlers/http-stream.ts +0 -152
  574. package/src/implementations/http/hono/handlers/http.ts +0 -145
  575. package/src/implementations/http/hono/handlers/rpc.ts +0 -54
  576. package/src/implementations/http/hono/path.test.ts +0 -96
  577. package/src/index.ts +0 -101
  578. package/src/schema/extract-json-schema.test.ts +0 -25
  579. package/src/schema/extract-json-schema.ts +0 -15
  580. package/src/schema/parser.test.ts +0 -182
  581. package/src/schema/parser.ts +0 -265
  582. package/src/schema/resolve-schema-lib.test.ts +0 -19
  583. package/src/schema/resolve-schema-lib.ts +0 -29
  584. package/src/schema/types.ts +0 -20
  585. package/src/types.ts +0 -133
  586. /package/build/{implementations/http → adapters}/astro/astro-context.d.ts +0 -0
  587. /package/build/{implementations/http → adapters}/astro/astro-context.js +0 -0
  588. /package/build/{implementations/http → adapters}/astro/create-handler.d.ts +0 -0
  589. /package/build/{implementations/http → adapters}/astro/create-handler.js +0 -0
  590. /package/build/{implementations/http → adapters}/astro/index.d.ts +0 -0
  591. /package/build/{implementations/http → adapters}/astro/index.js +0 -0
  592. /package/build/{implementations/http → adapters}/astro/index.test.d.ts +0 -0
  593. /package/build/{implementations/http → adapters}/astro/rewrite-request.d.ts +0 -0
  594. /package/build/{implementations/http → adapters}/astro/rewrite-request.js +0 -0
  595. /package/build/{create-http-stream.test.d.ts → adapters/hono/envelope-parity.test.d.ts} +0 -0
  596. /package/build/{implementations/http → adapters}/hono/handlers/http-stream.test.d.ts +0 -0
  597. /package/build/{implementations/http → adapters}/hono/handlers/http.test.d.ts +0 -0
  598. /package/build/{implementations/http → adapters}/hono/handlers/rpc.test.d.ts +0 -0
  599. /package/build/{implementations/http → adapters}/hono/handlers/stream.test.d.ts +0 -0
  600. /package/build/{implementations/http → adapters}/hono/index.test.d.ts +0 -0
  601. /package/build/{implementations/http → adapters/hono}/on-request-error.test.d.ts +0 -0
  602. /package/build/{implementations/http → adapters/hono}/route-errors.test.d.ts +0 -0
  603. /package/build/{create-http.test.d.ts → client/freeze.test.d.ts} +0 -0
  604. /package/build/{create-stream.test.d.ts → codegen/goldens.test.d.ts} +0 -0
  605. /package/build/{create.test.d.ts → core/create-http-stream.test.d.ts} +0 -0
  606. /package/build/{doc-envelope.test.d.ts → core/create-http.test.d.ts} +0 -0
  607. /package/build/{errors.test.d.ts → core/create-stream.test.d.ts} +0 -0
  608. /package/build/{implementations/http/doc-registry.test.d.ts → core/create.test.d.ts} +0 -0
  609. /package/build/{implementations/http/error-dispatch.test.d.ts → core/definition-site.test.d.ts} +0 -0
  610. /package/build/{implementations/http/error-taxonomy.test.d.ts → core/errors.test.d.ts} +0 -0
  611. /package/build/{errors.test.js → core/errors.test.js} +0 -0
  612. /package/build/{implementations/http/hono/path.test.d.ts → core/factory-options.test.d.ts} +0 -0
  613. /package/build/{migration.test.d.ts → core/migration.test.d.ts} +0 -0
  614. /package/build/{index.test.d.ts → core/procedures.test.d.ts} +0 -0
  615. /package/build/{implementations/http/hono → core}/types.js +0 -0
  616. /package/build/schema/{extract-json-schema.test.d.ts → adapter.test.d.ts} +0 -0
  617. /package/build/schema/{parser.test.d.ts → compile.test.d.ts} +0 -0
  618. /package/build/schema/{resolve-schema-lib.test.d.ts → typebox.test.d.ts} +0 -0
  619. /package/build/{stack-utils.test.d.ts → server/context.test.d.ts} +0 -0
  620. /package/build/{doc-envelope.js → server/doc-envelope.js} +0 -0
  621. /package/build/{doc-envelope.test.js → server/doc-envelope.test.js} +0 -0
  622. /package/build/{implementations → server}/types.js +0 -0
  623. /package/src/{implementations/http → adapters}/astro/README.md +0 -0
  624. /package/src/{implementations/http → adapters}/astro/astro-context.ts +0 -0
  625. /package/src/{implementations/http → adapters}/astro/create-handler.ts +0 -0
  626. /package/src/{implementations/http → adapters}/astro/index.ts +0 -0
  627. /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
+ })