ts-procedures 7.3.0 → 8.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (332) 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 +104 -53
  5. package/agent_config/claude-code/skills/ts-procedures/api-reference.md +205 -232
  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 +34 -48
  12. package/agent_config/cursor/cursorrules +34 -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 +418 -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 +351 -55
  39. package/build/codegen/emit-scope.js.map +1 -1
  40. package/build/codegen/emit-scope.test.js +540 -110
  41. package/build/codegen/emit-scope.test.js.map +1 -1
  42. package/build/codegen/emit-types.d.ts +6 -2
  43. package/build/codegen/emit-types.js +81 -20
  44. package/build/codegen/emit-types.js.map +1 -1
  45. package/build/codegen/emit-types.test.js +70 -1
  46. package/build/codegen/emit-types.test.js.map +1 -1
  47. package/build/codegen/pipeline.test.js +7 -7
  48. package/build/codegen/pipeline.test.js.map +1 -1
  49. package/build/codegen/resolve-envelope.js +1 -1
  50. package/build/codegen/resolve-envelope.js.map +1 -1
  51. package/build/codegen/resolve-envelope.test.js +5 -5
  52. package/build/codegen/resolve-envelope.test.js.map +1 -1
  53. package/build/codegen/targets/_shared/route-slots.d.ts +8 -3
  54. package/build/codegen/targets/_shared/route-slots.js +49 -8
  55. package/build/codegen/targets/_shared/route-slots.js.map +1 -1
  56. package/build/codegen/targets/_shared/route-slots.test.js +99 -26
  57. package/build/codegen/targets/_shared/route-slots.test.js.map +1 -1
  58. package/build/codegen/targets/kotlin/emit-route-kotlin.test.js +88 -17
  59. package/build/codegen/targets/kotlin/emit-route-kotlin.test.js.map +1 -1
  60. package/build/codegen/targets/kotlin/emit-scope-kotlin.test.js +9 -6
  61. package/build/codegen/targets/kotlin/emit-scope-kotlin.test.js.map +1 -1
  62. package/build/codegen/targets/kotlin/integration.test.js +6 -0
  63. package/build/codegen/targets/kotlin/integration.test.js.map +1 -1
  64. package/build/codegen/targets/swift/access-level.test.js +8 -11
  65. package/build/codegen/targets/swift/access-level.test.js.map +1 -1
  66. package/build/codegen/targets/swift/emit-route-swift.test.js +91 -20
  67. package/build/codegen/targets/swift/emit-route-swift.test.js.map +1 -1
  68. package/build/codegen/targets/swift/emit-scope-swift.test.js +12 -9
  69. package/build/codegen/targets/swift/emit-scope-swift.test.js.map +1 -1
  70. package/build/codegen/targets/swift/integration.test.js +6 -0
  71. package/build/codegen/targets/swift/integration.test.js.map +1 -1
  72. package/build/create-http-stream.d.ts +58 -0
  73. package/build/create-http-stream.js +122 -0
  74. package/build/create-http-stream.js.map +1 -0
  75. package/build/create-http-stream.test.js +88 -0
  76. package/build/create-http-stream.test.js.map +1 -0
  77. package/build/create-http.d.ts +49 -0
  78. package/build/create-http.js +108 -0
  79. package/build/create-http.js.map +1 -0
  80. package/build/create-http.test.js +137 -0
  81. package/build/create-http.test.js.map +1 -0
  82. package/build/create-stream.d.ts +35 -0
  83. package/build/create-stream.js +123 -0
  84. package/build/create-stream.js.map +1 -0
  85. package/build/create-stream.test.js +428 -0
  86. package/build/create-stream.test.js.map +1 -0
  87. package/build/create.d.ts +28 -0
  88. package/build/create.js +82 -0
  89. package/build/create.js.map +1 -0
  90. package/build/create.test.js +483 -0
  91. package/build/create.test.js.map +1 -0
  92. package/build/exports.d.ts +2 -0
  93. package/build/implementations/http/astro/index.test.js +20 -12
  94. package/build/implementations/http/astro/index.test.js.map +1 -1
  95. package/build/implementations/http/doc-registry.js +1 -1
  96. package/build/implementations/http/doc-registry.js.map +1 -1
  97. package/build/implementations/http/doc-registry.test.js +36 -5
  98. package/build/implementations/http/doc-registry.test.js.map +1 -1
  99. package/build/implementations/http/error-dispatch.d.ts +76 -0
  100. package/build/implementations/http/error-dispatch.js +77 -0
  101. package/build/implementations/http/error-dispatch.js.map +1 -0
  102. package/build/implementations/http/error-dispatch.test.js +254 -0
  103. package/build/implementations/http/error-dispatch.test.js.map +1 -0
  104. package/build/implementations/http/error-taxonomy.d.ts +5 -5
  105. package/build/implementations/http/hono/docs/http-doc.d.ts +6 -0
  106. package/build/implementations/http/hono/docs/http-doc.js +42 -0
  107. package/build/implementations/http/hono/docs/http-doc.js.map +1 -0
  108. package/build/implementations/http/hono/docs/http-stream-doc.d.ts +6 -0
  109. package/build/implementations/http/hono/docs/http-stream-doc.js +40 -0
  110. package/build/implementations/http/hono/docs/http-stream-doc.js.map +1 -0
  111. package/build/implementations/http/hono/docs/rpc-doc.d.ts +6 -0
  112. package/build/implementations/http/hono/docs/rpc-doc.js +24 -0
  113. package/build/implementations/http/hono/docs/rpc-doc.js.map +1 -0
  114. package/build/implementations/http/hono/docs/stream-doc.d.ts +6 -0
  115. package/build/implementations/http/hono/docs/stream-doc.js +42 -0
  116. package/build/implementations/http/hono/docs/stream-doc.js.map +1 -0
  117. package/build/implementations/http/hono/handlers/http-stream.d.ts +10 -0
  118. package/build/implementations/http/hono/handlers/http-stream.js +123 -0
  119. package/build/implementations/http/hono/handlers/http-stream.js.map +1 -0
  120. package/build/implementations/http/hono/handlers/http-stream.test.js +128 -0
  121. package/build/implementations/http/hono/handlers/http-stream.test.js.map +1 -0
  122. package/build/implementations/http/hono/handlers/http.d.ts +10 -0
  123. package/build/implementations/http/hono/handlers/http.js +115 -0
  124. package/build/implementations/http/hono/handlers/http.js.map +1 -0
  125. package/build/implementations/http/hono/handlers/http.test.js +118 -0
  126. package/build/implementations/http/hono/handlers/http.test.js.map +1 -0
  127. package/build/implementations/http/hono/handlers/rpc.d.ts +11 -0
  128. package/build/implementations/http/hono/handlers/rpc.js +32 -0
  129. package/build/implementations/http/hono/handlers/rpc.js.map +1 -0
  130. package/build/implementations/http/hono/handlers/rpc.test.js +73 -0
  131. package/build/implementations/http/hono/handlers/rpc.test.js.map +1 -0
  132. package/build/implementations/http/hono/handlers/stream.d.ts +23 -0
  133. package/build/implementations/http/hono/handlers/stream.js +147 -0
  134. package/build/implementations/http/hono/handlers/stream.js.map +1 -0
  135. package/build/implementations/http/hono/handlers/stream.test.d.ts +1 -0
  136. package/build/implementations/http/hono/handlers/stream.test.js +177 -0
  137. package/build/implementations/http/hono/handlers/stream.test.js.map +1 -0
  138. package/build/implementations/http/hono/index.d.ts +57 -0
  139. package/build/implementations/http/hono/index.js +149 -0
  140. package/build/implementations/http/hono/index.js.map +1 -0
  141. package/build/implementations/http/hono/index.test.d.ts +1 -0
  142. package/build/implementations/http/hono/index.test.js +274 -0
  143. package/build/implementations/http/hono/index.test.js.map +1 -0
  144. package/build/implementations/http/hono/path.d.ts +17 -0
  145. package/build/implementations/http/hono/path.js +39 -0
  146. package/build/implementations/http/hono/path.js.map +1 -0
  147. package/build/implementations/http/hono/path.test.d.ts +1 -0
  148. package/build/implementations/http/hono/path.test.js +83 -0
  149. package/build/implementations/http/hono/path.test.js.map +1 -0
  150. package/build/implementations/http/hono/types.d.ts +51 -0
  151. package/build/implementations/http/hono/types.js.map +1 -0
  152. package/build/implementations/http/on-request-error.test.js +6 -96
  153. package/build/implementations/http/on-request-error.test.js.map +1 -1
  154. package/build/implementations/http/route-errors.test.js +11 -59
  155. package/build/implementations/http/route-errors.test.js.map +1 -1
  156. package/build/implementations/types.d.ts +43 -9
  157. package/build/index.d.ts +124 -124
  158. package/build/index.js +10 -221
  159. package/build/index.js.map +1 -1
  160. package/build/index.test.js +20 -919
  161. package/build/index.test.js.map +1 -1
  162. package/build/migration.test.d.ts +1 -0
  163. package/build/migration.test.js +34 -0
  164. package/build/migration.test.js.map +1 -0
  165. package/build/schema/compute-schema.d.ts +11 -3
  166. package/build/schema/compute-schema.js +13 -7
  167. package/build/schema/compute-schema.js.map +1 -1
  168. package/build/schema/parser.d.ts +11 -3
  169. package/build/schema/parser.js +49 -9
  170. package/build/schema/parser.js.map +1 -1
  171. package/build/stack-utils.js +8 -0
  172. package/build/stack-utils.js.map +1 -1
  173. package/build/types.d.ts +142 -0
  174. package/build/types.js.map +1 -0
  175. package/docs/astro-adapter.md +5 -5
  176. package/docs/core.md +15 -17
  177. package/docs/http-integrations.md +83 -170
  178. package/docs/streaming.md +3 -60
  179. package/docs/superpowers/plans/2026-05-07-astro-adapter.md +2 -7
  180. package/docs/superpowers/plans/2026-05-08-create-http.md +3355 -0
  181. package/docs/superpowers/plans/2026-05-08-hono-app-builder-convergence.md +3365 -0
  182. package/docs/superpowers/specs/2026-05-07-astro-adapter-design.md +1 -3
  183. package/docs/superpowers/specs/2026-05-08-create-http-design.md +409 -0
  184. package/docs/superpowers/specs/2026-05-08-hono-app-builder-convergence-design.md +411 -0
  185. package/package.json +4 -22
  186. package/src/client/call.test.ts +26 -0
  187. package/src/client/call.ts +4 -1
  188. package/src/client/fetch-adapter.test.ts +14 -1
  189. package/src/client/fetch-adapter.ts +3 -1
  190. package/src/client/index.test.ts +7 -7
  191. package/src/client/request-builder.ts +2 -2
  192. package/src/client/stream.test.ts +39 -7
  193. package/src/client/stream.ts +16 -2
  194. package/src/client/typed-error-dispatch.test.ts +7 -97
  195. package/src/client/types.ts +21 -3
  196. package/src/codegen/__fixtures__/users-envelope.json +119 -38
  197. package/src/codegen/e2e.test.ts +452 -24
  198. package/src/codegen/emit-errors.integration.test.ts +1 -1
  199. package/src/codegen/emit-scope.test.ts +581 -110
  200. package/src/codegen/emit-scope.ts +390 -61
  201. package/src/codegen/emit-types.test.ts +73 -1
  202. package/src/codegen/emit-types.ts +82 -21
  203. package/src/codegen/pipeline.test.ts +7 -7
  204. package/src/codegen/resolve-envelope.test.ts +5 -5
  205. package/src/codegen/resolve-envelope.ts +1 -1
  206. package/src/codegen/targets/_shared/route-slots.test.ts +109 -26
  207. package/src/codegen/targets/_shared/route-slots.ts +48 -11
  208. package/src/codegen/targets/kotlin/__fixtures__/users-golden.kt +73 -0
  209. package/src/codegen/targets/kotlin/emit-route-kotlin.test.ts +100 -17
  210. package/src/codegen/targets/kotlin/emit-scope-kotlin.test.ts +9 -6
  211. package/src/codegen/targets/kotlin/integration.test.ts +19 -0
  212. package/src/codegen/targets/swift/__fixtures__/users-golden.swift +79 -0
  213. package/src/codegen/targets/swift/access-level.test.ts +8 -11
  214. package/src/codegen/targets/swift/emit-route-swift.test.ts +103 -20
  215. package/src/codegen/targets/swift/emit-scope-swift.test.ts +12 -9
  216. package/src/codegen/targets/swift/integration.test.ts +17 -0
  217. package/src/create-http-stream.test.ts +97 -0
  218. package/src/create-http-stream.ts +191 -0
  219. package/src/create-http.test.ts +163 -0
  220. package/src/create-http.ts +211 -0
  221. package/src/create-stream.test.ts +565 -0
  222. package/src/create-stream.ts +228 -0
  223. package/src/create.test.ts +658 -0
  224. package/src/create.ts +172 -0
  225. package/src/exports.ts +2 -0
  226. package/src/implementations/http/README.md +135 -95
  227. package/src/implementations/http/astro/README.md +4 -5
  228. package/src/implementations/http/astro/index.test.ts +25 -18
  229. package/src/implementations/http/doc-registry.test.ts +42 -5
  230. package/src/implementations/http/doc-registry.ts +1 -1
  231. package/src/implementations/http/error-dispatch.test.ts +283 -0
  232. package/src/implementations/http/error-dispatch.ts +176 -0
  233. package/src/implementations/http/error-taxonomy.ts +5 -5
  234. package/src/implementations/http/hono/docs/http-doc.ts +43 -0
  235. package/src/implementations/http/hono/docs/http-stream-doc.ts +44 -0
  236. package/src/implementations/http/hono/docs/rpc-doc.ts +34 -0
  237. package/src/implementations/http/hono/docs/stream-doc.ts +53 -0
  238. package/src/implementations/http/hono/handlers/http-stream.test.ts +150 -0
  239. package/src/implementations/http/hono/handlers/http-stream.ts +152 -0
  240. package/src/implementations/http/hono/handlers/http.test.ts +130 -0
  241. package/src/implementations/http/hono/handlers/http.ts +147 -0
  242. package/src/implementations/http/hono/handlers/rpc.test.ts +81 -0
  243. package/src/implementations/http/hono/handlers/rpc.ts +54 -0
  244. package/src/implementations/http/hono/handlers/stream.test.ts +198 -0
  245. package/src/implementations/http/hono/handlers/stream.ts +208 -0
  246. package/src/implementations/http/hono/index.test.ts +329 -0
  247. package/src/implementations/http/hono/index.ts +204 -0
  248. package/src/implementations/http/hono/path.test.ts +96 -0
  249. package/src/implementations/http/hono/path.ts +59 -0
  250. package/src/implementations/http/hono/types.ts +93 -0
  251. package/src/implementations/http/on-request-error.test.ts +10 -116
  252. package/src/implementations/http/route-errors.test.ts +11 -77
  253. package/src/implementations/types.ts +44 -9
  254. package/src/index.test.ts +22 -1249
  255. package/src/index.ts +49 -485
  256. package/src/migration.test.ts +48 -0
  257. package/src/schema/compute-schema.ts +26 -12
  258. package/src/schema/parser.ts +62 -12
  259. package/src/stack-utils.ts +8 -0
  260. package/src/types.ts +133 -0
  261. package/agent_config/claude-code/skills/ts-procedures-scaffold/templates/express-rpc.md +0 -137
  262. package/agent_config/claude-code/skills/ts-procedures-scaffold/templates/hono-api.md +0 -173
  263. package/agent_config/claude-code/skills/ts-procedures-scaffold/templates/hono-rpc.md +0 -142
  264. package/agent_config/claude-code/skills/ts-procedures-scaffold/templates/hono-stream.md +0 -147
  265. package/build/implementations/http/express-rpc/error-taxonomy.test.js +0 -83
  266. package/build/implementations/http/express-rpc/error-taxonomy.test.js.map +0 -1
  267. package/build/implementations/http/express-rpc/index.d.ts +0 -125
  268. package/build/implementations/http/express-rpc/index.js +0 -216
  269. package/build/implementations/http/express-rpc/index.js.map +0 -1
  270. package/build/implementations/http/express-rpc/index.test.js +0 -684
  271. package/build/implementations/http/express-rpc/index.test.js.map +0 -1
  272. package/build/implementations/http/express-rpc/types.d.ts +0 -11
  273. package/build/implementations/http/express-rpc/types.js.map +0 -1
  274. package/build/implementations/http/hono-api/error-taxonomy.test.js +0 -137
  275. package/build/implementations/http/hono-api/error-taxonomy.test.js.map +0 -1
  276. package/build/implementations/http/hono-api/index.d.ts +0 -151
  277. package/build/implementations/http/hono-api/index.js +0 -344
  278. package/build/implementations/http/hono-api/index.js.map +0 -1
  279. package/build/implementations/http/hono-api/index.test.js +0 -992
  280. package/build/implementations/http/hono-api/index.test.js.map +0 -1
  281. package/build/implementations/http/hono-api/types.d.ts +0 -13
  282. package/build/implementations/http/hono-api/types.js.map +0 -1
  283. package/build/implementations/http/hono-rpc/error-taxonomy.test.js +0 -64
  284. package/build/implementations/http/hono-rpc/error-taxonomy.test.js.map +0 -1
  285. package/build/implementations/http/hono-rpc/index.d.ts +0 -130
  286. package/build/implementations/http/hono-rpc/index.js +0 -209
  287. package/build/implementations/http/hono-rpc/index.js.map +0 -1
  288. package/build/implementations/http/hono-rpc/index.test.js +0 -828
  289. package/build/implementations/http/hono-rpc/index.test.js.map +0 -1
  290. package/build/implementations/http/hono-rpc/types.d.ts +0 -11
  291. package/build/implementations/http/hono-rpc/types.js +0 -2
  292. package/build/implementations/http/hono-rpc/types.js.map +0 -1
  293. package/build/implementations/http/hono-stream/error-taxonomy.test.js +0 -159
  294. package/build/implementations/http/hono-stream/error-taxonomy.test.js.map +0 -1
  295. package/build/implementations/http/hono-stream/index.d.ts +0 -171
  296. package/build/implementations/http/hono-stream/index.js +0 -415
  297. package/build/implementations/http/hono-stream/index.js.map +0 -1
  298. package/build/implementations/http/hono-stream/index.test.js +0 -1383
  299. package/build/implementations/http/hono-stream/index.test.js.map +0 -1
  300. package/build/implementations/http/hono-stream/types.d.ts +0 -15
  301. package/build/implementations/http/hono-stream/types.js +0 -2
  302. package/build/implementations/http/hono-stream/types.js.map +0 -1
  303. package/src/implementations/http/express-rpc/README.md +0 -280
  304. package/src/implementations/http/express-rpc/error-taxonomy.test.ts +0 -103
  305. package/src/implementations/http/express-rpc/index.test.ts +0 -957
  306. package/src/implementations/http/express-rpc/index.ts +0 -327
  307. package/src/implementations/http/express-rpc/types.ts +0 -16
  308. package/src/implementations/http/hono-api/README.md +0 -284
  309. package/src/implementations/http/hono-api/error-taxonomy.test.ts +0 -179
  310. package/src/implementations/http/hono-api/index.test.ts +0 -1341
  311. package/src/implementations/http/hono-api/index.ts +0 -519
  312. package/src/implementations/http/hono-api/types.ts +0 -16
  313. package/src/implementations/http/hono-rpc/README.md +0 -357
  314. package/src/implementations/http/hono-rpc/error-taxonomy.test.ts +0 -82
  315. package/src/implementations/http/hono-rpc/index.test.ts +0 -1107
  316. package/src/implementations/http/hono-rpc/index.ts +0 -320
  317. package/src/implementations/http/hono-rpc/types.ts +0 -16
  318. package/src/implementations/http/hono-stream/README.md +0 -559
  319. package/src/implementations/http/hono-stream/error-taxonomy.test.ts +0 -178
  320. package/src/implementations/http/hono-stream/index.test.ts +0 -1804
  321. package/src/implementations/http/hono-stream/index.ts +0 -622
  322. package/src/implementations/http/hono-stream/types.ts +0 -20
  323. /package/build/{implementations/http/express-rpc/error-taxonomy.test.d.ts → create-http-stream.test.d.ts} +0 -0
  324. /package/build/{implementations/http/express-rpc/index.test.d.ts → create-http.test.d.ts} +0 -0
  325. /package/build/{implementations/http/hono-api/error-taxonomy.test.d.ts → create-stream.test.d.ts} +0 -0
  326. /package/build/{implementations/http/hono-api/index.test.d.ts → create.test.d.ts} +0 -0
  327. /package/build/implementations/http/{hono-rpc/error-taxonomy.test.d.ts → error-dispatch.test.d.ts} +0 -0
  328. /package/build/implementations/http/{hono-rpc/index.test.d.ts → hono/handlers/http-stream.test.d.ts} +0 -0
  329. /package/build/implementations/http/{hono-stream/error-taxonomy.test.d.ts → hono/handlers/http.test.d.ts} +0 -0
  330. /package/build/implementations/http/{hono-stream/index.test.d.ts → hono/handlers/rpc.test.d.ts} +0 -0
  331. /package/build/implementations/http/{express-rpc → hono}/types.js +0 -0
  332. /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
- })