ts-procedures 7.2.0 → 8.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 (325) hide show
  1. package/README.md +65 -3
  2. package/agent_config/claude-code/agents/ts-procedures-architect.md +6 -8
  3. package/agent_config/claude-code/skills/ts-procedures/SKILL.md +30 -33
  4. package/agent_config/claude-code/skills/ts-procedures/anti-patterns.md +139 -53
  5. package/agent_config/claude-code/skills/ts-procedures/api-reference.md +208 -231
  6. package/agent_config/claude-code/skills/ts-procedures/patterns.md +80 -153
  7. package/agent_config/claude-code/skills/ts-procedures-review/SKILL.md +1 -1
  8. package/agent_config/claude-code/skills/ts-procedures-review/checklist.md +4 -5
  9. package/agent_config/claude-code/skills/ts-procedures-scaffold/SKILL.md +4 -7
  10. package/agent_config/claude-code/skills/ts-procedures-scaffold/templates/hono.md +223 -0
  11. package/agent_config/copilot/copilot-instructions.md +36 -48
  12. package/agent_config/cursor/cursorrules +36 -48
  13. package/build/client/call.js +4 -1
  14. package/build/client/call.js.map +1 -1
  15. package/build/client/call.test.js +23 -0
  16. package/build/client/call.test.js.map +1 -1
  17. package/build/client/fetch-adapter.js +3 -1
  18. package/build/client/fetch-adapter.js.map +1 -1
  19. package/build/client/fetch-adapter.test.js +11 -1
  20. package/build/client/fetch-adapter.test.js.map +1 -1
  21. package/build/client/index.test.js +7 -7
  22. package/build/client/index.test.js.map +1 -1
  23. package/build/client/request-builder.d.ts +1 -1
  24. package/build/client/request-builder.js +2 -2
  25. package/build/client/request-builder.js.map +1 -1
  26. package/build/client/stream.js +13 -2
  27. package/build/client/stream.js.map +1 -1
  28. package/build/client/stream.test.js +32 -7
  29. package/build/client/stream.test.js.map +1 -1
  30. package/build/client/typed-error-dispatch.test.js +8 -92
  31. package/build/client/typed-error-dispatch.test.js.map +1 -1
  32. package/build/client/types.d.ts +21 -3
  33. package/build/codegen/bin/cli.js +0 -0
  34. package/build/codegen/e2e.test.js +87 -23
  35. package/build/codegen/e2e.test.js.map +1 -1
  36. package/build/codegen/emit-errors.integration.test.js +1 -1
  37. package/build/codegen/emit-errors.integration.test.js.map +1 -1
  38. package/build/codegen/emit-scope.js +308 -47
  39. package/build/codegen/emit-scope.js.map +1 -1
  40. package/build/codegen/emit-scope.test.js +363 -110
  41. package/build/codegen/emit-scope.test.js.map +1 -1
  42. package/build/codegen/pipeline.test.js +7 -7
  43. package/build/codegen/pipeline.test.js.map +1 -1
  44. package/build/codegen/resolve-envelope.js +1 -1
  45. package/build/codegen/resolve-envelope.js.map +1 -1
  46. package/build/codegen/resolve-envelope.test.js +5 -5
  47. package/build/codegen/resolve-envelope.test.js.map +1 -1
  48. package/build/codegen/targets/_shared/route-slots.d.ts +8 -3
  49. package/build/codegen/targets/_shared/route-slots.js +49 -8
  50. package/build/codegen/targets/_shared/route-slots.js.map +1 -1
  51. package/build/codegen/targets/_shared/route-slots.test.js +99 -26
  52. package/build/codegen/targets/_shared/route-slots.test.js.map +1 -1
  53. package/build/codegen/targets/kotlin/emit-route-kotlin.test.js +88 -17
  54. package/build/codegen/targets/kotlin/emit-route-kotlin.test.js.map +1 -1
  55. package/build/codegen/targets/kotlin/emit-scope-kotlin.test.js +9 -6
  56. package/build/codegen/targets/kotlin/emit-scope-kotlin.test.js.map +1 -1
  57. package/build/codegen/targets/kotlin/integration.test.js +6 -0
  58. package/build/codegen/targets/kotlin/integration.test.js.map +1 -1
  59. package/build/codegen/targets/swift/access-level.test.js +8 -11
  60. package/build/codegen/targets/swift/access-level.test.js.map +1 -1
  61. package/build/codegen/targets/swift/emit-route-swift.test.js +91 -20
  62. package/build/codegen/targets/swift/emit-route-swift.test.js.map +1 -1
  63. package/build/codegen/targets/swift/emit-scope-swift.test.js +12 -9
  64. package/build/codegen/targets/swift/emit-scope-swift.test.js.map +1 -1
  65. package/build/codegen/targets/swift/integration.test.js +6 -0
  66. package/build/codegen/targets/swift/integration.test.js.map +1 -1
  67. package/build/create-http-stream.d.ts +58 -0
  68. package/build/create-http-stream.js +122 -0
  69. package/build/create-http-stream.js.map +1 -0
  70. package/build/create-http-stream.test.js +88 -0
  71. package/build/create-http-stream.test.js.map +1 -0
  72. package/build/create-http.d.ts +49 -0
  73. package/build/create-http.js +108 -0
  74. package/build/create-http.js.map +1 -0
  75. package/build/create-http.test.js +137 -0
  76. package/build/create-http.test.js.map +1 -0
  77. package/build/create-stream.d.ts +35 -0
  78. package/build/create-stream.js +123 -0
  79. package/build/create-stream.js.map +1 -0
  80. package/build/create-stream.test.js +428 -0
  81. package/build/create-stream.test.js.map +1 -0
  82. package/build/create.d.ts +28 -0
  83. package/build/create.js +82 -0
  84. package/build/create.js.map +1 -0
  85. package/build/create.test.js +483 -0
  86. package/build/create.test.js.map +1 -0
  87. package/build/exports.d.ts +2 -0
  88. package/build/implementations/http/astro/index.test.js +20 -12
  89. package/build/implementations/http/astro/index.test.js.map +1 -1
  90. package/build/implementations/http/doc-registry.js +1 -1
  91. package/build/implementations/http/doc-registry.js.map +1 -1
  92. package/build/implementations/http/doc-registry.test.js +36 -5
  93. package/build/implementations/http/doc-registry.test.js.map +1 -1
  94. package/build/implementations/http/error-dispatch.d.ts +76 -0
  95. package/build/implementations/http/error-dispatch.js +77 -0
  96. package/build/implementations/http/error-dispatch.js.map +1 -0
  97. package/build/implementations/http/error-dispatch.test.js +254 -0
  98. package/build/implementations/http/error-dispatch.test.js.map +1 -0
  99. package/build/implementations/http/error-taxonomy.d.ts +5 -5
  100. package/build/implementations/http/hono/docs/http-doc.d.ts +6 -0
  101. package/build/implementations/http/hono/docs/http-doc.js +42 -0
  102. package/build/implementations/http/hono/docs/http-doc.js.map +1 -0
  103. package/build/implementations/http/hono/docs/http-stream-doc.d.ts +6 -0
  104. package/build/implementations/http/hono/docs/http-stream-doc.js +40 -0
  105. package/build/implementations/http/hono/docs/http-stream-doc.js.map +1 -0
  106. package/build/implementations/http/hono/docs/rpc-doc.d.ts +6 -0
  107. package/build/implementations/http/hono/docs/rpc-doc.js +24 -0
  108. package/build/implementations/http/hono/docs/rpc-doc.js.map +1 -0
  109. package/build/implementations/http/hono/docs/stream-doc.d.ts +6 -0
  110. package/build/implementations/http/hono/docs/stream-doc.js +42 -0
  111. package/build/implementations/http/hono/docs/stream-doc.js.map +1 -0
  112. package/build/implementations/http/hono/handlers/http-stream.d.ts +10 -0
  113. package/build/implementations/http/hono/handlers/http-stream.js +123 -0
  114. package/build/implementations/http/hono/handlers/http-stream.js.map +1 -0
  115. package/build/implementations/http/hono/handlers/http-stream.test.js +128 -0
  116. package/build/implementations/http/hono/handlers/http-stream.test.js.map +1 -0
  117. package/build/implementations/http/hono/handlers/http.d.ts +10 -0
  118. package/build/implementations/http/hono/handlers/http.js +115 -0
  119. package/build/implementations/http/hono/handlers/http.js.map +1 -0
  120. package/build/implementations/http/hono/handlers/http.test.js +118 -0
  121. package/build/implementations/http/hono/handlers/http.test.js.map +1 -0
  122. package/build/implementations/http/hono/handlers/rpc.d.ts +11 -0
  123. package/build/implementations/http/hono/handlers/rpc.js +32 -0
  124. package/build/implementations/http/hono/handlers/rpc.js.map +1 -0
  125. package/build/implementations/http/hono/handlers/rpc.test.js +73 -0
  126. package/build/implementations/http/hono/handlers/rpc.test.js.map +1 -0
  127. package/build/implementations/http/hono/handlers/stream.d.ts +23 -0
  128. package/build/implementations/http/hono/handlers/stream.js +147 -0
  129. package/build/implementations/http/hono/handlers/stream.js.map +1 -0
  130. package/build/implementations/http/hono/handlers/stream.test.d.ts +1 -0
  131. package/build/implementations/http/hono/handlers/stream.test.js +177 -0
  132. package/build/implementations/http/hono/handlers/stream.test.js.map +1 -0
  133. package/build/implementations/http/hono/index.d.ts +57 -0
  134. package/build/implementations/http/hono/index.js +149 -0
  135. package/build/implementations/http/hono/index.js.map +1 -0
  136. package/build/implementations/http/hono/index.test.d.ts +1 -0
  137. package/build/implementations/http/hono/index.test.js +274 -0
  138. package/build/implementations/http/hono/index.test.js.map +1 -0
  139. package/build/implementations/http/hono/path.d.ts +17 -0
  140. package/build/implementations/http/hono/path.js +39 -0
  141. package/build/implementations/http/hono/path.js.map +1 -0
  142. package/build/implementations/http/hono/path.test.d.ts +1 -0
  143. package/build/implementations/http/hono/path.test.js +83 -0
  144. package/build/implementations/http/hono/path.test.js.map +1 -0
  145. package/build/implementations/http/hono/types.d.ts +51 -0
  146. package/build/implementations/http/hono/types.js.map +1 -0
  147. package/build/implementations/http/on-request-error.test.js +6 -96
  148. package/build/implementations/http/on-request-error.test.js.map +1 -1
  149. package/build/implementations/http/route-errors.test.js +11 -59
  150. package/build/implementations/http/route-errors.test.js.map +1 -1
  151. package/build/implementations/types.d.ts +43 -9
  152. package/build/index.d.ts +125 -115
  153. package/build/index.js +10 -222
  154. package/build/index.js.map +1 -1
  155. package/build/index.test.js +30 -822
  156. package/build/index.test.js.map +1 -1
  157. package/build/migration.test.d.ts +1 -0
  158. package/build/migration.test.js +34 -0
  159. package/build/migration.test.js.map +1 -0
  160. package/build/schema/compute-schema.d.ts +11 -3
  161. package/build/schema/compute-schema.js +13 -7
  162. package/build/schema/compute-schema.js.map +1 -1
  163. package/build/schema/parser.d.ts +11 -3
  164. package/build/schema/parser.js +49 -9
  165. package/build/schema/parser.js.map +1 -1
  166. package/build/stack-utils.js +8 -0
  167. package/build/stack-utils.js.map +1 -1
  168. package/build/types.d.ts +142 -0
  169. package/build/types.js.map +1 -0
  170. package/docs/astro-adapter.md +5 -5
  171. package/docs/core.md +34 -17
  172. package/docs/http-integrations.md +83 -170
  173. package/docs/streaming.md +3 -60
  174. package/docs/superpowers/plans/2026-05-07-astro-adapter.md +2 -7
  175. package/docs/superpowers/plans/2026-05-08-create-http.md +3355 -0
  176. package/docs/superpowers/plans/2026-05-08-hono-app-builder-convergence.md +3365 -0
  177. package/docs/superpowers/specs/2026-05-07-astro-adapter-design.md +1 -3
  178. package/docs/superpowers/specs/2026-05-08-create-http-design.md +409 -0
  179. package/docs/superpowers/specs/2026-05-08-hono-app-builder-convergence-design.md +411 -0
  180. package/package.json +4 -22
  181. package/src/client/call.test.ts +26 -0
  182. package/src/client/call.ts +4 -1
  183. package/src/client/fetch-adapter.test.ts +14 -1
  184. package/src/client/fetch-adapter.ts +3 -1
  185. package/src/client/index.test.ts +7 -7
  186. package/src/client/request-builder.ts +2 -2
  187. package/src/client/stream.test.ts +39 -7
  188. package/src/client/stream.ts +16 -2
  189. package/src/client/typed-error-dispatch.test.ts +7 -97
  190. package/src/client/types.ts +21 -3
  191. package/src/codegen/__fixtures__/users-envelope.json +119 -38
  192. package/src/codegen/e2e.test.ts +98 -24
  193. package/src/codegen/emit-errors.integration.test.ts +1 -1
  194. package/src/codegen/emit-scope.test.ts +395 -110
  195. package/src/codegen/emit-scope.ts +350 -55
  196. package/src/codegen/pipeline.test.ts +7 -7
  197. package/src/codegen/resolve-envelope.test.ts +5 -5
  198. package/src/codegen/resolve-envelope.ts +1 -1
  199. package/src/codegen/targets/_shared/route-slots.test.ts +109 -26
  200. package/src/codegen/targets/_shared/route-slots.ts +48 -11
  201. package/src/codegen/targets/kotlin/__fixtures__/users-golden.kt +73 -0
  202. package/src/codegen/targets/kotlin/emit-route-kotlin.test.ts +100 -17
  203. package/src/codegen/targets/kotlin/emit-scope-kotlin.test.ts +9 -6
  204. package/src/codegen/targets/kotlin/integration.test.ts +19 -0
  205. package/src/codegen/targets/swift/__fixtures__/users-golden.swift +79 -0
  206. package/src/codegen/targets/swift/access-level.test.ts +8 -11
  207. package/src/codegen/targets/swift/emit-route-swift.test.ts +103 -20
  208. package/src/codegen/targets/swift/emit-scope-swift.test.ts +12 -9
  209. package/src/codegen/targets/swift/integration.test.ts +17 -0
  210. package/src/create-http-stream.test.ts +97 -0
  211. package/src/create-http-stream.ts +191 -0
  212. package/src/create-http.test.ts +163 -0
  213. package/src/create-http.ts +211 -0
  214. package/src/create-stream.test.ts +565 -0
  215. package/src/create-stream.ts +228 -0
  216. package/src/create.test.ts +658 -0
  217. package/src/create.ts +172 -0
  218. package/src/exports.ts +2 -0
  219. package/src/implementations/http/README.md +135 -95
  220. package/src/implementations/http/astro/README.md +4 -5
  221. package/src/implementations/http/astro/index.test.ts +25 -18
  222. package/src/implementations/http/doc-registry.test.ts +42 -5
  223. package/src/implementations/http/doc-registry.ts +1 -1
  224. package/src/implementations/http/error-dispatch.test.ts +283 -0
  225. package/src/implementations/http/error-dispatch.ts +176 -0
  226. package/src/implementations/http/error-taxonomy.ts +5 -5
  227. package/src/implementations/http/hono/docs/http-doc.ts +43 -0
  228. package/src/implementations/http/hono/docs/http-stream-doc.ts +44 -0
  229. package/src/implementations/http/hono/docs/rpc-doc.ts +34 -0
  230. package/src/implementations/http/hono/docs/stream-doc.ts +53 -0
  231. package/src/implementations/http/hono/handlers/http-stream.test.ts +150 -0
  232. package/src/implementations/http/hono/handlers/http-stream.ts +152 -0
  233. package/src/implementations/http/hono/handlers/http.test.ts +130 -0
  234. package/src/implementations/http/hono/handlers/http.ts +147 -0
  235. package/src/implementations/http/hono/handlers/rpc.test.ts +81 -0
  236. package/src/implementations/http/hono/handlers/rpc.ts +54 -0
  237. package/src/implementations/http/hono/handlers/stream.test.ts +198 -0
  238. package/src/implementations/http/hono/handlers/stream.ts +208 -0
  239. package/src/implementations/http/hono/index.test.ts +329 -0
  240. package/src/implementations/http/hono/index.ts +204 -0
  241. package/src/implementations/http/hono/path.test.ts +96 -0
  242. package/src/implementations/http/hono/path.ts +59 -0
  243. package/src/implementations/http/hono/types.ts +93 -0
  244. package/src/implementations/http/on-request-error.test.ts +10 -116
  245. package/src/implementations/http/route-errors.test.ts +11 -77
  246. package/src/implementations/types.ts +44 -9
  247. package/src/index.test.ts +35 -1091
  248. package/src/index.ts +50 -474
  249. package/src/migration.test.ts +48 -0
  250. package/src/schema/compute-schema.ts +26 -12
  251. package/src/schema/parser.ts +62 -12
  252. package/src/stack-utils.ts +8 -0
  253. package/src/types.ts +133 -0
  254. package/agent_config/claude-code/skills/ts-procedures-scaffold/templates/express-rpc.md +0 -137
  255. package/agent_config/claude-code/skills/ts-procedures-scaffold/templates/hono-api.md +0 -173
  256. package/agent_config/claude-code/skills/ts-procedures-scaffold/templates/hono-rpc.md +0 -142
  257. package/agent_config/claude-code/skills/ts-procedures-scaffold/templates/hono-stream.md +0 -147
  258. package/build/implementations/http/express-rpc/error-taxonomy.test.js +0 -83
  259. package/build/implementations/http/express-rpc/error-taxonomy.test.js.map +0 -1
  260. package/build/implementations/http/express-rpc/index.d.ts +0 -125
  261. package/build/implementations/http/express-rpc/index.js +0 -216
  262. package/build/implementations/http/express-rpc/index.js.map +0 -1
  263. package/build/implementations/http/express-rpc/index.test.js +0 -684
  264. package/build/implementations/http/express-rpc/index.test.js.map +0 -1
  265. package/build/implementations/http/express-rpc/types.d.ts +0 -11
  266. package/build/implementations/http/express-rpc/types.js.map +0 -1
  267. package/build/implementations/http/hono-api/error-taxonomy.test.js +0 -137
  268. package/build/implementations/http/hono-api/error-taxonomy.test.js.map +0 -1
  269. package/build/implementations/http/hono-api/index.d.ts +0 -151
  270. package/build/implementations/http/hono-api/index.js +0 -344
  271. package/build/implementations/http/hono-api/index.js.map +0 -1
  272. package/build/implementations/http/hono-api/index.test.js +0 -992
  273. package/build/implementations/http/hono-api/index.test.js.map +0 -1
  274. package/build/implementations/http/hono-api/types.d.ts +0 -13
  275. package/build/implementations/http/hono-api/types.js.map +0 -1
  276. package/build/implementations/http/hono-rpc/error-taxonomy.test.js +0 -64
  277. package/build/implementations/http/hono-rpc/error-taxonomy.test.js.map +0 -1
  278. package/build/implementations/http/hono-rpc/index.d.ts +0 -130
  279. package/build/implementations/http/hono-rpc/index.js +0 -209
  280. package/build/implementations/http/hono-rpc/index.js.map +0 -1
  281. package/build/implementations/http/hono-rpc/index.test.js +0 -828
  282. package/build/implementations/http/hono-rpc/index.test.js.map +0 -1
  283. package/build/implementations/http/hono-rpc/types.d.ts +0 -11
  284. package/build/implementations/http/hono-rpc/types.js +0 -2
  285. package/build/implementations/http/hono-rpc/types.js.map +0 -1
  286. package/build/implementations/http/hono-stream/error-taxonomy.test.js +0 -159
  287. package/build/implementations/http/hono-stream/error-taxonomy.test.js.map +0 -1
  288. package/build/implementations/http/hono-stream/index.d.ts +0 -171
  289. package/build/implementations/http/hono-stream/index.js +0 -415
  290. package/build/implementations/http/hono-stream/index.js.map +0 -1
  291. package/build/implementations/http/hono-stream/index.test.js +0 -1383
  292. package/build/implementations/http/hono-stream/index.test.js.map +0 -1
  293. package/build/implementations/http/hono-stream/types.d.ts +0 -15
  294. package/build/implementations/http/hono-stream/types.js +0 -2
  295. package/build/implementations/http/hono-stream/types.js.map +0 -1
  296. package/src/implementations/http/express-rpc/README.md +0 -280
  297. package/src/implementations/http/express-rpc/error-taxonomy.test.ts +0 -103
  298. package/src/implementations/http/express-rpc/index.test.ts +0 -957
  299. package/src/implementations/http/express-rpc/index.ts +0 -327
  300. package/src/implementations/http/express-rpc/types.ts +0 -16
  301. package/src/implementations/http/hono-api/README.md +0 -284
  302. package/src/implementations/http/hono-api/error-taxonomy.test.ts +0 -179
  303. package/src/implementations/http/hono-api/index.test.ts +0 -1341
  304. package/src/implementations/http/hono-api/index.ts +0 -519
  305. package/src/implementations/http/hono-api/types.ts +0 -16
  306. package/src/implementations/http/hono-rpc/README.md +0 -357
  307. package/src/implementations/http/hono-rpc/error-taxonomy.test.ts +0 -82
  308. package/src/implementations/http/hono-rpc/index.test.ts +0 -1107
  309. package/src/implementations/http/hono-rpc/index.ts +0 -320
  310. package/src/implementations/http/hono-rpc/types.ts +0 -16
  311. package/src/implementations/http/hono-stream/README.md +0 -559
  312. package/src/implementations/http/hono-stream/error-taxonomy.test.ts +0 -178
  313. package/src/implementations/http/hono-stream/index.test.ts +0 -1804
  314. package/src/implementations/http/hono-stream/index.ts +0 -622
  315. package/src/implementations/http/hono-stream/types.ts +0 -20
  316. /package/build/{implementations/http/express-rpc/error-taxonomy.test.d.ts → create-http-stream.test.d.ts} +0 -0
  317. /package/build/{implementations/http/express-rpc/index.test.d.ts → create-http.test.d.ts} +0 -0
  318. /package/build/{implementations/http/hono-api/error-taxonomy.test.d.ts → create-stream.test.d.ts} +0 -0
  319. /package/build/{implementations/http/hono-api/index.test.d.ts → create.test.d.ts} +0 -0
  320. /package/build/implementations/http/{hono-rpc/error-taxonomy.test.d.ts → error-dispatch.test.d.ts} +0 -0
  321. /package/build/implementations/http/{hono-rpc/index.test.d.ts → hono/handlers/http-stream.test.d.ts} +0 -0
  322. /package/build/implementations/http/{hono-stream/error-taxonomy.test.d.ts → hono/handlers/http.test.d.ts} +0 -0
  323. /package/build/implementations/http/{hono-stream/index.test.d.ts → hono/handlers/rpc.test.d.ts} +0 -0
  324. /package/build/implementations/http/{express-rpc → hono}/types.js +0 -0
  325. /package/build/{implementations/http/hono-api/types.js → types.js} +0 -0
