ts-procedures 8.6.0 → 9.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 (627) hide show
  1. package/README.md +166 -101
  2. package/agent_config/claude-code/.claude-plugin/plugin.json +1 -1
  3. package/agent_config/claude-code/agents/ts-procedures-architect.md +11 -10
  4. package/agent_config/claude-code/skills/ts-procedures/SKILL.md +25 -12
  5. package/agent_config/claude-code/skills/ts-procedures/anti-patterns.md +10 -12
  6. package/agent_config/claude-code/skills/ts-procedures/api-reference.md +141 -45
  7. package/agent_config/claude-code/skills/ts-procedures/checklist.md +7 -6
  8. package/agent_config/claude-code/skills/ts-procedures/patterns.md +45 -6
  9. package/agent_config/claude-code/skills/ts-procedures/templates/client.md +1 -1
  10. package/agent_config/claude-code/skills/ts-procedures/templates/hono.md +1 -1
  11. package/agent_config/copilot/copilot-instructions.md +50 -33
  12. package/agent_config/cursor/cursorrules +50 -33
  13. package/build/adapters/astro/astro-context.js.map +1 -0
  14. package/build/adapters/astro/create-handler.js.map +1 -0
  15. package/build/adapters/astro/index.js.map +1 -0
  16. package/build/{implementations/http → adapters}/astro/index.test.js +1 -1
  17. package/build/adapters/astro/index.test.js.map +1 -0
  18. package/build/adapters/astro/rewrite-request.js.map +1 -0
  19. package/build/adapters/hono/envelope-parity.test.js +98 -0
  20. package/build/adapters/hono/envelope-parity.test.js.map +1 -0
  21. package/build/{implementations/http → adapters}/hono/handlers/http-stream.d.ts +1 -1
  22. package/build/adapters/hono/handlers/http-stream.js +55 -0
  23. package/build/adapters/hono/handlers/http-stream.js.map +1 -0
  24. package/build/{implementations/http → adapters}/hono/handlers/http-stream.test.js +1 -1
  25. package/build/adapters/hono/handlers/http-stream.test.js.map +1 -0
  26. package/build/{implementations/http → adapters}/hono/handlers/http.d.ts +1 -1
  27. package/build/adapters/hono/handlers/http.js +50 -0
  28. package/build/adapters/hono/handlers/http.js.map +1 -0
  29. package/build/{implementations/http → adapters}/hono/handlers/http.test.js +1 -1
  30. package/build/adapters/hono/handlers/http.test.js.map +1 -0
  31. package/build/{implementations/http → adapters}/hono/handlers/rpc.d.ts +2 -2
  32. package/build/adapters/hono/handlers/rpc.js +23 -0
  33. package/build/adapters/hono/handlers/rpc.js.map +1 -0
  34. package/build/{implementations/http → adapters}/hono/handlers/rpc.test.js +1 -1
  35. package/build/adapters/hono/handlers/rpc.test.js.map +1 -0
  36. package/build/adapters/hono/handlers/stream.d.ts +12 -0
  37. package/build/adapters/hono/handlers/stream.js +89 -0
  38. package/build/adapters/hono/handlers/stream.js.map +1 -0
  39. package/build/{implementations/http → adapters}/hono/handlers/stream.test.js +3 -2
  40. package/build/adapters/hono/handlers/stream.test.js.map +1 -0
  41. package/build/{implementations/http → adapters}/hono/index.d.ts +24 -12
  42. package/build/{implementations/http → adapters}/hono/index.js +19 -8
  43. package/build/adapters/hono/index.js.map +1 -0
  44. package/build/{implementations/http → adapters}/hono/index.test.js +2 -4
  45. package/build/adapters/hono/index.test.js.map +1 -0
  46. package/build/{implementations/http → adapters/hono}/on-request-error.test.js +2 -2
  47. package/build/adapters/hono/on-request-error.test.js.map +1 -0
  48. package/build/adapters/hono/request.d.ts +7 -0
  49. package/build/adapters/hono/request.js +22 -0
  50. package/build/adapters/hono/request.js.map +1 -0
  51. package/build/{implementations/http → adapters/hono}/route-errors.test.js +4 -4
  52. package/build/adapters/hono/route-errors.test.js.map +1 -0
  53. package/build/adapters/hono/types.d.ts +55 -0
  54. package/build/adapters/hono/types.js +19 -0
  55. package/build/adapters/hono/types.js.map +1 -0
  56. package/build/client/freeze.test.js +39 -0
  57. package/build/client/freeze.test.js.map +1 -0
  58. package/build/client/typed-error-dispatch.test.js +2 -2
  59. package/build/client/typed-error-dispatch.test.js.map +1 -1
  60. package/build/codegen/__fixtures__/make-envelope.d.ts +1 -1
  61. package/build/codegen/bin/cli.d.ts +5 -0
  62. package/build/codegen/bin/cli.js +139 -182
  63. package/build/codegen/bin/cli.js.map +1 -1
  64. package/build/codegen/bin/cli.test.js +12 -2
  65. package/build/codegen/bin/cli.test.js.map +1 -1
  66. package/build/codegen/bin/flag-specs.d.ts +9 -0
  67. package/build/codegen/bin/flag-specs.js +33 -31
  68. package/build/codegen/bin/flag-specs.js.map +1 -1
  69. package/build/codegen/bin/flag-specs.test.js +14 -1
  70. package/build/codegen/bin/flag-specs.test.js.map +1 -1
  71. package/build/codegen/collect-models.d.ts +1 -1
  72. package/build/codegen/emit/api-route.d.ts +8 -0
  73. package/build/codegen/emit/api-route.js +156 -0
  74. package/build/codegen/emit/api-route.js.map +1 -0
  75. package/build/codegen/emit/context.d.ts +30 -0
  76. package/build/codegen/emit/context.js +2 -0
  77. package/build/codegen/emit/context.js.map +1 -0
  78. package/build/codegen/emit/declarations.d.ts +24 -0
  79. package/build/codegen/emit/declarations.js +48 -0
  80. package/build/codegen/emit/declarations.js.map +1 -0
  81. package/build/codegen/emit/format-types.d.ts +61 -0
  82. package/build/codegen/emit/format-types.js +188 -0
  83. package/build/codegen/emit/format-types.js.map +1 -0
  84. package/build/codegen/emit/http-stream-route.d.ts +7 -0
  85. package/build/codegen/emit/http-stream-route.js +138 -0
  86. package/build/codegen/emit/http-stream-route.js.map +1 -0
  87. package/build/codegen/emit/route-shared.d.ts +35 -0
  88. package/build/codegen/emit/route-shared.js +88 -0
  89. package/build/codegen/emit/route-shared.js.map +1 -0
  90. package/build/codegen/emit/rpc-route.d.ts +7 -0
  91. package/build/codegen/emit/rpc-route.js +37 -0
  92. package/build/codegen/emit/rpc-route.js.map +1 -0
  93. package/build/codegen/emit/scope-file.d.ts +39 -0
  94. package/build/codegen/emit/scope-file.js +166 -0
  95. package/build/codegen/emit/scope-file.js.map +1 -0
  96. package/build/codegen/emit/stream-route.d.ts +7 -0
  97. package/build/codegen/emit/stream-route.js +62 -0
  98. package/build/codegen/emit/stream-route.js.map +1 -0
  99. package/build/codegen/emit-errors.d.ts +1 -1
  100. package/build/codegen/emit-errors.integration.test.js +1 -1
  101. package/build/codegen/emit-errors.integration.test.js.map +1 -1
  102. package/build/codegen/emit-scope.d.ts +13 -30
  103. package/build/codegen/emit-scope.js +15 -844
  104. package/build/codegen/emit-scope.js.map +1 -1
  105. package/build/codegen/goldens.test.js +69 -0
  106. package/build/codegen/goldens.test.js.map +1 -0
  107. package/build/codegen/group-routes.d.ts +1 -1
  108. package/build/codegen/pipeline.d.ts +1 -1
  109. package/build/codegen/resolve-envelope.d.ts +1 -1
  110. package/build/codegen/targets/_shared/error-schemas.d.ts +1 -1
  111. package/build/codegen/targets/_shared/route-slots.d.ts +1 -1
  112. package/build/codegen/targets/_shared/target-run.d.ts +1 -1
  113. package/build/codegen/targets/kotlin/emit-route-kotlin.d.ts +1 -1
  114. package/build/codegen/targets/swift/emit-route-swift.d.ts +1 -1
  115. package/build/core/create-http-stream.d.ts +50 -0
  116. package/build/core/create-http-stream.js +108 -0
  117. package/build/core/create-http-stream.js.map +1 -0
  118. package/build/{create-http-stream.test.js → core/create-http-stream.test.js} +1 -1
  119. package/build/core/create-http-stream.test.js.map +1 -0
  120. package/build/core/create-http.d.ts +51 -0
  121. package/build/core/create-http.js +65 -0
  122. package/build/core/create-http.js.map +1 -0
  123. package/build/{create-http.test.js → core/create-http.test.js} +13 -4
  124. package/build/core/create-http.test.js.map +1 -0
  125. package/build/core/create-stream.d.ts +26 -0
  126. package/build/core/create-stream.js +80 -0
  127. package/build/core/create-stream.js.map +1 -0
  128. package/build/{create-stream.test.js → core/create-stream.test.js} +23 -28
  129. package/build/core/create-stream.test.js.map +1 -0
  130. package/build/core/create.d.ts +22 -0
  131. package/build/core/create.js +71 -0
  132. package/build/core/create.js.map +1 -0
  133. package/build/{create.test.js → core/create.test.js} +25 -46
  134. package/build/core/create.test.js.map +1 -0
  135. package/build/core/definition-site.d.ts +24 -0
  136. package/build/{stack-utils.js → core/definition-site.js} +20 -20
  137. package/build/core/definition-site.js.map +1 -0
  138. package/build/{stack-utils.test.js → core/definition-site.test.js} +12 -3
  139. package/build/core/definition-site.test.js.map +1 -0
  140. package/build/{errors.d.ts → core/errors.d.ts} +19 -8
  141. package/build/{errors.js → core/errors.js} +21 -26
  142. package/build/core/errors.js.map +1 -0
  143. package/build/core/errors.test.js.map +1 -0
  144. package/build/core/factory-options.test.js +82 -0
  145. package/build/core/factory-options.test.js.map +1 -0
  146. package/build/core/http-route.d.ts +13 -0
  147. package/build/core/http-route.js +54 -0
  148. package/build/core/http-route.js.map +1 -0
  149. package/build/core/internal.d.ts +72 -0
  150. package/build/core/internal.js +128 -0
  151. package/build/core/internal.js.map +1 -0
  152. package/build/{migration.test.js → core/migration.test.js} +17 -1
  153. package/build/core/migration.test.js.map +1 -0
  154. package/build/core/procedures.d.ts +143 -0
  155. package/build/core/procedures.js +64 -0
  156. package/build/core/procedures.js.map +1 -0
  157. package/build/{index.test.js → core/procedures.test.js} +14 -11
  158. package/build/core/procedures.test.js.map +1 -0
  159. package/build/core/types.d.ts +182 -0
  160. package/build/{schema → core}/types.js.map +1 -1
  161. package/build/exports.d.ts +31 -11
  162. package/build/exports.js +23 -8
  163. package/build/exports.js.map +1 -1
  164. package/build/schema/adapter.d.ts +35 -0
  165. package/build/schema/adapter.js +13 -0
  166. package/build/schema/adapter.js.map +1 -0
  167. package/build/schema/adapter.test.js +53 -0
  168. package/build/schema/adapter.test.js.map +1 -0
  169. package/build/schema/compile.d.ts +37 -0
  170. package/build/schema/compile.js +38 -0
  171. package/build/schema/compile.js.map +1 -0
  172. package/build/schema/compile.test.js +78 -0
  173. package/build/schema/compile.test.js.map +1 -0
  174. package/build/schema/compute-schema.d.ts +47 -37
  175. package/build/schema/compute-schema.js +86 -29
  176. package/build/schema/compute-schema.js.map +1 -1
  177. package/build/schema/compute-schema.test.js +158 -40
  178. package/build/schema/compute-schema.test.js.map +1 -1
  179. package/build/schema/json-schema.d.ts +17 -0
  180. package/build/schema/json-schema.js +2 -0
  181. package/build/schema/json-schema.js.map +1 -0
  182. package/build/schema/typebox.d.ts +11 -0
  183. package/build/schema/typebox.js +24 -0
  184. package/build/schema/typebox.js.map +1 -0
  185. package/build/schema/typebox.test.js +34 -0
  186. package/build/schema/typebox.test.js.map +1 -0
  187. package/build/server/context.d.ts +8 -0
  188. package/build/server/context.js +7 -0
  189. package/build/server/context.js.map +1 -0
  190. package/build/server/context.test.js +16 -0
  191. package/build/server/context.test.js.map +1 -0
  192. package/build/{doc-envelope.d.ts → server/doc-envelope.d.ts} +1 -1
  193. package/build/server/doc-envelope.js.map +1 -0
  194. package/build/server/doc-envelope.test.d.ts +1 -0
  195. package/build/server/doc-envelope.test.js.map +1 -0
  196. package/build/{implementations/http → server}/doc-registry.d.ts +7 -2
  197. package/build/{implementations/http → server}/doc-registry.js +9 -5
  198. package/build/server/doc-registry.js.map +1 -0
  199. package/build/server/doc-registry.test.d.ts +1 -0
  200. package/build/{implementations/http → server}/doc-registry.test.js +27 -24
  201. package/build/server/doc-registry.test.js.map +1 -0
  202. package/build/server/docs/docs.test.d.ts +1 -0
  203. package/build/server/docs/docs.test.js +237 -0
  204. package/build/server/docs/docs.test.js.map +1 -0
  205. package/build/{implementations/http/hono → server}/docs/http-doc.d.ts +2 -2
  206. package/build/{implementations/http/hono → server}/docs/http-doc.js +1 -1
  207. package/build/server/docs/http-doc.js.map +1 -0
  208. package/build/{implementations/http/hono → server}/docs/http-stream-doc.d.ts +2 -2
  209. package/build/{implementations/http/hono → server}/docs/http-stream-doc.js +1 -1
  210. package/build/server/docs/http-stream-doc.js.map +1 -0
  211. package/build/{implementations/http/hono → server}/docs/rpc-doc.d.ts +2 -2
  212. package/build/{implementations/http/hono → server}/docs/rpc-doc.js +1 -1
  213. package/build/server/docs/rpc-doc.js.map +1 -0
  214. package/build/{implementations/http/hono → server}/docs/stream-doc.d.ts +2 -2
  215. package/build/{implementations/http/hono → server}/docs/stream-doc.js +1 -1
  216. package/build/server/docs/stream-doc.js.map +1 -0
  217. package/build/server/errors/dispatch.d.ts +96 -0
  218. package/build/{implementations/http/error-dispatch.js → server/errors/dispatch.js} +20 -10
  219. package/build/server/errors/dispatch.js.map +1 -0
  220. package/build/server/errors/dispatch.test.d.ts +1 -0
  221. package/build/server/errors/dispatch.test.js +418 -0
  222. package/build/server/errors/dispatch.test.js.map +1 -0
  223. package/build/{implementations/http/error-taxonomy.d.ts → server/errors/taxonomy.d.ts} +8 -17
  224. package/build/{implementations/http/error-taxonomy.js → server/errors/taxonomy.js} +6 -15
  225. package/build/server/errors/taxonomy.js.map +1 -0
  226. package/build/server/errors/taxonomy.test.d.ts +1 -0
  227. package/build/{implementations/http/error-taxonomy.test.js → server/errors/taxonomy.test.js} +45 -39
  228. package/build/server/errors/taxonomy.test.js.map +1 -0
  229. package/build/server/index.d.ts +29 -0
  230. package/build/server/index.js +27 -0
  231. package/build/server/index.js.map +1 -0
  232. package/build/server/no-framework-imports.test.d.ts +1 -0
  233. package/build/server/no-framework-imports.test.js +40 -0
  234. package/build/server/no-framework-imports.test.js.map +1 -0
  235. package/build/{implementations/http/hono/path.d.ts → server/paths.d.ts} +2 -3
  236. package/build/{implementations/http/hono/path.js → server/paths.js} +1 -1
  237. package/build/server/paths.js.map +1 -0
  238. package/build/server/paths.test.d.ts +1 -0
  239. package/build/server/paths.test.js +111 -0
  240. package/build/server/paths.test.js.map +1 -0
  241. package/build/server/request/params.d.ts +29 -0
  242. package/build/server/request/params.js +43 -0
  243. package/build/server/request/params.js.map +1 -0
  244. package/build/server/request/params.test.d.ts +1 -0
  245. package/build/server/request/params.test.js +91 -0
  246. package/build/server/request/params.test.js.map +1 -0
  247. package/build/server/request/query.d.ts +9 -0
  248. package/build/server/request/query.js +22 -0
  249. package/build/server/request/query.js.map +1 -0
  250. package/build/server/request/query.test.d.ts +1 -0
  251. package/build/server/request/query.test.js +60 -0
  252. package/build/server/request/query.test.js.map +1 -0
  253. package/build/server/sse.d.ts +70 -0
  254. package/build/server/sse.js +94 -0
  255. package/build/server/sse.js.map +1 -0
  256. package/build/server/sse.test.d.ts +1 -0
  257. package/build/server/sse.test.js +98 -0
  258. package/build/server/sse.test.js.map +1 -0
  259. package/build/{implementations → server}/types.d.ts +17 -15
  260. package/build/{implementations → server}/types.js.map +1 -1
  261. package/docs/astro-adapter.md +8 -9
  262. package/docs/client-and-codegen.md +4 -4
  263. package/docs/client-error-handling.md +5 -5
  264. package/docs/codegen-kotlin.md +2 -3
  265. package/docs/codegen-swift.md +1 -2
  266. package/docs/core.md +135 -54
  267. package/docs/http-integrations.md +58 -6
  268. package/docs/migration-v8-to-v9.md +192 -0
  269. package/docs/plans/2026-06-09-v9-rewrite.md +130 -0
  270. package/docs/specs/2026-06-09-v9-rewrite-design.md +221 -0
  271. package/docs/streaming.md +12 -0
  272. package/package.json +23 -47
  273. package/src/{implementations/http → adapters}/astro/index.test.ts +2 -2
  274. package/src/adapters/hono/__fixtures__/parity-envelope.json +389 -0
  275. package/src/adapters/hono/envelope-parity.test.ts +126 -0
  276. package/src/{implementations/http → adapters}/hono/handlers/http-stream.test.ts +1 -1
  277. package/src/adapters/hono/handlers/http-stream.ts +73 -0
  278. package/src/{implementations/http → adapters}/hono/handlers/http.test.ts +1 -1
  279. package/src/adapters/hono/handlers/http.ts +70 -0
  280. package/src/{implementations/http → adapters}/hono/handlers/rpc.test.ts +2 -2
  281. package/src/adapters/hono/handlers/rpc.ts +39 -0
  282. package/src/{implementations/http → adapters}/hono/handlers/stream.test.ts +4 -3
  283. package/src/{implementations/http → adapters}/hono/handlers/stream.ts +19 -92
  284. package/src/{implementations/http → adapters}/hono/index.test.ts +14 -16
  285. package/src/{implementations/http → adapters}/hono/index.ts +35 -30
  286. package/src/{implementations/http → adapters/hono}/on-request-error.test.ts +3 -3
  287. package/src/adapters/hono/request.ts +28 -0
  288. package/src/{implementations/http → adapters/hono}/route-errors.test.ts +5 -5
  289. package/src/{implementations/http → adapters}/hono/types.ts +43 -20
  290. package/src/client/freeze.test.ts +41 -0
  291. package/src/client/typed-error-dispatch.test.ts +3 -3
  292. package/src/codegen/__fixtures__/make-envelope.ts +1 -1
  293. package/src/codegen/__fixtures__/models-envelope.json +310 -0
  294. package/src/codegen/__goldens__/MANIFEST.json +85 -0
  295. package/src/codegen/__goldens__/kotlin-default--models/Billing.kt +112 -0
  296. package/src/codegen/__goldens__/kotlin-default--models/BillingReports.kt +26 -0
  297. package/src/codegen/__goldens__/kotlin-default--models/Orders.kt +88 -0
  298. package/src/codegen/__goldens__/kotlin-default--users/Users.kt +189 -0
  299. package/src/codegen/__goldens__/swift-default--models/Billing.swift +97 -0
  300. package/src/codegen/__goldens__/swift-default--models/BillingReports.swift +20 -0
  301. package/src/codegen/__goldens__/swift-default--models/Orders.swift +81 -0
  302. package/src/codegen/__goldens__/swift-default--users/Users.swift +204 -0
  303. package/src/codegen/__goldens__/ts-default--models/_client.ts +1319 -0
  304. package/src/codegen/__goldens__/ts-default--models/_errors.ts +90 -0
  305. package/src/codegen/__goldens__/ts-default--models/_models.ts +10 -0
  306. package/src/codegen/__goldens__/ts-default--models/_types.ts +502 -0
  307. package/src/codegen/__goldens__/ts-default--models/billing-reports.ts +29 -0
  308. package/src/codegen/__goldens__/ts-default--models/billing.ts +67 -0
  309. package/src/codegen/__goldens__/ts-default--models/index.ts +48 -0
  310. package/src/codegen/__goldens__/ts-default--models/orders.ts +80 -0
  311. package/src/codegen/__goldens__/ts-default--users/_client.ts +1319 -0
  312. package/src/codegen/__goldens__/ts-default--users/_errors.ts +90 -0
  313. package/src/codegen/__goldens__/ts-default--users/_types.ts +502 -0
  314. package/src/codegen/__goldens__/ts-default--users/index.ts +38 -0
  315. package/src/codegen/__goldens__/ts-default--users/users.ts +169 -0
  316. package/src/codegen/__goldens__/ts-external-runtime--models/_errors.ts +90 -0
  317. package/src/codegen/__goldens__/ts-external-runtime--models/_models.ts +10 -0
  318. package/src/codegen/__goldens__/ts-external-runtime--models/billing-reports.ts +29 -0
  319. package/src/codegen/__goldens__/ts-external-runtime--models/billing.ts +67 -0
  320. package/src/codegen/__goldens__/ts-external-runtime--models/index.ts +48 -0
  321. package/src/codegen/__goldens__/ts-external-runtime--models/orders.ts +80 -0
  322. package/src/codegen/__goldens__/ts-external-runtime--users/_errors.ts +90 -0
  323. package/src/codegen/__goldens__/ts-external-runtime--users/index.ts +38 -0
  324. package/src/codegen/__goldens__/ts-external-runtime--users/users.ts +169 -0
  325. package/src/codegen/__goldens__/ts-flat--models/_client.ts +1319 -0
  326. package/src/codegen/__goldens__/ts-flat--models/_errors.ts +87 -0
  327. package/src/codegen/__goldens__/ts-flat--models/_models.ts +10 -0
  328. package/src/codegen/__goldens__/ts-flat--models/_types.ts +502 -0
  329. package/src/codegen/__goldens__/ts-flat--models/billing-reports.ts +28 -0
  330. package/src/codegen/__goldens__/ts-flat--models/billing.ts +51 -0
  331. package/src/codegen/__goldens__/ts-flat--models/index.ts +42 -0
  332. package/src/codegen/__goldens__/ts-flat--models/orders.ts +73 -0
  333. package/src/codegen/__goldens__/ts-flat--users/_client.ts +1319 -0
  334. package/src/codegen/__goldens__/ts-flat--users/_errors.ts +87 -0
  335. package/src/codegen/__goldens__/ts-flat--users/_types.ts +502 -0
  336. package/src/codegen/__goldens__/ts-flat--users/index.ts +34 -0
  337. package/src/codegen/__goldens__/ts-flat--users/users.ts +126 -0
  338. package/src/codegen/__goldens__/ts-no-share-models--models/_client.ts +1319 -0
  339. package/src/codegen/__goldens__/ts-no-share-models--models/_errors.ts +90 -0
  340. package/src/codegen/__goldens__/ts-no-share-models--models/_types.ts +502 -0
  341. package/src/codegen/__goldens__/ts-no-share-models--models/billing-reports.ts +29 -0
  342. package/src/codegen/__goldens__/ts-no-share-models--models/billing.ts +111 -0
  343. package/src/codegen/__goldens__/ts-no-share-models--models/index.ts +48 -0
  344. package/src/codegen/__goldens__/ts-no-share-models--models/orders.ts +112 -0
  345. package/src/codegen/__goldens__/ts-no-share-models--users/_client.ts +1319 -0
  346. package/src/codegen/__goldens__/ts-no-share-models--users/_errors.ts +90 -0
  347. package/src/codegen/__goldens__/ts-no-share-models--users/_types.ts +502 -0
  348. package/src/codegen/__goldens__/ts-no-share-models--users/index.ts +38 -0
  349. package/src/codegen/__goldens__/ts-no-share-models--users/users.ts +169 -0
  350. package/src/codegen/__goldens__/ts-shared-models-module--models/_client.ts +1319 -0
  351. package/src/codegen/__goldens__/ts-shared-models-module--models/_errors.ts +90 -0
  352. package/src/codegen/__goldens__/ts-shared-models-module--models/_models.ts +7 -0
  353. package/src/codegen/__goldens__/ts-shared-models-module--models/_types.ts +502 -0
  354. package/src/codegen/__goldens__/ts-shared-models-module--models/billing-reports.ts +29 -0
  355. package/src/codegen/__goldens__/ts-shared-models-module--models/billing.ts +67 -0
  356. package/src/codegen/__goldens__/ts-shared-models-module--models/index.ts +48 -0
  357. package/src/codegen/__goldens__/ts-shared-models-module--models/orders.ts +80 -0
  358. package/src/codegen/bin/cli.test.ts +13 -2
  359. package/src/codegen/bin/cli.ts +181 -144
  360. package/src/codegen/bin/flag-specs.test.ts +16 -1
  361. package/src/codegen/bin/flag-specs.ts +43 -31
  362. package/src/codegen/bundle-size.test.ts +1 -1
  363. package/src/codegen/collect-models.ts +1 -1
  364. package/src/codegen/e2e.test.ts +1 -1
  365. package/src/codegen/emit/api-route.ts +184 -0
  366. package/src/codegen/emit/context.ts +32 -0
  367. package/src/codegen/emit/declarations.ts +49 -0
  368. package/src/codegen/emit/format-types.ts +232 -0
  369. package/src/codegen/emit/http-stream-route.ts +162 -0
  370. package/src/codegen/emit/route-shared.ts +102 -0
  371. package/src/codegen/emit/rpc-route.ts +49 -0
  372. package/src/codegen/emit/scope-file.ts +226 -0
  373. package/src/codegen/emit/stream-route.ts +81 -0
  374. package/src/codegen/emit-errors.integration.test.ts +2 -2
  375. package/src/codegen/emit-errors.test.ts +1 -1
  376. package/src/codegen/emit-errors.ts +1 -1
  377. package/src/codegen/emit-scope.test.ts +2 -2
  378. package/src/codegen/emit-scope.ts +15 -1048
  379. package/src/codegen/goldens.test.ts +89 -0
  380. package/src/codegen/group-routes.test.ts +1 -1
  381. package/src/codegen/group-routes.ts +1 -1
  382. package/src/codegen/pipeline.test.ts +1 -1
  383. package/src/codegen/pipeline.ts +1 -1
  384. package/src/codegen/resolve-envelope.test.ts +1 -1
  385. package/src/codegen/resolve-envelope.ts +1 -1
  386. package/src/codegen/targets/_shared/error-schemas.test.ts +1 -1
  387. package/src/codegen/targets/_shared/error-schemas.ts +1 -1
  388. package/src/codegen/targets/_shared/route-slots.test.ts +1 -1
  389. package/src/codegen/targets/_shared/route-slots.ts +1 -1
  390. package/src/codegen/targets/_shared/target-run.ts +1 -1
  391. package/src/codegen/targets/kotlin/emit-route-kotlin.test.ts +1 -1
  392. package/src/codegen/targets/kotlin/emit-route-kotlin.ts +1 -1
  393. package/src/codegen/targets/kotlin/emit-scope-kotlin.test.ts +1 -1
  394. package/src/codegen/targets/swift/access-level.test.ts +1 -1
  395. package/src/codegen/targets/swift/emit-route-swift.test.ts +1 -1
  396. package/src/codegen/targets/swift/emit-route-swift.ts +1 -1
  397. package/src/codegen/targets/swift/emit-scope-swift.test.ts +1 -1
  398. package/src/codegen/targets/ts/shared-models.test.ts +1 -1
  399. package/src/{create-http-stream.test.ts → core/create-http-stream.test.ts} +1 -1
  400. package/src/core/create-http-stream.ts +207 -0
  401. package/src/{create-http.test.ts → core/create-http.test.ts} +15 -4
  402. package/src/core/create-http.ts +126 -0
  403. package/src/{create-stream.test.ts → core/create-stream.test.ts} +28 -31
  404. package/src/core/create-stream.ts +142 -0
  405. package/src/{create.test.ts → core/create.test.ts} +25 -57
  406. package/src/core/create.ts +121 -0
  407. package/src/{stack-utils.test.ts → core/definition-site.test.ts} +14 -3
  408. package/src/{stack-utils.ts → core/definition-site.ts} +20 -23
  409. package/src/{errors.test.ts → core/errors.test.ts} +1 -1
  410. package/src/{errors.ts → core/errors.ts} +30 -28
  411. package/src/core/factory-options.test.ts +112 -0
  412. package/src/core/http-route.ts +73 -0
  413. package/src/core/internal.ts +203 -0
  414. package/src/{migration.test.ts → core/migration.test.ts} +23 -1
  415. package/src/{index.test.ts → core/procedures.test.ts} +13 -11
  416. package/src/core/procedures.ts +75 -0
  417. package/src/core/types.ts +195 -0
  418. package/src/exports.ts +60 -11
  419. package/src/schema/adapter.test.ts +58 -0
  420. package/src/schema/adapter.ts +45 -0
  421. package/src/schema/compile.test.ts +95 -0
  422. package/src/schema/compile.ts +64 -0
  423. package/src/schema/compute-schema.test.ts +222 -41
  424. package/src/schema/compute-schema.ts +145 -71
  425. package/src/schema/json-schema.ts +21 -0
  426. package/src/schema/typebox.test.ts +40 -0
  427. package/src/schema/typebox.ts +27 -0
  428. package/src/server/context.test.ts +22 -0
  429. package/src/server/context.ts +18 -0
  430. package/src/{doc-envelope.test.ts → server/doc-envelope.test.ts} +2 -2
  431. package/src/{doc-envelope.ts → server/doc-envelope.ts} +1 -1
  432. package/src/{implementations/http → server}/doc-registry.test.ts +32 -26
  433. package/src/{implementations/http → server}/doc-registry.ts +11 -7
  434. package/src/server/docs/docs.test.ts +287 -0
  435. package/src/{implementations/http/hono → server}/docs/http-doc.ts +3 -3
  436. package/src/{implementations/http/hono → server}/docs/http-stream-doc.ts +3 -3
  437. package/src/{implementations/http/hono → server}/docs/rpc-doc.ts +3 -3
  438. package/src/{implementations/http/hono → server}/docs/stream-doc.ts +3 -3
  439. package/src/server/errors/dispatch.test.ts +450 -0
  440. package/src/server/errors/dispatch.ts +189 -0
  441. package/src/{implementations/http/error-taxonomy.test.ts → server/errors/taxonomy.test.ts} +45 -39
  442. package/src/{implementations/http/error-taxonomy.ts → server/errors/taxonomy.ts} +8 -17
  443. package/src/server/index.ts +29 -0
  444. package/src/server/no-framework-imports.test.ts +43 -0
  445. package/src/server/paths.test.ts +141 -0
  446. package/src/{implementations/http/hono/path.ts → server/paths.ts} +2 -13
  447. package/src/server/request/params.test.ts +143 -0
  448. package/src/server/request/params.ts +68 -0
  449. package/src/server/request/query.test.ts +70 -0
  450. package/src/server/request/query.ts +24 -0
  451. package/src/server/sse.test.ts +113 -0
  452. package/src/server/sse.ts +117 -0
  453. package/src/{implementations → server}/types.ts +17 -16
  454. package/build/create-http-stream.d.ts +0 -58
  455. package/build/create-http-stream.js +0 -122
  456. package/build/create-http-stream.js.map +0 -1
  457. package/build/create-http-stream.test.js.map +0 -1
  458. package/build/create-http.d.ts +0 -49
  459. package/build/create-http.js +0 -108
  460. package/build/create-http.js.map +0 -1
  461. package/build/create-http.test.js.map +0 -1
  462. package/build/create-stream.d.ts +0 -35
  463. package/build/create-stream.js +0 -123
  464. package/build/create-stream.js.map +0 -1
  465. package/build/create-stream.test.js.map +0 -1
  466. package/build/create.d.ts +0 -28
  467. package/build/create.js +0 -82
  468. package/build/create.js.map +0 -1
  469. package/build/create.test.js.map +0 -1
  470. package/build/doc-envelope.js.map +0 -1
  471. package/build/doc-envelope.test.js.map +0 -1
  472. package/build/errors.js.map +0 -1
  473. package/build/errors.test.js.map +0 -1
  474. package/build/implementations/http/astro/astro-context.js.map +0 -1
  475. package/build/implementations/http/astro/create-handler.js.map +0 -1
  476. package/build/implementations/http/astro/index.js.map +0 -1
  477. package/build/implementations/http/astro/index.test.js.map +0 -1
  478. package/build/implementations/http/astro/rewrite-request.js.map +0 -1
  479. package/build/implementations/http/doc-registry.js.map +0 -1
  480. package/build/implementations/http/doc-registry.test.js.map +0 -1
  481. package/build/implementations/http/error-dispatch.d.ts +0 -76
  482. package/build/implementations/http/error-dispatch.js.map +0 -1
  483. package/build/implementations/http/error-dispatch.test.js +0 -254
  484. package/build/implementations/http/error-dispatch.test.js.map +0 -1
  485. package/build/implementations/http/error-taxonomy.js.map +0 -1
  486. package/build/implementations/http/error-taxonomy.test.js.map +0 -1
  487. package/build/implementations/http/hono/docs/http-doc.js.map +0 -1
  488. package/build/implementations/http/hono/docs/http-stream-doc.js.map +0 -1
  489. package/build/implementations/http/hono/docs/rpc-doc.js.map +0 -1
  490. package/build/implementations/http/hono/docs/stream-doc.js.map +0 -1
  491. package/build/implementations/http/hono/handlers/http-stream.js +0 -123
  492. package/build/implementations/http/hono/handlers/http-stream.js.map +0 -1
  493. package/build/implementations/http/hono/handlers/http-stream.test.js.map +0 -1
  494. package/build/implementations/http/hono/handlers/http.js +0 -110
  495. package/build/implementations/http/hono/handlers/http.js.map +0 -1
  496. package/build/implementations/http/hono/handlers/http.test.js.map +0 -1
  497. package/build/implementations/http/hono/handlers/rpc.js +0 -32
  498. package/build/implementations/http/hono/handlers/rpc.js.map +0 -1
  499. package/build/implementations/http/hono/handlers/rpc.test.js.map +0 -1
  500. package/build/implementations/http/hono/handlers/stream.d.ts +0 -23
  501. package/build/implementations/http/hono/handlers/stream.js +0 -147
  502. package/build/implementations/http/hono/handlers/stream.js.map +0 -1
  503. package/build/implementations/http/hono/handlers/stream.test.js.map +0 -1
  504. package/build/implementations/http/hono/index.js.map +0 -1
  505. package/build/implementations/http/hono/index.test.js.map +0 -1
  506. package/build/implementations/http/hono/path.js.map +0 -1
  507. package/build/implementations/http/hono/path.test.js +0 -83
  508. package/build/implementations/http/hono/path.test.js.map +0 -1
  509. package/build/implementations/http/hono/types.d.ts +0 -51
  510. package/build/implementations/http/hono/types.js.map +0 -1
  511. package/build/implementations/http/on-request-error.test.js.map +0 -1
  512. package/build/implementations/http/route-errors.test.js.map +0 -1
  513. package/build/index.d.ts +0 -175
  514. package/build/index.js +0 -47
  515. package/build/index.js.map +0 -1
  516. package/build/index.test.js.map +0 -1
  517. package/build/migration.test.js.map +0 -1
  518. package/build/schema/extract-json-schema.d.ts +0 -2
  519. package/build/schema/extract-json-schema.js +0 -12
  520. package/build/schema/extract-json-schema.js.map +0 -1
  521. package/build/schema/extract-json-schema.test.js +0 -23
  522. package/build/schema/extract-json-schema.test.js.map +0 -1
  523. package/build/schema/parser.d.ts +0 -36
  524. package/build/schema/parser.js +0 -210
  525. package/build/schema/parser.js.map +0 -1
  526. package/build/schema/parser.test.js +0 -120
  527. package/build/schema/parser.test.js.map +0 -1
  528. package/build/schema/resolve-schema-lib.d.ts +0 -12
  529. package/build/schema/resolve-schema-lib.js +0 -11
  530. package/build/schema/resolve-schema-lib.js.map +0 -1
  531. package/build/schema/resolve-schema-lib.test.js +0 -17
  532. package/build/schema/resolve-schema-lib.test.js.map +0 -1
  533. package/build/schema/types.d.ts +0 -8
  534. package/build/schema/types.js +0 -2
  535. package/build/stack-utils.d.ts +0 -25
  536. package/build/stack-utils.js.map +0 -1
  537. package/build/stack-utils.test.js.map +0 -1
  538. package/build/types.d.ts +0 -142
  539. package/build/types.js +0 -2
  540. package/build/types.js.map +0 -1
  541. package/docs/decisions/2026-06-02-monorepo-split-evaluation.md +0 -80
  542. package/docs/handoffs/2026-06-08-dx-round2-declines.md +0 -45
  543. package/docs/handoffs/ajsc-named-type-collision.md +0 -134
  544. package/docs/handoffs/ajsc-named-type-support.md +0 -181
  545. package/docs/handoffs/shared-models-auto-resolve-response.md +0 -181
  546. package/docs/npm-workspaces-migration-plan.md +0 -611
  547. package/docs/superpowers/plans/2026-04-24-doc-registry-simplification.md +0 -886
  548. package/docs/superpowers/plans/2026-04-24-kotlin-codegen-target.md +0 -1265
  549. package/docs/superpowers/plans/2026-04-25-ajsc-v7-kotlin-polish.md +0 -1993
  550. package/docs/superpowers/plans/2026-04-29-safe-result-api.md +0 -2293
  551. package/docs/superpowers/plans/2026-05-07-astro-adapter.md +0 -1391
  552. package/docs/superpowers/plans/2026-05-08-create-http.md +0 -3355
  553. package/docs/superpowers/plans/2026-05-08-hono-app-builder-convergence.md +0 -3365
  554. package/docs/superpowers/plans/2026-06-05-dx-feedback-round.md +0 -1292
  555. package/docs/superpowers/plans/2026-06-06-shared-models-convention-and-diagnostics.md +0 -659
  556. package/docs/superpowers/plans/2026-06-08-codegen-dx-surfacing.md +0 -428
  557. package/docs/superpowers/specs/2026-04-24-kotlin-swift-codegen-design.md +0 -401
  558. package/docs/superpowers/specs/2026-04-25-ajsc-v7-kotlin-polish-design.md +0 -314
  559. package/docs/superpowers/specs/2026-04-25-swift-codegen-design.md +0 -264
  560. package/docs/superpowers/specs/2026-04-29-safe-result-api-design.md +0 -324
  561. package/docs/superpowers/specs/2026-05-07-astro-adapter-design.md +0 -252
  562. package/docs/superpowers/specs/2026-05-08-create-http-design.md +0 -409
  563. package/docs/superpowers/specs/2026-05-08-hono-app-builder-convergence-design.md +0 -411
  564. package/docs/superpowers/specs/2026-06-05-dx-feedback-round-design.md +0 -285
  565. package/docs/superpowers/specs/2026-06-08-dx-feedback-round-2-design.md +0 -376
  566. package/src/create-http-stream.ts +0 -191
  567. package/src/create-http.ts +0 -210
  568. package/src/create-stream.ts +0 -228
  569. package/src/create.ts +0 -172
  570. package/src/implementations/http/README.md +0 -390
  571. package/src/implementations/http/error-dispatch.test.ts +0 -283
  572. package/src/implementations/http/error-dispatch.ts +0 -176
  573. package/src/implementations/http/hono/handlers/http-stream.ts +0 -152
  574. package/src/implementations/http/hono/handlers/http.ts +0 -145
  575. package/src/implementations/http/hono/handlers/rpc.ts +0 -54
  576. package/src/implementations/http/hono/path.test.ts +0 -96
  577. package/src/index.ts +0 -101
  578. package/src/schema/extract-json-schema.test.ts +0 -25
  579. package/src/schema/extract-json-schema.ts +0 -15
  580. package/src/schema/parser.test.ts +0 -182
  581. package/src/schema/parser.ts +0 -265
  582. package/src/schema/resolve-schema-lib.test.ts +0 -19
  583. package/src/schema/resolve-schema-lib.ts +0 -29
  584. package/src/schema/types.ts +0 -20
  585. package/src/types.ts +0 -133
  586. /package/build/{implementations/http → adapters}/astro/astro-context.d.ts +0 -0
  587. /package/build/{implementations/http → adapters}/astro/astro-context.js +0 -0
  588. /package/build/{implementations/http → adapters}/astro/create-handler.d.ts +0 -0
  589. /package/build/{implementations/http → adapters}/astro/create-handler.js +0 -0
  590. /package/build/{implementations/http → adapters}/astro/index.d.ts +0 -0
  591. /package/build/{implementations/http → adapters}/astro/index.js +0 -0
  592. /package/build/{implementations/http → adapters}/astro/index.test.d.ts +0 -0
  593. /package/build/{implementations/http → adapters}/astro/rewrite-request.d.ts +0 -0
  594. /package/build/{implementations/http → adapters}/astro/rewrite-request.js +0 -0
  595. /package/build/{create-http-stream.test.d.ts → adapters/hono/envelope-parity.test.d.ts} +0 -0
  596. /package/build/{implementations/http → adapters}/hono/handlers/http-stream.test.d.ts +0 -0
  597. /package/build/{implementations/http → adapters}/hono/handlers/http.test.d.ts +0 -0
  598. /package/build/{implementations/http → adapters}/hono/handlers/rpc.test.d.ts +0 -0
  599. /package/build/{implementations/http → adapters}/hono/handlers/stream.test.d.ts +0 -0
  600. /package/build/{implementations/http → adapters}/hono/index.test.d.ts +0 -0
  601. /package/build/{implementations/http → adapters/hono}/on-request-error.test.d.ts +0 -0
  602. /package/build/{implementations/http → adapters/hono}/route-errors.test.d.ts +0 -0
  603. /package/build/{create-http.test.d.ts → client/freeze.test.d.ts} +0 -0
  604. /package/build/{create-stream.test.d.ts → codegen/goldens.test.d.ts} +0 -0
  605. /package/build/{create.test.d.ts → core/create-http-stream.test.d.ts} +0 -0
  606. /package/build/{doc-envelope.test.d.ts → core/create-http.test.d.ts} +0 -0
  607. /package/build/{errors.test.d.ts → core/create-stream.test.d.ts} +0 -0
  608. /package/build/{implementations/http/doc-registry.test.d.ts → core/create.test.d.ts} +0 -0
  609. /package/build/{implementations/http/error-dispatch.test.d.ts → core/definition-site.test.d.ts} +0 -0
  610. /package/build/{implementations/http/error-taxonomy.test.d.ts → core/errors.test.d.ts} +0 -0
  611. /package/build/{errors.test.js → core/errors.test.js} +0 -0
  612. /package/build/{implementations/http/hono/path.test.d.ts → core/factory-options.test.d.ts} +0 -0
  613. /package/build/{migration.test.d.ts → core/migration.test.d.ts} +0 -0
  614. /package/build/{index.test.d.ts → core/procedures.test.d.ts} +0 -0
  615. /package/build/{implementations/http/hono → core}/types.js +0 -0
  616. /package/build/schema/{extract-json-schema.test.d.ts → adapter.test.d.ts} +0 -0
  617. /package/build/schema/{parser.test.d.ts → compile.test.d.ts} +0 -0
  618. /package/build/schema/{resolve-schema-lib.test.d.ts → typebox.test.d.ts} +0 -0
  619. /package/build/{stack-utils.test.d.ts → server/context.test.d.ts} +0 -0
  620. /package/build/{doc-envelope.js → server/doc-envelope.js} +0 -0
  621. /package/build/{doc-envelope.test.js → server/doc-envelope.test.js} +0 -0
  622. /package/build/{implementations → server}/types.js +0 -0
  623. /package/src/{implementations/http → adapters}/astro/README.md +0 -0
  624. /package/src/{implementations/http → adapters}/astro/astro-context.ts +0 -0
  625. /package/src/{implementations/http → adapters}/astro/create-handler.ts +0 -0
  626. /package/src/{implementations/http → adapters}/astro/index.ts +0 -0
  627. /package/src/{implementations/http → adapters}/astro/rewrite-request.ts +0 -0
