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
@@ -1,357 +0,0 @@
1
- # HonoRPCAppBuilder
2
-
3
- Hono integration for `ts-procedures` that creates type-safe RPC endpoints as POST routes. Works with Bun, Deno, Cloudflare Workers, and Node.js.
4
-
5
- ## Installation
6
-
7
- ```bash
8
- npm install ts-procedures hono
9
- ```
10
-
11
- ## Quick Start
12
-
13
- ```typescript
14
- import { Hono } from 'hono'
15
- import { Procedures } from 'ts-procedures'
16
- import { HonoRPCAppBuilder, RPCConfig } from 'ts-procedures/hono-rpc'
17
- import { Type } from 'typebox'
18
-
19
- // Define your context type
20
- type AppContext = { userId: string }
21
-
22
- // Create a procedure factory
23
- const RPC = Procedures<AppContext, RPCConfig>()
24
-
25
- // Define procedures
26
- RPC.Create(
27
- 'GetUser',
28
- {
29
- scope: ['users', 'profile'],
30
- version: 1,
31
- schema: {
32
- params: Type.Object({ id: Type.String() }),
33
- returnType: Type.Object({ id: Type.String(), name: Type.String() }),
34
- },
35
- },
36
- async (ctx, params) => {
37
- return { id: params.id, name: 'John Doe' }
38
- }
39
- )
40
-
41
- // Build the Hono app
42
- const builder = new HonoRPCAppBuilder({ pathPrefix: '/rpc' }).register(RPC, (c) => ({
43
- userId: c.req.header('x-user-id') || 'anonymous',
44
- }))
45
-
46
- const app = builder.build()
47
-
48
- // Bun
49
- export default app
50
-
51
- // Node.js
52
- // import { serve } from '@hono/node-server'
53
- // serve(app)
54
-
55
- // POST /rpc/users/profile/get-user/1 → { id: "123", name: "John Doe" }
56
- ```
57
-
58
- ## Configuration
59
-
60
- ```typescript
61
- type HonoRPCAppBuilderConfig = {
62
- app?: Hono // Existing Hono app (optional)
63
- pathPrefix?: string // Prefix for all routes (e.g., '/rpc/v1')
64
- onRequestStart?: (c: Context) => void
65
- onRequestEnd?: (c: Context) => void
66
- onSuccess?: (procedure: TProcedureRegistration, c: Context) => void
67
- onError?: (
68
- procedure: TProcedureRegistration,
69
- c: Context,
70
- error: Error
71
- ) => Response | Promise<Response>
72
- }
73
- ```
74
-
75
- | Option | Type | Description |
76
- | ---------------- | ---------------------------- | ------------------------------------------------- |
77
- | `app` | `Hono` | Use existing Hono app instead of creating new one |
78
- | `pathPrefix` | `string` | Prefix all routes (e.g., `/rpc/v1`) |
79
- | `onRequestStart` | `(c) => void` | Called at start of each request |
80
- | `onRequestEnd` | `(c) => void` | Called after handler completes |
81
- | `onSuccess` | `(proc, c) => void` | Called on successful handler execution |
82
- | `onError` | `(proc, c, err) => Response` | Imperative error handler (peer of `errors` taxonomy — see Error Handling) |
83
-
84
- ## Context Resolution
85
-
86
- The context resolver receives the Hono `Context` object:
87
-
88
- ```typescript
89
- builder.register(RPC, (c: Context) => ({
90
- userId: c.req.header('x-user-id') || 'anonymous',
91
- userAgent: c.req.header('user-agent'),
92
- ip: c.req.raw.headers.get('cf-connecting-ip'), // Cloudflare
93
- }))
94
-
95
- // Async context resolution
96
- builder.register(RPC, async (c) => {
97
- const token = c.req.header('authorization')?.replace('Bearer ', '')
98
- const user = await verifyToken(token)
99
- return { userId: user.id, roles: user.roles }
100
- })
101
- ```
102
-
103
- ## Extending Procedure Documentation
104
-
105
- The `register` method accepts an optional third parameter `extendProcedureDoc` that allows you to add custom fields to each procedure's documentation. This is useful for adding metadata like descriptions, tags, or custom fields for API documentation generators.
106
-
107
- ```typescript
108
- // Example of a factory extending the procedure config:
109
- type ExtendedRPCConfig = {
110
- description: string
111
- tags: string[]
112
- }
113
-
114
- builder.register(RPC, (c) => ({ userId: c.req.header('x-user-id') || 'anonymous' }), {
115
- extendProcedureDoc: ({ base, procedure }, { base: RPCHttpRouteDoc, procedure }) =>
116
- ({
117
- description: `Procedure: ${procedure.name}`,
118
- tags: Array.isArray(procedure.config.scope)
119
- ? procedure.config.scope
120
- : [procedure.config.scope],
121
- }),
122
- })
123
-
124
- // Access extended docs after build()
125
- const app = builder.build()
126
- console.log(builder.docs) // Each doc now includes description and tags
127
- ```
128
-
129
- The `extendProcedureDoc` callback receives:
130
-
131
- | Parameter | Type | Description |
132
- | ----------- | ------------------------ | ------------------------------------------------------------------------------------------ |
133
- | `base` | `RPCHttpRouteDoc` | The base documentation with `name`, `path`, `method`, `scope`, `version`, and `jsonSchema` |
134
- | `procedure` | `TProcedureRegistration` | The full procedure registration including `name`, `config`, and `handler` |
135
-
136
- This allows you to derive documentation fields from procedure config or add static metadata per factory registration.
137
-
138
- ## Abort Signal
139
-
140
- `HonoRPCAppBuilder` automatically injects the HTTP request's `AbortSignal` (`c.req.raw.signal`) into the handler context. When a client disconnects mid-request, `ctx.signal` aborts, cancelling any signal-aware operations:
141
-
142
- ```typescript
143
- RPC.Create(
144
- 'SlowQuery',
145
- { scope: 'data', version: 1 },
146
- async (ctx, params) => {
147
- // Automatically cancelled if client disconnects
148
- const response = await fetch('https://slow-api.example.com/data', {
149
- signal: ctx.signal,
150
- })
151
- return response.json()
152
- }
153
- )
154
- ```
155
-
156
- To use `ctx.signal` with type safety, include `signal: AbortSignal` in your context type:
157
-
158
- ```typescript
159
- type AppContext = { userId: string; signal: AbortSignal }
160
- const RPC = Procedures<AppContext, RPCConfig>()
161
- ```
162
-
163
- ## Error Handling
164
-
165
- Declare error classes via `defineErrorTaxonomy` and pass them to the builder's `errors` option. Handlers `throw` their classes; the builder auto-serializes to the configured status + body.
166
-
167
- ```typescript
168
- import { defineErrorTaxonomy } from 'ts-procedures/hono-rpc'
169
-
170
- const appErrors = defineErrorTaxonomy({
171
- AuthError: { class: AuthError, statusCode: 401 },
172
- })
173
-
174
- new HonoRPCAppBuilder({
175
- errors: appErrors,
176
- unknownError: { toResponse: () => ({ error: 'Internal server error' }) },
177
- })
178
- ```
179
-
180
- Full contract (both peer error modes, `onRequestError` observer, per-route narrowing via `RPCConfig<keyof typeof appErrors & string>`): see **[docs/http-integrations.md § Error Handling](../../../../docs/http-integrations.md#error-handling)** — the canonical explanation shared across all four HTTP builders.
181
-
182
- ## Using Existing Hono App
183
-
184
- You can add RPC routes to an existing Hono application:
185
-
186
- ```typescript
187
- const app = new Hono()
188
-
189
- // Add custom middleware and routes
190
- app.use('*', cors())
191
- app.get('/custom', (c) => c.json({ custom: true }))
192
-
193
- const builder = new HonoRPCAppBuilder({ app }).register(RPC, contextResolver)
194
-
195
- builder.build() // Adds RPC routes to existing app
196
- ```
197
-
198
- ## Runtime Compatibility
199
-
200
- HonoRPCAppBuilder works across all Hono-supported runtimes:
201
-
202
- ### Bun
203
-
204
- ```typescript
205
- const app = builder.build()
206
- export default app
207
- ```
208
-
209
- ### Node.js
210
-
211
- ```typescript
212
- import { serve } from '@hono/node-server'
213
-
214
- const app = builder.build()
215
- serve(app)
216
- ```
217
-
218
- ### Deno
219
-
220
- ```typescript
221
- import { serve } from 'https://deno.land/std/http/server.ts'
222
-
223
- const app = builder.build()
224
- serve(app.fetch)
225
- ```
226
-
227
- ### Cloudflare Workers
228
-
229
- ```typescript
230
- const app = builder.build()
231
- export default app
232
- ```
233
-
234
- ## API Reference
235
-
236
- ### Constructor
237
-
238
- ```typescript
239
- new HonoRPCAppBuilder(config?: HonoRPCAppBuilderConfig)
240
- ```
241
-
242
- ### Methods
243
-
244
- | Method | Signature | Description |
245
- | ---------------------- | ------------------------------------------------- | ------------------------------------------------------------------ |
246
- | `register` | `register<T>(factory, context, options?): this` | Register procedure factory with context and optional doc extension |
247
- | `build` | `build(): Hono` | Build routes and return app |
248
- | `makeRPCHttpRoutePath` | `makeRPCHttpRoutePath(config: RPCConfig): string` | Generate route path |
249
-
250
- ### Static Methods
251
-
252
- | Method | Signature | Description |
253
- | ---------------------- | --------------------------------------------------------- | -------------------------------------- |
254
- | `makeRPCHttpRoutePath` | `static makeRPCHttpRoutePath({ config, prefix }): string` | Generate route path with custom prefix |
255
-
256
- ### Properties
257
-
258
- | Property | Type | Description |
259
- | -------- | ------------------------- | ------------------------------------- |
260
- | `app` | `Hono` | The Hono application instance |
261
- | `docs` | `RPCHttpRouteDoc[]` | Route documentation (after `build()`) |
262
- | `config` | `HonoRPCAppBuilderConfig` | The configuration object |
263
-
264
- ## TypeScript Types
265
-
266
- ```typescript
267
- import { HonoRPCAppBuilder, defineErrorTaxonomy } from 'ts-procedures/hono-rpc'
268
- import type {
269
- HonoRPCAppBuilderConfig,
270
- RPCConfig,
271
- RPCHttpRouteDoc,
272
- ErrorTaxonomy,
273
- ErrorTaxonomyEntry,
274
- UnknownErrorConfig,
275
- OnRequestErrorContext,
276
- } from 'ts-procedures/hono-rpc'
277
- ```
278
-
279
- ## Full Example
280
-
281
- ```typescript
282
- import { Hono, Context } from 'hono'
283
- import { Procedures } from 'ts-procedures'
284
- import { HonoRPCAppBuilder, RPCConfig } from 'ts-procedures/hono-rpc'
285
- import { Type } from 'typebox'
286
-
287
- // Context types
288
- type PublicContext = { source: 'public' }
289
- type AuthContext = { source: 'auth'; userId: string }
290
-
291
- // Create factories
292
- const PublicRPC = Procedures<PublicContext, RPCConfig>()
293
- const AuthRPC = Procedures<AuthContext, RPCConfig>()
294
-
295
- // Public procedures
296
- PublicRPC.Create('HealthCheck', { scope: 'health', version: 1 }, async () => ({
297
- status: 'ok',
298
- }))
299
-
300
- PublicRPC.Create('GetVersion', { scope: ['system', 'version'], version: 1 }, async () => ({
301
- version: '1.0.0',
302
- }))
303
-
304
- // Authenticated procedures
305
- AuthRPC.Create(
306
- 'GetProfile',
307
- {
308
- scope: ['users', 'profile'],
309
- version: 1,
310
- schema: { returnType: Type.Object({ userId: Type.String() }) },
311
- },
312
- async (ctx) => ({ userId: ctx.userId })
313
- )
314
-
315
- AuthRPC.Create(
316
- 'UpdateProfile',
317
- {
318
- scope: ['users', 'profile'],
319
- version: 2,
320
- schema: { params: Type.Object({ name: Type.String() }) },
321
- },
322
- async (ctx, params) => ({ userId: ctx.userId, name: params.name })
323
- )
324
-
325
- // Build app
326
- const builder = new HonoRPCAppBuilder({
327
- pathPrefix: '/rpc',
328
- onRequestStart: (c) => console.log(`→ ${c.req.method} ${c.req.path}`),
329
- onRequestEnd: (c) => console.log(`← completed`),
330
- onSuccess: (proc) => console.log(`✓ ${proc.name}`),
331
- onError: (proc, c, err) => {
332
- console.error(`✗ ${proc.name}:`, err.message)
333
- return c.json({ error: err.message }, 500)
334
- },
335
- })
336
-
337
- builder
338
- .register(PublicRPC, () => ({ source: 'public' as const }))
339
- .register(AuthRPC, (c) => ({
340
- source: 'auth' as const,
341
- userId: c.req.header('x-user-id') || 'anonymous',
342
- }))
343
-
344
- const app = builder.build()
345
-
346
- // Generated routes:
347
- // POST /rpc/health/health-check/1
348
- // POST /rpc/system/version/get-version/1
349
- // POST /rpc/users/profile/get-profile/1
350
- // POST /rpc/users/profile/update-profile/2
351
-
352
- console.log(
353
- 'Routes:',
354
- builder.docs.map((d) => d.path)
355
- )
356
- export default app
357
- ```
@@ -1,82 +0,0 @@
1
- import { describe, expect, test, vi } from 'vitest'
2
- import { Type } from 'typebox'
3
- import { Procedures } from '../../../index.js'
4
- import { RPCConfig } from '../../types.js'
5
- import { HonoRPCAppBuilder, defineErrorTaxonomy } from './index.js'
6
-
7
- class UseCaseError extends Error {
8
- constructor(
9
- readonly externalMsg: string,
10
- readonly internalMsg: string
11
- ) {
12
- super(externalMsg)
13
- this.name = 'UseCaseError'
14
- Object.setPrototypeOf(this, UseCaseError.prototype)
15
- }
16
- }
17
-
18
- describe('HonoRPCAppBuilder — error taxonomy', () => {
19
- test('taxonomy catches user error thrown from RPC handler', async () => {
20
- const errors = defineErrorTaxonomy({
21
- UseCaseError: {
22
- class: UseCaseError,
23
- statusCode: 422,
24
- toResponse: (err) => ({ name: 'UseCaseError', message: err.externalMsg }),
25
- },
26
- })
27
-
28
- const RPC = Procedures<{}, RPCConfig>()
29
- RPC.Create(
30
- 'Boom',
31
- { scope: 'test', version: 1, schema: { params: Type.Object({}) } },
32
- async () => {
33
- throw new UseCaseError('ext', 'int')
34
- }
35
- )
36
-
37
- const app = new HonoRPCAppBuilder({ errors }).register(RPC, () => ({})).build()
38
- const res = await app.request('/test/boom/1', {
39
- method: 'POST',
40
- headers: { 'Content-Type': 'application/json' },
41
- body: '{}',
42
- })
43
- expect(res.status).toBe(422)
44
- expect(await res.json()).toEqual({ name: 'UseCaseError', message: 'ext' })
45
- })
46
-
47
- test('unknownError catches unmapped errors', async () => {
48
- const RPC = Procedures<{}, RPCConfig>()
49
- RPC.Create(
50
- 'Boom',
51
- { scope: 'test', version: 1, schema: { params: Type.Object({}) } },
52
- async () => {
53
- throw new TypeError('db down')
54
- }
55
- )
56
- const app = new HonoRPCAppBuilder({
57
- unknownError: { statusCode: 503, toResponse: () => ({ name: 'ServiceUnavailable' }) },
58
- })
59
- .register(RPC, () => ({}))
60
- .build()
61
-
62
- const res = await app.request('/test/boom/1', { method: 'POST', body: '{}' })
63
- expect(res.status).toBe(503)
64
- expect(await res.json()).toEqual({ name: 'ServiceUnavailable' })
65
- })
66
-
67
- test('onError callback runs when taxonomy does not match', async () => {
68
- const onError = vi.fn(async (_p: any, c: any) => c.json({ legacy: true }, 418))
69
- const RPC = Procedures<{}, RPCConfig>()
70
- RPC.Create(
71
- 'Boom',
72
- { scope: 'test', version: 1, schema: { params: Type.Object({}) } },
73
- async () => {
74
- throw new TypeError('legacy')
75
- }
76
- )
77
- const app = new HonoRPCAppBuilder({ onError }).register(RPC, () => ({})).build()
78
- const res = await app.request('/test/boom/1', { method: 'POST', body: '{}' })
79
- expect(res.status).toBe(418)
80
- expect(onError).toHaveBeenCalledOnce()
81
- })
82
- })