package/src/index.ts CHANGED
@@ -1,92 +1,53 @@
1
- import {
2
- ProcedureError,
3
- ProcedureValidationError,
4
- ProcedureYieldValidationError,
5
- } from './errors.js'
6
- import { computeSchema } from './schema/compute-schema.js'
7
- import { Prettify, TJSONSchema, TSchemaLib } from './schema/types.js'
8
- import { captureDefinitionInfo } from './stack-utils.js'
9
-
10
- export type TNoContextProvided = unknown
11
-
12
- export type TLocalContext = {
13
- error: (message: string, meta?: object) => ProcedureError
14
- signal?: AbortSignal
15
- }
16
-
17
- export type TStreamContext = TLocalContext & {
18
- signal: AbortSignal
19
- }
20
-
21
- export type TProcedureRegistration<TContext = unknown, TExtendedConfig = unknown> = {
22
- name: string
23
- isStream?: false
24
- config: {
25
- description?: string
26
- schema?: {
27
- params?: TJSONSchema
28
- returnType?: TJSONSchema
29
- input?: Record<string, TJSONSchema>
30
- }
31
- validation?: {
32
- params?: (params: any) => { errors?: any[] }
33
- input?: Record<string, (value: any) => { errors?: any[] }>
34
- }
35
- } & TExtendedConfig
1
+ import { Prettify, TJSONSchema } from './schema/types.js'
2
+ import { makeCreate } from './create.js'
3
+ import { makeCreateStream } from './create-stream.js'
4
+ import { makeCreateHttp } from './create-http.js'
5
+ import { makeCreateHttpStream } from './create-http-stream.js'
6
+
7
+ export {
8
+ type TNoContextProvided,
9
+ type TLocalContext,
10
+ type TStreamContext,
11
+ type ProcedureKind,
12
+ type TProcedureRegistration,
13
+ type TStreamProcedureRegistration,
14
+ type THttpProcedureRegistration,
15
+ type THttpStreamProcedureRegistration,
16
+ type TBuilderConfig,
17
+ } from './types.js'
36
18
 
37
- handler: (ctx: TContext, params?: any) => Promise<any>
38
- }
39
-
40
- export type TStreamProcedureRegistration<TContext = unknown, TExtendedConfig = unknown> = {
41
- name: string
42
- isStream: true
43
- config: {
44
- description?: string
45
- schema?: {
46
- params?: TJSONSchema
47
- yieldType?: TJSONSchema
48
- returnType?: TJSONSchema
49
- input?: Record<string, TJSONSchema>
50
- }
51
- validation?: {
52
- params?: (params: any) => { errors?: any[] }
53
- yield?: (value: any) => { errors?: any[] }
54
- input?: Record<string, (value: any) => { errors?: any[] }>
55
- }
56
- } & TExtendedConfig
57
-
58
- handler: (ctx: TContext, params?: any) => AsyncGenerator<any, any, unknown>
59
- }
19
+ import {
20
+ TNoContextProvided,
21
+ TBuilderConfig,
22
+ TProcedureRegistration,
23
+ TStreamProcedureRegistration,
24
+ THttpProcedureRegistration,
25
+ THttpStreamProcedureRegistration,
26
+ } from './types.js'
60
27
 
