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
@@ -3,6 +3,7 @@ import type {
3
3
  RPCHttpRouteDoc,
4
4
  APIHttpRouteDoc,
5
5
  StreamHttpRouteDoc,
6
+ HttpStreamRouteDoc,
6
7
  } from '../implementations/types.js'
7
8
  import {
8
9
  jsonSchemaToTypeString,
@@ -58,7 +59,8 @@ interface EmitRouteContext {
58
59
  * This provides backward compatibility with servers running older ts-procedures
59
60
  * versions that don't set `kind` on route docs.
60
61
  */
61
- function inferRouteKind(route: Record<string, unknown>): 'rpc' | 'api' | 'stream' {
62
+ function inferRouteKind(route: Record<string, unknown>): 'rpc' | 'api' | 'stream' | 'http-stream' {
63
+ if ('streamMode' in route && 'fullPath' in route) return 'http-stream'
62
64
  if ('streamMode' in route) return 'stream'
63
65
  if ('fullPath' in route) return 'api'
64
66
  return 'rpc'
@@ -291,92 +293,383 @@ async function emitRpcRoute(route: RPCHttpRouteDoc, ctx: EmitRouteContext): Prom
291
293
  return { typeDeclarations: declarations, callable, hasStream: false, hasErrors: hasErrorsInjected }
292
294
  }
293
295
 
296
+ /**
297
+ * Formats a group of named types into a nested sub-namespace block (for namespace mode).
298
+ * Returns an array of lines to be inserted into the parent namespace, and a map of
299
+ * shortName → qualified type reference for use in callables.
300
+ *
301
+ * In flat mode, returns declarations like `export type ${prefix}${shortName} = ...`
302
+ * and refs like `${prefix}${shortName}`.
303
+ */
304
+ async function formatSubNamespace(
305
+ routePascal: string,
306
+ nsName: string, // e.g. 'Req' or 'Response'
307
+ types: NamedType[],
308
+ ctx: EmitRouteContext,
309
+ taken: Set<string>,
310
+ ): Promise<{ nsBlock: string | null; refs: Record<string, string> }> {
311
+ const refs: Record<string, string> = {}
312
+ const nsLines: string[] = []
313
+ const seenDeclarations = new Set<string>()
314
+
315
+ // Pre-reserve short names to prevent sub-type extraction collision
316
+ for (const t of types) {
317
+ if (t.schema != null) taken.add(t.shortName)
318
+ }
319
+
320
+ for (const { shortName, schema } of types) {
321
+ if (schema == null) continue
322
+
323
+ if (ctx.namespaceTypes) {
324
+ const rawResult = await jsonSchemaToExtractedTypes(schema, ctx.ajsc)
325
+ if (rawResult == null) continue
326
+
327
+ const result = renameExtractedTypes(rawResult, taken)
328
+
329
+ for (const decl of result.declarations) {
330
+ if (!seenDeclarations.has(decl)) {
331
+ seenDeclarations.add(decl)
332
+ nsLines.push(indent(decl, ' '))
333
+ }
334
+ }
335
+
336
+ nsLines.push(` export type ${shortName} = ${result.body}`)
337
+ refs[shortName] = `${ctx.scopePascal}.${routePascal}.${nsName}.${shortName}`
338
+ } else {
339
+ const flatName = `${routePascal}${nsName}${shortName}`
340
+ const body = await jsonSchemaToTypeBody(schema, ctx.ajsc)
341
+ if (body == null) continue
342
+ refs[shortName] = flatName
343
+ }
344
+ }
345
+
346
+ if (ctx.namespaceTypes) {
347
+ if (nsLines.length === 0) return { nsBlock: null, refs }
348
+ const nsBlock = ` export namespace ${nsName} {\n${nsLines.join('\n')}\n }`
349
+ return { nsBlock, refs }
350
+ }
351
+
352
+ return { nsBlock: null, refs }
353
+ }
354
+
355
+ /**
356
+ * Builds the conditional return type string for an API or http-stream callable.
357
+ *
358
+ * - Both body + headers → `{ body: <Body>; headers: <Headers> }`
359
+ * - Only body → `<Body>`
360
+ * - Only headers → `{ headers: <Headers> }`
361
+ * - Neither → `void`
362
+ */
363
+ function buildApiReturnType(
364
+ bodyRef: string | undefined,
365
+ headersRef: string | undefined,
366
+ ): string {
367
+ if (bodyRef && headersRef) {
368
+ return `{ body: ${bodyRef}; headers: ${headersRef} }`
369
+ }
370
+ if (bodyRef) return bodyRef
371
+ if (headersRef) return `{ headers: ${headersRef} }`
372
+ return 'void'
373
+ }
374
+
294
375
  async function emitApiRoute(route: APIHttpRouteDoc, ctx: EmitRouteContext): Promise<RouteChunks> {
295
376
  const pascal = toPascalCase(route.name)
296
- const channelKeys = ['pathParams', 'query', 'body', 'headers'] as const
377
+ const req = route.jsonSchema.req ?? {}
378
+ const res = route.jsonSchema.res ?? {}
297
379
 
298
- // Build channel types + structured Params type
299
- const channelTypes: NamedType[] = []
380
+ // Request channels
381
+ const reqChannelKeys = ['pathParams', 'query', 'body', 'headers'] as const
382
+ const reqTypes: NamedType[] = []
300
383
  const presentChannels: string[] = []
301
384
 
302
- for (const channel of channelKeys) {
303
- const channelSchema = route.jsonSchema[channel]
304
- if (channelSchema != null) {
305
- channelTypes.push({ shortName: toPascalCase(channel), schema: channelSchema })
385
+ for (const channel of reqChannelKeys) {
386
+ const schema = req[channel]
387
+ if (schema != null) {
388
+ reqTypes.push({ shortName: toPascalCase(channel), schema })
306
389
  presentChannels.push(channel)
307
390
  }
308
391
  }
309
392
 
310
- // Add response
311
- channelTypes.push({ shortName: 'Response', schema: route.jsonSchema.response })
393
+ // Response slots
394
+ const resTypes: NamedType[] = [
395
+ { shortName: 'Body', schema: res.body },
396
+ { shortName: 'Headers', schema: res.headers },
397
+ ]
312
398
 
313
- // `Params` is injected into the namespace below (after formatTypes), so
314
- // reserve it up-front to keep ajsc-extracted sub-types from shadowing it.
315
- const { declarations, refs } = await formatTypes(
316
- pascal,
317
- channelTypes,
318
- ctx,
319
- new Set(['Params']),
320
- )
399
+ const scopeStr = route.scope ?? 'default'
400
+ const errorUnion = buildErrorUnion(route.errors, ctx)
401
+ const hasErrors = errorUnion !== null
402
+ const errorsRef = ctx.namespaceTypes
403
+ ? `${ctx.scopePascal}.${pascal}.Errors`
404
+ : `${pascal}Errors`
321
405
 
322
- // Compose structured Params type from channels
406
+ const declarations: string[] = []
323
407
  let paramsTypeName = 'unknown'
324
- if (presentChannels.length > 0) {
325
- if (ctx.namespaceTypes) {
326
- const structureFields = presentChannels
327
- .map((ch) => `${ch}: ${toPascalCase(ch)}`)
408
+ let returnTypeName = 'void'
409
+
410
+ // Track reserved names across all sub-namespaces
411
+ const taken = new Set<string>(['Req', 'Response'])
412
+
413
+ if (ctx.namespaceTypes) {
414
+ // Namespace mode: emit nested Req {} and Response {} namespaces inside route namespace.
415
+ // Also emit merged type aliases `export type Req = { ... }` and `export type Response = ...`
416
+ // so they can be used as type arguments to bindCallable (TS requires a TYPE, not a namespace).
417
+ const nsLines: string[] = []
418
+
419
+ const { nsBlock: reqBlock, refs: reqRefs } = await formatSubNamespace(
420
+ pascal, 'Req', reqTypes, ctx, taken
421
+ )
422
+ if (reqBlock) {
423
+ nsLines.push(reqBlock)
424
+ // Merged type alias for Req so it can be used as a generic type arg
425
+ const reqFields = presentChannels
426
+ .map((ch) => `${ch}: Req.${toPascalCase(ch)}`)
328
427
  .join('; ')
329
- // Insert Params type into the namespace block (before closing brace)
330
- const lastIdx = declarations.length - 1
331
- if (lastIdx >= 0) {
332
- const lastDecl = declarations[lastIdx]!
333
- const closingIdx = lastDecl.lastIndexOf(' }')
334
- if (closingIdx !== -1) {
335
- declarations[lastIdx] =
336
- lastDecl.slice(0, closingIdx) +
337
- ` export type Params = { ${structureFields} }\n` +
338
- lastDecl.slice(closingIdx)
339
- }
340
- }
341
- paramsTypeName = `${ctx.scopePascal}.${pascal}.Params`
342
- } else {
428
+ nsLines.push(` export type Req = { ${reqFields} }`)
429
+ }
430
+
431
+ const { nsBlock: resBlock, refs: resRefs } = await formatSubNamespace(
432
+ pascal, 'Response', resTypes, ctx, taken
433
+ )
434
+ if (resBlock) {
435
+ nsLines.push(resBlock)
436
+ // No merged Response type alias needed: we reference Response.Body / Response.Headers
437
+ // directly in the return type string, which are namespace-qualified paths (valid).
438
+ }
439
+
440
+ // Emit Errors type last (injected by injectRouteErrors below)
441
+ // Build the route namespace block
442
+ if (nsLines.length > 0) {
443
+ declarations.push(` export namespace ${pascal} {\n${nsLines.join('\n\n')}\n }`)
444
+ }
445
+
446
+ // Params type: use the merged Req type alias
447
+ if (presentChannels.length > 0) {
448
+ paramsTypeName = `${ctx.scopePascal}.${pascal}.Req`
449
+ }
450
+
451
+ // Return type
452
+ const bodyRef = resRefs['Body']
453
+ const headersRef = resRefs['Headers']
454
+ returnTypeName = buildApiReturnType(bodyRef, headersRef)
455
+ } else {
456
+ // Flat mode: emit individual types prefixed with route + sub-namespace name
457
+ for (const { shortName, schema } of reqTypes) {
458
+ if (schema == null) continue
459
+ const flatName = `${pascal}Req${shortName}`
460
+ const body = await jsonSchemaToTypeBody(schema, ctx.ajsc)
461
+ if (body == null) continue
462
+ declarations.push(`export type ${flatName} = ${body}`)
463
+ }
464
+
465
+ // Flat mode: compose structured Req type
466
+ if (presentChannels.length > 0) {
343
467
  const structureFields = presentChannels
344
- .map((ch) => `${ch}: ${refs[toPascalCase(ch)]}`)
468
+ .map((ch) => `${ch}: ${pascal}Req${toPascalCase(ch)}`)
345
469
  .join('; ')
346
- declarations.push(`export type ${pascal}Params = { ${structureFields} }`)
347
- paramsTypeName = `${pascal}Params`
470
+ declarations.push(`export type ${pascal}Req = { ${structureFields} }`)
471
+ paramsTypeName = `${pascal}Req`
348
472
  }
349
- }
350
473
 
351
- const responseTypeName = refs['Response'] ?? 'unknown'
352
- const scopeStr = route.scope ?? 'default'
474
+ // Flat mode: emit response types
475
+ let bodyRef: string | undefined
476
+ let headersRef: string | undefined
353
477
 
354
- const errorUnion = buildErrorUnion(route.errors, ctx)
355
- const hasErrors = errorUnion !== null
356
- const errorsRef = ctx.namespaceTypes
357
- ? `${ctx.scopePascal}.${pascal}.Errors`
358
- : `${pascal}Errors`
478
+ for (const { shortName, schema } of resTypes) {
479
+ if (schema == null) continue
480
+ const flatName = `${pascal}Response${shortName}`
481
+ const body = await jsonSchemaToTypeBody(schema, ctx.ajsc)
482
+ if (body == null) continue
483
+ declarations.push(`export type ${flatName} = ${body}`)
484
+ if (shortName === 'Body') bodyRef = flatName
485
+ if (shortName === 'Headers') headersRef = flatName
486
+ }
487
+
488
+ returnTypeName = buildApiReturnType(bodyRef, headersRef)
489
+
490
+ // Flat mode errors
491
+ if (errorUnion) {
492
+ declarations.push(`export type ${pascal}Errors = ${errorUnion}`)
493
+ }
494
+ }
495
+
496
+ const responseHeadersDeclared = res.headers != null
359
497
  const helperCall = hasErrors
360
- ? `client.bindCallableTyped<${paramsTypeName}, ${responseTypeName}, ${errorsRef}>`
361
- : `client.bindCallable<${paramsTypeName}, ${responseTypeName}>`
498
+ ? `client.bindCallableTyped<${paramsTypeName}, ${returnTypeName}, ${errorsRef}>`
499
+ : `client.bindCallable<${paramsTypeName}, ${returnTypeName}>`
362
500
 
363
- // Property key uses route.name verbatim (preserving the prior API emission contract).
364
- const callable = [
365
- ` /** ${route.method.toUpperCase()} ${route.fullPath} */`,
366
- ` ${route.name}: ${helperCall}({`,
501
+ const descriptorLines = [
367
502
  ` name: '${route.name}',`,
368
503
  ` scope: '${scopeStr}',`,
369
504
  ` path: '${route.fullPath}',`,
370
505
  ` method: '${route.method}',`,
371
506
  ` kind: 'api',`,
507
+ ...(responseHeadersDeclared ? [` responseHeadersDeclared: true,`] : []),
508
+ ]
509
+
510
+ const callable = [
511
+ ` /** ${route.method.toUpperCase()} ${route.fullPath} */`,
512
+ ` ${route.name}: ${helperCall}({`,
513
+ ...descriptorLines,
372
514
  ` }),`,
373
515
  ].join('\n')
374
516
 
375
- const hasErrorsInjected = injectRouteErrors(declarations, pascal, errorUnion, ctx.namespaceTypes)
517
+ const hasErrorsInjected = ctx.namespaceTypes
518
+ ? injectRouteErrors(declarations, pascal, errorUnion, ctx.namespaceTypes)
519
+ : errorUnion !== null // flat mode already emitted errors above
376
520
 
377
521
  return { typeDeclarations: declarations, callable, hasStream: false, hasErrors: hasErrorsInjected }
378
522
  }
379
523
 
524
+ async function emitHttpStreamRoute(route: HttpStreamRouteDoc, ctx: EmitRouteContext): Promise<RouteChunks> {
525
+ const pascal = toPascalCase(route.name)
526
+ const req = route.jsonSchema.req ?? {}
527
+ const res = route.jsonSchema.res ?? {}
528
+
529
+ // Request channels
530
+ const reqChannelKeys = ['pathParams', 'query', 'body', 'headers'] as const
531
+ const reqTypes: NamedType[] = []
532
+ const presentChannels: string[] = []
533
+
534
+ for (const channel of reqChannelKeys) {
535
+ const schema = req[channel]
536
+ if (schema != null) {
537
+ reqTypes.push({ shortName: toPascalCase(channel), schema })
538
+ presentChannels.push(channel)
539
+ }
540
+ }
541
+
542
+ // Yield + ReturnType
543
+ const yieldSchema = route.jsonSchema.yield
544
+ const returnSchema = route.jsonSchema.returnType
545
+ const resHeadersSchema = res.headers
546
+
547
+ const scopeStr = route.scope ?? 'default'
548
+ const declarations: string[] = []
549
+ let paramsTypeName = 'unknown'
550
+ let yieldTypeName = 'unknown'
551
+ let returnTypeName = 'void'
552
+
553
+ const taken = new Set<string>(['Req', 'Response', 'Yield', 'ReturnType'])
554
+
555
+ if (ctx.namespaceTypes) {
556
+ const nsLines: string[] = []
557
+
558
+ // Req sub-namespace
559
+ const { nsBlock: reqBlock, refs: reqRefs } = await formatSubNamespace(
560
+ pascal, 'Req', reqTypes, ctx, taken
561
+ )
562
+ if (reqBlock) {
563
+ nsLines.push(reqBlock)
564
+ // Merged type alias so Req can be used as a generic type arg (same pattern as emitApiRoute)
565
+ const reqFields = presentChannels
566
+ .map((ch) => `${ch}: Req.${toPascalCase(ch)}`)
567
+ .join('; ')
568
+ nsLines.push(` export type Req = { ${reqFields} }`)
569
+ }
570
+
571
+ // Response sub-namespace (headers only for http-stream)
572
+ const resTypes: NamedType[] = [{ shortName: 'Headers', schema: resHeadersSchema }]
573
+ const { nsBlock: resBlock } = await formatSubNamespace(
574
+ pascal, 'Response', resTypes, ctx, taken
575
+ )
576
+ if (resBlock) nsLines.push(resBlock)
577
+
578
+ // Yield and ReturnType directly in the route namespace
579
+ const directTypes: NamedType[] = [
580
+ { shortName: 'Yield', schema: yieldSchema },
581
+ { shortName: 'ReturnType', schema: returnSchema },
582
+ ]
583
+ const seenDeclarations = new Set<string>()
584
+ for (const { shortName, schema } of directTypes) {
585
+ if (schema == null) continue
586
+ const rawResult = await jsonSchemaToExtractedTypes(schema, ctx.ajsc)
587
+ if (rawResult == null) continue
588
+ const result = renameExtractedTypes(rawResult, taken)
589
+ for (const decl of result.declarations) {
590
+ if (!seenDeclarations.has(decl)) {
591
+ seenDeclarations.add(decl)
592
+ nsLines.push(indent(decl, ' '))
593
+ }
594
+ }
595
+ nsLines.push(` export type ${shortName} = ${result.body}`)
596
+ }
597
+
598
+ if (nsLines.length > 0) {
599
+ declarations.push(` export namespace ${pascal} {\n${nsLines.join('\n')}\n }`)
600
+ }
601
+
602
+ if (presentChannels.length > 0) {
603
+ paramsTypeName = `${ctx.scopePascal}.${pascal}.Req`
604
+ }
605
+
606
+ if (yieldSchema != null) yieldTypeName = `${ctx.scopePascal}.${pascal}.Yield`
607
+ if (returnSchema != null) returnTypeName = `${ctx.scopePascal}.${pascal}.ReturnType`
608
+ } else {
609
+ // Flat mode
610
+ for (const { shortName, schema } of reqTypes) {
611
+ if (schema == null) continue
612
+ const flatName = `${pascal}Req${shortName}`
613
+ const body = await jsonSchemaToTypeBody(schema, ctx.ajsc)
614
+ if (body == null) continue
615
+ declarations.push(`export type ${flatName} = ${body}`)
616
+ }
617
+
618
+ if (presentChannels.length > 0) {
619
+ const structureFields = presentChannels
620
+ .map((ch) => `${ch}: ${pascal}Req${toPascalCase(ch)}`)
621
+ .join('; ')
622
+ declarations.push(`export type ${pascal}Req = { ${structureFields} }`)
623
+ paramsTypeName = `${pascal}Req`
624
+ }
625
+
626
+ if (resHeadersSchema != null) {
627
+ const body = await jsonSchemaToTypeBody(resHeadersSchema, ctx.ajsc)
628
+ if (body != null) declarations.push(`export type ${pascal}ResponseHeaders = ${body}`)
629
+ }
630
+
631
+ if (yieldSchema != null) {
632
+ const body = await jsonSchemaToTypeBody(yieldSchema, ctx.ajsc)
633
+ if (body != null) {
634
+ declarations.push(`export type ${pascal}Yield = ${body}`)
635
+ yieldTypeName = `${pascal}Yield`
636
+ }
637
+ }
638
+
639
+ if (returnSchema != null) {
640
+ const body = await jsonSchemaToTypeBody(returnSchema, ctx.ajsc)
641
+ if (body != null) {
642
+ declarations.push(`export type ${pascal}ReturnType = ${body}`)
643
+ returnTypeName = `${pascal}ReturnType`
644
+ }
645
+ }
646
+ }
647
+
648
+ const callable = [
649
+ ` /** ${route.method.toUpperCase()} ${route.fullPath} */`,
650
+ ` ${route.name}(req: ${paramsTypeName}, options?: ProcedureCallOptions): TypedStream<${yieldTypeName}, ${returnTypeName}> {`,
651
+ ` return client.stream<${yieldTypeName}, ${returnTypeName}>({`,
652
+ ` name: '${route.name}',`,
653
+ ` scope: '${scopeStr}',`,
654
+ ` path: '${route.fullPath}',`,
655
+ ` method: '${route.method}',`,
656
+ ` kind: 'http-stream',`,
657
+ ` streamMode: '${route.streamMode}',`,
658
+ ` params: req,`,
659
+ ` }, options)`,
660
+ ` },`,
661
+ ].join('\n')
662
+
663
+ const hasErrors = injectRouteErrors(
664
+ declarations,
665
+ pascal,
666
+ buildErrorUnion(route.errors, ctx),
667
+ ctx.namespaceTypes
668
+ )
669
+
670
+ return { typeDeclarations: declarations, callable, hasStream: true, hasErrors }
671
+ }
672
+
380
673
  async function emitStreamRoute(route: StreamHttpRouteDoc, ctx: EmitRouteContext): Promise<RouteChunks> {
381
674
  const pascal = versionedPascal(route.name, route.version)
382
675
 
@@ -470,6 +763,8 @@ export async function emitScopeFile(
470
763
  chunks = await emitApiRoute(route as APIHttpRouteDoc, ctx)
471
764
  } else if (kind === 'stream') {
472
765
  chunks = await emitStreamRoute(route as StreamHttpRouteDoc, ctx)
766
+ } else if (kind === 'http-stream') {
767
+ chunks = await emitHttpStreamRoute(route as HttpStreamRouteDoc, ctx)
473
768
  } else {
474
769
  throw new Error(`Unknown route kind "${kind}"`)
475
770
  }
@@ -375,7 +375,7 @@ describe('runPipeline (kotlin target)', () => {
375
375
  scope: 'users',
376
376
  method: 'GET',
377
377
  fullPath: '/users/:id',
378
- schema: { input: { pathParams: { type: 'object' } }, returnType: { type: 'object' } },
378
+ jsonSchema: { req: { pathParams: { type: 'object' } }, res: { body: { type: 'object' } } },
379
379
  errors: [],
380
380
  },
381
381
  ],
@@ -438,7 +438,7 @@ describe('runPipeline (kotlin target)', () => {
438
438
  routes: [
439
439
  {
440
440
  kind: 'api', name: 'GetUser', scope: 'users', method: 'GET', fullPath: '/users/:id',
441
- schema: { input: { pathParams: { type: 'object' } }, returnType: { type: 'object' } },
441
+ jsonSchema: { req: { pathParams: { type: 'object' } }, res: { body: { type: 'object' } } },
442
442
  errors: [],
443
443
  },
444
444
  ],
@@ -466,9 +466,9 @@ describe('runPipeline (kotlin target)', () => {
466
466
  const envelope = {
467
467
  basePath: '/api', headers: [], version: '1' as const, errors: [],
468
468
  routes: [
469
- { kind: 'stream', name: 'WatchA', scope: 's', method: 'GET', path: '/a', schema: {}, errors: [] },
470
- { kind: 'stream', name: 'WatchB', scope: 's', method: 'GET', path: '/b', schema: {}, errors: [] },
471
- { kind: 'api', name: 'GetThing', scope: 's', method: 'GET', fullPath: '/c', schema: { returnType: { type: 'object' } }, errors: [] },
469
+ { kind: 'stream', name: 'WatchA', scope: 's', method: 'GET', path: '/a', jsonSchema: {}, errors: [] },
470
+ { kind: 'stream', name: 'WatchB', scope: 's', method: 'GET', path: '/b', jsonSchema: {}, errors: [] },
471
+ { kind: 'api', name: 'GetThing', scope: 's', method: 'GET', fullPath: '/c', jsonSchema: { res: { body: { type: 'object' } } }, errors: [] },
472
472
  ],
473
473
  } as any
474
474
  await runPipeline({
@@ -493,7 +493,7 @@ describe('runPipeline (kotlin target)', () => {
493
493
  try {
494
494
  const envelope = {
495
495
  basePath: '/api', headers: [], version: '1' as const, errors: [],
496
- routes: [{ kind: 'api', name: 'X', scope: 's', method: 'GET', fullPath: '/x', schema: { returnType: { type: 'object' } }, errors: [] }],
496
+ routes: [{ kind: 'api', name: 'X', scope: 's', method: 'GET', fullPath: '/x', jsonSchema: { res: { body: { type: 'object' } } }, errors: [] }],
497
497
  } as any
498
498
  await runPipeline({
499
499
  envelope, outDir: 'out', dryRun: true,
@@ -518,7 +518,7 @@ describe('runPipeline (kotlin target)', () => {
518
518
 
519
519
  const envelope = {
520
520
  basePath: '/api', headers: [], version: '1' as const, errors: [],
521
- routes: [{ kind: 'api', name: 'GetUser', scope: 'users', method: 'GET', fullPath: '/u', schema: { returnType: { type: 'object' } }, errors: [] }],
521
+ routes: [{ kind: 'api', name: 'GetUser', scope: 'users', method: 'GET', fullPath: '/u', jsonSchema: { res: { body: { type: 'object' } } }, errors: [] }],
522
522
  } as any
523
523
 
524
524
  await runPipeline({
@@ -54,11 +54,11 @@ describe('resolveEnvelope', () => {
54
54
  })
55
55
 
56
56
  // Defensive (downstream bug repro): forgetting `builder.build()` is a common
57
- // cause of an empty routes array because hono-rpc/hono-api/hono-stream/express-rpc
58
- // builders only populate their `_docs` array inside `build()`. The current
59
- // error message says "Register at least one procedure", which led the
60
- // downstream dev to look in the wrong place. The message should mention
61
- // `.build()` as a likely cause so the next person hits the right fix faster.
57
+ // cause of an empty routes array because the hono builder only populates
58
+ // its `_docs` array inside `build()`. The current error message says
59
+ // "Register at least one procedure", which led the downstream dev to look
60
+ // in the wrong place. The message should mention `.build()` as a likely
61
+ // cause so the next person hits the right fix faster.
62
62
  it('empty-routes error message mentions builder.build() as a likely cause', async () => {
63
63
  const empty: DocEnvelope = { basePath: '', headers: [], errors: [], routes: [] }
64
64
  await expect(resolveEnvelope({ envelope: empty })).rejects.toThrow(/\.build\(\)/)
@@ -55,7 +55,7 @@ export async function resolveEnvelope(input: ResolveInput): Promise<DocEnvelope>
55
55
  throw new Error(
56
56
  '[ts-procedures-codegen] DocEnvelope has an empty "routes" array. ' +
57
57
  'Common causes: (1) you forgot to call `builder.build()` before passing ' +
58
- 'the builder to `DocRegistry.from(...)` — hono/express builders only populate ' +
58
+ 'the builder to `DocRegistry.from(...)` — hono builders only populate ' +
59
59
  '`docs` inside `build()`; (2) no procedures registered with the builder.'
60
60
  )
61
61
  }