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
@@ -0,0 +1,450 @@
1
+ import { afterEach, describe, expect, test, vi } from 'vitest'
2
+ import { ProcedureError } from '../../core/errors.js'
3
+ import type {
4
+ TProcedureRegistration,
5
+ TStreamProcedureRegistration,
6
+ } from '../../core/types.js'
7
+ import { defineErrorTaxonomy } from './taxonomy.js'
8
+ import { dispatchMidStreamError, dispatchPreStreamError } from './dispatch.js'
9
+
10
+ class UseCaseError extends Error {
11
+ constructor(readonly externalMsg: string) {
12
+ super(externalMsg)
13
+ this.name = 'UseCaseError'
14
+ Object.setPrototypeOf(this, UseCaseError.prototype)
15
+ }
16
+ }
17
+
18
+ const rpcProcedure = {
19
+ name: 'DoThing',
20
+ kind: 'rpc',
21
+ config: {},
22
+ handler: async () => {},
23
+ } as unknown as TProcedureRegistration
24
+
25
+ const streamProcedure = {
26
+ name: 'Watch',
27
+ kind: 'rpc-stream',
28
+ config: {},
29
+ handler: async function* () {},
30
+ } as unknown as TStreamProcedureRegistration<any, any>
31
+
32
+ afterEach(() => {
33
+ vi.restoreAllMocks()
34
+ })
35
+
36
+ // ---------------------------------------------------------------------------
37
+ // dispatchPreStreamError
38
+ // ---------------------------------------------------------------------------
39
+
40
+ describe('dispatchPreStreamError', () => {
41
+ test('taxonomy match returns { type: body } with status and serialized body', async () => {
42
+ const taxonomy = defineErrorTaxonomy({
43
+ UseCaseError: {
44
+ class: UseCaseError,
45
+ statusCode: 422,
46
+ toResponse: (err: UseCaseError) => ({ name: 'UseCaseError', message: err.externalMsg }),
47
+ },
48
+ })
49
+ const result = await dispatchPreStreamError({
50
+ err: new UseCaseError('nope'),
51
+ procedure: rpcProcedure,
52
+ raw: {},
53
+ cfg: { errors: taxonomy },
54
+ })
55
+ expect(result).toEqual({
56
+ type: 'body',
57
+ statusCode: 422,
58
+ body: { name: 'UseCaseError', message: 'nope' },
59
+ })
60
+ })
61
+
62
+ test('taxonomy onCatch side effect runs before the result is returned', async () => {
63
+ const calls: string[] = []
64
+ const taxonomy = defineErrorTaxonomy({
65
+ UseCaseError: {
66
+ class: UseCaseError,
67
+ statusCode: 422,
68
+ onCatch: async () => {
69
+ await new Promise((r) => setTimeout(r, 5))
70
+ calls.push('onCatch')
71
+ },
72
+ },
73
+ })
74
+ const result = await dispatchPreStreamError({
75
+ err: new UseCaseError('nope'),
76
+ procedure: rpcProcedure,
77
+ raw: {},
78
+ cfg: { errors: taxonomy },
79
+ })
80
+ expect(result.type).toBe('body')
81
+ expect(calls).toEqual(['onCatch'])
82
+ })
83
+
84
+ test('unknownError handles non-taxonomy errors via { type: body }', async () => {
85
+ const result = await dispatchPreStreamError({
86
+ err: new Error('boom'),
87
+ procedure: rpcProcedure,
88
+ raw: {},
89
+ cfg: {
90
+ unknownError: {
91
+ statusCode: 502,
92
+ toResponse: () => ({ name: 'InternalServerError' }),
93
+ },
94
+ },
95
+ })
96
+ expect(result).toEqual({
97
+ type: 'body',
98
+ statusCode: 502,
99
+ body: { name: 'InternalServerError' },
100
+ })
101
+ })
102
+
103
+ test('hard default: { error: message } with status 500 when nothing is configured', async () => {
104
+ const result = await dispatchPreStreamError({
105
+ err: new Error('kaput'),
106
+ procedure: rpcProcedure,
107
+ raw: {},
108
+ cfg: {},
109
+ })
110
+ expect(result).toEqual({
111
+ type: 'body',
112
+ statusCode: 500,
113
+ body: { error: 'kaput' },
114
+ })
115
+ })
116
+
117
+ test('imperative onError produces { type: response } with the framework response', async () => {
118
+ const frameworkResponse = { marker: 'framework-response' }
119
+ const result = await dispatchPreStreamError({
120
+ err: new Error('boom'),
121
+ procedure: rpcProcedure,
122
+ raw: { marker: 'raw' },
123
+ cfg: {
124
+ onError: async (procedure, raw, err) => {
125
+ expect(procedure).toBe(rpcProcedure)
126
+ expect(raw).toEqual({ marker: 'raw' })
127
+ expect(err.message).toBe('boom')
128
+ return frameworkResponse
129
+ },
130
+ },
131
+ })
132
+ expect(result.type).toBe('response')
133
+ expect((result as { type: 'response'; response: unknown }).response).toBe(frameworkResponse)
134
+ })
135
+
136
+ test('taxonomy wins over onError when both are configured', async () => {
137
+ const onError = vi.fn(async () => ({ marker: 'never' }))
138
+ const taxonomy = defineErrorTaxonomy({
139
+ UseCaseError: { class: UseCaseError, statusCode: 422 },
140
+ })
141
+ const result = await dispatchPreStreamError({
142
+ err: new UseCaseError('nope'),
143
+ procedure: rpcProcedure,
144
+ raw: {},
145
+ cfg: { errors: taxonomy, onError },
146
+ })
147
+ expect(result.type).toBe('body')
148
+ expect(onError).not.toHaveBeenCalled()
149
+ })
150
+
151
+ test('onError is reached when the taxonomy does not match', async () => {
152
+ const taxonomy = defineErrorTaxonomy({
153
+ UseCaseError: { class: UseCaseError, statusCode: 422 },
154
+ })
155
+ const result = await dispatchPreStreamError({
156
+ err: new Error('not a use-case error'),
157
+ procedure: rpcProcedure,
158
+ raw: {},
159
+ cfg: { errors: taxonomy, onError: async () => 'imperative' },
160
+ })
161
+ expect(result).toEqual({ type: 'response', response: 'imperative' })
162
+ })
163
+
164
+ test('onRequestError observer is awaited BEFORE dispatch and cannot mutate the response', async () => {
165
+ const order: string[] = []
166
+ const taxonomy = defineErrorTaxonomy({
167
+ UseCaseError: {
168
+ class: UseCaseError,
169
+ statusCode: 422,
170
+ onCatch: () => {
171
+ order.push('onCatch')
172
+ },
173
+ },
174
+ })
175
+ const result = await dispatchPreStreamError({
176
+ err: new UseCaseError('nope'),
177
+ procedure: rpcProcedure,
178
+ raw: {},
179
+ cfg: {
180
+ errors: taxonomy,
181
+ onRequestError: async ({ err, procedure, raw }) => {
182
+ await new Promise((r) => setTimeout(r, 5))
183
+ expect((err as Error).message).toBe('nope')
184
+ expect(procedure).toBe(rpcProcedure)
185
+ expect(raw).toEqual({})
186
+ order.push('observer')
187
+ },
188
+ },
189
+ })
190
+ expect(order).toEqual(['observer', 'onCatch'])
191
+ // Observer can't change the outcome — taxonomy still resolves.
192
+ expect(result.type).toBe('body')
193
+ })
194
+
195
+ test('onRequestError throws are swallowed (console.error) and dispatch proceeds', async () => {
196
+ const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
197
+ const result = await dispatchPreStreamError({
198
+ err: new Error('boom'),
199
+ procedure: rpcProcedure,
200
+ raw: {},
201
+ cfg: {
202
+ onRequestError: () => {
203
+ throw new Error('observer exploded')
204
+ },
205
+ },
206
+ })
207
+ expect(errorSpy).toHaveBeenCalledOnce()
208
+ expect(String(errorSpy.mock.calls[0]![0])).toContain('onRequestError')
209
+ expect(result).toEqual({ type: 'body', statusCode: 500, body: { error: 'boom' } })
210
+ })
211
+
212
+ test('order: observer → taxonomy → onError → hard default', async () => {
213
+ const order: string[] = []
214
+ const taxonomy = defineErrorTaxonomy({
215
+ UseCaseError: {
216
+ class: UseCaseError,
217
+ statusCode: 422,
218
+ onCatch: () => {
219
+ order.push('taxonomy')
220
+ },
221
+ },
222
+ })
223
+ const cfg = {
224
+ errors: taxonomy,
225
+ onRequestError: () => {
226
+ order.push('observer')
227
+ },
228
+ onError: async () => {
229
+ order.push('onError')
230
+ return 'resp'
231
+ },
232
+ }
233
+
234
+ // 1. Taxonomy match: observer then taxonomy; onError skipped.
235
+ await dispatchPreStreamError({
236
+ err: new UseCaseError('x'),
237
+ procedure: rpcProcedure,
238
+ raw: {},
239
+ cfg,
240
+ })
241
+ expect(order).toEqual(['observer', 'taxonomy'])
242
+
243
+ // 2. No taxonomy match: observer then onError.
244
+ order.length = 0
245
+ await dispatchPreStreamError({
246
+ err: new Error('y'),
247
+ procedure: rpcProcedure,
248
+ raw: {},
249
+ cfg,
250
+ })
251
+ expect(order).toEqual(['observer', 'onError'])
252
+
253
+ // 3. Nothing configured beyond the observer: hard default.
254
+ order.length = 0
255
+ const result = await dispatchPreStreamError({
256
+ err: new Error('z'),
257
+ procedure: rpcProcedure,
258
+ raw: {},
259
+ cfg: { onRequestError: cfg.onRequestError },
260
+ })
261
+ expect(order).toEqual(['observer'])
262
+ expect(result).toEqual({ type: 'body', statusCode: 500, body: { error: 'z' } })
263
+ })
264
+
265
+ test('unwraps a ProcedureError wrapper — cause is matched before the outer error', async () => {
266
+ const original = new UseCaseError('the real problem')
267
+ const wrapped = new ProcedureError('DoThing', 'wrapped')
268
+ ;(wrapped as { cause?: unknown }).cause = original
269
+
270
+ const taxonomy = defineErrorTaxonomy({
271
+ UseCaseError: {
272
+ class: UseCaseError,
273
+ statusCode: 422,
274
+ toResponse: (err: UseCaseError) => ({ name: 'UseCaseError', message: err.externalMsg }),
275
+ },
276
+ })
277
+ const result = await dispatchPreStreamError({
278
+ err: wrapped,
279
+ procedure: rpcProcedure,
280
+ raw: {},
281
+ cfg: { errors: taxonomy },
282
+ })
283
+ expect(result).toEqual({
284
+ type: 'body',
285
+ statusCode: 422,
286
+ body: { name: 'UseCaseError', message: 'the real problem' },
287
+ })
288
+ })
289
+
290
+ test('direct ProcedureError (no cause) hits the default taxonomy entry', async () => {
291
+ const result = await dispatchPreStreamError({
292
+ err: new ProcedureError('DoThing', 'from ctx.error'),
293
+ procedure: rpcProcedure,
294
+ raw: {},
295
+ cfg: {},
296
+ })
297
+ expect(result.type).toBe('body')
298
+ const bodyResult = result as { type: 'body'; statusCode: number; body: any }
299
+ expect(bodyResult.statusCode).toBe(500)
300
+ expect(bodyResult.body.name).toBe('ProcedureError')
301
+ })
302
+ })
303
+
304
+ // ---------------------------------------------------------------------------
305
+ // dispatchMidStreamError
306
+ // ---------------------------------------------------------------------------
307
+
308
+ describe('dispatchMidStreamError', () => {
309
+ test('taxonomy match returns { data, sseEvent: error, runOnCatch }', async () => {
310
+ const calls: string[] = []
311
+ const taxonomy = defineErrorTaxonomy({
312
+ UseCaseError: {
313
+ class: UseCaseError,
314
+ statusCode: 422,
315
+ toResponse: (err: UseCaseError) => ({ name: 'UseCaseError', message: err.externalMsg }),
316
+ onCatch: () => {
317
+ calls.push('onCatch')
318
+ },
319
+ },
320
+ })
321
+ const result = await dispatchMidStreamError({
322
+ err: new UseCaseError('mid-stream nope'),
323
+ procedure: streamProcedure,
324
+ raw: {},
325
+ cfg: { errors: taxonomy },
326
+ })
327
+ expect(result.data).toEqual({ name: 'UseCaseError', message: 'mid-stream nope' })
328
+ expect(result.sseEvent).toBe('error')
329
+ expect(result.runOnCatch).toBeDefined()
330
+ // onCatch is deferred — caller decides when to run it.
331
+ expect(calls).toEqual([])
332
+ await result.runOnCatch!()
333
+ expect(calls).toEqual(['onCatch'])
334
+ })
335
+
336
+ test('onMidStreamError fallback returns { data, sseEvent: procedure.name }', async () => {
337
+ const result = await dispatchMidStreamError({
338
+ err: new Error('boom'),
339
+ procedure: streamProcedure,
340
+ raw: {},
341
+ cfg: {
342
+ onMidStreamError: (procedure, _raw, err) => ({
343
+ data: { custom: true, from: procedure.name, message: err.message },
344
+ }),
345
+ },
346
+ })
347
+ expect(result.data).toEqual({ custom: true, from: 'Watch', message: 'boom' })
348
+ expect(result.sseEvent).toBe('Watch')
349
+ expect(result.runOnCatch).toBeUndefined()
350
+ })
351
+
352
+ test('taxonomy wins over onMidStreamError when both are configured', async () => {
353
+ const onMidStreamError = vi.fn(() => ({ data: { never: true } }))
354
+ const taxonomy = defineErrorTaxonomy({
355
+ UseCaseError: { class: UseCaseError, statusCode: 422 },
356
+ })
357
+ const result = await dispatchMidStreamError({
358
+ err: new UseCaseError('nope'),
359
+ procedure: streamProcedure,
360
+ raw: {},
361
+ cfg: { errors: taxonomy, onMidStreamError },
362
+ })
363
+ expect(result.sseEvent).toBe('error')
364
+ expect(onMidStreamError).not.toHaveBeenCalled()
365
+ })
366
+
367
+ test('onMidStreamError returning undefined data falls through to the hard default', async () => {
368
+ const result = await dispatchMidStreamError({
369
+ err: new Error('boom'),
370
+ procedure: streamProcedure,
371
+ raw: {},
372
+ cfg: { onMidStreamError: () => undefined },
373
+ })
374
+ expect(result.data).toEqual({ error: 'boom' })
375
+ expect(result.sseEvent).toBe('error')
376
+ })
377
+
378
+ test('hard default: { data: { error }, sseEvent: error } when nothing is configured', async () => {
379
+ const result = await dispatchMidStreamError({
380
+ err: new Error('stream died'),
381
+ procedure: streamProcedure,
382
+ raw: {},
383
+ cfg: {},
384
+ })
385
+ expect(result).toEqual({
386
+ data: { error: 'stream died' },
387
+ sseEvent: 'error',
388
+ })
389
+ })
390
+
391
+ test('onRequestError observer is awaited before mid-stream dispatch', async () => {
392
+ const order: string[] = []
393
+ const result = await dispatchMidStreamError({
394
+ err: new Error('boom'),
395
+ procedure: streamProcedure,
396
+ raw: {},
397
+ cfg: {
398
+ onRequestError: async () => {
399
+ await new Promise((r) => setTimeout(r, 5))
400
+ order.push('observer')
401
+ },
402
+ onMidStreamError: () => {
403
+ order.push('onMidStreamError')
404
+ return { data: { handled: true } }
405
+ },
406
+ },
407
+ })
408
+ expect(order).toEqual(['observer', 'onMidStreamError'])
409
+ expect(result.data).toEqual({ handled: true })
410
+ })
411
+
412
+ test('onRequestError throws are swallowed (console.error) in the mid-stream path', async () => {
413
+ const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
414
+ const result = await dispatchMidStreamError({
415
+ err: new Error('boom'),
416
+ procedure: streamProcedure,
417
+ raw: {},
418
+ cfg: {
419
+ onRequestError: () => {
420
+ throw new Error('observer exploded')
421
+ },
422
+ },
423
+ })
424
+ expect(errorSpy).toHaveBeenCalledOnce()
425
+ expect(String(errorSpy.mock.calls[0]![0])).toContain('mid-stream')
426
+ expect(result.data).toEqual({ error: 'boom' })
427
+ })
428
+
429
+ test('unwraps a ProcedureError wrapper in the mid-stream path', async () => {
430
+ const original = new UseCaseError('inner')
431
+ const wrapped = new ProcedureError('Watch', 'wrapped')
432
+ ;(wrapped as { cause?: unknown }).cause = original
433
+
434
+ const taxonomy = defineErrorTaxonomy({
435
+ UseCaseError: {
436
+ class: UseCaseError,
437
+ statusCode: 422,
438
+ toResponse: (err: UseCaseError) => ({ name: 'UseCaseError', message: err.externalMsg }),
439
+ },
440
+ })
441
+ const result = await dispatchMidStreamError({
442
+ err: wrapped,
443
+ procedure: streamProcedure,
444
+ raw: {},
445
+ cfg: { errors: taxonomy },
446
+ })
447
+ expect(result.data).toEqual({ name: 'UseCaseError', message: 'inner' })
448
+ expect(result.sseEvent).toBe('error')
449
+ })
450
+ })
@@ -0,0 +1,189 @@
1
+ /**
2
+ * Transport-agnostic error dispatch.
3
+ *
4
+ * Unlike v8 (where dispatch produced Hono `Response` objects directly), v9
5
+ * dispatch returns plain data and the server adapter translates it into its
6
+ * framework's response primitive. `TRaw` is the adapter's request context
7
+ * (Hono `Context`, Fastify `Request`, ...) and `TResponse` its response type —
8
+ * both flow through untouched.
9
+ */
10
+ import {
11
+ resolveErrorResponse,
12
+ type ErrorTaxonomy,
13
+ type TAnyProcedureRegistration,
14
+ type UnknownErrorConfig,
15
+ } from './taxonomy.js'
16
+ import type {
17
+ TStreamProcedureRegistration,
18
+ THttpStreamProcedureRegistration,
19
+ } from '../../core/types.js'
20
+
21
+ /** Public alias of {@link TAnyProcedureRegistration}. */
22
+ export type AnyServedProcedure = TAnyProcedureRegistration
23
+
24
+ export type OnRequestErrorContext<TRaw = unknown> = {
25
+ err: unknown
26
+ procedure: AnyServedProcedure
27
+ raw: TRaw
28
+ }
29
+
30
+ /**
31
+ * Cross-cutting observer for logging/tracing — fires for every caught error
32
+ * BEFORE dispatch. Awaited; cannot mutate the response; throws are swallowed.
33
+ */
34
+ export type OnRequestErrorObserver<TRaw = unknown> = (
35
+ ctx: OnRequestErrorContext<TRaw>,
36
+ ) => void | Promise<void>
37
+
38
+ /** Imperative error handler — produces a framework response directly. */
39
+ export type PreStreamOnError<TRaw = unknown, TResponse = unknown> = (
40
+ procedure: AnyServedProcedure,
41
+ raw: TRaw,
42
+ err: Error,
43
+ ) => TResponse | Promise<TResponse>
44
+
45
+ export type PreStreamErrorConfig<TRaw = unknown, TResponse = unknown> = {
46
+ errors?: ErrorTaxonomy
47
+ unknownError?: UnknownErrorConfig
48
+ onError?: PreStreamOnError<TRaw, TResponse>
49
+ onRequestError?: OnRequestErrorObserver<TRaw>
50
+ }
51
+
52
+ /**
53
+ * What the adapter should send. Either resolved data (taxonomy / unknownError /
54
+ * hard default — serialize `body` as JSON with `statusCode`) or a framework
55
+ * response produced by the imperative `onError` callback (send as-is).
56
+ */
57
+ export type PreStreamDispatchResult<TResponse = unknown> =
58
+ | { type: 'body'; statusCode: number; body: unknown }
59
+ | { type: 'response'; response: TResponse }
60
+
61
+ /**
62
+ * Used by rpc, http, and the pre-stream guard in stream / http-stream
63
+ * handlers. Order: onRequestError observer (awaited, throws swallowed) →
64
+ * taxonomy resolver → imperative onError → hard default
65
+ * (`{ error: message }`, 500).
66
+ */
67
+ export async function dispatchPreStreamError<TRaw, TResponse>(params: {
68
+ err: unknown
69
+ procedure: AnyServedProcedure
70
+ raw: TRaw
71
+ cfg: PreStreamErrorConfig<TRaw, TResponse>
72
+ }): Promise<PreStreamDispatchResult<TResponse>> {
73
+ const { err, procedure, raw, cfg } = params
74
+
75
+ if (cfg.onRequestError) {
76
+ try {
77
+ await cfg.onRequestError({ err, procedure, raw })
78
+ } catch (observerErr) {
79
+ console.error('[ts-procedures] onRequestError threw — swallowed:', observerErr)
80
+ }
81
+ }
82
+
83
+ const resolved = resolveErrorResponse({
84
+ err,
85
+ userTaxonomy: cfg.errors,
86
+ unknownError: cfg.unknownError,
87
+ procedure,
88
+ raw,
89
+ })
90
+ if (resolved) {
91
+ await resolved.runOnCatch()
92
+ return { type: 'body', statusCode: resolved.statusCode, body: resolved.body }
93
+ }
94
+
95
+ if (cfg.onError) {
96
+ return { type: 'response', response: await cfg.onError(procedure, raw, err as Error) }
97
+ }
98
+
99
+ return { type: 'body', statusCode: 500, body: { error: (err as Error).message } }
100
+ }
101
+
102
+ /**
103
+ * Return shape of an `onMidStreamError` callback.
104
+ *
105
+ * - `data`: the value to write to the open stream as the error payload.
106
+ * Decorate with the `sse(data, { event, id, retry })` helper to attach SSE
107
+ * metadata; the stream handler reads that metadata via `getSSEMeta` after
108
+ * dispatch.
109
+ * - `closeStream`: reserved for future use. The stream currently always
110
+ * closes naturally after the catch block returns.
111
+ */
112
+ export type MidStreamErrorResult<TErrorData = unknown> = {
113
+ data: TErrorData
114
+ closeStream?: boolean
115
+ }
116
+
117
+ export type OnMidStreamError<TErrorData = unknown, TRaw = unknown> = (
118
+ procedure: TStreamProcedureRegistration<any, any> | THttpStreamProcedureRegistration<any>,
119
+ raw: TRaw,
120
+ err: Error,
121
+ ) => MidStreamErrorResult<TErrorData> | undefined
122
+
123
+ export type MidStreamErrorConfig<TErrorData = unknown, TRaw = unknown> = {
124
+ errors?: ErrorTaxonomy
125
+ unknownError?: UnknownErrorConfig
126
+ onMidStreamError?: OnMidStreamError<TErrorData, TRaw>
127
+ onRequestError?: OnRequestErrorObserver<TRaw>
128
+ }
129
+
130
+ export type DispatchedMidStreamError = {
131
+ data: unknown
132
+ sseEvent?: string
133
+ sseId?: string
134
+ sseRetry?: number
135
+ runOnCatch?: () => Promise<void>
136
+ }
137
+
138
+ /**
139
+ * Used inside SSE / text-stream generator catch blocks. Returns the bytes
140
+ * to write to the open stream — the HTTP status is already committed, so a
141
+ * Response is impossible. Order matches dispatchPreStreamError: observer
142
+ * (awaited, throws swallowed) → taxonomy → onMidStreamError → hard default.
143
+ */
144
+ export async function dispatchMidStreamError<TErrorData, TRaw>(params: {
145
+ err: unknown
146
+ procedure: TStreamProcedureRegistration<any, any> | THttpStreamProcedureRegistration<any>
147
+ raw: TRaw
148
+ cfg: MidStreamErrorConfig<TErrorData, TRaw>
149
+ }): Promise<DispatchedMidStreamError> {
150
+ const { err, procedure, raw, cfg } = params
151
+
152
+ if (cfg.onRequestError) {
153
+ try {
154
+ await cfg.onRequestError({ err, procedure, raw })
155
+ } catch (observerErr) {
156
+ console.error('[ts-procedures] onRequestError (mid-stream) threw — swallowed:', observerErr)
157
+ }
158
+ }
159
+
160
+ const resolved = resolveErrorResponse({
161
+ err,
162
+ userTaxonomy: cfg.errors,
163
+ unknownError: cfg.unknownError,
164
+ procedure,
165
+ raw,
166
+ })
167
+ if (resolved) {
168
+ return {
169
+ data: resolved.body,
170
+ sseEvent: 'error',
171
+ runOnCatch: resolved.runOnCatch,
172
+ }
173
+ }
174
+
175
+ if (cfg.onMidStreamError) {
176
+ const result = cfg.onMidStreamError(procedure, raw, err as Error)
177
+ if (result?.data !== undefined) {
178
+ return {
179
+ data: result.data,
180
+ sseEvent: procedure.name,
181
+ }
182
+ }
183
+ }
184
+
185
+ return {
186
+ data: { error: (err as Error).message },
187
+ sseEvent: 'error',
188
+ }
189
+ }