ts-procedures 8.6.0 → 9.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (630) hide show
  1. package/CHANGELOG.md +540 -0
  2. package/README.md +166 -101
  3. package/agent_config/claude-code/.claude-plugin/plugin.json +1 -1
  4. package/agent_config/claude-code/agents/ts-procedures-architect.md +11 -10
  5. package/agent_config/claude-code/skills/ts-procedures/SKILL.md +25 -12
  6. package/agent_config/claude-code/skills/ts-procedures/anti-patterns.md +10 -12
  7. package/agent_config/claude-code/skills/ts-procedures/api-reference.md +141 -45
  8. package/agent_config/claude-code/skills/ts-procedures/checklist.md +7 -6
  9. package/agent_config/claude-code/skills/ts-procedures/patterns.md +45 -6
  10. package/agent_config/claude-code/skills/ts-procedures/templates/client.md +1 -1
  11. package/agent_config/claude-code/skills/ts-procedures/templates/hono.md +1 -1
  12. package/agent_config/copilot/copilot-instructions.md +50 -33
  13. package/agent_config/cursor/cursorrules +50 -33
  14. package/build/adapters/astro/astro-context.js.map +1 -0
  15. package/build/adapters/astro/create-handler.js.map +1 -0
  16. package/build/adapters/astro/index.js.map +1 -0
  17. package/build/{implementations/http → adapters}/astro/index.test.js +1 -1
  18. package/build/adapters/astro/index.test.js.map +1 -0
  19. package/build/adapters/astro/rewrite-request.js.map +1 -0
  20. package/build/adapters/hono/envelope-parity.test.js +98 -0
  21. package/build/adapters/hono/envelope-parity.test.js.map +1 -0
  22. package/build/{implementations/http → adapters}/hono/handlers/http-stream.d.ts +1 -1
  23. package/build/adapters/hono/handlers/http-stream.js +55 -0
  24. package/build/adapters/hono/handlers/http-stream.js.map +1 -0
  25. package/build/{implementations/http → adapters}/hono/handlers/http-stream.test.js +1 -1
  26. package/build/adapters/hono/handlers/http-stream.test.js.map +1 -0
  27. package/build/{implementations/http → adapters}/hono/handlers/http.d.ts +1 -1
  28. package/build/adapters/hono/handlers/http.js +50 -0
  29. package/build/adapters/hono/handlers/http.js.map +1 -0
  30. package/build/{implementations/http → adapters}/hono/handlers/http.test.js +1 -1
  31. package/build/adapters/hono/handlers/http.test.js.map +1 -0
  32. package/build/{implementations/http → adapters}/hono/handlers/rpc.d.ts +2 -2
  33. package/build/adapters/hono/handlers/rpc.js +23 -0
  34. package/build/adapters/hono/handlers/rpc.js.map +1 -0
  35. package/build/{implementations/http → adapters}/hono/handlers/rpc.test.js +1 -1
  36. package/build/adapters/hono/handlers/rpc.test.js.map +1 -0
  37. package/build/adapters/hono/handlers/stream.d.ts +12 -0
  38. package/build/adapters/hono/handlers/stream.js +89 -0
  39. package/build/adapters/hono/handlers/stream.js.map +1 -0
  40. package/build/{implementations/http → adapters}/hono/handlers/stream.test.js +3 -2
  41. package/build/adapters/hono/handlers/stream.test.js.map +1 -0
  42. package/build/{implementations/http → adapters}/hono/index.d.ts +24 -12
  43. package/build/{implementations/http → adapters}/hono/index.js +19 -8
  44. package/build/adapters/hono/index.js.map +1 -0
  45. package/build/{implementations/http → adapters}/hono/index.test.js +2 -4
  46. package/build/adapters/hono/index.test.js.map +1 -0
  47. package/build/{implementations/http → adapters/hono}/on-request-error.test.js +2 -2
  48. package/build/adapters/hono/on-request-error.test.js.map +1 -0
  49. package/build/adapters/hono/request.d.ts +7 -0
  50. package/build/adapters/hono/request.js +22 -0
  51. package/build/adapters/hono/request.js.map +1 -0
  52. package/build/{implementations/http → adapters/hono}/route-errors.test.js +4 -4
  53. package/build/adapters/hono/route-errors.test.js.map +1 -0
  54. package/build/adapters/hono/types.d.ts +55 -0
  55. package/build/adapters/hono/types.js +19 -0
  56. package/build/adapters/hono/types.js.map +1 -0
  57. package/build/client/freeze.test.js +39 -0
  58. package/build/client/freeze.test.js.map +1 -0
  59. package/build/client/typed-error-dispatch.test.js +2 -2
  60. package/build/client/typed-error-dispatch.test.js.map +1 -1
  61. package/build/codegen/__fixtures__/make-envelope.d.ts +1 -1
  62. package/build/codegen/bin/cli.d.ts +5 -0
  63. package/build/codegen/bin/cli.js +139 -182
  64. package/build/codegen/bin/cli.js.map +1 -1
  65. package/build/codegen/bin/cli.test.js +12 -2
  66. package/build/codegen/bin/cli.test.js.map +1 -1
  67. package/build/codegen/bin/flag-specs.d.ts +9 -0
  68. package/build/codegen/bin/flag-specs.js +33 -31
  69. package/build/codegen/bin/flag-specs.js.map +1 -1
  70. package/build/codegen/bin/flag-specs.test.js +14 -1
  71. package/build/codegen/bin/flag-specs.test.js.map +1 -1
  72. package/build/codegen/collect-models.d.ts +1 -1
  73. package/build/codegen/emit/api-route.d.ts +8 -0
  74. package/build/codegen/emit/api-route.js +156 -0
  75. package/build/codegen/emit/api-route.js.map +1 -0
  76. package/build/codegen/emit/context.d.ts +30 -0
  77. package/build/codegen/emit/context.js +2 -0
  78. package/build/codegen/emit/context.js.map +1 -0
  79. package/build/codegen/emit/declarations.d.ts +24 -0
  80. package/build/codegen/emit/declarations.js +48 -0
  81. package/build/codegen/emit/declarations.js.map +1 -0
  82. package/build/codegen/emit/format-types.d.ts +61 -0
  83. package/build/codegen/emit/format-types.js +188 -0
  84. package/build/codegen/emit/format-types.js.map +1 -0
  85. package/build/codegen/emit/http-stream-route.d.ts +7 -0
  86. package/build/codegen/emit/http-stream-route.js +138 -0
  87. package/build/codegen/emit/http-stream-route.js.map +1 -0
  88. package/build/codegen/emit/route-shared.d.ts +37 -0
  89. package/build/codegen/emit/route-shared.js +88 -0
  90. package/build/codegen/emit/route-shared.js.map +1 -0
  91. package/build/codegen/emit/rpc-route.d.ts +7 -0
  92. package/build/codegen/emit/rpc-route.js +37 -0
  93. package/build/codegen/emit/rpc-route.js.map +1 -0
  94. package/build/codegen/emit/scope-file.d.ts +39 -0
  95. package/build/codegen/emit/scope-file.js +166 -0
  96. package/build/codegen/emit/scope-file.js.map +1 -0
  97. package/build/codegen/emit/stream-route.d.ts +7 -0
  98. package/build/codegen/emit/stream-route.js +62 -0
  99. package/build/codegen/emit/stream-route.js.map +1 -0
  100. package/build/codegen/emit-errors.d.ts +1 -1
  101. package/build/codegen/emit-errors.integration.test.js +1 -1
  102. package/build/codegen/emit-errors.integration.test.js.map +1 -1
  103. package/build/codegen/emit-scope.d.ts +13 -30
  104. package/build/codegen/emit-scope.js +15 -844
  105. package/build/codegen/emit-scope.js.map +1 -1
  106. package/build/codegen/emit-scope.test.js +67 -0
  107. package/build/codegen/emit-scope.test.js.map +1 -1
  108. package/build/codegen/goldens.test.js +69 -0
  109. package/build/codegen/goldens.test.js.map +1 -0
  110. package/build/codegen/group-routes.d.ts +1 -1
  111. package/build/codegen/pipeline.d.ts +1 -1
  112. package/build/codegen/resolve-envelope.d.ts +1 -1
  113. package/build/codegen/targets/_shared/error-schemas.d.ts +1 -1
  114. package/build/codegen/targets/_shared/route-slots.d.ts +1 -1
  115. package/build/codegen/targets/_shared/target-run.d.ts +1 -1
  116. package/build/codegen/targets/kotlin/emit-route-kotlin.d.ts +1 -1
  117. package/build/codegen/targets/swift/emit-route-swift.d.ts +1 -1
  118. package/build/core/create-http-stream.d.ts +50 -0
  119. package/build/core/create-http-stream.js +108 -0
  120. package/build/core/create-http-stream.js.map +1 -0
  121. package/build/{create-http-stream.test.js → core/create-http-stream.test.js} +1 -1
  122. package/build/core/create-http-stream.test.js.map +1 -0
  123. package/build/core/create-http.d.ts +51 -0
  124. package/build/core/create-http.js +65 -0
  125. package/build/core/create-http.js.map +1 -0
  126. package/build/{create-http.test.js → core/create-http.test.js} +27 -4
  127. package/build/core/create-http.test.js.map +1 -0
  128. package/build/core/create-stream.d.ts +26 -0
  129. package/build/core/create-stream.js +80 -0
  130. package/build/core/create-stream.js.map +1 -0
  131. package/build/{create-stream.test.js → core/create-stream.test.js} +23 -28
  132. package/build/core/create-stream.test.js.map +1 -0
  133. package/build/core/create.d.ts +22 -0
  134. package/build/core/create.js +71 -0
  135. package/build/core/create.js.map +1 -0
  136. package/build/{create.test.js → core/create.test.js} +25 -46
  137. package/build/core/create.test.js.map +1 -0
  138. package/build/core/definition-site.d.ts +24 -0
  139. package/build/{stack-utils.js → core/definition-site.js} +20 -20
  140. package/build/core/definition-site.js.map +1 -0
  141. package/build/{stack-utils.test.js → core/definition-site.test.js} +12 -3
  142. package/build/core/definition-site.test.js.map +1 -0
  143. package/build/{errors.d.ts → core/errors.d.ts} +19 -8
  144. package/build/{errors.js → core/errors.js} +21 -26
  145. package/build/core/errors.js.map +1 -0
  146. package/build/core/errors.test.js.map +1 -0
  147. package/build/core/factory-options.test.js +82 -0
  148. package/build/core/factory-options.test.js.map +1 -0
  149. package/build/core/http-route.d.ts +13 -0
  150. package/build/core/http-route.js +54 -0
  151. package/build/core/http-route.js.map +1 -0
  152. package/build/core/internal.d.ts +72 -0
  153. package/build/core/internal.js +128 -0
  154. package/build/core/internal.js.map +1 -0
  155. package/build/{migration.test.js → core/migration.test.js} +17 -1
  156. package/build/core/migration.test.js.map +1 -0
  157. package/build/core/procedures.d.ts +143 -0
  158. package/build/core/procedures.js +64 -0
  159. package/build/core/procedures.js.map +1 -0
  160. package/build/{index.test.js → core/procedures.test.js} +14 -11
  161. package/build/core/procedures.test.js.map +1 -0
  162. package/build/core/types.d.ts +183 -0
  163. package/build/{schema → core}/types.js.map +1 -1
  164. package/build/exports.d.ts +31 -11
  165. package/build/exports.js +23 -8
  166. package/build/exports.js.map +1 -1
  167. package/build/schema/adapter.d.ts +35 -0
  168. package/build/schema/adapter.js +13 -0
  169. package/build/schema/adapter.js.map +1 -0
  170. package/build/schema/adapter.test.js +53 -0
  171. package/build/schema/adapter.test.js.map +1 -0
  172. package/build/schema/compile.d.ts +37 -0
  173. package/build/schema/compile.js +38 -0
  174. package/build/schema/compile.js.map +1 -0
  175. package/build/schema/compile.test.js +78 -0
  176. package/build/schema/compile.test.js.map +1 -0
  177. package/build/schema/compute-schema.d.ts +47 -37
  178. package/build/schema/compute-schema.js +86 -29
  179. package/build/schema/compute-schema.js.map +1 -1
  180. package/build/schema/compute-schema.test.js +158 -40
  181. package/build/schema/compute-schema.test.js.map +1 -1
  182. package/build/schema/json-schema.d.ts +17 -0
  183. package/build/schema/json-schema.js +2 -0
  184. package/build/schema/json-schema.js.map +1 -0
  185. package/build/schema/typebox.d.ts +11 -0
  186. package/build/schema/typebox.js +24 -0
  187. package/build/schema/typebox.js.map +1 -0
  188. package/build/schema/typebox.test.js +34 -0
  189. package/build/schema/typebox.test.js.map +1 -0
  190. package/build/server/context.d.ts +8 -0
  191. package/build/server/context.js +7 -0
  192. package/build/server/context.js.map +1 -0
  193. package/build/server/context.test.js +16 -0
  194. package/build/server/context.test.js.map +1 -0
  195. package/build/{doc-envelope.d.ts → server/doc-envelope.d.ts} +1 -1
  196. package/build/server/doc-envelope.js.map +1 -0
  197. package/build/server/doc-envelope.test.d.ts +1 -0
  198. package/build/server/doc-envelope.test.js.map +1 -0
  199. package/build/{implementations/http → server}/doc-registry.d.ts +7 -2
  200. package/build/{implementations/http → server}/doc-registry.js +9 -5
  201. package/build/server/doc-registry.js.map +1 -0
  202. package/build/server/doc-registry.test.d.ts +1 -0
  203. package/build/{implementations/http → server}/doc-registry.test.js +27 -24
  204. package/build/server/doc-registry.test.js.map +1 -0
  205. package/build/server/docs/docs.test.d.ts +1 -0
  206. package/build/server/docs/docs.test.js +237 -0
  207. package/build/server/docs/docs.test.js.map +1 -0
  208. package/build/{implementations/http/hono → server}/docs/http-doc.d.ts +2 -2
  209. package/build/{implementations/http/hono → server}/docs/http-doc.js +1 -1
  210. package/build/server/docs/http-doc.js.map +1 -0
  211. package/build/{implementations/http/hono → server}/docs/http-stream-doc.d.ts +2 -2
  212. package/build/{implementations/http/hono → server}/docs/http-stream-doc.js +1 -1
  213. package/build/server/docs/http-stream-doc.js.map +1 -0
  214. package/build/{implementations/http/hono → server}/docs/rpc-doc.d.ts +2 -2
  215. package/build/{implementations/http/hono → server}/docs/rpc-doc.js +1 -1
  216. package/build/server/docs/rpc-doc.js.map +1 -0
  217. package/build/{implementations/http/hono → server}/docs/stream-doc.d.ts +2 -2
  218. package/build/{implementations/http/hono → server}/docs/stream-doc.js +1 -1
  219. package/build/server/docs/stream-doc.js.map +1 -0
  220. package/build/server/errors/dispatch.d.ts +96 -0
  221. package/build/{implementations/http/error-dispatch.js → server/errors/dispatch.js} +20 -10
  222. package/build/server/errors/dispatch.js.map +1 -0
  223. package/build/server/errors/dispatch.test.d.ts +1 -0
  224. package/build/server/errors/dispatch.test.js +418 -0
  225. package/build/server/errors/dispatch.test.js.map +1 -0
  226. package/build/{implementations/http/error-taxonomy.d.ts → server/errors/taxonomy.d.ts} +8 -17
  227. package/build/{implementations/http/error-taxonomy.js → server/errors/taxonomy.js} +6 -15
  228. package/build/server/errors/taxonomy.js.map +1 -0
  229. package/build/server/errors/taxonomy.test.d.ts +1 -0
  230. package/build/{implementations/http/error-taxonomy.test.js → server/errors/taxonomy.test.js} +45 -39
  231. package/build/server/errors/taxonomy.test.js.map +1 -0
  232. package/build/server/index.d.ts +29 -0
  233. package/build/server/index.js +27 -0
  234. package/build/server/index.js.map +1 -0
  235. package/build/server/no-framework-imports.test.d.ts +1 -0
  236. package/build/server/no-framework-imports.test.js +40 -0
  237. package/build/server/no-framework-imports.test.js.map +1 -0
  238. package/build/{implementations/http/hono/path.d.ts → server/paths.d.ts} +2 -3
  239. package/build/{implementations/http/hono/path.js → server/paths.js} +1 -1
  240. package/build/server/paths.js.map +1 -0
  241. package/build/server/paths.test.d.ts +1 -0
  242. package/build/server/paths.test.js +111 -0
  243. package/build/server/paths.test.js.map +1 -0
  244. package/build/server/request/params.d.ts +29 -0
  245. package/build/server/request/params.js +43 -0
  246. package/build/server/request/params.js.map +1 -0
  247. package/build/server/request/params.test.d.ts +1 -0
  248. package/build/server/request/params.test.js +91 -0
  249. package/build/server/request/params.test.js.map +1 -0
  250. package/build/server/request/query.d.ts +9 -0
  251. package/build/server/request/query.js +22 -0
  252. package/build/server/request/query.js.map +1 -0
  253. package/build/server/request/query.test.d.ts +1 -0
  254. package/build/server/request/query.test.js +60 -0
  255. package/build/server/request/query.test.js.map +1 -0
  256. package/build/server/sse.d.ts +70 -0
  257. package/build/server/sse.js +94 -0
  258. package/build/server/sse.js.map +1 -0
  259. package/build/server/sse.test.d.ts +1 -0
  260. package/build/server/sse.test.js +98 -0
  261. package/build/server/sse.test.js.map +1 -0
  262. package/build/{implementations → server}/types.d.ts +17 -15
  263. package/build/{implementations → server}/types.js.map +1 -1
  264. package/docs/astro-adapter.md +8 -9
  265. package/docs/client-and-codegen.md +10 -4
  266. package/docs/client-error-handling.md +5 -5
  267. package/docs/codegen-kotlin.md +2 -3
  268. package/docs/codegen-swift.md +1 -2
  269. package/docs/core.md +135 -54
  270. package/docs/http-integrations.md +58 -6
  271. package/docs/migration-v8-to-v9.md +200 -0
  272. package/docs/plans/2026-06-09-v9-rewrite.md +130 -0
  273. package/docs/specs/2026-06-09-v9-rewrite-design.md +221 -0
  274. package/docs/streaming.md +12 -0
  275. package/package.json +25 -48
  276. package/src/{implementations/http → adapters}/astro/index.test.ts +2 -2
  277. package/src/adapters/hono/__fixtures__/parity-envelope.json +389 -0
  278. package/src/adapters/hono/envelope-parity.test.ts +126 -0
  279. package/src/{implementations/http → adapters}/hono/handlers/http-stream.test.ts +1 -1
  280. package/src/adapters/hono/handlers/http-stream.ts +73 -0
  281. package/src/{implementations/http → adapters}/hono/handlers/http.test.ts +1 -1
  282. package/src/adapters/hono/handlers/http.ts +70 -0
  283. package/src/{implementations/http → adapters}/hono/handlers/rpc.test.ts +2 -2
  284. package/src/adapters/hono/handlers/rpc.ts +39 -0
  285. package/src/{implementations/http → adapters}/hono/handlers/stream.test.ts +4 -3
  286. package/src/{implementations/http → adapters}/hono/handlers/stream.ts +19 -92
  287. package/src/{implementations/http → adapters}/hono/index.test.ts +14 -16
  288. package/src/{implementations/http → adapters}/hono/index.ts +35 -30
  289. package/src/{implementations/http → adapters/hono}/on-request-error.test.ts +3 -3
  290. package/src/adapters/hono/request.ts +28 -0
  291. package/src/{implementations/http → adapters/hono}/route-errors.test.ts +5 -5
  292. package/src/{implementations/http → adapters}/hono/types.ts +43 -20
  293. package/src/client/freeze.test.ts +41 -0
  294. package/src/client/typed-error-dispatch.test.ts +3 -3
  295. package/src/codegen/__fixtures__/make-envelope.ts +1 -1
  296. package/src/codegen/__fixtures__/models-envelope.json +310 -0
  297. package/src/codegen/__goldens__/MANIFEST.json +85 -0
  298. package/src/codegen/__goldens__/kotlin-default--models/Billing.kt +112 -0
  299. package/src/codegen/__goldens__/kotlin-default--models/BillingReports.kt +26 -0
  300. package/src/codegen/__goldens__/kotlin-default--models/Orders.kt +88 -0
  301. package/src/codegen/__goldens__/kotlin-default--users/Users.kt +189 -0
  302. package/src/codegen/__goldens__/swift-default--models/Billing.swift +97 -0
  303. package/src/codegen/__goldens__/swift-default--models/BillingReports.swift +20 -0
  304. package/src/codegen/__goldens__/swift-default--models/Orders.swift +81 -0
  305. package/src/codegen/__goldens__/swift-default--users/Users.swift +204 -0
  306. package/src/codegen/__goldens__/ts-default--models/_client.ts +1319 -0
  307. package/src/codegen/__goldens__/ts-default--models/_errors.ts +90 -0
  308. package/src/codegen/__goldens__/ts-default--models/_models.ts +10 -0
  309. package/src/codegen/__goldens__/ts-default--models/_types.ts +502 -0
  310. package/src/codegen/__goldens__/ts-default--models/billing-reports.ts +29 -0
  311. package/src/codegen/__goldens__/ts-default--models/billing.ts +67 -0
  312. package/src/codegen/__goldens__/ts-default--models/index.ts +48 -0
  313. package/src/codegen/__goldens__/ts-default--models/orders.ts +80 -0
  314. package/src/codegen/__goldens__/ts-default--users/_client.ts +1319 -0
  315. package/src/codegen/__goldens__/ts-default--users/_errors.ts +90 -0
  316. package/src/codegen/__goldens__/ts-default--users/_types.ts +502 -0
  317. package/src/codegen/__goldens__/ts-default--users/index.ts +38 -0
  318. package/src/codegen/__goldens__/ts-default--users/users.ts +169 -0
  319. package/src/codegen/__goldens__/ts-external-runtime--models/_errors.ts +90 -0
  320. package/src/codegen/__goldens__/ts-external-runtime--models/_models.ts +10 -0
  321. package/src/codegen/__goldens__/ts-external-runtime--models/billing-reports.ts +29 -0
  322. package/src/codegen/__goldens__/ts-external-runtime--models/billing.ts +67 -0
  323. package/src/codegen/__goldens__/ts-external-runtime--models/index.ts +48 -0
  324. package/src/codegen/__goldens__/ts-external-runtime--models/orders.ts +80 -0
  325. package/src/codegen/__goldens__/ts-external-runtime--users/_errors.ts +90 -0
  326. package/src/codegen/__goldens__/ts-external-runtime--users/index.ts +38 -0
  327. package/src/codegen/__goldens__/ts-external-runtime--users/users.ts +169 -0
  328. package/src/codegen/__goldens__/ts-flat--models/_client.ts +1319 -0
  329. package/src/codegen/__goldens__/ts-flat--models/_errors.ts +87 -0
  330. package/src/codegen/__goldens__/ts-flat--models/_models.ts +10 -0
  331. package/src/codegen/__goldens__/ts-flat--models/_types.ts +502 -0
  332. package/src/codegen/__goldens__/ts-flat--models/billing-reports.ts +28 -0
  333. package/src/codegen/__goldens__/ts-flat--models/billing.ts +51 -0
  334. package/src/codegen/__goldens__/ts-flat--models/index.ts +42 -0
  335. package/src/codegen/__goldens__/ts-flat--models/orders.ts +73 -0
  336. package/src/codegen/__goldens__/ts-flat--users/_client.ts +1319 -0
  337. package/src/codegen/__goldens__/ts-flat--users/_errors.ts +87 -0
  338. package/src/codegen/__goldens__/ts-flat--users/_types.ts +502 -0
  339. package/src/codegen/__goldens__/ts-flat--users/index.ts +34 -0
  340. package/src/codegen/__goldens__/ts-flat--users/users.ts +126 -0
  341. package/src/codegen/__goldens__/ts-no-share-models--models/_client.ts +1319 -0
  342. package/src/codegen/__goldens__/ts-no-share-models--models/_errors.ts +90 -0
  343. package/src/codegen/__goldens__/ts-no-share-models--models/_types.ts +502 -0
  344. package/src/codegen/__goldens__/ts-no-share-models--models/billing-reports.ts +29 -0
  345. package/src/codegen/__goldens__/ts-no-share-models--models/billing.ts +111 -0
  346. package/src/codegen/__goldens__/ts-no-share-models--models/index.ts +48 -0
  347. package/src/codegen/__goldens__/ts-no-share-models--models/orders.ts +112 -0
  348. package/src/codegen/__goldens__/ts-no-share-models--users/_client.ts +1319 -0
  349. package/src/codegen/__goldens__/ts-no-share-models--users/_errors.ts +90 -0
  350. package/src/codegen/__goldens__/ts-no-share-models--users/_types.ts +502 -0
  351. package/src/codegen/__goldens__/ts-no-share-models--users/index.ts +38 -0
  352. package/src/codegen/__goldens__/ts-no-share-models--users/users.ts +169 -0
  353. package/src/codegen/__goldens__/ts-shared-models-module--models/_client.ts +1319 -0
  354. package/src/codegen/__goldens__/ts-shared-models-module--models/_errors.ts +90 -0
  355. package/src/codegen/__goldens__/ts-shared-models-module--models/_models.ts +7 -0
  356. package/src/codegen/__goldens__/ts-shared-models-module--models/_types.ts +502 -0
  357. package/src/codegen/__goldens__/ts-shared-models-module--models/billing-reports.ts +29 -0
  358. package/src/codegen/__goldens__/ts-shared-models-module--models/billing.ts +67 -0
  359. package/src/codegen/__goldens__/ts-shared-models-module--models/index.ts +48 -0
  360. package/src/codegen/__goldens__/ts-shared-models-module--models/orders.ts +80 -0
  361. package/src/codegen/bin/cli.test.ts +13 -2
  362. package/src/codegen/bin/cli.ts +181 -144
  363. package/src/codegen/bin/flag-specs.test.ts +16 -1
  364. package/src/codegen/bin/flag-specs.ts +43 -31
  365. package/src/codegen/bundle-size.test.ts +1 -1
  366. package/src/codegen/collect-models.ts +1 -1
  367. package/src/codegen/e2e.test.ts +1 -1
  368. package/src/codegen/emit/api-route.ts +184 -0
  369. package/src/codegen/emit/context.ts +32 -0
  370. package/src/codegen/emit/declarations.ts +49 -0
  371. package/src/codegen/emit/format-types.ts +232 -0
  372. package/src/codegen/emit/http-stream-route.ts +162 -0
  373. package/src/codegen/emit/route-shared.ts +104 -0
  374. package/src/codegen/emit/rpc-route.ts +49 -0
  375. package/src/codegen/emit/scope-file.ts +226 -0
  376. package/src/codegen/emit/stream-route.ts +81 -0
  377. package/src/codegen/emit-errors.integration.test.ts +2 -2
  378. package/src/codegen/emit-errors.test.ts +1 -1
  379. package/src/codegen/emit-errors.ts +1 -1
  380. package/src/codegen/emit-scope.test.ts +75 -2
  381. package/src/codegen/emit-scope.ts +15 -1048
  382. package/src/codegen/goldens.test.ts +89 -0
  383. package/src/codegen/group-routes.test.ts +1 -1
  384. package/src/codegen/group-routes.ts +1 -1
  385. package/src/codegen/pipeline.test.ts +1 -1
  386. package/src/codegen/pipeline.ts +1 -1
  387. package/src/codegen/resolve-envelope.test.ts +1 -1
  388. package/src/codegen/resolve-envelope.ts +1 -1
  389. package/src/codegen/targets/_shared/error-schemas.test.ts +1 -1
  390. package/src/codegen/targets/_shared/error-schemas.ts +1 -1
  391. package/src/codegen/targets/_shared/route-slots.test.ts +1 -1
  392. package/src/codegen/targets/_shared/route-slots.ts +1 -1
  393. package/src/codegen/targets/_shared/target-run.ts +1 -1
  394. package/src/codegen/targets/kotlin/emit-route-kotlin.test.ts +1 -1
  395. package/src/codegen/targets/kotlin/emit-route-kotlin.ts +1 -1
  396. package/src/codegen/targets/kotlin/emit-scope-kotlin.test.ts +1 -1
  397. package/src/codegen/targets/swift/access-level.test.ts +1 -1
  398. package/src/codegen/targets/swift/emit-route-swift.test.ts +1 -1
  399. package/src/codegen/targets/swift/emit-route-swift.ts +1 -1
  400. package/src/codegen/targets/swift/emit-scope-swift.test.ts +1 -1
  401. package/src/codegen/targets/ts/shared-models.test.ts +1 -1
  402. package/src/{create-http-stream.test.ts → core/create-http-stream.test.ts} +1 -1
  403. package/src/core/create-http-stream.ts +207 -0
  404. package/src/{create-http.test.ts → core/create-http.test.ts} +31 -4
  405. package/src/core/create-http.ts +126 -0
  406. package/src/{create-stream.test.ts → core/create-stream.test.ts} +28 -31
  407. package/src/core/create-stream.ts +142 -0
  408. package/src/{create.test.ts → core/create.test.ts} +25 -57
  409. package/src/core/create.ts +121 -0
  410. package/src/{stack-utils.test.ts → core/definition-site.test.ts} +14 -3
  411. package/src/{stack-utils.ts → core/definition-site.ts} +20 -23
  412. package/src/{errors.test.ts → core/errors.test.ts} +1 -1
  413. package/src/{errors.ts → core/errors.ts} +30 -28
  414. package/src/core/factory-options.test.ts +112 -0
  415. package/src/core/http-route.ts +73 -0
  416. package/src/core/internal.ts +203 -0
  417. package/src/{migration.test.ts → core/migration.test.ts} +23 -1
  418. package/src/{index.test.ts → core/procedures.test.ts} +13 -11
  419. package/src/core/procedures.ts +75 -0
  420. package/src/core/types.ts +196 -0
  421. package/src/exports.ts +60 -11
  422. package/src/schema/adapter.test.ts +58 -0
  423. package/src/schema/adapter.ts +45 -0
  424. package/src/schema/compile.test.ts +95 -0
  425. package/src/schema/compile.ts +64 -0
  426. package/src/schema/compute-schema.test.ts +222 -41
  427. package/src/schema/compute-schema.ts +145 -71
  428. package/src/schema/json-schema.ts +21 -0
  429. package/src/schema/typebox.test.ts +40 -0
  430. package/src/schema/typebox.ts +27 -0
  431. package/src/server/context.test.ts +22 -0
  432. package/src/server/context.ts +18 -0
  433. package/src/{doc-envelope.test.ts → server/doc-envelope.test.ts} +2 -2
  434. package/src/{doc-envelope.ts → server/doc-envelope.ts} +1 -1
  435. package/src/{implementations/http → server}/doc-registry.test.ts +32 -26
  436. package/src/{implementations/http → server}/doc-registry.ts +11 -7
  437. package/src/server/docs/docs.test.ts +287 -0
  438. package/src/{implementations/http/hono → server}/docs/http-doc.ts +3 -3
  439. package/src/{implementations/http/hono → server}/docs/http-stream-doc.ts +3 -3
  440. package/src/{implementations/http/hono → server}/docs/rpc-doc.ts +3 -3
  441. package/src/{implementations/http/hono → server}/docs/stream-doc.ts +3 -3
  442. package/src/server/errors/dispatch.test.ts +450 -0
  443. package/src/server/errors/dispatch.ts +189 -0
  444. package/src/{implementations/http/error-taxonomy.test.ts → server/errors/taxonomy.test.ts} +45 -39
  445. package/src/{implementations/http/error-taxonomy.ts → server/errors/taxonomy.ts} +8 -17
  446. package/src/server/index.ts +29 -0
  447. package/src/server/no-framework-imports.test.ts +43 -0
  448. package/src/server/paths.test.ts +141 -0
  449. package/src/{implementations/http/hono/path.ts → server/paths.ts} +2 -13
  450. package/src/server/request/params.test.ts +143 -0
  451. package/src/server/request/params.ts +68 -0
  452. package/src/server/request/query.test.ts +70 -0
  453. package/src/server/request/query.ts +24 -0
  454. package/src/server/sse.test.ts +113 -0
  455. package/src/server/sse.ts +117 -0
  456. package/src/{implementations → server}/types.ts +17 -16
  457. package/build/create-http-stream.d.ts +0 -58
  458. package/build/create-http-stream.js +0 -122
  459. package/build/create-http-stream.js.map +0 -1
  460. package/build/create-http-stream.test.js.map +0 -1
  461. package/build/create-http.d.ts +0 -49
  462. package/build/create-http.js +0 -108
  463. package/build/create-http.js.map +0 -1
  464. package/build/create-http.test.js.map +0 -1
  465. package/build/create-stream.d.ts +0 -35
  466. package/build/create-stream.js +0 -123
  467. package/build/create-stream.js.map +0 -1
  468. package/build/create-stream.test.js.map +0 -1
  469. package/build/create.d.ts +0 -28
  470. package/build/create.js +0 -82
  471. package/build/create.js.map +0 -1
  472. package/build/create.test.js.map +0 -1
  473. package/build/doc-envelope.js.map +0 -1
  474. package/build/doc-envelope.test.js.map +0 -1
  475. package/build/errors.js.map +0 -1
  476. package/build/errors.test.js.map +0 -1
  477. package/build/implementations/http/astro/astro-context.js.map +0 -1
  478. package/build/implementations/http/astro/create-handler.js.map +0 -1
  479. package/build/implementations/http/astro/index.js.map +0 -1
  480. package/build/implementations/http/astro/index.test.js.map +0 -1
  481. package/build/implementations/http/astro/rewrite-request.js.map +0 -1
  482. package/build/implementations/http/doc-registry.js.map +0 -1
  483. package/build/implementations/http/doc-registry.test.js.map +0 -1
  484. package/build/implementations/http/error-dispatch.d.ts +0 -76
  485. package/build/implementations/http/error-dispatch.js.map +0 -1
  486. package/build/implementations/http/error-dispatch.test.js +0 -254
  487. package/build/implementations/http/error-dispatch.test.js.map +0 -1
  488. package/build/implementations/http/error-taxonomy.js.map +0 -1
  489. package/build/implementations/http/error-taxonomy.test.js.map +0 -1
  490. package/build/implementations/http/hono/docs/http-doc.js.map +0 -1
  491. package/build/implementations/http/hono/docs/http-stream-doc.js.map +0 -1
  492. package/build/implementations/http/hono/docs/rpc-doc.js.map +0 -1
  493. package/build/implementations/http/hono/docs/stream-doc.js.map +0 -1
  494. package/build/implementations/http/hono/handlers/http-stream.js +0 -123
  495. package/build/implementations/http/hono/handlers/http-stream.js.map +0 -1
  496. package/build/implementations/http/hono/handlers/http-stream.test.js.map +0 -1
  497. package/build/implementations/http/hono/handlers/http.js +0 -110
  498. package/build/implementations/http/hono/handlers/http.js.map +0 -1
  499. package/build/implementations/http/hono/handlers/http.test.js.map +0 -1
  500. package/build/implementations/http/hono/handlers/rpc.js +0 -32
  501. package/build/implementations/http/hono/handlers/rpc.js.map +0 -1
  502. package/build/implementations/http/hono/handlers/rpc.test.js.map +0 -1
  503. package/build/implementations/http/hono/handlers/stream.d.ts +0 -23
  504. package/build/implementations/http/hono/handlers/stream.js +0 -147
  505. package/build/implementations/http/hono/handlers/stream.js.map +0 -1
  506. package/build/implementations/http/hono/handlers/stream.test.js.map +0 -1
  507. package/build/implementations/http/hono/index.js.map +0 -1
  508. package/build/implementations/http/hono/index.test.js.map +0 -1
  509. package/build/implementations/http/hono/path.js.map +0 -1
  510. package/build/implementations/http/hono/path.test.js +0 -83
  511. package/build/implementations/http/hono/path.test.js.map +0 -1
  512. package/build/implementations/http/hono/types.d.ts +0 -51
  513. package/build/implementations/http/hono/types.js.map +0 -1
  514. package/build/implementations/http/on-request-error.test.js.map +0 -1
  515. package/build/implementations/http/route-errors.test.js.map +0 -1
  516. package/build/index.d.ts +0 -175
  517. package/build/index.js +0 -47
  518. package/build/index.js.map +0 -1
  519. package/build/index.test.js.map +0 -1
  520. package/build/migration.test.js.map +0 -1
  521. package/build/schema/extract-json-schema.d.ts +0 -2
  522. package/build/schema/extract-json-schema.js +0 -12
  523. package/build/schema/extract-json-schema.js.map +0 -1
  524. package/build/schema/extract-json-schema.test.js +0 -23
  525. package/build/schema/extract-json-schema.test.js.map +0 -1
  526. package/build/schema/parser.d.ts +0 -36
  527. package/build/schema/parser.js +0 -210
  528. package/build/schema/parser.js.map +0 -1
  529. package/build/schema/parser.test.js +0 -120
  530. package/build/schema/parser.test.js.map +0 -1
  531. package/build/schema/resolve-schema-lib.d.ts +0 -12
  532. package/build/schema/resolve-schema-lib.js +0 -11
  533. package/build/schema/resolve-schema-lib.js.map +0 -1
  534. package/build/schema/resolve-schema-lib.test.js +0 -17
  535. package/build/schema/resolve-schema-lib.test.js.map +0 -1
  536. package/build/schema/types.d.ts +0 -8
  537. package/build/schema/types.js +0 -2
  538. package/build/stack-utils.d.ts +0 -25
  539. package/build/stack-utils.js.map +0 -1
  540. package/build/stack-utils.test.js.map +0 -1
  541. package/build/types.d.ts +0 -142
  542. package/build/types.js +0 -2
  543. package/build/types.js.map +0 -1
  544. package/docs/decisions/2026-06-02-monorepo-split-evaluation.md +0 -80
  545. package/docs/handoffs/2026-06-08-dx-round2-declines.md +0 -45
  546. package/docs/handoffs/ajsc-named-type-collision.md +0 -134
  547. package/docs/handoffs/ajsc-named-type-support.md +0 -181
  548. package/docs/handoffs/shared-models-auto-resolve-response.md +0 -181
  549. package/docs/npm-workspaces-migration-plan.md +0 -611
  550. package/docs/superpowers/plans/2026-04-24-doc-registry-simplification.md +0 -886
  551. package/docs/superpowers/plans/2026-04-24-kotlin-codegen-target.md +0 -1265
  552. package/docs/superpowers/plans/2026-04-25-ajsc-v7-kotlin-polish.md +0 -1993
  553. package/docs/superpowers/plans/2026-04-29-safe-result-api.md +0 -2293
  554. package/docs/superpowers/plans/2026-05-07-astro-adapter.md +0 -1391
  555. package/docs/superpowers/plans/2026-05-08-create-http.md +0 -3355
  556. package/docs/superpowers/plans/2026-05-08-hono-app-builder-convergence.md +0 -3365
  557. package/docs/superpowers/plans/2026-06-05-dx-feedback-round.md +0 -1292
  558. package/docs/superpowers/plans/2026-06-06-shared-models-convention-and-diagnostics.md +0 -659
  559. package/docs/superpowers/plans/2026-06-08-codegen-dx-surfacing.md +0 -428
  560. package/docs/superpowers/specs/2026-04-24-kotlin-swift-codegen-design.md +0 -401
  561. package/docs/superpowers/specs/2026-04-25-ajsc-v7-kotlin-polish-design.md +0 -314
  562. package/docs/superpowers/specs/2026-04-25-swift-codegen-design.md +0 -264
  563. package/docs/superpowers/specs/2026-04-29-safe-result-api-design.md +0 -324
  564. package/docs/superpowers/specs/2026-05-07-astro-adapter-design.md +0 -252
  565. package/docs/superpowers/specs/2026-05-08-create-http-design.md +0 -409
  566. package/docs/superpowers/specs/2026-05-08-hono-app-builder-convergence-design.md +0 -411
  567. package/docs/superpowers/specs/2026-06-05-dx-feedback-round-design.md +0 -285
  568. package/docs/superpowers/specs/2026-06-08-dx-feedback-round-2-design.md +0 -376
  569. package/src/create-http-stream.ts +0 -191
  570. package/src/create-http.ts +0 -210
  571. package/src/create-stream.ts +0 -228
  572. package/src/create.ts +0 -172
  573. package/src/implementations/http/README.md +0 -390
  574. package/src/implementations/http/error-dispatch.test.ts +0 -283
  575. package/src/implementations/http/error-dispatch.ts +0 -176
  576. package/src/implementations/http/hono/handlers/http-stream.ts +0 -152
  577. package/src/implementations/http/hono/handlers/http.ts +0 -145
  578. package/src/implementations/http/hono/handlers/rpc.ts +0 -54
  579. package/src/implementations/http/hono/path.test.ts +0 -96
  580. package/src/index.ts +0 -101
  581. package/src/schema/extract-json-schema.test.ts +0 -25
  582. package/src/schema/extract-json-schema.ts +0 -15
  583. package/src/schema/parser.test.ts +0 -182
  584. package/src/schema/parser.ts +0 -265
  585. package/src/schema/resolve-schema-lib.test.ts +0 -19
  586. package/src/schema/resolve-schema-lib.ts +0 -29
  587. package/src/schema/types.ts +0 -20
  588. package/src/types.ts +0 -133
  589. /package/build/{implementations/http → adapters}/astro/astro-context.d.ts +0 -0
  590. /package/build/{implementations/http → adapters}/astro/astro-context.js +0 -0
  591. /package/build/{implementations/http → adapters}/astro/create-handler.d.ts +0 -0
  592. /package/build/{implementations/http → adapters}/astro/create-handler.js +0 -0
  593. /package/build/{implementations/http → adapters}/astro/index.d.ts +0 -0
  594. /package/build/{implementations/http → adapters}/astro/index.js +0 -0
  595. /package/build/{implementations/http → adapters}/astro/index.test.d.ts +0 -0
  596. /package/build/{implementations/http → adapters}/astro/rewrite-request.d.ts +0 -0
  597. /package/build/{implementations/http → adapters}/astro/rewrite-request.js +0 -0
  598. /package/build/{create-http-stream.test.d.ts → adapters/hono/envelope-parity.test.d.ts} +0 -0
  599. /package/build/{implementations/http → adapters}/hono/handlers/http-stream.test.d.ts +0 -0
  600. /package/build/{implementations/http → adapters}/hono/handlers/http.test.d.ts +0 -0
  601. /package/build/{implementations/http → adapters}/hono/handlers/rpc.test.d.ts +0 -0
  602. /package/build/{implementations/http → adapters}/hono/handlers/stream.test.d.ts +0 -0
  603. /package/build/{implementations/http → adapters}/hono/index.test.d.ts +0 -0
  604. /package/build/{implementations/http → adapters/hono}/on-request-error.test.d.ts +0 -0
  605. /package/build/{implementations/http → adapters/hono}/route-errors.test.d.ts +0 -0
  606. /package/build/{create-http.test.d.ts → client/freeze.test.d.ts} +0 -0
  607. /package/build/{create-stream.test.d.ts → codegen/goldens.test.d.ts} +0 -0
  608. /package/build/{create.test.d.ts → core/create-http-stream.test.d.ts} +0 -0
  609. /package/build/{doc-envelope.test.d.ts → core/create-http.test.d.ts} +0 -0
  610. /package/build/{errors.test.d.ts → core/create-stream.test.d.ts} +0 -0
  611. /package/build/{implementations/http/doc-registry.test.d.ts → core/create.test.d.ts} +0 -0
  612. /package/build/{implementations/http/error-dispatch.test.d.ts → core/definition-site.test.d.ts} +0 -0
  613. /package/build/{implementations/http/error-taxonomy.test.d.ts → core/errors.test.d.ts} +0 -0
  614. /package/build/{errors.test.js → core/errors.test.js} +0 -0
  615. /package/build/{implementations/http/hono/path.test.d.ts → core/factory-options.test.d.ts} +0 -0
  616. /package/build/{migration.test.d.ts → core/migration.test.d.ts} +0 -0
  617. /package/build/{index.test.d.ts → core/procedures.test.d.ts} +0 -0
  618. /package/build/{implementations/http/hono → core}/types.js +0 -0
  619. /package/build/schema/{extract-json-schema.test.d.ts → adapter.test.d.ts} +0 -0
  620. /package/build/schema/{parser.test.d.ts → compile.test.d.ts} +0 -0
  621. /package/build/schema/{resolve-schema-lib.test.d.ts → typebox.test.d.ts} +0 -0
  622. /package/build/{stack-utils.test.d.ts → server/context.test.d.ts} +0 -0
  623. /package/build/{doc-envelope.js → server/doc-envelope.js} +0 -0
  624. /package/build/{doc-envelope.test.js → server/doc-envelope.test.js} +0 -0
  625. /package/build/{implementations → server}/types.js +0 -0
  626. /package/src/{implementations/http → adapters}/astro/README.md +0 -0
  627. /package/src/{implementations/http → adapters}/astro/astro-context.ts +0 -0
  628. /package/src/{implementations/http → adapters}/astro/create-handler.ts +0 -0
  629. /package/src/{implementations/http → adapters}/astro/index.ts +0 -0
  630. /package/src/{implementations/http → adapters}/astro/rewrite-request.ts +0 -0
