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,559 +0,0 @@
1
- # Hono Stream Implementation
2
-
3
- HTTP streaming and SSE endpoints for streaming procedures created with `CreateStream`.
4
-
5
- ## Overview
6
-
7
- `HonoStreamAppBuilder` provides a builder pattern for creating streaming HTTP endpoints in Hono. It handles `AsyncGenerator` handlers and supports both Server-Sent Events (SSE) and plain text streaming modes.
8
-
9
- ## Installation
10
-
11
- Requires `hono` as a peer dependency:
12
-
13
- ```bash
14
- npm install hono
15
- ```
16
-
17
- ## Quick Start
18
-
19
- ```typescript
20
- import { Procedures } from 'ts-procedures'
21
- import { HonoStreamAppBuilder, sse } from 'ts-procedures/hono-stream'
22
-
23
- // Define your context and config types
24
- type StreamContext = { userId: string }
25
- interface RPCConfig {
26
- scope: string | string[]
27
- version: number
28
- }
29
-
30
- // Create a procedures factory
31
- const StreamRPC = Procedures<StreamContext, RPCConfig>()
32
-
33
- // Create a streaming procedure — yield domain data directly
34
- StreamRPC.CreateStream(
35
- 'WatchNotifications',
36
- {
37
- scope: ['user', 'notifications'],
38
- version: 1,
39
- schema: {
40
- yieldType: Type.Object({ id: Type.Number(), message: Type.String() }),
41
- },
42
- },
43
- async function* (ctx) {
44
- for (let i = 1; i <= 10; i++) {
45
- // Yield domain data directly — matches yieldType schema
46
- yield { id: i, message: `Notification ${i}` }
47
- await new Promise((r) => setTimeout(r, 1000))
48
- }
49
- }
50
- )
51
-
52
- // Build the Hono app
53
- const builder = new HonoStreamAppBuilder()
54
- .register(StreamRPC, (c) => ({
55
- userId: c.req.header('x-user-id') || 'anonymous',
56
- }))
57
-
58
- const app = builder.build()
59
-
60
- // Access documentation
61
- const docs = builder.docs
62
- ```
63
-
64
- ## HTTP Methods
65
-
66
- Both GET and POST methods are supported for each streaming endpoint:
67
-
68
- ```
69
- GET /{pathPrefix}/{scope...}/{procedureName}/{version}?param1=value1
70
- POST /{pathPrefix}/{scope...}/{procedureName}/{version} (JSON body)
71
- ```
72
-
73
- - **GET**: For EventSource/SSE clients, params passed via query string
74
- - **POST**: For clients needing complex JSON body params
75
-
76
- ## Yielding in SSE Mode
77
-
78
- Procedures yield **domain data directly** — the same shape as your `yieldType` schema. `HonoStreamAppBuilder` handles the SSE envelope (`event:`, `id:`, `data:`) automatically.
79
-
80
- ```typescript
81
- async function* (ctx) {
82
- // Domain objects are JSON.stringify'd into the SSE data: field
83
- yield { count: 1 }
84
-
85
- // Strings are passed through as-is (no double-stringify)
86
- yield 'raw string value'
87
- }
88
- ```
89
-
90
- By default, SSE events use the procedure name as the `event:` field and auto-increment the `id:` field.
91
-
92
- ### `sse()` Helper
93
-
94
- To override SSE metadata (event name, id, retry) for individual yields, use the `sse()` helper:
95
-
96
- ```typescript
97
- import { sse } from 'ts-procedures/hono-stream'
98
-
99
- async function* (ctx) {
100
- // Custom event name
101
- yield sse({ type: 'heartbeat' }, { event: 'ping' })
102
-
103
- // Custom id
104
- yield sse({ msg: 'hello' }, { id: 'msg-001' })
105
-
106
- // All SSE options
107
- yield sse({ done: true }, { event: 'complete', id: 'final', retry: 5000 })
108
-
109
- // sse() with no options — same as plain yield (uses defaults)
110
- yield sse({ count: 1 })
111
- }
112
- ```
113
-
114
- `sse()` attaches metadata via a `WeakMap` side-channel, so:
115
- - The domain object passes through core validation (`validateYields`) unmodified
116
- - `JSON.stringify` only sees domain properties
117
- - AJV's `removeAdditional: true` doesn't affect the metadata
118
-
119
- > **Note:** `sse()` requires object values (used as `WeakMap` keys). Strings and other primitives cannot carry SSE metadata — they use the default event name (procedure name) and auto-incremented id.
120
-
121
- **Text mode is unaffected** — both plain objects and `sse()`-tagged objects are JSON-stringified identically. SSE metadata is ignored in text mode.
122
-
123
- ## Stream Modes
124
-
125
- ### SSE Mode (default)
126
-
127
- Returns `text/event-stream` content type with SSE-formatted messages:
128
-
129
- ```typescript
130
- const builder = new HonoStreamAppBuilder({ defaultStreamMode: 'sse' })
131
- ```
132
-
133
- Response format:
134
- ```
135
- event: WatchNotifications
136
- data: {"id":1,"message":"Notification 1"}
137
- id: 0
138
-
139
- event: WatchNotifications
140
- data: {"id":2,"message":"Notification 2"}
141
- id: 1
142
- ```
143
-
144
- Client usage:
145
- ```typescript
146
- const eventSource = new EventSource('/user/notifications/watch-notifications/1')
147
- // Listen for default event (procedure name)
148
- eventSource.addEventListener('WatchNotifications', (event) => {
149
- const data = JSON.parse(event.data)
150
- console.log(data)
151
- })
152
- // Listen for custom events (when using sse() helper)
153
- eventSource.addEventListener('notification', (event) => {
154
- const data = JSON.parse(event.data)
155
- console.log(data)
156
- })
157
- ```
158
-
159
- ### Text Mode
160
-
161
- Returns `text/plain` content type with newline-delimited JSON:
162
-
163
- ```typescript
164
- const builder = new HonoStreamAppBuilder({ defaultStreamMode: 'text' })
165
- ```
166
-
167
- Response format:
168
- ```
169
- {"key":"value"}
170
- {"key":"value2"}
171
- ```
172
-
173
- Client usage:
174
- ```typescript
175
- const response = await fetch('/user/notifications/watch-notifications/1')
176
- const reader = response.body.getReader()
177
- const decoder = new TextDecoder()
178
-
179
- while (true) {
180
- const { done, value } = await reader.read()
181
- if (done) break
182
- const lines = decoder.decode(value).split('\n')
183
- for (const line of lines) {
184
- if (line) console.log(JSON.parse(line))
185
- }
186
- }
187
- ```
188
-
189
- ## Configuration
190
-
191
- ### Builder Config
192
-
193
- ```typescript
194
- interface HonoStreamAppBuilderConfig<TErrorData = unknown> {
195
- app?: Hono // Use existing Hono instance
196
- pathPrefix?: string // Prefix for all routes
197
- defaultStreamMode?: 'sse' | 'text' // Default: 'sse'
198
- onRequestStart?: (c: Context) => void
199
- onRequestEnd?: (c: Context) => void
200
- onStreamStart?: (procedure: TStreamProcedureRegistration, c: Context, streamMode: StreamMode) => void
201
- onStreamEnd?: (procedure: TStreamProcedureRegistration, c: Context, streamMode: StreamMode) => void
202
-
203
- // Error handling (see Error Handling section)
204
- errors?: ErrorTaxonomy
205
- unknownError?: UnknownErrorConfig
206
- onError?: (procedure, c, error: ProcedureValidationError | Error) => Response | Promise<Response>
207
- onRequestError?: (ctx) => void | Promise<void>
208
- onMidStreamError?: (procedure, c, error) => MidStreamErrorResult<TErrorData> | undefined
209
- }
210
- ```
211
-
212
- ### Per-Factory Options
213
-
214
- ```typescript
215
- builder.register(factory, context, {
216
- streamMode: 'text', // Override default mode for this factory
217
- extendProcedureDoc: ({ base, procedure }) => ({
218
- summary: 'Custom documentation',
219
- tags: ['streaming'],
220
- }),
221
- })
222
- ```
223
-
224
- ## Using an Existing Hono App
225
-
226
- Pass an existing `Hono` instance to mount stream routes alongside other routes — including those registered by `HonoRPCAppBuilder` and `HonoAPIAppBuilder`. One Hono server can host all three builders.
227
-
228
- ```typescript
229
- const app = new Hono()
230
- app.use('*', cors())
231
-
232
- new HonoStreamAppBuilder({ app })
233
- .register(StreamFactory, contextResolver)
234
- .build()
235
- ```
236
-
237
- For the full single-server pattern (RPC + API + Stream + `DocRegistry` on one app), see [docs/http-integrations.md § One Hono Server, Multiple Builders](../../../../docs/http-integrations.md#one-hono-server-multiple-builders).
238
-
239
- ## Lifecycle Hooks
240
-
241
- Hooks execute in the following order:
242
-
243
- ```
244
- onRequestStart → [validation] → onStreamStart(proc, c, streamMode) → [yields...] → onStreamEnd(proc, c, streamMode) → onRequestEnd
245
- ↓ ↓
246
- (validation error) (stream error)
247
- ↓ ↓
248
- HTTP 400 response error sent in stream
249
-
250
- onStreamEnd
251
- ```
252
-
253
- `onStreamStart` and `onStreamEnd` receive the resolved `streamMode` (`'sse'` or `'text'`) as their third argument.
254
-
255
- ## Validation
256
-
257
- ### Pre-validation
258
-
259
- `HonoStreamAppBuilder` validates parameters **before** starting the stream to return proper HTTP error responses. It passes `isPrevalidated: true` internally to skip redundant procedure-level validation. This is transparent to handler authors — the flag is not exposed on the handler context type.
260
-
261
- 1. Params are validated by `HonoStreamAppBuilder` at the HTTP layer
262
- 2. If validation fails, a 400 response is returned (no stream starts)
263
- 3. If validation passes, the procedure handler runs without duplicate validation
264
-
265
- This design allows for:
266
- - Clean HTTP error responses (400 status code, JSON body) instead of stream errors
267
- - No duplicate validation overhead
268
- - Consistent error handling at the framework level
269
-
270
- ## Error Handling
271
-
272
- Streaming has two distinct error phases with different semantics:
273
-
274
- | Phase | When | Handling |
275
- |-------|------|---------|
276
- | **Pre-stream** | Validation, auth, context resolution | Declarative taxonomy via `errors` + `unknownError` — same shape as all other HTTP builders |
277
- | **Mid-stream** | Generator throws during iteration | `onMidStreamError` — writes a value into the open stream (status is already committed) |
278
-
279
- ### Pre-Stream — Error Taxonomy
280
-
281
- ```typescript
282
- import { defineErrorTaxonomy } from 'ts-procedures/hono-stream'
283
-
284
- const builder = new HonoStreamAppBuilder({
285
- errors: defineErrorTaxonomy({
286
- AuthError: { class: AuthError, statusCode: 401 },
287
- }),
288
- unknownError: { toResponse: () => ({ error: 'Internal server error' }) },
289
- onMidStreamError: (/* ... */) => ({ /* mid-stream only */ }),
290
- })
291
- ```
292
-
293
- Full contract (both peer error modes, `onRequestError` observer, per-route `errors` narrowing): see **[docs/http-integrations.md § Error Handling](../../../../docs/http-integrations.md#error-handling)** — the canonical explanation shared across all four HTTP builders.
294
-
295
- ### Mid-Stream Errors (`onMidStreamError`)
296
-
297
- Errors that occur **during** streaming (generator throws). Since the stream is already open, HTTP status cannot change. Return `{ data, closeStream? }` — the `data` value is written as the SSE `data:` field content.
298
-
299
- To control SSE metadata (event name, id, retry) on the error yield, wrap the data object with `sse()`:
300
-
301
- ```typescript
302
- const builder = new HonoStreamAppBuilder({
303
- onMidStreamError: (procedure, c, error) => {
304
- return {
305
- // Data written as the SSE data: field (should match your yieldType schema)
306
- // Use sse() to attach custom event/id/retry metadata
307
- data: sse({
308
- type: 'error',
309
- code: 'STREAM_ERROR',
310
- message: error.message,
311
- retryable: false,
312
- }, { event: 'error', id: 'err-001' }),
313
- // Optional: whether to close stream after (default: true)
314
- closeStream: true,
315
- }
316
- },
317
- })
318
- ```
319
-
320
- **Return values:**
321
- - `{ data, closeStream? }` - Write `data` to stream. Use `sse(data, opts)` for custom SSE event/id/retry
322
- - `undefined` - Use default behavior: `{ error: message }` with `event: 'error'`
323
-
324
- Without `sse()`, the event defaults to the procedure name when custom data is provided, or `'error'` when using the default error format. The id auto-increments.
325
-
326
- ### Type-Safe Error Handling with Union Types
327
-
328
- Use the generic `TErrorData` parameter on `HonoStreamAppBuilder` to constrain the `onMidStreamError` return type:
329
-
330
- ```typescript
331
- type ErrorPayload = {
332
- type: 'error'
333
- code: string
334
- message: string
335
- retryable: boolean
336
- }
337
-
338
- // TErrorData constrains what onMidStreamError can return
339
- const builder = new HonoStreamAppBuilder<ErrorPayload>({
340
- onMidStreamError: (proc, c, error) => ({
341
- data: {
342
- type: 'error',
343
- code: 'STREAM_ERROR',
344
- message: error.message,
345
- retryable: false,
346
- }
347
- })
348
- })
349
- ```
350
-
351
- For type-safe error handling, define your `yieldType` as a discriminated union:
352
-
353
- ```typescript
354
- StreamRPC.CreateStream('WatchEvents', {
355
- scope: 'events',
356
- version: 1,
357
- schema: {
358
- params: Type.Object({ roomId: Type.String() }),
359
- // Union type: clients know exactly what to expect
360
- yieldType: Type.Union([
361
- Type.Object({
362
- type: Type.Literal('event'),
363
- eventType: Type.String(),
364
- data: Type.Any()
365
- }),
366
- Type.Object({
367
- type: Type.Literal('error'),
368
- code: Type.String(),
369
- message: Type.String(),
370
- retryable: Type.Boolean()
371
- })
372
- ])
373
- }
374
- }, async function* (ctx, params) {
375
- // Yield domain data directly — matches yieldType schema
376
- yield { type: 'event', eventType: 'user_joined', data: { userId: ctx.userId } }
377
- // On error, the procedure throws and onMidStreamError handles it
378
- })
379
- ```
380
-
381
- **Output (SSE mode):**
382
- ```
383
- event: WatchEvents
384
- data: {"type":"event","eventType":"user_joined","data":{"userId":"123"}}
385
-
386
- event: WatchEvents
387
- data: {"type":"error","code":"STREAM_ERROR","message":"Connection lost","retryable":false}
388
- ```
389
-
390
- ### Default Error Output
391
-
392
- Without custom callbacks, errors are written as:
393
-
394
- **SSE Mode:**
395
- ```
396
- event: error
397
- data: {"error":"Error message"}
398
- ```
399
-
400
- **Text Mode:**
401
- ```
402
- {"error":"Error message"}
403
- ```
404
-
405
- ## Route Documentation
406
-
407
- Access generated documentation via `builder.docs`:
408
-
409
- ```typescript
410
- interface StreamHttpRouteDoc {
411
- name: string
412
- path: string
413
- methods: ('get' | 'post')[]
414
- streamMode: 'sse' | 'text'
415
- scope: string | string[]
416
- version: number
417
- jsonSchema: {
418
- params?: Record<string, unknown> // From schema.params
419
- yieldType?: Record<string, unknown> // SSE: envelope with data/event/id/retry; Text: from schema.yieldType
420
- returnType?: Record<string, unknown> // From schema.returnType
421
- }
422
- }
423
- ```
424
-
425
- In SSE mode, the documented `yieldType` is an SSE envelope schema with the user's `yieldType` nested under the `data` property:
426
-
427
- ```json
428
- {
429
- "type": "object",
430
- "description": "SSE message envelope. The data field contains the procedure yield value.",
431
- "required": ["data", "event", "id"],
432
- "properties": {
433
- "data": { /* user's yieldType schema */ },
434
- "event": { "type": "string" },
435
- "id": { "type": "string" },
436
- "retry": { "type": "number" }
437
- }
438
- }
439
- ```
440
-
441
- ## Procedure Filtering
442
-
443
- Only streaming procedures (created with `CreateStream`) are registered. Regular procedures created with `Create` are ignored by this builder.
444
-
445
- ```typescript
446
- const RPC = Procedures<Context, RPCConfig>()
447
-
448
- // This will NOT be registered by HonoStreamAppBuilder
449
- RPC.Create('GetUser', config, async (ctx) => ({ user: 'data' }))
450
-
451
- // This WILL be registered
452
- RPC.CreateStream('WatchUser', config, async function* (ctx) {
453
- yield { update: 'data' }
454
- })
455
- ```
456
-
457
- ## Client Disconnect Handling
458
-
459
- When a client disconnects, the stream's `onAbort` handler is triggered, which calls `generator.return()` to clean up. The `ctx.signal` in your handler will be aborted.
460
-
461
- `HonoStreamAppBuilder` injects the HTTP request's `AbortSignal` (`c.req.raw.signal`) into the handler context. This is combined with the stream's internal `AbortController` via `AbortSignal.any()`, so `ctx.signal` aborts on either client disconnect or normal stream completion.
462
-
463
- Use `signal.reason` to distinguish between the two:
464
-
465
- ```typescript
466
- RPC.CreateStream('LongStream', config, async function* (ctx) {
467
- try {
468
- while (!ctx.signal.aborted) {
469
- yield { tick: Date.now() }
470
- await new Promise((r) => setTimeout(r, 1000))
471
- }
472
- } finally {
473
- if (ctx.signal.reason === 'stream-completed') {
474
- // Generator finished normally
475
- } else {
476
- // Client disconnected — clean up resources
477
- await releaseResources()
478
- }
479
- }
480
- })
481
- ```
482
-
483
- ## TypeScript Types
484
-
485
- ```typescript
486
- import {
487
- HonoStreamAppBuilder,
488
- defineErrorTaxonomy,
489
- sse, // Helper to attach SSE metadata (event/id/retry) to yielded objects
490
- } from 'ts-procedures/hono-stream'
491
- import type {
492
- HonoStreamAppBuilderConfig, // Generic: HonoStreamAppBuilderConfig<TErrorData = unknown>
493
- StreamHttpRouteDoc,
494
- StreamMode,
495
- SSEOptions, // Type for sse() options: { event?, id?, retry? }
496
- MidStreamErrorResult, // Generic: MidStreamErrorResult<TErrorData = unknown>
497
- ErrorTaxonomy,
498
- ErrorTaxonomyEntry,
499
- UnknownErrorConfig,
500
- OnRequestErrorContext, // Passed to the onRequestError observer: { err, procedure, raw: Context }
501
- } from 'ts-procedures/hono-stream'
502
- ```
503
-
504
- ## Migration Notes
505
-
506
- ### Migrating from v5
507
-
508
- **Breaking: `MidStreamErrorResult` no longer has `event` and `id` fields.** Use `sse()` to attach SSE metadata to error data instead:
509
-
510
- ```typescript
511
- // Before (v5)
512
- onMidStreamError: (proc, c, error) => ({
513
- data: { type: 'error', message: error.message },
514
- event: 'custom-error',
515
- id: 'err-1',
516
- })
517
-
518
- // After (v6)
519
- onMidStreamError: (proc, c, error) => ({
520
- data: sse(
521
- { type: 'error', message: error.message },
522
- { event: 'custom-error', id: 'err-1' }
523
- ),
524
- })
525
- ```
526
-
527
- **Breaking: `onStreamStart` / `onStreamEnd` now receive `streamMode` as a third parameter.**
528
-
529
- ```typescript
530
- // Before (v5)
531
- onStreamStart: (procedure, c) => { ... }
532
- onStreamEnd: (procedure, c) => { ... }
533
-
534
- // After (v6)
535
- onStreamStart: (procedure, c, streamMode) => { ... }
536
- onStreamEnd: (procedure, c, streamMode) => { ... }
537
- ```
538
-
539
- **Breaking: `StreamHttpRouteDoc.jsonSchema` fields narrowed from `object` to `Record<string, unknown>`.**
540
-
541
- **New: Generic `TErrorData` parameter** on `HonoStreamAppBuilder` and `MidStreamErrorResult` for type-safe `onMidStreamError` callbacks.
542
-
543
- **Breaking: `onPreStreamError` renamed to `onError`** for consistency with the other three HTTP builders. Signature unchanged.
544
-
545
- ```typescript
546
- // Before (v5)
547
- new HonoStreamAppBuilder({
548
- onPreStreamError: (proc, c, err) => c.json({ error: err.message }, 400),
549
- })
550
-
551
- // After (v6)
552
- new HonoStreamAppBuilder({
553
- onError: (proc, c, err) => c.json({ error: err.message }, 400),
554
- })
555
- ```
556
-
557
- **Breaking: hard-default status for `ProcedureValidationError` changed from 400 to 500.** Previously, `HonoStreamAppBuilder` special-cased validation errors to 400 when no `errors` / `onError` was configured. v6 drops this inconsistency — the hard default is now 500 across all four builders. To preserve the 400 response, configure the taxonomy (`errors: {}` engages framework defaults) or handle it in `onError`.
558
-
559
- **New: `onRequestError` cross-cutting observer.** Fires for every caught pre-stream error, before dispatch. Awaited, cannot mutate the response. Use for logging, tracing, metrics. See [docs/http-integrations.md § Error Handling](../../../../docs/http-integrations.md#error-handling).