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
@@ -8,12 +8,13 @@ export type TSchemaParsed = {
8
8
  params?: TJSONSchema
9
9
  returnType?: TJSONSchema
10
10
  yieldType?: TJSONSchema
11
- input?: Record<string, TJSONSchema>
11
+ req?: Record<string, TJSONSchema>
12
+ res?: { body?: TJSONSchema; headers?: TJSONSchema }
12
13
  }
13
14
  validation: {
14
15
  params?: (params: any) => { errors?: TSchemaValidationError[] }
15
16
  yield?: (value: any) => { errors?: TSchemaValidationError[] }
16
- input?: Record<string, (value: any) => { errors?: TSchemaValidationError[] }>
17
+ req?: Record<string, (value: any) => { errors?: TSchemaValidationError[] }>
17
18
  }
18
19
  }
19
20
 
@@ -30,7 +31,13 @@ const ajv = addFormats(
30
31
  )
31
32
 
32
33
  export function schemaParser(
33
- schema: { params?: unknown; returnType?: unknown; yieldType?: unknown; input?: Record<string, unknown> },
34
+ schema: {
35
+ params?: unknown
36
+ returnType?: unknown
37
+ yieldType?: unknown
38
+ req?: Record<string, unknown>
39
+ res?: { body?: unknown; headers?: unknown }
40
+ },
34
41
  onParseError: (errors: Record<string, string>) => void
35
42
  ): TSchemaParsed {
36
43
  const jsonSchema: TSchemaParsed['jsonSchema'] = {}
@@ -150,11 +157,11 @@ export function schemaParser(
150
157
  }
151
158
  }
152
159
 