@@ -1,51 +1,63 @@
1
1
  export interface FlagSpec {
2
2
  name: string
3
+ /** Display-only value placeholder rendered in --help (e.g. `<url>`). */
3
4
  arg?: string
4
5
  description: string
5
6
  group: 'Source' | 'Output' | 'Codegen' | 'Targets' | 'Misc'
6
7
  default?: string
8
+ /**
9
+ * Whether the flag consumes the next argv token as its value. This is the
10
+ * parsing-arity source of truth consumed by parseArgs; `arg` above is its
11
+ * display twin. A test asserts the two never diverge.
12
+ */
13
+ takesValue: boolean
7
14
  }
8
15
 
9
16
  export const FLAG_SPECS: readonly FlagSpec[] = [
10
17
  // Source
11
- { name: '--url', arg: '<url>', description: 'Fetch the doc envelope from a running server', group: 'Source' },
12
- { name: '--file', arg: '<path>', description: 'Read the doc envelope from a JSON file (see writeDocEnvelope)', group: 'Source' },
13
- { name: '--config', arg: '<path>', description: 'Load options from a JSON config file', group: 'Source' },
18
+ { name: '--url', arg: '<url>', takesValue: true, description: 'Fetch the doc envelope from a running server', group: 'Source' },
19
+ { name: '--file', arg: '<path>', takesValue: true, description: 'Read the doc envelope from a JSON file (see writeDocEnvelope)', group: 'Source' },
20
+ { name: '--config', arg: '<path>', takesValue: true, description: 'Load options from a JSON config file', group: 'Source' },
14
21
  // Output
15
- { name: '--out', arg: '<dir>', description: 'Output directory for generated files', group: 'Output' },
16
- { name: '--dry-run', description: 'Print what would be written without touching disk', group: 'Output' },
17
- { name: '--clean-out-dir', description: 'Prune orphaned generator-owned files before writing', group: 'Output', default: 'on' },
18
- { name: '--no-clean-out-dir', description: 'Disable orphan pruning', group: 'Output' },
19
- { name: '--watch', description: 'Regenerate on envelope change', group: 'Output' },
20
- { name: '--interval', arg: '<ms>', description: 'Poll interval for --watch (default 3000)', group: 'Output' },
22
+ { name: '--out', arg: '<dir>', takesValue: true, description: 'Output directory for generated files', group: 'Output' },
23
+ { name: '--dry-run', takesValue: false, description: 'Print what would be written without touching disk', group: 'Output' },
24
+ { name: '--clean-out-dir', takesValue: false, description: 'Prune orphaned generator-owned files before writing', group: 'Output', default: 'on' },
25
+ { name: '--no-clean-out-dir', takesValue: false, description: 'Disable orphan pruning', group: 'Output' },
26
+ { name: '--watch', takesValue: false, description: 'Regenerate on envelope change', group: 'Output' },
27
+ { name: '--interval', arg: '<ms>', takesValue: true, description: 'Poll interval for --watch (default 3000)', group: 'Output' },
21
28
  // Codegen
22
- { name: '--service-name', arg: '<name>', description: 'Service identifier driving generated names', group: 'Codegen', default: 'Api' },
23
- { name: '--namespace-types', description: 'Wrap types in nested namespaces', group: 'Codegen', default: 'on' },
24
- { name: '--no-namespace-types', description: 'Flat type names', group: 'Codegen' },
25
- { name: '--self-contained', description: 'Bundle the client runtime into the output dir', group: 'Codegen', default: 'on' },
26
- { name: '--no-self-contained', description: 'Import the client runtime from ts-procedures/client', group: 'Codegen' },
27
- { name: '--client-import-path', arg: '<path>', description: 'Override the client runtime import path', group: 'Codegen' },
28
- { name: '--share-models', description: 'Hoist $id-bearing schemas into a shared _models.ts', group: 'Codegen', default: 'on' },
29
- { name: '--no-share-models', description: 'Inline every type per route (legacy behaviour)', group: 'Codegen' },
30
- { name: '--shared-models-module', arg: '<module>', description: 'Re-export every $id model from one module (convention; sharedTypesImport overrides)', group: 'Codegen' },
31
- { name: '--strict-shared-models', description: 'Fail if any $id model would be generated as a local twin', group: 'Codegen' },
32
- { name: '--jsdoc', description: 'Emit JSDoc on generated types', group: 'Codegen', default: 'on' },
33
- { name: '--no-jsdoc', description: 'Suppress JSDoc', group: 'Codegen' },
34
- { name: '--enum-style', arg: '<union|enum>', description: 'How to emit enums (namespace mode)', group: 'Codegen' },
35
- { name: '--depluralize', description: 'Depluralize extracted array-item type names', group: 'Codegen' },
36
- { name: '--array-item-naming', arg: '<name|false>', description: 'Naming for extracted array-item types', group: 'Codegen' },
37
- { name: '--uncountable-words', arg: '<csv>', description: 'Words exempt from depluralization', group: 'Codegen' },
29
+ { name: '--service-name', arg: '<name>', takesValue: true, description: 'Service identifier driving generated names', group: 'Codegen', default: 'Api' },
30
+ { name: '--namespace-types', takesValue: false, description: 'Wrap types in nested namespaces', group: 'Codegen', default: 'on' },
31
+ { name: '--no-namespace-types', takesValue: false, description: 'Flat type names', group: 'Codegen' },
32
+ { name: '--self-contained', takesValue: false, description: 'Bundle the client runtime into the output dir', group: 'Codegen', default: 'on' },
33
+ { name: '--no-self-contained', takesValue: false, description: 'Import the client runtime from ts-procedures/client', group: 'Codegen' },
34
+ { name: '--client-import-path', arg: '<path>', takesValue: true, description: 'Override the client runtime import path', group: 'Codegen' },
35
+ { name: '--share-models', takesValue: false, description: 'Hoist $id-bearing schemas into a shared _models.ts', group: 'Codegen', default: 'on' },
36
+ { name: '--no-share-models', takesValue: false, description: 'Inline every type per route (legacy behaviour)', group: 'Codegen' },
37
+ { name: '--shared-models-module', arg: '<module>', takesValue: true, description: 'Re-export every $id model from one module (convention; sharedTypesImport overrides)', group: 'Codegen' },
38
+ { name: '--strict-shared-models', takesValue: false, description: 'Fail if any $id model would be generated as a local twin', group: 'Codegen' },
39
+ { name: '--jsdoc', takesValue: false, description: 'Emit JSDoc on generated types', group: 'Codegen', default: 'on' },
40
+ { name: '--no-jsdoc', takesValue: false, description: 'Suppress JSDoc', group: 'Codegen' },
41
+ { name: '--enum-style', arg: '<union|enum>', takesValue: true, description: 'How to emit enums (namespace mode)', group: 'Codegen' },
42
+ { name: '--depluralize', takesValue: false, description: 'Depluralize extracted array-item type names', group: 'Codegen' },
43
+ { name: '--array-item-naming', arg: '<name|false>', takesValue: true, description: 'Naming for extracted array-item types', group: 'Codegen' },
44
+ { name: '--uncountable-words', arg: '<csv>', takesValue: true, description: 'Words exempt from depluralization', group: 'Codegen' },
38
45
  // Targets
39
- { name: '--target', arg: '<ts|kotlin|swift>', description: 'Output language', group: 'Targets', default: 'ts' },
40
- { name: '--kotlin-package', arg: '<pkg>', description: 'Package for Kotlin output (required for --target kotlin)', group: 'Targets' },
41
- { name: '--kotlin-serializer', arg: '<kotlinx|none>', description: 'Kotlin serialization annotations', group: 'Targets', default: 'kotlinx' },
42
- { name: '--swift-serializer', arg: '<codable|none>', description: 'Swift Codable conformance', group: 'Targets', default: 'codable' },
43
- { name: '--swift-access-level', arg: '<public|internal>', description: 'Swift access level', group: 'Targets', default: 'public' },
44
- { name: '--unsupported-unions', arg: '<throw|fallback>', description: 'Behaviour for untagged oneOf schemas', group: 'Targets', default: 'throw' },
46
+ { name: '--target', arg: '<ts|kotlin|swift>', takesValue: true, description: 'Output language', group: 'Targets', default: 'ts' },
47
+ { name: '--kotlin-package', arg: '<pkg>', takesValue: true, description: 'Package for Kotlin output (required for --target kotlin)', group: 'Targets' },
48
+ { name: '--kotlin-serializer', arg: '<kotlinx|none>', takesValue: true, description: 'Kotlin serialization annotations', group: 'Targets', default: 'kotlinx' },
49
+ { name: '--swift-serializer', arg: '<codable|none>', takesValue: true, description: 'Swift Codable conformance', group: 'Targets', default: 'codable' },
50
+ { name: '--swift-access-level', arg: '<public|internal>', takesValue: true, description: 'Swift access level', group: 'Targets', default: 'public' },
51
+ { name: '--unsupported-unions', arg: '<throw|fallback>', takesValue: true, description: 'Behaviour for untagged oneOf schemas', group: 'Targets', default: 'throw' },
45
52
  ]