@@ -1,285 +0,0 @@
1
- # DX Feedback Round — Design
2
-
3
- **Date:** 2026-06-05
4
- **Status:** Approved (pending spec review)
5
- **Source:** Downstream-developer feedback from building the demo server (`src/server`), 5 findings.
6
-
7
- This round addresses five findings. Each section states the problem, the decision
8
- taken (with the alternatives considered), and the concrete shape of the change.
9
-
10
- Two items are cross-logged but **explicitly out of scope** this round (no scope creep):
11
- - The `ts-channels` codegen has the identical #3 (per-route inlining) defect — coordinating the two is follow-up.
12
- - #5 is cross-logged with `ts-channels #9` / `mvc-kit #2` — aligning all three scaffolders is follow-up.
13
-
14
- ---
15
-
16
- ## #1 — `ts-procedures-codegen --help` (MEDIUM)
17
-
18
- ### Problem
19
- `--help` falls through to the unknown-flag branch and exits non-zero with a
20
- misfiring did-you-mean suggestion (`Did you mean --url?`). There is no usage text
21
- anywhere in the CLI, so the rich flag surface (`--watch`, `--dry-run`,
22
- `--enum-style`, Kotlin/Swift targets, …) is undiscoverable without reading the
23
- built JS.
24
-
25
- ### Decision: structured flags as the single source of truth
26
- Replace the bare-string `KNOWN_FLAGS` array (`src/codegen/bin/cli.ts`) with a
27
- structured table — one entry per flag:
28
-
29
- ```ts
30
- interface FlagSpec {
31
- name: string // '--service-name'
32
- arg?: string // '<name>' (omitted for booleans)
33
- description: string // one line
34
- group: 'Source' | 'Output' | 'Codegen' | 'Targets' | 'Misc'
35
- default?: string // shown in help when meaningful, e.g. 'Api'
36
- }
37
- ```
38
-
39
- Derive **both** the validation set (the current `KNOWN_FLAGS` membership check)
40
- and the `--help` output from this table — adding a flag now documents it and the
41
- two can never drift. This directly resolves the CLAUDE.md warning that
42
- `KNOWN_FLAGS` must be hand-synced with `parseArgs`.
43
-
44
- Behaviour:
45
- - `--help` / `-h` / **bare invocation with no args** → print grouped usage
46
- (header, usage line, flags grouped by `group` with aligned descriptions and
47
- defaults) and **exit 0**.
48
- - Exclude `--help`/`-h` from the did-you-mean candidate set so the suggestion is
49
- never actively misleading.
50
-
51
- ### Alternatives considered
52
- - *Hand-written usage block, `KNOWN_FLAGS` stays bare strings.* Rejected:
53
- descriptions live apart from the flag list and drift — exactly the duplication
54
- the codebase warns against.
55
-
56
- ---
57
-
58
- ## #2 — Offline doc-envelope emission (LOW)
59
-
60
- ### Problem
61
- `--file <path>` codegen works, but nothing *produces* that file. The envelope
62
- only exists in-process via `builder.toDocEnvelope(cfg)`, so the regen loop forces
63
- either a running server (`codegen --url`) or a hand-written `JSON.stringify`
64
- script.
65
-
66
- ### Decision: exported helper only
67
- Ship a public, stable helper:
68
-
69
- ```ts
70
- export async function writeDocEnvelope(
71
- source: DocEnvelope | { toDocEnvelope(): DocEnvelope } | DocRegistry,
72
- path: string,
73
- ): Promise<void>
74
- ```
75
-
76
- - Accepts a built `HonoAppBuilder`/`DocRegistry` (anything with
77
- `toDocEnvelope()`) **or** a plain `DocEnvelope`.
78
- - Serializes pretty JSON to `path` (creating parent dirs).
79
- - Exported from a stable entry point and documented with a 3-line script:
80
- `build builder → writeDocEnvelope(builder, 'docs.json') → codegen --file docs.json`.
81
-
82
- No module-loading magic in the CLI — the user owns the tiny emit script, which is
83
- the most portable option and avoids a TS-loader dependency.
84
-
85
- ### Alternatives considered
86
- - *CLI `--builder <module>` that imports the user's builder in-process.* Rejected
87
- for now: importing user TS/ESM requires a loader (tsx/jiti) the package doesn't
88
- carry; pushes complexity and failure modes into the CLI.
89
-
90
- ---
91
-
92
- ## #3 — Shared types keyed on `$id` (MEDIUM)
93
-
94
- ### Problem
95
- Codegen inlines a fresh structural literal at every route site. The same `Message`
96
- entity is emitted ~4× under different names in one scope file
97
- (`GetThread.Response.Message`, `ListMessages.Response.RootType`,
98
- `SendMessage.Response.Body`, nested in `CreateThread`), none connected to the
99
- authored `@shared/schemas` `Message`. The copies type-check only because the
100
- shapes coincide today; a field change one codegen path misses drifts **silently**.
101
-
102
- ### Decision: hoist in codegen, leave the envelope untouched
103
-
104
- **Architectural call.** The `$id` already rides along on route subschemas in the
105
- envelope (verified: `Type.Object({…}, { $id, title })` survives `toDocEnvelope()`).
106
- Rather than change the envelope into a `$defs`/`$ref` table — which would break
107
- every already-serialized envelope and force the Kotlin/Swift pipelines to become
108
- `$ref`-aware — the **TS codegen** does a pre-pass over the inlined schemas. Result:
109
- zero envelope-shape change, zero impact on Kotlin/Swift, already-serialized
110
- envelopes keep working.
111
-
112
- #### Identity rule (correctness-critical)
113
- - Hoist **only** schemas carrying `$id`. `$id` is the dedup key (globally unique).
114
- - The generated type **name** comes from `title` (fallback: derive from `$id`).
115
- - Schemas with a `title` but no `$id` are **not** hoisted — they stay inlined
116
- exactly as today. Identity is *declared*, never inferred from structure.
117
- - If two subschemas share an `$id` but have **divergent bodies** → **hard error**
118
- at collect time (never silently pick one).
119
- - Schemas without `$id` produce byte-identical output to today, so existing
120
- consumers who never set `$id` see no change.
121
-
122
- #### Components
123
- 1. **`src/codegen/collect-models.ts`** (new) — walks every route's JSON schema
124
- recursively (all four route kinds: rpc/api/stream/http-stream slots), collects
125
- each subschema carrying `$id` into a model registry
126
- `{ $id, name, schema }`. Detects `$id` body-divergence collisions and throws.
127
- Also detects generated-name collisions between distinct `$id`s and disambiguates
128
- (`Message`, `Message2`, …) deterministically by first-seen order.
129
-
130
- 2. **`_models.ts`** (new emitted file) — the single hub:
131
- - For each model whose `$id` is in the `sharedTypesImport` map → emit a
132
- re-export: `export { Message } from '@shared/schemas'` (renamed to the
133
- model name if `name` differs).
134
- - Otherwise → emit a generated named type for the model schema (via ajsc).
135
- - Scopes **always** import from `_models.ts`, regardless of generated-vs-vendored.
136
- Flipping a type between the two is a one-line change here with zero scope churn.
137
-
138
- 3. **`emit-scope` change** — when a route's schema contains a hoisted `$id`
139
- subschema, the emitted route type **references** the model's name (import in
140
- flat mode, qualified `${Service}Models.Message` in namespace mode) instead of
141
- inlining the literal. Non-hoisted subschemas emit unchanged.
142
-
143
- 4. **`sharedTypesImport` config** (config-file only — a map doesn't fit a CLI flag):
144
- ```jsonc
145
- // ts-procedures-codegen.config.json
146
- "sharedTypesImport": {
147
- "https://schemas.example/message": { "module": "@shared/schemas", "name": "Message" }
148
- }
149
- ```
150
-
151
- 5. **Flag:** `--share-models` defaults **on** (consistent with the other
152
- good-by-default flags); `--no-share-models` opts out and restores pure
153
- per-route inlining. Added to the structured flag table from #1.
154
-
155
- #### Scope
156
- - **TS target only.** Kotlin/Swift continue to inline (they simply ignore `$id`
157
- metadata; the envelope is unchanged so their pipelines are untouched).
158
-
159
- #### Key technical risk — verify first
160
- The plan opens with a short **verification spike** before committing the emission
161
- mechanism:
162
- - **ajsc `$ref` behaviour (v7.2.0):** confirm how ajsc emits a `$ref` to a hoisted
163
- model — whether it produces a *reference* to a named type or inlines it. Per
164
- CLAUDE.md ajsc "resolves `$defs`/`$ref`", which may mean *inline*. Two paths:
165
- - *Preferred:* feed ajsc a route schema with the hoisted subschema replaced by
166
- `{ $ref: '#/$defs/Message' }` + a `$defs` table, and capture ajsc's named-type
167
- reference.
168
- - *Fallback:* emit model types standalone, then post-emit string-substitute the
169
- inlined literal with the model name — the same word-boundary patching approach
170
- `renameExtractedTypes` (`src/codegen/emit-types.ts`) already uses.
171
- - **Nested `$id` survival:** confirm `extractJsonSchema` preserves `$id` on
172
- **nested** subschemas (root `$id` is verified; nested is not). If stripped, fix
173
- extraction to preserve it — this is a prerequisite for hoisting nested entities
174
- like `Thread.messages: Message[]`.
175
-
176
- ### Alternatives considered
177
- - *Change the envelope to `$defs`/`$ref`.* Rejected: breaks serialized envelopes
178
- and forces all targets to become `$ref`-aware.
179
- - *Hoist into existing `_types.ts`.* Rejected: mixes domain entities with
180
- transport/runtime types and only works in self-contained mode.
181
- - *Per-scope first-declarer-owns.* Rejected: implicit cross-scope ordering/coupling.
182
- - *Auto-merge structurally-identical types.* Explicitly rejected per the feedback:
183
- coincidental shape-equality must not couple two scopes.
184
- - *Dedup by `$id` OR `title`.* Rejected: two unrelated schemas titled `Response`
185
- would wrongly merge — weak identity.
186
-
187
- ---
188
-
189
- ## #4 — Dynamic auth seam: function-valued headers (MEDIUM)
190
-
191
- ### Problem
192
- `config.headers` is a static `Record<string,string>` (`src/client/types.ts:202`),
193
- captured once at construction. The only dynamic seam is `onBeforeRequest`, which
194
- is discoverable only by reading `_client.ts`. A live bearer token set in `headers`
195
- silently goes stale after the next login (the easy-and-wrong path compiles).
196
-
197
- ### Decision: function-valued headers + signposting docs
198
- Make `headers` accept a function on both the client `defaults` and per-call options:
199
-
200
- ```ts
201
- type HeadersInit =
202
- | Record<string, string>
203
- | (() => Record<string, string> | Promise<Record<string, string>>)
204
- ```
205
-
206
- - `ProcedureCallDefaults.headers` and `ProcedureCallOptions.headers` adopt
207
- `HeadersInit` (`src/client/types.ts`).
208
- - Resolution happens on the **async request path**: `call.ts` / `stream.ts` await
209
- header resolution before building the `AdapterRequest`. The static-record path
210
- stays synchronous and fast (no behavioural change when headers is a plain object).
211
- - Merge semantics preserved: resolve defaults-headers and per-call-headers to
212
- records, then merge with per-call winning — `onBeforeRequest` still has final say.
213
- - The resolved `AdapterRequest.headers` remains a plain `Record<string,string>` —
214
- adapters are unaffected.
215
- - Regenerates into self-contained `_types.ts` / `_client.ts`; direct consumers get
216
- it from `ts-procedures/client`.
217
-
218
- **Docs:** add a short auth section (in the generated-client docs /
219
- `client-and-codegen.md`) presenting function-valued `headers` and `onBeforeRequest`
220
- as the canonical auth seams, with an explicit "a token in static `headers` goes
221
- stale — use a function or the hook" warning.
222
-
223
- ### Alternatives considered
224
- - *First-class `auth`/`bearer` option wiring the hook internally.* Not chosen this
225
- round — function-valued headers is the general-purpose primitive that also covers
226
- auth; a dedicated `auth` sugar can layer on later if wanted.
227
- - *Docs only.* Rejected: the easy-and-wrong static-headers path still compiles.
228
-
229
- ---
230
-
231
- ## #5 — Scaffolder file-naming knobs (LOW)
232
-
233
- ### Problem
234
- The scaffolder (the AI skill `agent_config/claude-code/skills/ts-procedures/SKILL.md`
235
- scaffold mode + `templates/*.md`, **not** a CLI) hardcodes `{{Name}}.procedure.ts` —
236
- PascalCase, one file per procedure — so every scaffold is renamed and folded into a
237
- per-scope file by hand.
238
-
239
- ### Decision: `fileNameStyle` + `groupBy` with auto-default
240
- Add two scaffold-mode args, surfaced in `SKILL.md` and consumed by the templates:
241
- - `fileNameStyle`: `'PascalCase'` (current) | `'kebab.concern'` (e.g.
242
- `get-user.procedure.ts`).
243
- - `groupBy`: `'flat'` (current, CWD) | `'scope'` (e.g. `<scope>/get-user.procedure.ts`,
244
- scope taken from the procedure config).
245
-
246
- When **unspecified**, the skill infers the default by inspecting the target
247
- directory's existing convention (per-scope kebab files vs flat Pascal) — so a
248
- scaffold matches the consumer's layout with no manual rename pass.
249
-
250
- Changes:
251
- - `SKILL.md` scaffold-mode section: document the two args + the auto-default
252
- inference instruction; update the "Files Generated" table to show the computed
253
- filename.
254
- - `templates/*.md`: replace the hardcoded `{{Name}}.<concern>.ts` output with a
255
- computed `{{fileName}}` placeholder and an optional `{{scope}}/` directory
256
- prefix, plus the placeholder-derivation notes (`{{kebab}}` already exists).
257
-
258
- ### Alternatives considered
259
- - *Add knobs, explicit-only (no auto-detect).* Rejected: misses the zero-config
260
- match to the repo layout, which is the actual friction.
261
- - *Defer.* Rejected by scope decision (all three findings actioned this round).
262
-
263
- ---
264
-
265
- ## Testing strategy
266
- - **#1:** unit test `--help`/`-h`/bare → exit 0 + usage contains every flag from the
267
- table; `--help` not in did-you-mean candidates; unknown flag still errors.
268
- - **#2:** unit test `writeDocEnvelope` with (a) a plain envelope, (b) a builder-like
269
- object — round-trips to disk and re-loads via `resolveEnvelope --file`.
270
- - **#3:** spike verification first; then fixture-based tests on the canonical
271
- `__fixtures__/users-envelope.json` extended with `$id`'d schemas — assert one
272
- `_models.ts` declaration per `$id`, route types reference it, `$id` collision
273
- throws, `sharedTypesImport` produces re-exports, and `--no-share-models` restores
274
- byte-identical legacy output. Add a no-`$id` regression asserting unchanged output.
275
- - **#4:** unit test function-valued `headers` (sync + async) resolves per request,
276
- merges with per-call, and `onBeforeRequest` still overrides; static-record path
277
- unchanged.
278
- - **#5:** verify by scaffolding under each `fileNameStyle`/`groupBy` combination and
279
- the auto-default inference (skill-level / template review).
280
-
281
- ## Out of scope (follow-up)
282
- - `ts-channels` codegen shared-types fix (identical to #3).
283
- - Cross-scaffolder alignment with `ts-channels #9` / `mvc-kit #2` (#5).
284
- - A first-class `auth`/`bearer` client option (sugar over #4).
285
- - Kotlin/Swift shared-model emission.
@@ -1,376 +0,0 @@
1
- # DX Feedback Round 2 — Design
2
-
3
- **Date:** 2026-06-08
4
- **Status:** Triage approved. **Workstream A ✅ shipped** (2026-06-08, commits `0d53329`,
5
- `ec9ea04`, `ced4b86`, `8ad5e9f` on `master`; 1077 tests green). **Round closed 2026-06-08:**
6
- B → docs (already authorable), C documented as a boundary (no API), D high-value docs shipped
7
- (#6 recipe, #10/#8 prose; #7/#12/CORS deferred), E ✗ dropped, #13/#11 decline reply sent. Net:
8
- Workstream A was the only *code* — everything else was already-shipped capability to document or
9
- out of our lane. See roadmap for the per-item landing.
10
- **Source:** Downstream-developer feedback from building the demo server (`src/server`)
11
- and wiring its generated client into the client layer. Findings #6–#15 + three
12
- cross-cutting notes. (Findings #1–#5 were resolved in 8.4.0 / 8.5.0 — see
13
- `2026-06-05-dx-feedback-round-design.md`.)
14
-
15
- This round is deliberately **not** a single spec. The findings sort by *boundary*,
16
- not by the severity label the downstream assigned, and the boundary determines how
17
- each is treated:
18
-
19
- 1. **"The capability exists; it's just invisible in the generated output."** Pure
20
- codegen *surfacing*. Unambiguously ours, low-risk, high-value. → **Workstream A**.
21
- 2. **"The typed error survives our throw but is flattened downstream."** Cross-cutting
22
- error-handling design; the actual blocker lives in mvc-kit, not here. → **Workstream
23
- C** (a design brainstorm, not a code sprint).
24
- 3. **"Our codegen faithfully reflects a schema the downstream authored
25
- inconsistently."** Mislabeled as a codegen gap; fixing it would mean codegen
26
- *overriding* the author's contract. → **Declined** (documented, no code).
27
-
28
- The guiding principle for this round: ts-procedures owns the *transport, schema, and
29
- codegen surface*. It does **not** own the consumer's state-management ergonomics
30
- (mvc-kit) or the consumer's schema-authoring choices. We surface and document what we
31
- own; we decline to absorb the rest, with written rationale so downstream isn't left
32
- guessing.
33
-
34
- ---
35
-
36
- ## Disposition at a glance
37
-
38
- | # | Severity (theirs) | Disposition | Workstream |
39
- |---|---|---|---|
40
- | #8 | HIGH | **Do** — surface the per-call options bag (`signal`/`timeout`) in the generated per-route artifact | A |
41
- | #9 | MEDIUM | **Do** — emit `void` for input-less routes instead of `unknown` | A |
42
- | #10 | MEDIUM | **Do (shallow half)** — surface declared `Errors` + `isApiError` guard on the throwing path | A |
43
- | #15 | LOW | **Do** — emit a per-scope client interface (`MessagesClient`, …) | A |
44
- | #6 | LOW | **Document** — already authorable today (omit `res.body` → `Promise<void>`, no model, 204); fold into D | D |
45
- | #14 | HIGH | **Brainstorm** — error-mapping seam; hold the line that flattening is mvc-kit's gap | C |
46
- | #10 (deep half) | MEDIUM | **Brainstorm** — why `.safe()` is avoided ties into C | C |
47
- | #7 | LOW | **Document** — `Type.Record` can't be `$id`-shared | D |
48
- | #12 | LOW | **Document** — form Model owns a value type + `toBody()`; partly inherent | D |
49
- | basePath/CORS | MEDIUM | **Document** (+ optional dev-time warning) | D |
50
- | correlation-id | MEDIUM | **Dropped** — `onRequestStart` + function-form `factoryContext` + client headers already cover it; no new API earns its surface | — |
51
- | #13 | MEDIUM | **Decline code** (document convention) — downstream schema inconsistency | — |
52
- | #11 | LOW | **Decline code** (document the `as Partial` bridge) — relationship not in JSON Schema | — |
53
-
54
- ---
55
-
56
- ## Workstream A — Codegen DX surfacing (ready to plan)
57
-
58
- Four findings, one cohesive batch: every one is "the capability already exists (or is
59
- a one-line detection); make it visible/ergonomic in the generated output." All touch
60
- `emit-scope.ts` / `emit-types.ts` / `emit-index.ts` and the client `types.ts`. Lowest
61
- risk, highest signal. **This is the one workstream ready for a TDD plan now.**
62
-
63
- ### A.1 — #9: input-less routes are typed `unknown`, forcing a noise `({})` arg
64
-
65
- **Problem.** Routes with no pathParams/query/body bind their first param as `unknown`
66
- (`emit-scope.ts:536,679` — `paramsTypeName` defaults to `'unknown'`). Because the type
67
- is `unknown` (not `void`/optional), consumers must pass a placeholder `({})` —
68
- `api.auth.Me({})`, `api.users.ListUsers({})` — and worse, `unknown` accepts garbage
69
- (`api.auth.Me({ nonsense: 1 })` type-checks). Six call sites across five files; the
70
- single most-repeated papercut.
71
-
72
- **Decision.** When a route has **zero present input channels**, emit `void` as the
73
- param type — `bindCallable<void, T>` / `bindCallableTyped<void, T, E>` — so the callable
74
- is `(params: void, options?) => Promise<T>`. Detection point is trivial: after
75
- `presentChannels` is built (`emit-scope.ts:514-520` API, `664-670` http-stream; the RPC
76
- `refs['Params'] ?? 'unknown'` path at `398-399,408-409`), branch on
77
- `presentChannels.length === 0`. **No runtime change** — only the emitted `TParams` type
78
- arg changes.
79
-
80
- > **Verified (2026-06-08, `tsc --strict`).** A required parameter of type `void` *can*
81
- > be omitted at the call site: `Me()` compiles, `Me({})` / `Me({ nonsense: 1 })` are type
82
- > errors. So the existing `(params, options?)` shape needs no restructuring — `void` for
83
- > `TParams` is sufficient. The one wart: passing options on a no-input route reads
84
- > `Me(undefined, { signal })`. That awkwardness is rare (no-input + explicit signal) and
85
- > is the price of a **uniform** call shape across all routes; the plan should call it out
86
- > but I recommend keeping the uniform shape rather than special-casing no-input routes to
87
- > `(options?) =>` (which would need runtime rewiring and break shape uniformity).
88
-
89
- **Alternatives considered.** *Optional `params?: SomeEmptyObject`.* Rejected: still
90
- accepts a stray object. *Drop params entirely → `(options?) => T` for no-input routes.*
91
- Rejected: nicer `Me({ signal })` ergonomics but breaks call-shape uniformity and needs
92
- runtime rewiring of the binder. *Leave as-is, document `({})`.* Rejected: highest-frequency
93
- papercut, and the type fix is a single branch per route kind.
94
-
95
- ### A.2 — #8: the per-call `AbortSignal`/`timeout` seam is invisible in the generated artifact
96
-
97
- **Problem.** Every bound callable is `(params, options?: ProcedureCallOptions) =>
98
- Promise<T>` and `ProcedureCallOptions` carries `signal`/`timeout`/`headers`/`basePath`/
99
- `meta` (`client/types.ts:218-230`). But the generated per-route namespace surfaces only
100
- the first param (`.Req`); the JSDoc emitted at `emit-scope.ts:412-419,640-644,822-833`
101
- never mentions the second `options` argument. The result is a real downstream
102
- mis-modeling: resources state as fact "there's no per-call AbortSignal seam" and **every
103
- resource omits cancellation**, violating their own P7. The seam was reachable all along
104
- — just not through the surface they treat as the contract.
105
-
106
- **Decision.** Surface the options bag in the generated per-route artifact — **JSDoc is
107
- the primary, highest-value half**:
108
- 1. Add a JSDoc line to every bound callable mentioning `options?.signal` / `options?.timeout`
109
- (and that the second arg is `ProcedureCallOptions`), so it shows on hover *at the call
110
- site* — which is where the consumer reads the contract.
111
- 2. *(Open — likely drop.)* A per-route `export type Options = ProcedureCallOptions` alias
112
- next to `.Req`. On reflection this is probably **over-production**: the alias is
113
- byte-identical on every route, so it adds generated noise (`SendMessage.Options` is just
114
- `ProcedureCallOptions`) without naming anything route-specific. The plan should default to
115
- JSDoc-only and emit the alias only if a concrete discoverability gap remains after the
116
- JSDoc lands. Readable generated output > a redundant alias on every route.
117
-
118
- **Alternatives considered.** *Docs only (external).* Rejected: the whole finding is that the
119
- capability is undiscoverable *from the generated namespace* — the place a consumer treats as
120
- the contract. The JSDoc has to land in the generated code. *Per-route alias as the headline
121
- fix.* Demoted (see above) — repetition isn't discoverability.
122
-
123
- ### A.3 — #10 (shallow half): declared typed errors are invisible on the throwing path
124
-
125
- **Problem.** A route that declares `errors` already gets `export type Errors = <union>`
126
- emitted (`emit-scope.ts:363-384`), but the `ETyped` union is wired **only** to `.safe()`
127
- (`client/types.ts:293-298`); the throwing callable resolves to plain `Promise<TResponse>`.
128
- Since the data layer uses the throwing form (per its P2/P5), TypeScript gives no signal
129
- about which typed errors a call can throw — the declared `Conflict` on `CreateUser`/
130
- `UpdateUser` is never handled. The route-level taxonomy codegen computes is dead weight
131
- on the common path.
132
-
133
- **Decision (shallow half only).** *Grounding correction (2026-06-08):* the per-route
134
- `Errors` type is **already emitted and reachable** — `Users.GetUser.Errors` (namespace) /
135
- `GetUserErrors` (flat), confirmed in `emit-scope.test.ts:740,750`. And because the generated
136
- error classes are **real exported classes**, `if (e instanceof Api.Errors.Conflict)` already
137
- works on the throwing path. So #10 is **not** a missing-type problem — it's pure
138
- *discoverability*. That moves the bulk of #10 into the docs pass (Workstream D); the only
139
- code in Workstream A is a one-line JSDoc breadcrumb:
140
- 1. The per-route callable's JSDoc names its declared errors and points at `Scope.Route.Errors`
141
- + "narrow with `instanceof` on the throwing path, or use `.safe()` for a `Result`."
142
- 2. *(Optional, deferred.)* A generic `isProcedureError(e, Cls)` guard in the client runtime
143
- (sugar over `instanceof`; classes are tagged `__tsProceduresTyped` at `client/call.ts:170`).
144
- Held back unless docs + the breadcrumb prove insufficient — `instanceof` already works, so
145
- a guard is ergonomics, not capability. Don't over-produce.
146
-
147
- **The deep half is Workstream C.** *Why* the data layer avoids `.safe()` at all (it
148
- defeats mvc-kit's async tracking) is the cross-cutting error-handling question — not
149
- solvable by a codegen tweak. Do **not** conflate the two.
150
-
151
- **Alternatives considered.** *Attach `ETyped` to the throwing signature too.* Rejected:
152
- TS can't express "throws X" — the union would have to ride a phantom return position and
153
- would mislead. A guard + reachable `Errors` type is the honest surface.
154
-
155
- ### A.4 — #15: no per-scope client interface, forcing `as unknown as typeof api` casts
156
-
157
- **Problem.** The aggregate client is the only exported shape, so a Resource that uses one
158
- scope must type its dependency as the whole `typeof api`
159
- (`users.resource.ts:26`), and every test fake force-casts a two-method partial
160
- `as unknown as typeof api` (`thread-messages.resource.test.ts:16`,
161
- `threads.resource.test.ts:30`). The double-cast is the tell that the injected port is far
162
- wider than the dependency used — defeating the type safety the generated client exists to
163
- provide, exactly at the DI seam.
164
-
165
- **Decision.** Emit a per-scope client interface alongside the aggregate — the bound
166
- callables for that scope as a named type — produced where `emit-index.ts` already
167
- assembles the scope namespaces. A Resource then types its dependency as the narrow port
168
- (`constructor(client: MessagesClient = api.messages)`) and a fake implements just that
169
- interface with no cast.
170
-
171
- > **Derive from the existing factory return type (verified 2026-06-08).** `emit-index.ts`
172
- > already emits `create${Service}Bindings(client)` returning `{ users: …, posts: … }` and
173
- > already uses `ReturnType<typeof ${factoryName}>` (lines 128,145). So the per-scope client
174
- > type is just an indexed access into that — **zero duplication, reuses the pattern already in
175
- > the file**:
176
- > ```ts
177
- > export type ${Service}Client = ReturnType<typeof create${Service}Bindings>
178
- > export type UsersClient = ${Service}Client['users'] // one per scope
179
- > ```
180
- > This does **not** collide with the `Api.Users` *type* namespace (Params/Response/…) — that
181
- > namespace is the route data types; `UsersClient` is the callable bundle. The `*Client` suffix
182
- > cleanly distinguishes the two. No parallel scheme, no hand-written interface — the types fall
183
- > out of the factory the file already emits. A Resource then writes
184
- > `constructor(client: UsersClient = api.users)` and a two-method fake satisfies `UsersClient`
185
- > with no cast.
186
-
187
- **Alternatives considered.** *Hand-written per-Resource port type (consumer side).*
188
- Works, but every consumer reinvents (and mis-scopes) it — codegen already knows the exact
189
- scope shape, so it should emit it.
190
-
191
- ---
192
-
193
- ## Workstream B — No-content response (#6) — → DOCS (folded into D, 2026-06-08)
194
-
195
- **Problem.** The downstream invented phantom bodies for naturally-empty responses:
196
- `Logout` carries a dead-weight `LogoutResponse` model (call site discards it), `LeaveThread`
197
- returns a `ThreadList` it didn't need. They believed an empty response couldn't be declared.
198
-
199
- **Vetted (2026-06-08) — no-content is ALREADY fully authorable today; #6 is a docs gap, not a
200
- build.** The capability exists end-to-end:
201
- - **Authoring type:** `schema.res` and `res.body` are both optional (`src/types.ts:100-102`).
202
- An author can omit `res` (or `res.body`) entirely.
203
- - **Handler type:** `HttpReturn<TRes>` falls through to `void` when there's no body
204
- (`src/create-http.ts:17-21`), so the handler types as `Promise<void>` and `return undefined`
205
- type-checks (`create-http.test.ts:110-119`).
206
- - **Envelope:** omitting `res.body` emits an absent/empty `res` (`hono/docs/http-doc.ts:14-27`).
207
- - **Codegen:** empty `res` → `client.bindCallable<void, void>`, **no** response model
208
- (`emit-scope.test.ts:1253-1256`).
209
- - **Runtime:** `undefined` return → clean 204 (`hono/handlers/http.ts:126`;
210
- `http.test.ts:70-82`).
211
-
212
- So `CreateHttp('Logout', { /* no res */ }, async () => undefined)` already gives a
213
- `Promise<void>` client, no model, 204 at runtime — exactly what they wanted. They invented a
214
- body only because the "declare nothing" path **wasn't visible**. Same shape as Workstream E:
215
- capability exists; the gap is discoverability. **No new API (no `Type.Void()` marker, no
216
- status-only seam needed).** Fold a short recipe into the **D** docs pass: "for 204 / no-content,
217
- omit `schema.res.body` and return `undefined` — you get `Promise<void>` and no generated model."
218
-
219
- **Out of scope.** Consumer-side response *projection* (the `CreateThread` / `Pick`-one-field
220
- observation) — the procedure's call, not the call site's; the Resource caches the full
221
- projection anyway. Flagged in the feedback as "no action needed."
222
-
223
- ---
224
-
225
- ## Workstream C — Error-handling, cross-cutting (brainstorm, not a build)
226
-
227
- **Covers:** #14 (fire-and-forget command error dead-ends) + the "typed errors flattened
228
- by mvc-kit async tracking" cross-cutting note + the *deep* half of #10 (why `.safe()` is
229
- avoided).
230
-
231
- **Boundary stance (to defend in the brainstorm).** ts-procedures **already does its job
232
- correctly**: it throws `instanceof`-catchable typed classes. The error "dead-ends"
233
- because *mvc-kit's async tracker* collapses the typed instance into a closed 10-value
234
- `code` union and keeps the original only on a private field. That is mvc-kit's gap — the
235
- feedback itself says "either side alone closes the gap." We should not bolt on features to
236
- paper over a downstream flattening problem.
237
-
238
- **The one genuinely ts-procedures-side ask:** a client-level error **transform/map** seam.
239
- We already have a global `onError` hook, but it is an *observer* (`client/hooks.ts`,
240
- `client/types.ts:118-122`) — it cannot transform or swallow. The open question is whether
241
- a transform hook (or `errorMap` on `createClient`) earns its surface area, or whether
242
- mvc-kit surfacing `cause` on its `TaskState` is the cleaner fix that makes our side a
243
- no-op.
244
-
245
- **Why a brainstorm, not a plan.** This is a real API-design decision with overlap against
246
- existing hooks and the error taxonomy. It needs the requirements explored before any code.
247
- The brainstorm should produce either (a) a spec for a transform seam with a crisp boundary,
248
- or (b) a documented decision that the fix is mvc-kit's, with our side limited to documenting
249
- the existing typed-throw contract.
250
-
251
- ---
252
-
253
- ## Workstream D — Documentation pass (small session)
254
-
255
- - **#7 — `Type.Record` opaque to `$id` sharing.** Accurate and inherent: a bare record has
256
- no `$id`, so its value type is always re-inlined. Not a blocker (it rides inside an
257
- `$id`-bearing parent like `Thread`). **Document** the limitation in the shared-models
258
- docs; do **not** teach `--share-models` to synthesize names for records (scope creep for
259
- a non-blocker).
260
- - **basePath/CORS footgun.** A cross-origin absolute `basePath` issues a plain
261
- cross-origin `fetch`; with no server CORS the first call fails with a generic `Failed to
262
- fetch` and nothing on the client surface hints why. **Document** a one-line callout in the
263
- codegen `basePath` docs (browser + different origin ⇒ server CORS *or* same-origin dev
264
- proxy). *Optional small code add:* the fetch adapter detects a cross-origin `basePath` and
265
- emits a clearer dev-time error than the browser's generic one. The HTTP↔WS auth-seam
266
- asymmetry half is cross-logged with ts-channels; our half is the basePath/CORS doc.
267
- - **#12 — form shape not derivable.** The feedback admits this is "partly inherent (draft
268
- state ≠ request body)." **Document** that form Models are expected to own a value type +
269
- `toBody()` mapper (`LocationFormModel.toBody` as the recommended shape). No codegen change.
270
-
271
- ---
272
-
273
- ## Workstream E — Framework-level correlation / request id — ✗ DROPPED (2026-06-08)
274
-
275
- **Was:** promoted from the declined body-level `clientId` ask to a transport-level Hono-builder
276
- feature (a `requestId` config block: read-or-mint an id, echo it on the response header for all
277
- four kinds, expose `ctx.requestId`).
278
-
279
- **Why dropped (brainstorm outcome).** The existing primitives already deliver the *entire*
280
- feature with no new API — a dedicated block buys only ~6 saved lines and a named knob, which
281
- doesn't earn permanent public surface on a transport library. Concretely:
282
- - **Read + mint + echo (server):** `onRequestStart(c)` gets the Hono `Context` and can
283
- `c.req.header(name) ?? crypto.randomUUID()`, `c.set(...)`, and `c.header(name, id)` — the
284
- header set there persists onto every response, streams included.
285
- - **Expose on `ctx` (server):** `factoryContext` already accepts a **function form** receiving
286
- `c` (`hono/types.ts:42-49`), so `factoryContext: (c) => ({ ...base, requestId: c.get('requestId') })`
287
- gives handlers `ctx.requestId` with zero framework change.
288
- - **Outbound id (client):** function-valued `headers` (shipped 8.5.0) + `onBeforeRequest`
289
- already attach/rotate a correlation header per request.
290
-
291
- The gap was **discoverability, not capability** — and the user's call (2026-06-08) was that even
292
- a docs recipe isn't warranted: the two-seam composition is a normal use of documented primitives.
293
- Same boundary as #13/#11 — the library does not grow surface to wrap what it already enables.
294
-
295
- **Decision: no feature, no recipe, no spec.** This consumes the original downstream
296
- correlation-id ask (transport-level) in full.
297
-
298
- ---
299
-
300
- ## Declined (no code change) — with rationale
301
-
302
- These are the "don't bend" set. We faithfully reflect the author's schema; we don't
303
- override it.
304
-
305
- - **#13 — Create/Update spell "empty" two ways (`undefined` vs `string | null`).** Root
306
- cause is downstream schema inconsistency: `Type.Optional(Type.String())` on create vs
307
- `Type.Optional(Type.Union([String, Null]))` on update (`user.schema.ts:40` vs `:53`;
308
- `location.schema.ts:28` vs `:39`). Codegen reflects exactly what they wrote. The
309
- suggested "codegen emits a per-body normalizer / widens the optional type" would have us
310
- **override the author's contract** — the opposite of our job. **Decline the code change.**
311
- Remedy is for the author to make the pair consistent (both `string | undefined` *or* both
312
- `string | null`). We will document the convention so consumers stop rediscovering it
313
- field-by-field.
314
-
315
- - **#11 — `UpdateXBody extends Partial<Entity>`.** `collect-models.ts:61-94` does zero
316
- structural inference and *cannot*: the Partial/subset relationship isn't expressible in
317
- the JSON Schema we receive (it's flattened to an independent `$id` model before codegen
318
- sees it). We can't synthesize a relationship the wire format doesn't carry. **Decline the
319
- codegen change.** We will document the intended `as Partial<Entity>` bridge so consumers
320
- don't each reinvent (and mis-scope) it.
321
-
322
- ---
323
-
324
- ## Session roadmap (sequencing)
325
-
326
- 1. ✅ **DONE — Workstream A** (`docs/superpowers/plans/2026-06-08-codegen-dx-surfacing.md`)
327
- — #9, #8, #10-breadcrumb, #15 shipped 2026-06-08. **Process lesson (encode in future
328
- plans):** changing `__fixtures__/users-envelope.json` is **cross-target** — it feeds every
329
- target's e2e/integration tests, so a new route regenerates the Kotlin *and* Swift goldens
330
- (`UPDATE_GOLDENS=1`), not just a 5→6 count bump. Pre-flight for any plan that inlines test
331
- code should also grep the referenced test fixtures (the real `emit-index.test.ts` scopes are
332
- `users`/`billing`/`adminUsers`, not the assumed `users`/`posts`), not only production-file
333
- line anchors.
334
- 2. ✗ **DROPPED — Workstream E** (correlation id). Brainstormed 2026-06-08; existing primitives
335
- cover it, no new API or docs warranted. See the Workstream E section above.
336
- 3. → **DOCS — Workstream B** (no-content #6). Vetted 2026-06-08; already authorable today,
337
- folded into D as a recipe. See the Workstream B section above.
338
- 4. ✅ **DONE — Workstream C** (error-handling). Documented as a boundary, **no new API**:
339
- `docs/client-error-handling.md` §11 "Design note: typed errors and your state layer" — we throw
340
- instanceof-catchable classes; a state layer that flattens them is the state layer's concern;
341
- `onError` is an observer by design.
342
- 5. ✅ **DONE (high-value) — Workstream D docs.** #6 no-content recipe →
343
- `docs/http-integrations.md`; #10/#8 discoverability prose → `docs/client-error-handling.md`
344
- §3a. **Deferred (optional follow-up):** #7 (`$id`/Record), #12 (form `toBody`), basePath/CORS
345
- + cross-origin adapter warning — lower-value, pull in on request.
346
- 6. ✅ **DONE — Decline reply** → `docs/handoffs/2026-06-08-dx-round2-declines.md` (#13/#11 with
347
- the schema-authoring remedies, framed remedy-first).
348
-
349
- **Net for the round:** Workstream A (codegen DX) was the only *code*; everything else is either
350
- already-shipped capability that needs documenting (B, #6) or out of our lane (E dropped; #13/#11
351
- declined; C likely mvc-kit's). That's the boundary holding — the library surfaces and documents
352
- what it owns, and doesn't grow to wrap what it already enables.
353
-
354
- Each workstream produces working, testable software (or a documented decision) on its own —
355
- no cross-workstream barriers.
356
-
357
- ## Testing strategy (per workstream, filled in at plan time)
358
-
359
- - **A:** fixture tests asserting (#9) input-less routes emit `void` and reject a stray
360
- object; (#8) generated JSDoc + `Options` alias surface the options bag; (#10) `Errors`
361
- type reachable at call site + `is${Service}Error` guard narrows; (#15) per-scope
362
- interface satisfied by a two-method fake with no cast. Regression: routes *with* input
363
- unchanged.
364
- - **B:** no code — the no-content path already has coverage (`create-http.test.ts:110-119`,
365
- `http.test.ts:70-82`, `emit-scope.test.ts:1253-1256`); the docs recipe should compile against
366
- those existing behaviors.
367
- - **C:** determined by the brainstorm outcome.
368
- - **D:** doc review only.
369
-
370
- ## Out of scope (this round)
371
-
372
- - mvc-kit's half of the error-flattening fix (surface `cause` on `TaskState`) — their repo.
373
- - ts-channels WS correlation / `?token=` auth-seam asymmetry — cross-logged there.
374
- - Consumer-side response projection (`Pick`-one-field) — procedure's call, not ours.
375
- - A first-class body-level `clientId`/idempotency convention — superseded by Workstream E's
376
- transport-level approach.