61
28
  export function Procedures<TContext = TNoContextProvided, TExtendedConfig = unknown>(
62
29
  /**
63
30
  * Optionally provided builder to register Procedures
64
31
  */
65
32
  builder?: {
33
+ config?: TBuilderConfig
34
+ /**
35
+ * Called once per registered procedure. Receives the full registration
36
+ * object for any of the four procedure kinds:
37
+ * - `TProcedureRegistration` (Create)
38
+ * - `TStreamProcedureRegistration` (CreateStream)
39
+ * - `THttpProcedureRegistration` (CreateHttp)
40
+ * - `THttpStreamProcedureRegistration` (CreateHttpStream)
41
+ *
42
+ * Use the `kind` discriminant to narrow to the specific shape.
43
+ */
66
44
  onCreate?: (
67
- procedure: Prettify<{
68
- name: string
69
- isStream?: boolean
70
- handler:
71
- | ((ctx: Prettify<TContext>, params?: any) => Promise<any>)
72
- | ((ctx: Prettify<TContext>, params?: any) => AsyncGenerator<any, any, unknown>)
73
- config: Prettify<
74
- {
75
- description?: string
76
- schema?: {
77
- params?: TJSONSchema
78
- yieldType?: TJSONSchema
79
- returnType?: TJSONSchema
80
- input?: Record<string, TJSONSchema>
81
- }
82
- validation?: {
83
- params?: (params: any) => { errors?: any[] }
84
- yield?: (value: any) => { errors?: any[] }
85
- input?: Record<string, (value: any) => { errors?: any[] }>
86
- }
87
- } & TExtendedConfig
88
- >
89
- }>
45
+ procedure: Prettify<
46
+ | TProcedureRegistration<TContext, TExtendedConfig>
47
+ | TStreamProcedureRegistration<TContext, TExtendedConfig>
48
+ | THttpProcedureRegistration<TContext>
49
+ | THttpStreamProcedureRegistration<TContext>
50
+ >
90
51
  ) => void
91
52
  }
92
53
  ) {
@@ -94,401 +55,14 @@ export function Procedures<TContext = TNoContextProvided, TExtendedConfig = unkn
94
55
  string,
95
56
  | TProcedureRegistration<TContext, TExtendedConfig>
96
57
  | TStreamProcedureRegistration<TContext, TExtendedConfig>
58
+ | THttpProcedureRegistration<TContext>
59
+ | THttpStreamProcedureRegistration<TContext>
97
60
  > = new Map()
98
61
 
99
- function Create<
100
- TName extends string,
101
- TParams,
102
- TReturnType,
103
- TInput extends Record<string, unknown> | undefined = undefined,
104
- >(
105
- name: TName,
106
- config: {
107
- description?: string
108
- schema?: {
109
- params?: TParams
110
- returnType?: TReturnType
111
- input?: TInput
112
- }
113
- } & TExtendedConfig,
114
- handler: (
115
- ctx: Prettify<TContext & TLocalContext>,
116
- params: TInput extends Record<string, unknown>
117
- ? Prettify<{ [K in keyof TInput]: TSchemaLib<TInput[K]> }>
118
- : TSchemaLib<TParams>
119
- ) => Promise<TSchemaLib<TReturnType>>
120
- ) {
121
- // Capture definition location as first action
122
- const definitionInfo = captureDefinitionInfo()
123
-
124
- // BEFORE computeSchema - fail fast on duplicate
125
- if (procedures.has(name)) {
126
- throw new Error(`Procedure with name ${name} is already registered`)
127
- }
128
-
129
- const { jsonSchema, validations } = computeSchema(name, config.schema, definitionInfo)
130
-
131
- // Create error factory once at registration time (outside handler)
132
- const errorFactory = (message: string, meta?: object) => {
133
- return new ProcedureError(name, message, meta, definitionInfo)
134
- }
135
-
136
- const registeredProcedure = {
137
- name,
138
- config: {
139
- // ctx: config.hook, ??? why was this here
140
- ...config,
141
- description: config.description,
142
- schema: jsonSchema,
143
- validation: {
144
- params: validations.params,
145
- ...(validations.input && { input: validations.input }),
146
- },
147
- },
148
-
149
- handler: async (ctx: Prettify<TContext>, params: TSchemaLib<TParams>) => {
150
- try {
151
- // Skip validation if caller has already validated (e.g., HonoStreamAppBuilder)
152
- const isPrevalidated = (ctx as { isPrevalidated?: boolean }).isPrevalidated
153
- if (validations?.params && !isPrevalidated) {
154
- const { errors } = validations.params(params)
155
-
156
- if (errors) {
157
- throw new ProcedureValidationError(
158
- name,
159
- `Validation error for ${name}`,
160
- errors,
161
- definitionInfo
162
- )
163
- }
164
- }
165
-
166
- // Validate each input channel independently for better error messages.
167
- // Double validation (per-channel + merged) is intentional for developer experience;
168
- // the cost is negligible — revisit if validation becomes a performance bottleneck.
169
- if (validations?.input && !isPrevalidated) {
170
- for (const [channel, validator] of Object.entries(validations.input)) {
171
- const channelValue = (params as Record<string, unknown>)?.[channel]
172
- const { errors } = validator(channelValue)
173
-
174
- if (errors) {
175
- throw new ProcedureValidationError(
176
- name,
177
- `Validation error for ${name} in input.${channel}`,
178
- errors,
179
- definitionInfo
180
- )
181
- }
182
- }
183
- }
184
-
185
- const localCtx: TLocalContext = {
186
- error: errorFactory, // Reuse pre-created factory
187
- }
188
-
189
- // params is correctly typed at the public API boundary via conditional type;
190
- // cast here because TS cannot narrow conditional generics inside implementations
191
- return await handler(
192
- {
193
- ...ctx,
194
- ...localCtx,
195
- } as Prettify<TContext & TLocalContext>,
196
- params as any
197
- )
198
- } catch (error: any) {
199
- if (error instanceof ProcedureError) {
200
- throw error
201
- } else {
202
- const err = new ProcedureError(
203
- name,
204
- `Error in handler for ${name} - ${error?.message}`,
205
- undefined,
206
- definitionInfo
207
- )
208
- err.cause = error // Preserve original error
209
- // Preserve original stack but append definition info
210
- if (error.stack && definitionInfo.definedAt) {
211
- const { file, line, column } = definitionInfo.definedAt
212
- err.stack =
213
- error.stack +
214
- `\n--- Procedure "${name}" defined at ---\n at ${file}:${line}:${column}`
215
- } else if (error.stack) {
216
- err.stack = error.stack
217
- }
218
- throw err
219
- }
220
- }
221
- },
222
- }
223
-
224
- procedures.set(name, registeredProcedure)
225
-
226
- if (builder?.onCreate) {
227
- builder.onCreate(registeredProcedure)
228
- }
229
-
230
- const info = {
231
- name,
232
- ...registeredProcedure.config,
233
- }
234
-
235
- // return so can be called directly (ie: int/unit tests)
236
- return {
237
- [name]: registeredProcedure.handler,
238
- procedure: registeredProcedure.handler,
239
- info,
240
- } as {
241
- [K in TName]: (
242
- ctx: Prettify<TContext>,
243
- params: TInput extends Record<string, unknown>
244
- ? Prettify<{ [K in keyof TInput]: TSchemaLib<TInput[K]> }>
245
- : TSchemaLib<TParams>
246
- ) => Promise<TSchemaLib<TReturnType>>
247
- } & {
248
- procedure: (
249
- ctx: Prettify<TContext>,
250
- params: TInput extends Record<string, unknown>
251
- ? Prettify<{ [K in keyof TInput]: TSchemaLib<TInput[K]> }>
252
- : TSchemaLib<TParams>
253
- ) => Promise<TSchemaLib<TReturnType>>
254
- info: {
255
- name: TName
256
- description?: string
257
- schema: {
258
- params?: TParams
259
- returnType?: TReturnType
260
- input?: TInput
261
- }
262
- validation?: {
263
- params?: (params: any) => { errors?: any[] }
264
- input?: Record<string, (value: any) => { errors?: any[] }>
265
- }
266
- } & TExtendedConfig
267
- }
268
- }
269
-
270
- function CreateStream<
271
- TName extends string,
272
- TParams,
273
- TYieldType,
274
- TReturnType = void,
275
- TInput extends Record<string, unknown> | undefined = undefined,
276
- >(
277
- name: TName,
278
- config: {
279
- description?: string
280
- schema?: {
281
- params?: TParams
282
- yieldType?: TYieldType
283
- returnType?: TReturnType
284
- input?: TInput
285
- }
286
- validateYields?: boolean
287
- } & TExtendedConfig,
288
- handler: (
289
- ctx: Prettify<TContext & TStreamContext>,
290
- params: TInput extends Record<string, unknown>
291
- ? Prettify<{ [K in keyof TInput]: TSchemaLib<TInput[K]> }>
292
- : TSchemaLib<TParams>
293
- ) => AsyncGenerator<TSchemaLib<TYieldType>, TSchemaLib<TReturnType> | void, unknown>
294
- ) {
295
- // Capture definition location as first action
296
- const definitionInfo = captureDefinitionInfo()
297
-
298
- // BEFORE computeSchema - fail fast on duplicate
299
- if (procedures.has(name)) {
300
- throw new Error(`Procedure with name ${name} is already registered`)
301
- }
302
-
303
- const { jsonSchema, validations } = computeSchema(name, config.schema, definitionInfo)
304
-
305
- // Create error factory once at registration time (outside handler)
306
- const errorFactory = (message: string, meta?: object) => {
307
- return new ProcedureError(name, message, meta, definitionInfo)
308
- }
309
-
310
- const validateYields = config.validateYields ?? false
311
-
312
- const registeredProcedure: TStreamProcedureRegistration<TContext, TExtendedConfig> = {
313
- name,
314
- isStream: true,
315
- config: {
316
- ...config,
317
- description: config.description,
318
- schema: jsonSchema,
319
- validation: {
320
- params: validations.params,
321
- yield: validations.yield,
322
- ...(validations.input && { input: validations.input }),
323
- },
324
- },
325
-
326
- handler: async function* wrappedHandler(
327
- ctx: Prettify<TContext>,
328
- params: TSchemaLib<TParams>
329
- ) {
330
- // Create abort controller for this stream
331
- const abortController = new AbortController()
332
-
333
- // Skip validation if caller has already validated (e.g., HonoStreamAppBuilder)
334
- const isPrevalidated = (ctx as { isPrevalidated?: boolean }).isPrevalidated
335
- if (validations?.params && !isPrevalidated) {
336
- const { errors } = validations.params(params)
337
-
338
- if (errors) {
339
- throw new ProcedureValidationError(
340
- name,
341
- `Validation error for ${name}`,
342
- errors,
343
- definitionInfo
344
- )
345
- }
346
- }
347
-
348
- // Validate each input channel independently (see Create for rationale)
349
- if (validations?.input && !isPrevalidated) {
350
- for (const [channel, validator] of Object.entries(validations.input)) {
351
- const channelValue = (params as Record<string, unknown>)?.[channel]
352
- const { errors } = validator(channelValue)
353
-
354
- if (errors) {
355
- throw new ProcedureValidationError(
356
- name,
357
- `Validation error for ${name} in input.${channel}`,
358
- errors,
359
- definitionInfo
360
- )
361
- }
362
- }
363
- }
364
-
365
- // Combine with external signal (e.g., from HTTP request) if provided
366
- const incomingSignal = (ctx as { signal?: AbortSignal }).signal
367
- const signal = incomingSignal
368
- ? AbortSignal.any([incomingSignal, abortController.signal])
369
- : abortController.signal
370
-
371
- const streamCtx: TStreamContext = {
372
- error: errorFactory,
373
- signal,
374
- }
375
-
376
- // params is correctly typed at the public API boundary via conditional type;
377
- // cast here because TS cannot narrow conditional generics inside implementations
378
- const userGenerator = handler(
379
- {
380
- ...ctx,
381
- ...streamCtx,
382
- } as Prettify<TContext & TStreamContext>,
383
- params as any
384
- )
385
-
386
- const userIterator = userGenerator[Symbol.asyncIterator]()
387
- try {
388
- let userIterResult = await userIterator.next()
389
-
390
- while (!userIterResult.done) {
391
- const value = userIterResult.value
392
-
393
- // Only validate if explicitly enabled via validateYields: true
394
- if (validateYields && validations.yield) {
395
- const { errors } = validations.yield(value)
396
- if (errors) {
397
- throw new ProcedureYieldValidationError(
398
- name,
399
- `Yield validation error for ${name}`,
400
- errors,
401
- definitionInfo
402
- )
403
- }
404
- }
405
-
406
- yield value
407
- userIterResult = await userIterator.next()
408
- }
409
-
410
- // Propagate the generator's return value so consumers (e.g. HonoStreamAppBuilder)
411
- // can send it as a special 'return' SSE event
412
- return userIterResult.value
413
- } catch (error: any) {
414
- // Preserve the original error class so HTTP builders' taxonomies and
415
- // `onMidStreamError` callbacks see the actual thrown type — boxing
416
- // user-defined errors inside ProcedureError defeats route-declared
417
- // typed-error dispatch on the client. Augment the stack trace in
418
- // place with the procedure's definition site when available.
419
- if (
420
- definitionInfo.definedAt &&
421
- error &&
422
- typeof error.stack === 'string'
423
- ) {
424
- const { file, line, column } = definitionInfo.definedAt
425
- error.stack =
426
- `${error.stack}\n--- Procedure "${name}" defined at ---\n at ${file}:${line}:${column}`
427
- }
428
- throw error
429
- } finally {
430
- // Propagate `.return()` to the user generator so its `finally`
431
- // blocks (and any `signal`-driven cleanup) run when the consumer
432
- // closes the stream early. No-op when iteration already completed.
433
- try {
434
- await userIterator.return?.(undefined)
435
- } catch {
436
- // Swallow — cleanup must not mask the primary error path
437
- }
438
- abortController.abort('stream-completed')
439
- }
440
- } as (ctx: TContext, params?: any) => AsyncGenerator<any, any, unknown>,
441
- }
442
-
443
- procedures.set(name, registeredProcedure)
444
-
445
- if (builder?.onCreate) {
446
- builder.onCreate(registeredProcedure)
447
- }
448
-
449
- const info = {
450
- name,
451
- isStream: true as const,
452
- ...registeredProcedure.config,
453
- }
454
-
455
- // return so can be called directly (ie: int/unit tests)
456
- return {
457
- [name]: registeredProcedure.handler,
458
- procedure: registeredProcedure.handler,
459
- info,
460
- } as {
461
- [K in TName]: (
462
- ctx: Prettify<TContext>,
463
- params: TInput extends Record<string, unknown>
464
- ? Prettify<{ [K in keyof TInput]: TSchemaLib<TInput[K]> }>
465
- : TSchemaLib<TParams>
466
- ) => AsyncGenerator<TSchemaLib<TYieldType>, TSchemaLib<TReturnType> | void, unknown>
467
- } & {
468
- procedure: (
469
- ctx: Prettify<TContext>,
470
- params: TInput extends Record<string, unknown>
471
- ? Prettify<{ [K in keyof TInput]: TSchemaLib<TInput[K]> }>
472
- : TSchemaLib<TParams>
473
- ) => AsyncGenerator<TSchemaLib<TYieldType>, TSchemaLib<TReturnType> | void, unknown>
474
- info: {
475
- name: TName
476
- isStream: true
477
- description?: string
478
- schema: {
479
- params?: TParams
480
- yieldType?: TYieldType
481
- returnType?: TReturnType
482
- input?: TInput
483
- }
484
- validation?: {
485
- params?: (params: any) => { errors?: any[] }
486
- yield?: (value: any) => { errors?: any[] }
487
- input?: Record<string, (value: any) => { errors?: any[] }>
488
- }
489
- } & TExtendedConfig
490
- }
491
- }
62
+ const Create = makeCreate<TContext, TExtendedConfig>(procedures, builder)
63
+ const CreateStream = makeCreateStream<TContext, TExtendedConfig>(procedures, builder)
64
+ const CreateHttp = makeCreateHttp<TContext>(procedures, builder)
65
+ const CreateHttpStream = makeCreateHttpStream<TContext>(procedures, builder)
492
66
 
493
67
  return {
494
68
  /**
@@ -521,5 +95,7 @@ export function Procedures<TContext = TNoContextProvided, TExtendedConfig = unkn
521
95
 
522
96
  Create,
523
97
  CreateStream,
98
+ CreateHttp,
99
+ CreateHttpStream,
524
100
  }
525
101
  }
@@ -0,0 +1,48 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { Procedures } from './index.js'
3
+ import { ProcedureRegistrationError } from './errors.js'
4
+ import { Type } from 'typebox'
5
+
6
+ describe('v7 → v8 migration errors', () => {
7
+ it('rejects schema.input on Create with v8 message', () => {
8
+ const procs = Procedures()
9
+ expect(() =>
10
+ procs.Create('Legacy', { schema: { input: { body: Type.Object({}) } } } as any, async () => undefined)
11
+ ).toThrow(ProcedureRegistrationError)
12
+ expect(() =>
13
+ procs.Create('Legacy2', { schema: { input: { body: Type.Object({}) } } } as any, async () => undefined)
14
+ ).toThrow(/schema.input was removed in v8/)
15
+ })
16
+
17
+ it('rejects schema.input on CreateStream with v8 message', () => {
18
+ const procs = Procedures()
19
+ expect(() =>
20
+ procs.CreateStream('Legacy', {
21
+ schema: { input: { body: Type.Object({}) }, yieldType: Type.Number() } as any,
22
+ }, async function* () {})
23
+ ).toThrow(/schema.input was removed in v8/)
24
+ })
25
+ })
26
+
27
+ describe('HTTP fields rejected on RPC creators', () => {
28
+ const HTTP_FIELDS = ['path', 'method', 'req', 'res', 'successStatus'] as const
29
+
30
+ for (const field of HTTP_FIELDS) {
31
+ it(`Create rejects ${field}`, () => {
32
+ const procs = Procedures()
33
+ expect(() =>
34
+ procs.Create('Foo', { [field]: 'x', schema: { params: Type.Object({}) } } as any, async () => undefined)
35
+ ).toThrow(/HTTP fields require CreateHttp/)
36
+ })
37
+
38
+ it(`CreateStream rejects ${field}`, () => {
39
+ const procs = Procedures()
40
+ expect(() =>
41
+ procs.CreateStream('Bar', {
42
+ [field]: 'x',
43
+ schema: { params: Type.Object({}), yieldType: Type.Number() },
44
+ } as any, async function* () {})
45
+ ).toThrow(/HTTP fields require CreateHttp/)
46
+ })
47
+ }
48
+ })
@@ -17,7 +17,8 @@ export function computeSchema<TParamsSchemaType, TReturnTypeSchemaType, TYieldTy
17
17
  params?: TParamsSchemaType
18
18
  returnType?: TReturnTypeSchemaType
19
19
  yieldType?: TYieldTypeSchemaType
20
- input?: Record<string, unknown>
20
+ req?: Record<string, unknown>
21
+ res?: { body?: unknown; headers?: unknown }
21
22
  },
22
23
  // Used for error stack trace details
23
24
  definitionInfo?: DefinitionInfo
@@ -26,44 +27,56 @@ export function computeSchema<TParamsSchemaType, TReturnTypeSchemaType, TYieldTy
26
27
  params?: TJSONSchema
27
28
  returnType?: TJSONSchema
28
29
  yieldType?: TJSONSchema
29
- input?: Record<string, TJSONSchema>
30
+ req?: Record<string, TJSONSchema>
31
+ res?: { body?: TJSONSchema; headers?: TJSONSchema }
30
32
  }
31
33
  validations: {
32
34
  params?: (params?: any) => { errors?: TSchemaValidationError[] }
33
35
  yield?: (value?: any) => { errors?: TSchemaValidationError[] }
34
- input?: Record<string, (value?: any) => { errors?: TSchemaValidationError[] }>
36
+ req?: Record<string, (value?: any) => { errors?: TSchemaValidationError[] }>
35
37
  }
36
38
  } {
37
39
  const jsonSchema: {
38
40
  params?: TJSONSchema
39
41
  returnType?: TJSONSchema
40
42
  yieldType?: TJSONSchema
41
- input?: Record<string, TJSONSchema>
43
+ req?: Record<string, TJSONSchema>
44
+ res?: { body?: TJSONSchema; headers?: TJSONSchema }
42
45
  } = {
43
46
  params: undefined,
44
47
  returnType: undefined,
45
48
  yieldType: undefined,
46
- input: undefined,
49
+ req: undefined,
50
+ res: undefined,
47
51
  }
48
52
 
49
53
  const validations: {
50
54
  params?: (params?: any) => { errors?: TSchemaValidationError[] }
51
55
  yield?: (value?: any) => { errors?: TSchemaValidationError[] }
52
- input?: Record<string, (value?: any) => { errors?: TSchemaValidationError[] }>
56
+ req?: Record<string, (value?: any) => { errors?: TSchemaValidationError[] }>
53
57
  } = {}
54
58
 
55
- // Mutual exclusivity: params and input cannot both be defined
56
- if (schema?.params && schema?.input) {
59
+ // Migration guard: schema.input was removed in v8
60
+ if (schema && 'input' in schema && (schema as any).input !== undefined) {
57
61
  throw new ProcedureRegistrationError(
58
62
  name,
59
- `schema.params and schema.input are mutually exclusive for procedure "${name}". Use schema.params for flat input or schema.input for structured multi-channel input.`,
63
+ `schema.input was removed in v8. Use CreateHttp / CreateHttpStream for per-channel HTTP validation. Procedure: "${name}".`,
64
+ definitionInfo,
65
+ )
66
+ }
67
+
68
+ // Mutual exclusivity: params and req cannot both be defined
69
+ if (schema?.params && schema?.req) {
70
+ throw new ProcedureRegistrationError(
71
+ name,
72
+ `schema.params and schema.req are mutually exclusive for procedure "${name}". Use schema.params for RPC procedures or schema.req for HTTP procedures.`,
60
73
  definitionInfo
61
74
  )
62
75
  }
63
76
 
64
77
  if (schema) {
65
78
  const {
66
- jsonSchema: { params, returnType, yieldType, input },
79
+ jsonSchema: { params, returnType, yieldType, req, res },
67
80
  validation,
68
81
  } = schemaParser(schema, (errors) => {
69
82
  throw new ProcedureRegistrationError(
@@ -78,10 +91,11 @@ export function computeSchema<TParamsSchemaType, TReturnTypeSchemaType, TYieldTy
78
91
  jsonSchema.params = params
79
92
  jsonSchema.returnType = returnType
80
93
  jsonSchema.yieldType = yieldType
81
- jsonSchema.input = input
94
+ jsonSchema.req = req
95
+ jsonSchema.res = res
82
96
  validations.params = validation.params
83
97
  validations.yield = validation.yield
84
- validations.input = validation.input
98
+ validations.req = validation.req
85
99
  }
86
100
 
87
101
  return { jsonSchema, validations }