153
- if (schema.input) {
154
- jsonSchema.input = {}
155
- validation.input = {}
160
+ if (schema.req) {
161
+ jsonSchema.req = {}
162
+ validation.req = {}
156
163
 
157
- for (const [channelName, channelSchema] of Object.entries(schema.input)) {
164
+ for (const [channelName, channelSchema] of Object.entries(schema.req)) {
158
165
  if (!channelSchema) continue
159
166
 
160
167
  let channelJsonSchema: TJSONSchema | undefined
@@ -163,17 +170,17 @@ export function schemaParser(
163
170
  const extracted = extractJsonSchema(channelSchema as TJSONSchema)
164
171
  if (extracted) {
165
172
  channelJsonSchema = extracted
166
- jsonSchema.input[channelName] = extracted
173
+ jsonSchema.req![channelName] = extracted
167
174
  }
168
175
  } catch (e: any) {
169
176
  onParseError({
170
- [`input.${channelName}`]: `Error extracting json schema schema.input.${channelName} - ${e.message}`,
177
+ [`req.${channelName}`]: `Error extracting json schema schema.req.${channelName} - ${e.message}`,
171
178
  })
172
179
  }
173
180
 
174
181
  if (!channelJsonSchema) {
175
182
  onParseError({
176
- [`input.${channelName}`]: `Error extracting json schema schema.input.${channelName} - might be empty or not a valid suretype or typebox type`,
183
+ [`req.${channelName}`]: `Error extracting json schema schema.req.${channelName} - might be empty or not a valid suretype or typebox type`,
177
184
  })
178
185
  } else {
179
186
  let channelValidator: AJV.ValidateFunction | undefined
@@ -182,11 +189,11 @@ export function schemaParser(
182
189
  channelValidator = ajv.compile(channelJsonSchema as TJSONSchema)
183
190
  } catch (e: any) {
184
191
  onParseError({
185
- [`input.${channelName}`]: `Error compiling schema.input.${channelName} for validator - ${e.message}`,
192
+ [`req.${channelName}`]: `Error compiling schema.req.${channelName} for validator - ${e.message}`,
186
193
  })
187
194
  }
188
195
 
189
- validation.input[channelName] = (value: any) => {
196
+ validation.req![channelName] = (value: any) => {
190
197
  if (!channelValidator) {
191
198
  return {
192
199
  errors: [
@@ -211,5 +218,48 @@ export function schemaParser(
211
218
  }
212
219
  }
213
220
 
221
+ // res: compute JSON Schema for body and headers (documentation/codegen only — no validators)
222
+ if (schema.res) {
223
+ jsonSchema.res = {}
224
+
225
+ if (schema.res.body) {
226
+ try {
227
+ const extracted = extractJsonSchema(schema.res.body as TJSONSchema)
228
+ if (extracted) {
229
+ jsonSchema.res.body = extracted
230
+ }
231
+ } catch (e: any) {
232
+ onParseError({
233
+ 'res.body': `Error extracting json schema schema.res.body - ${e.message}`,
234
+ })
235
+ }
236
+
237
+ if (!jsonSchema.res.body) {
238
+ onParseError({
239
+ 'res.body': `Error extracting json schema schema.res.body - might be empty or not a valid suretype or typebox type`,
240
+ })
241
+ }
242
+ }
243
+
244
+ if (schema.res.headers) {
245
+ try {
246
+ const extracted = extractJsonSchema(schema.res.headers as TJSONSchema)
247
+ if (extracted) {
248
+ jsonSchema.res.headers = extracted
249
+ }
250
+ } catch (e: any) {
251
+ onParseError({
252
+ 'res.headers': `Error extracting json schema schema.res.headers - ${e.message}`,
253
+ })
254
+ }
255
+
256
+ if (!jsonSchema.res.headers) {
257
+ onParseError({
258
+ 'res.headers': `Error extracting json schema schema.res.headers - might be empty or not a valid suretype or typebox type`,
259
+ })
260
+ }
261
+ }
262
+ }
263
+
214
264
  return { jsonSchema, validation }
215
265
  }
@@ -23,6 +23,14 @@ export type DefinitionInfo = {
23
23
  const INTERNAL_FILES = [
24
24
  '/index.ts',
25
25
  '/index.js',
26
+ '/create.ts',
27
+ '/create.js',
28
+ '/create-stream.ts',
29
+ '/create-stream.js',
30
+ '/create-http.ts',
31
+ '/create-http.js',
32
+ '/create-http-stream.ts',
33
+ '/create-http-stream.js',
26
34
  '/errors.ts',
27
35
  '/errors.js',
28
36
  '/stack-utils.ts',
package/src/types.ts ADDED
@@ -0,0 +1,133 @@
1
+ import { ProcedureError } from './errors.js'
2
+ import { TJSONSchema } from './schema/types.js'
3
+
4
+ export type TNoContextProvided = unknown
5
+
6
+ export type TLocalContext = {
7
+ error: (message: string, meta?: object) => ProcedureError
8
+ signal?: AbortSignal
9
+ }
10
+
11
+ export type TStreamContext = TLocalContext & {
12
+ signal: AbortSignal
13
+ }
14
+
15
+ /**
16
+ * Discriminant on every procedure registration that drives builder routing.
17
+ *
18
+ * - `'rpc'` — Create() registration
19
+ * - `'rpc-stream'` — CreateStream() registration
20
+ * - `'http'` — CreateHttp() registration
21
+ * - `'http-stream'` — CreateHttpStream() registration
22
+ *
23
+ * `HonoAppBuilder` dispatches all four kinds from a single registration. Use
24
+ * `kind` for any logic that needs to distinguish procedure shape; the legacy
25
+ * `isStream` field on TStreamProcedureRegistration is kept for back-compat but
26
+ * new code should branch on `kind`.
27
+ */
28
+ export type ProcedureKind = 'rpc' | 'rpc-stream' | 'http' | 'http-stream'
29
+
30
+ export type TProcedureRegistration<TContext = unknown, TExtendedConfig = unknown> = {
31
+ name: string
32
+ kind: 'rpc'
33
+ config: {
34
+ description?: string
35
+ schema?: {
36
+ params?: TJSONSchema
37
+ returnType?: TJSONSchema
38
+ }
39
+ validation?: {
40
+ params?: (params: any) => { errors?: any[] }
41
+ }
42
+ } & TExtendedConfig
43
+ handler: (ctx: TContext, params?: any) => Promise<any>
44
+ }
45
+
46
+ export type TStreamProcedureRegistration<TContext = unknown, TExtendedConfig = unknown> = {
47
+ name: string
48
+ kind: 'rpc-stream'
49
+ isStream: true
50
+ config: {
51
+ description?: string
52
+ schema?: {
53
+ params?: TJSONSchema
54
+ yieldType?: TJSONSchema
55
+ returnType?: TJSONSchema
56
+ }
57
+ validation?: {
58
+ params?: (params: any) => { errors?: any[] }
59
+ yield?: (value: any) => { errors?: any[] }
60
+ }
61
+ validateYields?: boolean
62
+ } & TExtendedConfig
63
+ handler: (ctx: TContext, params?: any) => AsyncGenerator<any, any, unknown>
64
+ }
65
+
66
+ export type TBuilderConfig = {
67
+ /**
68
+ * Skip per-call AJV runtime validation. JSON Schema is still computed at
69
+ * registration time. Intended for trusted internal factories whose callers
70
+ * are already type-checked at build time.
71
+ */
72
+ noRuntimeValidation?: true
73
+ }
74
+
75
+ export type HttpMethod = 'get' | 'post' | 'put' | 'delete' | 'patch' | 'head'
76
+
77
+ export type TCreateHttpConfig<TReq, TRes, TErrorKey extends string = string> = {
78
+ path: string
79
+ method: HttpMethod
80
+ successStatus?: number
81
+ scope?: string
82
+ errors?: TErrorKey[]
83
+ description?: string
84
+ schema: {
85
+ req?: TReq
86
+ res?: TRes
87
+ }
88
+ }
89
+
90
+ export type THttpProcedureRegistration<TContext = unknown> = {
91
+ name: string
92
+ kind: 'http'
93
+ config: {
94
+ path: string
95
+ method: HttpMethod
96
+ successStatus?: number
97
+ scope?: string
98
+ errors?: string[]
99
+ description?: string
100
+ schema?: {
101
+ req?: Record<string, TJSONSchema>
102
+ res?: { body?: TJSONSchema; headers?: TJSONSchema }
103
+ }
104
+ validation?: {
105
+ req?: Record<string, (value: any) => { errors?: any[] }>
106
+ }
107
+ }
108
+ handler: (ctx: TContext, req?: any) => Promise<any>
109
+ }
110
+
111
+ export type THttpStreamProcedureRegistration<TContext = unknown> = {
112
+ name: string
113
+ kind: 'http-stream'
114
+ config: {
115
+ path: string
116
+ method: HttpMethod
117
+ scope?: string
118
+ errors?: string[]
119
+ description?: string
120
+ schema?: {
121
+ req?: Record<string, TJSONSchema>
122
+ res?: { headers?: TJSONSchema }
123
+ yield?: TJSONSchema
124
+ returnType?: TJSONSchema
125
+ }
126
+ validation?: {
127
+ req?: Record<string, (value: any) => { errors?: any[] }>
128
+ yield?: (value: any) => { errors?: any[] }
129
+ }
130
+ validateYields?: boolean
131
+ }
132
+ handler: (ctx: TContext, req?: any) => Promise<{ stream: AsyncGenerator<any, any, unknown>; initialHeaders?: Record<string, string> }>
133
+ }
@@ -1,137 +0,0 @@
1
- # Express RPC Template: {{Name}}
2
-
3
- ## Implementation — `{{Name}}.rpc.ts`
4
-
5
- ```typescript
6
- import { Procedures } from 'ts-procedures'
7
- import { defineErrorTaxonomy } from 'ts-procedures/express-rpc'
8
- import { ExpressRPCAppBuilder } from 'ts-procedures/express-rpc'
9
- import type { RPCConfig } from 'ts-procedures/http'
10
- import { Type } from 'typebox'
11
-
12
- // ─── Context ──────────────────────────────────────────────
13
-
14
- type {{Name}}Context = {
15
- userId: string
16
- requestId: string
17
- }
18
-
19
- // ─── Procedures ───────────────────────────────────────────
20
-
21
- const RPC = Procedures<{{Name}}Context, RPCConfig>()
22
-
23
- export const { GetItem } = RPC.Create(
24
- 'GetItem',
25
- {
26
- scope: '{{name}}', // TODO: set URL scope
27
- version: 1,
28
- description: 'Fetch item by ID',
29
- schema: {
30
- params: Type.Object({
31
- id: Type.String(),
32
- }),
33
- returnType: Type.Object({
34
- id: Type.String(),
35
- name: Type.String(),
36
- }),
37
- },
38
- },
39
- async (ctx, params) => {
40
- // TODO: implement
41
- return { id: params.id, name: 'Example' }
42
- }
43
- )
44
-
45
- export const { ListItems } = RPC.Create(
46
- 'ListItems',
47
- {
48
- scope: '{{name}}',
49
- version: 1,
50
- description: 'List all items',
51
- schema: {
52
- params: Type.Object({
53
- page: Type.Optional(Type.Number()),
54
- limit: Type.Optional(Type.Number()),
55
- }),
56
- },
57
- },
58
- async (ctx, params) => {
59
- // TODO: implement
60
- return { items: [], total: 0 }
61
- }
62
- )
63
-
64
- // ─── Error Taxonomy ───────────────────────────────────────
65
- // Declare the error classes this service throws. Framework errors
66
- // (ProcedureValidationError → 400, ctx.error() → 500) are caught by the
67
- // default taxonomy automatically. Add your own classes here — handlers just
68
- // `throw` them and the builder serializes via this map. See
69
- // docs/http-integrations.md#error-handling for the full contract.
70
-
71
- const {{name}}Errors = defineErrorTaxonomy({
72
- // Example — replace with your app's error classes:
73
- // NotFoundError: { class: NotFoundError, statusCode: 404 },
74
- // AuthError: { class: AuthError, statusCode: 401 },
75
- })
76
-
77
- // ─── Express App Builder ──────────────────────────────────
78
-
79
- export const {{name}}App = new ExpressRPCAppBuilder({
80
- pathPrefix: '/api',
81
- errors: {{name}}Errors,
82
- unknownError: {
83
- statusCode: 500,
84
- toResponse: () => ({ name: 'InternalServerError', message: 'Unexpected error' }),
85
- onCatch: (err, { procedure }) => console.error(`[${procedure.name}]`, err),
86
- },
87
- })
88
- .register(RPC, async (req) => ({
89
- userId: req.headers['x-user-id'] as string || 'anonymous',
90
- requestId: req.headers['x-request-id'] as string || crypto.randomUUID(),
91
- }))
92
- .build()
93
-
94
- // Route map:
95
- // POST /api/{{name}}/get-item/1
96
- // POST /api/{{name}}/list-items/1
97
-
98
- // Documentation: {{name}}App.docs
99
- ```
100
-
101
- ## Test — `{{Name}}.rpc.test.ts`
102
-
103
- ```typescript
104
- import { describe, test, expect } from 'vitest'
105
- import supertest from 'supertest'
106
- import { {{name}}App } from './{{Name}}.rpc'
107
-
108
- describe('{{Name}} RPC', () => {
109
- test('GET item returns expected result', async () => {
110
- const res = await supertest({{name}}App)
111
- .post('/api/{{name}}/get-item/1')
112
- .send({ id: 'item-1' })
113
- .expect(200)
114
-
115
- expect(res.body).toEqual({ id: 'item-1', name: 'Example' })
116
- })
117
-
118
- test('returns 400 for invalid params', async () => {
119
- const res = await supertest({{name}}App)
120
- .post('/api/{{name}}/get-item/1')
121
- .send({})
122
- .expect(400)
123
-
124
- expect(res.body.error).toBeDefined()
125
- })
126
-
127
- test('LIST items returns array', async () => {
128
- const res = await supertest({{name}}App)
129
- .post('/api/{{name}}/list-items/1')
130
- .send({ page: 1, limit: 10 })
131
- .expect(200)
132
-
133
- expect(res.body).toHaveProperty('items')
134
- expect(res.body).toHaveProperty('total')
135
- })
136
- })
137
- ```
@@ -1,173 +0,0 @@
1
- # Hono API Template: {{Name}}
2
-
3
- ## Implementation — `{{Name}}.api.ts`
4
-
5
- ```typescript
6
- import { Procedures } from 'ts-procedures'
7
- import { defineErrorTaxonomy } from 'ts-procedures/hono-api'
8
- import { HonoAPIAppBuilder } from 'ts-procedures/hono-api'
9
- import type { APIConfig, APIInput } from 'ts-procedures/http'
10
- import { Type } from 'typebox'
11
-
12
- // ─── Context ──────────────────────────────────────────────
13
-
14
- type {{Name}}Context = {
15
- userId: string
16
- requestId: string
17
- }
18
-
19
- // ─── Procedures ───────────────────────────────────────────
20
-
21
- const API = Procedures<{{Name}}Context, APIConfig>()
22
-
23
- export const { GetItem } = API.Create(
24
- 'GetItem',
25
- {
26
- path: '/{{name}}/:id', // TODO: set route path
27
- method: 'get',
28
- description: 'Fetch item by ID',
29
- schema: {
30
- input: {
31
- pathParams: Type.Object({ id: Type.String() }),
32
- } satisfies APIInput,
33
- returnType: Type.Object({
34
- id: Type.String(),
35
- name: Type.String(),
36
- }),
37
- },
38
- },
39
- async (ctx, { pathParams }) => {
40
- // TODO: implement
41
- return { id: pathParams.id, name: 'Example' }
42
- }
43
- )
44
-
45
- export const { CreateItem } = API.Create(
46
- 'CreateItem',
47
- {
48
- path: '/{{name}}',
49
- method: 'post',
50
- description: 'Create a new item',
51
- schema: {
52
- input: {
53
- body: Type.Object({
54
- name: Type.String(),
55
- }),
56
- } satisfies APIInput,
57
- returnType: Type.Object({
58
- id: Type.String(),
59
- name: Type.String(),
60
- }),
61
- },
62
- },
63
- async (ctx, { body }) => {
64
- // TODO: implement
65
- return { id: 'new-id', name: body.name }
66
- }
67
- )
68
-
69
- export const { DeleteItem } = API.Create(
70
- 'DeleteItem',
71
- {
72
- path: '/{{name}}/:id',
73
- method: 'delete',
74
- schema: {
75
- input: {
76
- pathParams: Type.Object({ id: Type.String() }),
77
- } satisfies APIInput,
78
- },
79
- },
80
- async (ctx, { pathParams }) => {
81
- // TODO: implement deletion
82
- }
83
- )
84
-
85
- // ─── Error Taxonomy ───────────────────────────────────────
86
- // Declare the error classes this service throws. Framework errors
87
- // (ProcedureValidationError → 400, ctx.error() → 500) are caught by the
88
- // default taxonomy automatically. Add your own classes here — handlers just
89
- // `throw` them and the builder serializes via this map. See
90
- // docs/http-integrations.md#error-handling for the full contract.
91
-
92
- const {{name}}Errors = defineErrorTaxonomy({
93
- // Example — replace with your app's error classes:
94
- // NotFoundError: { class: NotFoundError, statusCode: 404 },
95
- // AuthError: { class: AuthError, statusCode: 401 },
96
- })
97
-
98
- // ─── Hono App Builder ─────────────────────────────────────
99
-
100
- export const {{name}}App = new HonoAPIAppBuilder({
101
- pathPrefix: '/api',
102
- errors: {{name}}Errors,
103
- unknownError: {
104
- statusCode: 500,
105
- toResponse: () => ({ name: 'InternalServerError', message: 'Unexpected error' }),
106
- onCatch: (err, { procedure }) => console.error(`[${procedure.name}]`, err),
107
- },
108
- })
109
- .register(API, (c) => ({
110
- userId: c.req.header('x-user-id') || 'anonymous',
111
- requestId: c.req.header('x-request-id') || crypto.randomUUID(),
112
- }))
113
- .build()
114
-
115
- // Route map:
116
- // GET /api/{{name}}/:id → 200
117
- // POST /api/{{name}} → 201
118
- // DELETE /api/{{name}}/:id → 204
119
-
120
- // Documentation: builder.docs or compose with DocRegistry from 'ts-procedures/http-docs'
121
- ```
122
-
123
- ## Test — `{{Name}}.api.test.ts`
124
-
125
- ```typescript
126
- import { describe, test, expect } from 'vitest'
127
- import { {{name}}App } from './{{Name}}.api'
128
-
129
- describe('{{Name}} API', () => {
130
- test('GET item returns expected result', async () => {
131
- const app = await {{name}}App
132
- const res = await app.request('/api/{{name}}/item-1')
133
-
134
- expect(res.status).toBe(200)
135
- const body = await res.json()
136
- expect(body).toEqual({ id: 'item-1', name: 'Example' })
137
- })
138
-
139
- test('POST item returns 201', async () => {
140
- const app = await {{name}}App
141
- const res = await app.request('/api/{{name}}', {
142
- method: 'POST',
143
- headers: { 'Content-Type': 'application/json' },
144
- body: JSON.stringify({ name: 'New Item' }),
145
- })
146
-
147
- expect(res.status).toBe(201)
148
- const body = await res.json()
149
- expect(body).toHaveProperty('id')
150
- expect(body.name).toBe('New Item')
151
- })
152
-
153
- test('DELETE item returns 204', async () => {
154
- const app = await {{name}}App
155
- const res = await app.request('/api/{{name}}/item-1', {
156
- method: 'DELETE',
157
- })
158
-
159
- expect(res.status).toBe(204)
160
- })
161
-
162
- test('returns 400 for invalid POST body', async () => {
163
- const app = await {{name}}App
164
- const res = await app.request('/api/{{name}}', {
165
- method: 'POST',
166
- headers: { 'Content-Type': 'application/json' },
167
- body: JSON.stringify({}),
168
- })
169
-
170
- expect(res.status).toBe(400)
171
- })
172
- })
173
- ```