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