46
53
 
47
54
  export const KNOWN_FLAGS: readonly string[] = FLAG_SPECS.map((s) => s.name)
48
55
 
56
+ /** Name-keyed view of FLAG_SPECS for O(1) lookup during parsing. */
57
+ export const FLAG_SPEC_BY_NAME: ReadonlyMap<string, FlagSpec> = new Map(
58
+ FLAG_SPECS.map((s) => [s.name, s]),
59
+ )
60
+
49
61
  const GROUP_ORDER: FlagSpec['group'][] = ['Source', 'Output', 'Codegen', 'Targets', 'Misc']
50
62
 
51
63
  export function formatHelp(): string {
@@ -3,7 +3,7 @@ import { generateClient } from './index.js'
3
3
  import * as path from 'node:path'
4
4
  import * as os from 'node:os'
5
5
  import * as fs from 'node:fs/promises'
6
- import type { DocEnvelope } from '../implementations/types.js'
6
+ import type { DocEnvelope } from '../server/types.js'
7
7
 
8
8
  function makeEnvelope(routeCount: number): DocEnvelope {
9
9
  const routes = Array.from({ length: routeCount }, (_, i) => ({
@@ -1,4 +1,4 @@
1
- import type { AnyHttpRouteDoc } from '../implementations/types.js'
1
+ import type { AnyHttpRouteDoc } from '../server/types.js'
2
2
  import { toPascalCase } from './naming.js'
3
3
  import { walkSubschemas } from './schema-walk.js'
4
4
 
@@ -3,7 +3,7 @@ import { generateClient } from './index.js'
3
3
  import { mkdirSync, rmSync, readFileSync, writeFileSync, existsSync } from 'node:fs'
4
4
  import { join } from 'node:path'
5
5
  import { tmpdir } from 'node:os'
6
- import type { DocEnvelope, RPCHttpRouteDoc, APIHttpRouteDoc, StreamHttpRouteDoc, HttpStreamRouteDoc, ErrorDoc } from '../implementations/types.js'
6
+ import type { DocEnvelope, RPCHttpRouteDoc, APIHttpRouteDoc, StreamHttpRouteDoc, HttpStreamRouteDoc, ErrorDoc } from '../server/types.js'
7
7
  import { runTsc } from './test-helpers/run-tsc.js'
8
8
  import { Type } from 'typebox'
9
9
 
@@ -0,0 +1,184 @@
1
+ /**
2
+ * Emitter for `kind: 'api'` routes — structured multi-channel `Req` types,
3
+ * `Response.Body` / `Response.Headers` slots, and the bindCallable /
4
+ * bindCallableTyped callable.
5
+ */
6
+ import type { APIHttpRouteDoc } from '../../server/types.js'
7
+ import { toPascalCase } from '../naming.js'
8
+ import type { EmitRouteContext, RouteChunks } from './context.js'
9
+ import { convertBody, formatSubNamespace, type NamedType } from './format-types.js'
10
+ import { buildCallableJsDoc, buildErrorUnion, injectRouteErrors } from './route-shared.js'
11
+
12
+ /**
13
+ * Builds the conditional return type string for an API or http-stream callable.
14
+ *
15
+ * - Both body + headers → `{ body: <Body>; headers: <Headers> }`
16
+ * - Only body → `<Body>`
17
+ * - Only headers → `{ headers: <Headers> }`
18
+ * - Neither → `void`
19
+ */
20
+ function buildApiReturnType(
21
+ bodyRef: string | undefined,
22
+ headersRef: string | undefined,
23
+ ): string {
24
+ if (bodyRef && headersRef) {
25
+ return `{ body: ${bodyRef}; headers: ${headersRef} }`
26
+ }
27
+ if (bodyRef) return bodyRef
28
+ if (headersRef) return `{ headers: ${headersRef} }`
29
+ return 'void'
30
+ }
31
+
32
+ export async function emitApiRoute(route: APIHttpRouteDoc, ctx: EmitRouteContext): Promise<RouteChunks> {
33
+ const pascal = toPascalCase(route.name)
34
+ const req = route.jsonSchema.req ?? {}
35
+ const res = route.jsonSchema.res ?? {}
36
+
37
+ // Request channels
38
+ const reqChannelKeys = ['pathParams', 'query', 'body', 'headers'] as const
39
+ const reqTypes: NamedType[] = []
40
+ const presentChannels: string[] = []
41
+
42
+ for (const channel of reqChannelKeys) {
43
+ const schema = req[channel]
44
+ if (schema != null) {
45
+ reqTypes.push({ shortName: toPascalCase(channel), schema })
46
+ presentChannels.push(channel)
47
+ }
48
+ }
49
+
50
+ // Response slots
51
+ const resTypes: NamedType[] = [
52
+ { shortName: 'Body', schema: res.body },
53
+ { shortName: 'Headers', schema: res.headers },
54
+ ]
55
+
56
+ const scopeStr = route.scope ?? 'default'
57
+ const errorUnion = buildErrorUnion(route.errors, ctx)
58
+ const hasErrors = errorUnion !== null
59
+ const errorsRef = ctx.namespaceTypes
60
+ ? `${ctx.scopePascal}.${pascal}.Errors`
61
+ : `${pascal}Errors`
62
+
63
+ const declarations: string[] = []
64
+ let paramsTypeName = 'void'
65
+ let returnTypeName = 'void'
66
+
67
+ // Track reserved names across all sub-namespaces. Model names are reserved
68
+ // so an ajsc-extracted sub-type can't silently merge with a referenced model.
69
+ const taken = new Set<string>(['Req', 'Response'])
70
+
71
+ if (ctx.namespaceTypes) {
72
+ // Namespace mode: emit nested Req {} and Response {} namespaces inside route namespace.
73
+ // Also emit merged type aliases `export type Req = { ... }` and `export type Response = ...`
74
+ // so they can be used as type arguments to bindCallable (TS requires a TYPE, not a namespace).
75
+ const nsLines: string[] = []
76
+
77
+ const { nsBlock: reqBlock, refs: reqRefs } = await formatSubNamespace(
78
+ pascal, 'Req', reqTypes, ctx, taken
79
+ )
80
+ if (reqBlock) {
81
+ nsLines.push(reqBlock)
82
+ // Merged type alias for Req so it can be used as a generic type arg
83
+ const reqFields = presentChannels
84
+ .map((ch) => `${ch}: Req.${toPascalCase(ch)}`)
85
+ .join('; ')
86
+ nsLines.push(` export type Req = { ${reqFields} }`)
87
+ }
88
+
89
+ const { nsBlock: resBlock, refs: resRefs } = await formatSubNamespace(
90
+ pascal, 'Response', resTypes, ctx, taken
91
+ )
92
+ if (resBlock) {
93
+ nsLines.push(resBlock)
94
+ // No merged Response type alias needed: we reference Response.Body / Response.Headers
95
+ // directly in the return type string, which are namespace-qualified paths (valid).
96
+ }
97
+
98
+ // Emit Errors type last (injected by injectRouteErrors below)
99
+ // Build the route namespace block
100
+ if (nsLines.length > 0) {
101
+ declarations.push(` export namespace ${pascal} {\n${nsLines.join('\n\n')}\n }`)
102
+ }
103
+
104
+ // Params type: use the merged Req type alias
105
+ if (presentChannels.length > 0) {
106
+ paramsTypeName = `${ctx.scopePascal}.${pascal}.Req`
107
+ }
108
+
109
+ // Return type
110
+ const bodyRef = resRefs['Body']
111
+ const headersRef = resRefs['Headers']
112
+ returnTypeName = buildApiReturnType(bodyRef, headersRef)
113
+ } else {
114
+ // Flat mode: emit individual types prefixed with route + sub-namespace name
115
+ for (const { shortName, schema } of reqTypes) {
116
+ if (schema == null) continue
117
+ const flatName = `${pascal}Req${shortName}`
118
+ const body = await convertBody(schema, ctx)
119
+ if (body == null) continue
120
+ declarations.push(`export type ${flatName} = ${body}`)
121
+ }
122
+
123
+ // Flat mode: compose structured Req type
124
+ if (presentChannels.length > 0) {
125
+ const structureFields = presentChannels
126
+ .map((ch) => `${ch}: ${pascal}Req${toPascalCase(ch)}`)
127
+ .join('; ')
128
+ declarations.push(`export type ${pascal}Req = { ${structureFields} }`)
129
+ paramsTypeName = `${pascal}Req`
130
+ }
131
+
132
+ // Flat mode: emit response types
133
+ let bodyRef: string | undefined
134
+ let headersRef: string | undefined
135
+
136
+ for (const { shortName, schema } of resTypes) {
137
+ if (schema == null) continue
138
+ const flatName = `${pascal}Response${shortName}`
139
+ const body = await convertBody(schema, ctx)
140
+ if (body == null) continue
141
+ declarations.push(`export type ${flatName} = ${body}`)
142
+ if (shortName === 'Body') bodyRef = flatName
143
+ if (shortName === 'Headers') headersRef = flatName
144
+ }
145
+
146
+ returnTypeName = buildApiReturnType(bodyRef, headersRef)
147
+
148
+ // Flat mode errors
149
+ if (errorUnion) {
150
+ declarations.push(`export type ${pascal}Errors = ${errorUnion}`)
151
+ }
152
+ }
153
+
154
+ const responseHeadersDeclared = res.headers != null
155
+ const helperCall = hasErrors
156
+ ? `client.bindCallableTyped<${paramsTypeName}, ${returnTypeName}, ${errorsRef}>`
157
+ : `client.bindCallable<${paramsTypeName}, ${returnTypeName}>`
158
+
159
+ const descriptorLines = [
160
+ ` name: '${route.name}',`,
161
+ ` scope: '${scopeStr}',`,
162
+ ` path: '${route.fullPath}',`,
163
+ ` method: '${route.method}',`,
164
+ ` kind: 'api',`,
165
+ ...(responseHeadersDeclared ? [` responseHeadersDeclared: true,`] : []),
166
+ ]
167
+
168
+ const callable = [
169
+ buildCallableJsDoc({
170
+ methodLabel: route.method.toUpperCase(),
171
+ path: route.fullPath,
172
+ errorsRef: hasErrors ? errorsRef : null,
173
+ }),
174
+ ` ${route.name}: ${helperCall}({`,
175
+ ...descriptorLines,
176
+ ` }),`,
177
+ ].join('\n')
178
+
179
+ const hasErrorsInjected = ctx.namespaceTypes
180
+ ? injectRouteErrors(declarations, pascal, errorUnion, ctx.namespaceTypes)
181
+ : errorUnion !== null // flat mode already emitted errors above
182
+
183
+ return { typeDeclarations: declarations, callable, hasStream: false, hasErrors: hasErrorsInjected }
184
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Shared per-route emission contracts for the TS scope emitter.
3
+ *
4
+ * `EmitRouteContext` is threaded through every route emitter and formatting
5
+ * helper; `RouteChunks` is what each per-kind emitter returns to
6
+ * `emitScopeFile` for final assembly.
7
+ */
8
+ import type { AjscOptions } from '../emit-types.js'
9
+
10
+ export interface RouteChunks {
11
+ typeDeclarations: string[]
12
+ callable: string
13
+ hasStream: boolean
14
+ /** True when this route emitted an `Errors` type (drives the `_errors` import at the top of the scope file). */
15
+ hasErrors: boolean
16
+ }
17
+
18
+ export interface EmitRouteContext {
19
+ ajsc?: AjscOptions
20
+ namespaceTypes: boolean
21
+ scopePascal: string
22
+ serviceName: string
23
+ errorKeys?: Set<string>
24
+ /** `$id` → shared model type name; drives the substitute wrappers. */
25
+ idToModelName?: Map<string, string>
26
+ /**
27
+ * Accumulates the model names ajsc reported as `referencedNamedTypes` across
28
+ * every route in the scope. Drives the `import type { … } from './_models'`
29
+ * line assembled in `emitScopeFile`.
30
+ */
31
+ referencedModels: Set<string>
32
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Declaration bookkeeping for namespace-mode emission: the `indent` text
3
+ * helper and the `DeclarationCollector` duplicate-identifier guard.
4
+ */
5
+ import { extractedDeclName } from '../emit-types.js'
6
+
7
+ /** Indent each line of a multi-line string by a given prefix. */
8
+ export function indent(text: string, prefix: string): string {
9
+ return text.split('\n').map((line) => (line ? prefix + line : line)).join('\n')
10
+ }
11
+
12
+ /**
13
+ * Tracks extracted declarations emitted into a single namespace, guarding
14
+ * against duplicate identifiers (defense-in-depth on top of the rename pass).
15
+ *
16
+ * - Exact-string duplicates (the same sub-type extracted from two schemas) are
17
+ * silently skipped.
18
+ * - A same-name-but-different-body declaration is a genuine collision the
19
+ * rename pass failed to resolve; emitting it would produce an opaque
20
+ * `TS2300: Duplicate identifier` in the consumer's build. We fail fast at
21
+ * codegen with a message that names the offending identifier instead.
22
+ *
23
+ * Returns the indented declaration line to push, or `null` when it should be
24
+ * skipped (exact duplicate).
25
+ */
26
+ export class DeclarationCollector {
27
+ private readonly seenStrings = new Set<string>()
28
+ private readonly seenNames = new Map<string, string>()
29
+
30
+ constructor(private readonly context: string) {}
31
+
32
+ /** Returns the indented line to emit, or `null` for an exact duplicate. */
33
+ accept(decl: string, indentPrefix: string): string | null {
34
+ if (this.seenStrings.has(decl)) return null
35
+ const name = extractedDeclName(decl)
36
+ if (name != null) {
37
+ if (this.seenNames.has(name)) {
38
+ throw new Error(
39
+ `[ts-procedures-codegen] duplicate identifier '${name}' while emitting ${this.context}. ` +
40
+ `An extracted sub-type collided with another of the same name and could not be renamed. ` +
41
+ `This is a codegen bug — please report it with the offending schema.`,
42
+ )
43
+ }
44
+ this.seenNames.set(name, decl)
45
+ }
46
+ this.seenStrings.add(decl)
47
+ return indent(decl, indentPrefix)
48
+ }
49
+ }
@@ -0,0 +1,232 @@
1
+ /**
2
+ * Schema → TypeScript conversion layer for the scope emitter: shared-model
3
+ * conversion wrappers (`convertExtracted` / `convertBody`), the
4
+ * `assertNoModelNameCollision` guard, and the `formatTypes` /
5
+ * `formatSubNamespace` declaration builders used by the per-kind route
6
+ * emitters.
7
+ */
8
+ import {
9
+ jsonSchemaToTypeBody,
10
+ jsonSchemaToTypeBodyWithRefs,
11
+ jsonSchemaToExtractedTypes,
12
+ renameExtractedTypes,
13
+ type ExtractedTypeOutput,
14
+ } from '../emit-types.js'
15
+ import { substituteModelRefs } from '../model-refs.js'
16
+ import type { EmitRouteContext } from './context.js'
17
+ import { DeclarationCollector } from './declarations.js'
18
+
19
+ // ---------------------------------------------------------------------------
20
+ // Shared-model conversion wrappers (ajsc x-named-type)
21
+ // ---------------------------------------------------------------------------
22
+
23
+ /**
24
+ * Drop-in replacement for `jsonSchemaToExtractedTypes` that, when shared models
25
+ * are in play, rewrites every `$id` subschema into an `x-named-type` node before
26
+ * ajsc. ajsc emits each as a bare verbatim reference and reports it via
27
+ * `referencedNamedTypes`; this wrapper folds those names into
28
+ * `ctx.referencedModels` so `emitScopeFile` can build the `_models` import.
29
+ *
30
+ * Short-circuits to the exact original call when no models are configured, so
31
+ * output is byte-identical for envelopes without `$id` models.
32
+ */
33
+ export async function convertExtracted(
34
+ schema: Record<string, unknown>,
35
+ ctx: EmitRouteContext,
36
+ ): Promise<ExtractedTypeOutput | undefined> {
37
+ if (ctx.idToModelName == null || ctx.idToModelName.size === 0) {
38
+ return jsonSchemaToExtractedTypes(schema, ctx.ajsc)
39
+ }
40
+ const { schema: sub } = substituteModelRefs(schema, ctx.idToModelName)
41
+ const result = await jsonSchemaToExtractedTypes(sub, ctx.ajsc)
42
+ if (result != null) {
43
+ assertNoModelNameCollision(result)
44
+ for (const name of result.referencedNamedTypes) ctx.referencedModels.add(name)
45
+ }
46
+ return result
47
+ }
48
+
49
+ /**
50
+ * Guards ajsc's documented `x-named-type` collision caveat. If a shared-model
51
+ * name (referenced via `x-named-type`) also matches the name ajsc derives for a
52
+ * sibling *structural* sub-type, ajsc silently MERGES them — the model reference
53
+ * resolves to the unrelated structural type. The merge happens inside ajsc's own
54
+ * pass, so it can't be disentangled afterward (a post-hoc rename would rewrite
55
+ * the model reference too, producing a silently-wrong type). Instead we detect
56
+ * the collision — a name present in BOTH `referencedNamedTypes` AND
57
+ * `extractedTypeNames` (ajsc's own documented collision signal) — and fail fast
58
+ * with an actionable message. The throw is wrapped with the route's name/scope
59
+ * by `emitScopeFile`.
60
+ */
61
+ function assertNoModelNameCollision(result: ExtractedTypeOutput): void {
62
+ if (result.referencedNamedTypes.length === 0 || result.extractedTypeNames.length === 0) return
63
+ const extracted = new Set(result.extractedTypeNames)
64
+ const collisions = result.referencedNamedTypes.filter((name) => extracted.has(name))
65
+ if (collisions.length === 0) return
66
+ const names = collisions.map((n) => `'${n}'`).join(', ')
67
+ throw new Error(
68
+ `shared model ${names} collides with a generated type of the same name derived ` +
69
+ `from a property in this route's schema — ajsc would silently merge them. Rename ` +
70
+ `the colliding property, or change the model's $id/title so the generated names differ`,
71
+ )
72
+ }
73
+
74
+ /**
75
+ * Drop-in replacement for `jsonSchemaToTypeBody` mirroring {@link convertExtracted}
76
+ * for the bare-body (flat / merged-alias) conversion path.
77
+ */
78
+ export async function convertBody(
79
+ schema: Record<string, unknown>,
80
+ ctx: EmitRouteContext,
81
+ ): Promise<string | undefined> {
82
+ if (ctx.idToModelName == null || ctx.idToModelName.size === 0) {
83
+ return jsonSchemaToTypeBody(schema, ctx.ajsc)
84
+ }
85
+ const { schema: sub } = substituteModelRefs(schema, ctx.idToModelName)
86
+ const result = await jsonSchemaToTypeBodyWithRefs(sub, ctx.ajsc)
87
+ if (result == null) return undefined
88
+ for (const name of result.referencedNamedTypes) ctx.referencedModels.add(name)
89
+ return result.body
90
+ }
91
+
92
+ export interface NamedType {
93
+ /** Short name for namespace mode (e.g., 'Params', 'Response'). */
94
+ shortName: string
95
+ /** Schema to convert, or undefined if not present. */
96
+ schema: Record<string, unknown> | undefined
97
+ }
98
+
99
+ export interface FormattedTypes {
100
+ /** Type declarations to add to the file. */
101
+ declarations: string[]
102
+ /** Map of shortName → qualified type reference (for callables). */
103
+ refs: Record<string, string>
104
+ }
105
+
106
+ /**
107
+ * Converts multiple schemas into type declarations and type references.
108
+ * In flat mode: `export type ${routePascal}${shortName} = <body>`
109
+ * In namespace mode: extracted sub-types + named types inside `export namespace ${routePascal} { ... }`
110
+ *
111
+ * `extraReserved` lets the caller pre-reserve identifier names that the route
112
+ * will inject AFTER formatTypes returns (e.g. emitApiRoute's structured
113
+ * `Params`). Without this, an ajsc-extracted sub-type could shadow the
114
+ * injected one and produce duplicate `export type Params` declarations.
115
+ */
116
+ export async function formatTypes(
117
+ routePascal: string,
118
+ types: NamedType[],
119
+ ctx: EmitRouteContext,
120
+ extraReserved?: ReadonlySet<string>,
121
+ ): Promise<FormattedTypes> {
122
+ const declarations: string[] = []
123
+ const refs: Record<string, string> = {}
124
+
125
+ if (ctx.namespaceTypes) {
126
+ const nsLines: string[] = []
127
+ const collector = new DeclarationCollector(`namespace ${routePascal}`)
128
+
129
+ // Pre-reserve every name the route will declare itself (each shortName +
130
+ // any caller-supplied extras). Extracted sub-types whose names land in
131
+ // this set get renamed (e.g. `Params` → `Params_`) by renameExtractedTypes,
132
+ // and the body string is patched in lockstep so the reference still
133
+ // resolves. The set is mutated as we go, so a sub-type renamed in schema A
134
+ // is also reserved against schema B.
135
+ const taken = new Set<string>(extraReserved ?? [])
136
+ for (const t of types) {
137
+ if (t.schema != null) taken.add(t.shortName)
138
+ }
139
+
140
+ for (const { shortName, schema } of types) {
141
+ if (schema == null) continue
142
+
143
+ const rawResult = await convertExtracted(schema, ctx)
144
+ if (rawResult == null) continue
145
+
146
+ const result = renameExtractedTypes(rawResult, taken)
147
+
148
+ // Collect extracted sub-types (dedupe exact dups; throw on real collisions)
149
+ for (const decl of result.declarations) {
150
+ const line = collector.accept(decl, ' ')
151
+ if (line != null) nsLines.push(line)
152
+ }
153
+
154
+ nsLines.push(` export type ${shortName} = ${result.body}`)
155
+ refs[shortName] = `${ctx.scopePascal}.${routePascal}.${shortName}`
156
+ }
157
+
158
+ if (nsLines.length > 0) {
159
+ declarations.push(` export namespace ${routePascal} {\n${nsLines.join('\n')}\n }`)
160
+ }
161
+ } else {
162
+ for (const { shortName, schema } of types) {
163
+ if (schema == null) continue
164
+
165
+ const flatName = `${routePascal}${shortName}`
166
+ const body = await convertBody(schema, ctx)
167
+ if (body == null) continue
168
+
169
+ declarations.push(`export type ${flatName} = ${body}`)
170
+ refs[shortName] = flatName
171
+ }
172
+ }
173
+
174
+ return { declarations, refs }
175
+ }
176
+
177
+ /**
178
+ * Formats a group of named types into a nested sub-namespace block (for namespace mode).
179
+ * Returns an array of lines to be inserted into the parent namespace, and a map of
180
+ * shortName → qualified type reference for use in callables.
181
+ *
182
+ * In flat mode, returns declarations like `export type ${prefix}${shortName} = ...`
183
+ * and refs like `${prefix}${shortName}`.
184
+ */
185
+ export async function formatSubNamespace(
186
+ routePascal: string,
187
+ nsName: string, // e.g. 'Req' or 'Response'
188
+ types: NamedType[],
189
+ ctx: EmitRouteContext,
190
+ taken: Set<string>,
191
+ ): Promise<{ nsBlock: string | null; refs: Record<string, string> }> {
192
+ const refs: Record<string, string> = {}
193
+ const nsLines: string[] = []
194
+ const collector = new DeclarationCollector(`namespace ${routePascal}.${nsName}`)
195
+
196
+ // Pre-reserve short names to prevent sub-type extraction collision
197
+ for (const t of types) {
198
+ if (t.schema != null) taken.add(t.shortName)
199
+ }
200
+
201
+ for (const { shortName, schema } of types) {
202
+ if (schema == null) continue
203
+
204
+ if (ctx.namespaceTypes) {
205
+ const rawResult = await convertExtracted(schema, ctx)
206
+ if (rawResult == null) continue
207
+
208
+ const result = renameExtractedTypes(rawResult, taken)
209
+
210
+ for (const decl of result.declarations) {
211
+ const line = collector.accept(decl, ' ')
212
+ if (line != null) nsLines.push(line)
213
+ }
214
+
215
+ nsLines.push(` export type ${shortName} = ${result.body}`)
216
+ refs[shortName] = `${ctx.scopePascal}.${routePascal}.${nsName}.${shortName}`
217
+ } else {
218
+ const flatName = `${routePascal}${nsName}${shortName}`
219
+ const body = await convertBody(schema, ctx)
220
+ if (body == null) continue
221
+ refs[shortName] = flatName
222
+ }
223
+ }
224
+
225
+ if (ctx.namespaceTypes) {
226
+ if (nsLines.length === 0) return { nsBlock: null, refs }
227
+ const nsBlock = ` export namespace ${nsName} {\n${nsLines.join('\n')}\n }`
228
+ return { nsBlock, refs }
229
+ }
230
+
231
+ return { nsBlock: null, refs }
232
+ }