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
@@ -0,0 +1,48 @@
1
+ // Auto-generated by ts-procedures-codegen (v8.6.0) — do not edit
2
+ // Source hash: cb5f789247045d83e6eb2611821116bb
3
+ import { createClient } from './_client'
4
+ import type { ClientInstance, CreateClientConfig } from './_types'
5
+ import * as _orders from './orders'
6
+ import * as _billing from './billing'
7
+ import * as _billingReports from './billing-reports'
8
+ import * as _errorsModule from './_errors'
9
+
10
+ export namespace Api {
11
+ export import Orders = _orders.Orders
12
+ export import Billing = _billing.Billing
13
+ export import BillingReports = _billingReports.BillingReports
14
+ export import Errors = _errorsModule.ApiErrors
15
+ }
16
+
17
+ export function createApiBindings(client: ClientInstance) {
18
+ return {
19
+ orders: _orders.Orders.bindScope(client),
20
+ billing: _billing.Billing.bindScope(client),
21
+ billingReports: _billingReports.BillingReports.bindScope(client),
22
+ }
23
+ }
24
+
25
+ /** Full typed client surface — every scope of `Api`. */
26
+ export type ApiClient = ReturnType<typeof createApiBindings>
27
+ /** Narrow port for the `orders` scope — inject as a DI seam without casting the aggregate client. */
28
+ export type OrdersClient = ApiClient['orders']
29
+ /** Narrow port for the `billing` scope — inject as a DI seam without casting the aggregate client. */
30
+ export type BillingClient = ApiClient['billing']
31
+ /** Narrow port for the `billingReports` scope — inject as a DI seam without casting the aggregate client. */
32
+ export type BillingReportsClient = ApiClient['billingReports']
33
+
34
+ /**
35
+ * Creates a typed client for this service with the generated error
36
+ * registry pre-configured. Non-2xx responses whose body `name` matches
37
+ * a registered error are thrown as typed class instances instead of
38
+ * generic `ClientHttpError`s.
39
+ */
40
+ export function createApiClient(
41
+ config: Omit<CreateClientConfig<ReturnType<typeof createApiBindings>>, 'scopes' | 'errorRegistry'>
42
+ ) {
43
+ return createClient({
44
+ ...config,
45
+ errorRegistry: _errorsModule.ApiErrors.ApiErrorRegistry,
46
+ scopes: (client) => createApiBindings(client),
47
+ })
48
+ }
@@ -0,0 +1,80 @@
1
+ // Auto-generated by ts-procedures-codegen (v8.6.0) — do not edit
2
+ // Source hash: cb5f789247045d83e6eb2611821116bb
3
+ import type { ClientInstance, ProcedureCallOptions, TypedStream } from './_types'
4
+ import type { ApiErrors } from './_errors'
5
+ import type { Customer, Money, OrderEvent } from './_models'
6
+
7
+ export namespace Orders {
8
+ export namespace GetOrder {
9
+ export type Params = { orderId: string; }
10
+ export type Response = Customer
11
+ export type Errors = ApiErrors.OrderRejected
12
+ }
13
+
14
+ export namespace SubmitOrder {
15
+ export type Params = { orderId: string; total: Money; }
16
+ export type Response = { ok: boolean; }
17
+ export type Errors = ApiErrors.OrderRejected
18
+ }
19
+
20
+ export namespace WatchOrders {
21
+ export type Params = { orderId: string; }
22
+ export type Yield = OrderEvent
23
+ export type Return = { total: number; }
24
+ }
25
+
26
+ /** Binds every callable in this scope to a configured client. */
27
+ export function bindScope(client: ClientInstance) {
28
+ return {
29
+ /**
30
+ * POST /orders/get-order/1
31
+ *
32
+ * @param options Optional per-call {@link ProcedureCallOptions} —
33
+ * `signal` (cancel on dispose), `timeout`, `headers`, `basePath`.
34
+ * @throws Declared typed errors: {@link Orders.GetOrder.Errors}. Narrow with
35
+ * `instanceof` on the throwing path, or call `.safe()` for a `Result`.
36
+ */
37
+ GetOrder: client.bindCallableTyped<Orders.GetOrder.Params, Orders.GetOrder.Response, Orders.GetOrder.Errors>({
38
+ name: 'GetOrder',
39
+ scope: 'orders',
40
+ path: '/orders/get-order/1',
41
+ method: 'post',
42
+ kind: 'rpc',
43
+ }),
44
+
45
+ /**
46
+ * POST /orders/submit-order/1
47
+ *
48
+ * @param options Optional per-call {@link ProcedureCallOptions} —
49
+ * `signal` (cancel on dispose), `timeout`, `headers`, `basePath`.
50
+ * @throws Declared typed errors: {@link Orders.SubmitOrder.Errors}. Narrow with
51
+ * `instanceof` on the throwing path, or call `.safe()` for a `Result`.
52
+ */
53
+ SubmitOrder: client.bindCallableTyped<Orders.SubmitOrder.Params, Orders.SubmitOrder.Response, Orders.SubmitOrder.Errors>({
54
+ name: 'SubmitOrder',
55
+ scope: 'orders',
56
+ path: '/orders/submit-order/1',
57
+ method: 'post',
58
+ kind: 'rpc',
59
+ }),
60
+
61
+ /**
62
+ * POST|GET /orders/watch-orders/1
63
+ *
64
+ * @param options Optional per-call {@link ProcedureCallOptions} —
65
+ * `signal` (cancel on dispose), `timeout`, `headers`, `basePath`.
66
+ */
67
+ WatchOrders(params: Orders.WatchOrders.Params, options?: ProcedureCallOptions): TypedStream<Orders.WatchOrders.Yield, Orders.WatchOrders.Return> {
68
+ return client.stream<Orders.WatchOrders.Yield, Orders.WatchOrders.Return>({
69
+ name: 'WatchOrders',
70
+ scope: 'orders',
71
+ path: '/orders/watch-orders/1',
72
+ method: 'post',
73
+ kind: 'stream',
74
+ streamMode: 'sse',
75
+ params,
76
+ }, options)
77
+ },
78
+ }
79
+ }
80
+ }
@@ -1,7 +1,7 @@
1
1
  import { describe, it, expect } from 'vitest'
2
2
  import { vi } from 'vitest'
3
- import { parseArgs, loadConfigFile, extractConfigPath, printPostRunHints, warnIfKotlinNoOpFlags, shouldShowHelp, type CodegenConfig } from './cli.js'
4
- import { formatHelp } from './flag-specs.js'
3
+ import { parseArgs, loadConfigFile, extractConfigPath, printPostRunHints, warnIfKotlinNoOpFlags, shouldShowHelp, PARSE_HANDLED_FLAGS, type CodegenConfig } from './cli.js'
4
+ import { KNOWN_FLAGS, formatHelp } from './flag-specs.js'
5
5
 
6
6
  describe('parseArgs', () => {
7
7
  it('parses --url and --out', () => {
@@ -433,6 +433,17 @@ describe('cli — unsupported-unions flag', () => {
433
433
  })
434
434
  })
435
435
 
436
+ describe('flag catalog ↔ parser parity', () => {
437
+ // FLAG_SPECS is the single source of truth: a flag that parseArgs can
438
+ // dispatch but that isn't cataloged would be invisible in --help and
439
+ // did-you-mean; a cataloged flag with no handler would hit parseArgs'
440
+ // internal-error throw. Pinning the two sets equal makes both drifts
441
+ // impossible to land.
442
+ it('every flag handled by parseArgs is cataloged in FLAG_SPECS and vice versa', () => {
443
+ expect([...PARSE_HANDLED_FLAGS].sort()).toEqual([...KNOWN_FLAGS].sort())
444
+ })
445
+ })
446
+
436
447
  describe('--help handling', () => {
437
448
  it('shouldShowHelp true for --help, -h, and bare (no args)', () => {
438
449
  expect(shouldShowHelp(['--help'])).toBe(true)
@@ -5,7 +5,7 @@ import { createHash } from 'node:crypto'
5
5
  import { generateClient, type GenerateClientOptions } from '../index.js'
6
6
  import type { AjscOptions } from '../emit-types.js'
7
7
  import type { SharedTypesImportMap } from '../collect-models.js'
8
- import { KNOWN_FLAGS, formatHelp } from './flag-specs.js'
8
+ import { KNOWN_FLAGS, FLAG_SPEC_BY_NAME, formatHelp } from './flag-specs.js'
9
9
 
10
10
  // ---------------------------------------------------------------------------
11
11
  // Types
@@ -146,6 +146,120 @@ function closestKnownFlag(unknown: string): string | undefined {
146
146
  // parseArgs
147
147
  // ---------------------------------------------------------------------------
148
148
 
149
+ /**
150
+ * Mutable accumulator threaded through the per-flag handlers. Seeded from the
151
+ * config file (when present); CLI flags then override field by field.
152
+ */
153
+ interface ParseState {
154
+ url: string | undefined
155
+ file: string | undefined
156
+ outDir: string | undefined
157
+ watch: boolean
158
+ interval: number
159
+ ajsc: AjscOptions
160
+ clientImportPath: string | undefined
161
+ dryRun: boolean
162
+ namespaceTypes: boolean
163
+ selfContained: boolean
164
+ serviceName: string | undefined
165
+ cleanOutDir: boolean
166
+ target: 'ts' | 'kotlin' | 'swift' | undefined
167
+ kotlinPackage: string | undefined
168
+ kotlinSerializer: 'kotlinx' | 'none' | undefined
169
+ swiftSerializer: 'codable' | 'none' | undefined
170
+ swiftAccessLevel: 'public' | 'internal' | undefined
171
+ unsupportedUnions: 'throw' | 'fallback' | undefined
172
+ shareModels: boolean
173
+ sharedModelsModule: string | undefined
174
+ strictSharedModels: boolean
175
+ configPath: string | undefined
176
+ }
177
+
178
+ /**
179
+ * Applies one flag to the parse state. `value` is the consumed argv token for
180
+ * flags whose spec has `takesValue: true`, and always `undefined` otherwise.
181
+ */
182
+ type FlagHandler = (state: ParseState, value: string | undefined) => void
183
+
184
+ /** Renders an allowed-values list the way the CLI errors phrase it: `'a' or 'b'` / `'a', 'b', or 'c'`. */
185
+ function humanizeAllowed(allowed: readonly string[]): string {
186
+ const quoted = allowed.map((v) => `'${v}'`)
187
+ if (quoted.length === 2) return `${quoted[0]} or ${quoted[1]}`
188
+ return `${quoted.slice(0, -1).join(', ')}, or ${quoted[quoted.length - 1]}`
189
+ }
190
+
191
+ /** Builds a handler for a value flag restricted to a closed set; invalid/missing values throw. */
192
+ function enumValueFlag<T extends string>(
193
+ flag: string,
194
+ allowed: readonly T[],
195
+ assign: (state: ParseState, value: T) => void,
196
+ ): FlagHandler {
197
+ return (state, value) => {
198
+ if (value !== undefined && (allowed as readonly string[]).includes(value)) {
199
+ assign(state, value as T)
200
+ } else {
201
+ throw new Error(`Invalid ${flag} value: ${value ?? '(missing)'} (expected ${humanizeAllowed(allowed)})`)
202
+ }
203
+ }
204
+ }
205
+
206
+ /**
207
+ * Per-flag parse behavior, keyed by flag name. Arity (does the flag consume a
208
+ * value token?) comes from FLAG_SPECS.takesValue — this table only says what to
209
+ * do with the flag once recognized. Every key here MUST have a FLAG_SPECS entry
210
+ * and vice versa; parseArgs refuses to dispatch a flag missing from either
211
+ * side, and a test pins the two sets equal.
212
+ */
213
+ const FLAG_HANDLERS: Record<string, FlagHandler> = {
214
+ '--url': (s, v) => { s.url = v },
215
+ '--file': (s, v) => { s.file = v },
216
+ '--out': (s, v) => { s.outDir = v },
217
+ '--watch': (s) => { s.watch = true },
218
+ '--interval': (s, v) => { s.interval = parseInt(v ?? '3000', 10) },
219
+ // Invalid --enum-style values are silently ignored (legacy lenience), unlike
220
+ // the enumValueFlag flags below which throw.
221
+ '--enum-style': (s, v) => {
222
+ if (v === 'union' || v === 'enum') {
223
+ s.ajsc.enumStyle = v
224
+ }
225
+ },
226
+ '--depluralize': (s) => { s.ajsc.depluralize = true },
227
+ '--array-item-naming': (s, v) => { s.ajsc.arrayItemNaming = v === 'false' ? false : v },
228
+ '--uncountable-words': (s, v) => {
229
+ s.ajsc.uncountableWords = (v ?? '').split(',').map((w) => w.trim()).filter(Boolean)
230
+ },
231
+ '--jsdoc': (s) => { s.ajsc.jsdoc = true },
232
+ '--no-jsdoc': (s) => { s.ajsc.jsdoc = false },
233
+ '--client-import-path': (s, v) => { s.clientImportPath = v },
234
+ '--dry-run': (s) => { s.dryRun = true },
235
+ '--namespace-types': (s) => { s.namespaceTypes = true },
236
+ '--no-namespace-types': (s) => { s.namespaceTypes = false },
237
+ '--self-contained': (s) => { s.selfContained = true },
238
+ '--no-self-contained': (s) => { s.selfContained = false },
239
+ '--service-name': (s, v) => { s.serviceName = v },
240
+ '--clean-out-dir': (s) => { s.cleanOutDir = true },
241
+ '--no-clean-out-dir': (s) => { s.cleanOutDir = false },
242
+ '--target': enumValueFlag('--target', ['ts', 'kotlin', 'swift'], (s, v) => { s.target = v }),
243
+ '--kotlin-package': (s, v) => { s.kotlinPackage = v },
244
+ '--kotlin-serializer': enumValueFlag('--kotlin-serializer', ['kotlinx', 'none'], (s, v) => { s.kotlinSerializer = v }),
245
+ '--swift-serializer': enumValueFlag('--swift-serializer', ['codable', 'none'], (s, v) => { s.swiftSerializer = v }),
246
+ '--swift-access-level': enumValueFlag('--swift-access-level', ['public', 'internal'], (s, v) => { s.swiftAccessLevel = v }),
247
+ '--unsupported-unions': enumValueFlag('--unsupported-unions', ['throw', 'fallback'], (s, v) => { s.unsupportedUnions = v }),
248
+ '--share-models': (s) => { s.shareModels = true },
249
+ '--no-share-models': (s) => { s.shareModels = false },
250
+ '--shared-models-module': (s, v) => { s.sharedModelsModule = v },
251
+ '--strict-shared-models': (s) => { s.strictSharedModels = true },
252
+ // configPath is consumed by the caller (main) before parseArgs is called with the loaded config.
253
+ // When called from main, config is already loaded. When called directly (tests), configPath is ignored.
254
+ '--config': (s, v) => { s.configPath = v },
255
+ }
256
+
257
+ /**
258
+ * The set of flags parseArgs can dispatch — exported so a test can pin it
259
+ * equal to KNOWN_FLAGS (i.e. FLAG_SPECS), keeping catalog and parser in sync.
260
+ */
261
+ export const PARSE_HANDLED_FLAGS: readonly string[] = Object.keys(FLAG_HANDLERS)
262
+
149
263
  /**
150
264
  * Parses CLI argv (pass process.argv.slice(2)).
151
265
  * Throws with a descriptive message on validation errors.
@@ -154,125 +268,41 @@ function closestKnownFlag(unknown: string): string | undefined {
154
268
  * and CLI flags override them.
155
269
  */
156
270
  export function parseArgs(argv: string[], config?: CodegenConfig): ParsedArgs {
157
- let url: string | undefined = config?.url
158
- let file: string | undefined = config?.file
159
- let outDir: string | undefined = config?.outDir
160
- let watch = config?.watch ?? false
161
- let interval = config?.interval ?? 3000
162
- const ajsc: AjscOptions = { jsdoc: true, ...config?.ajsc }
163
- let clientImportPath: string | undefined = config?.clientImportPath
164
- let dryRun = config?.dryRun ?? false
165
- let namespaceTypes = config?.namespaceTypes ?? true
166
- let selfContained = config?.selfContained ?? true
167
- let serviceName: string | undefined = config?.serviceName
168
- let cleanOutDir = config?.cleanOutDir ?? true
169
- let target: 'ts' | 'kotlin' | 'swift' | undefined = config?.target
170
- let kotlinPackage: string | undefined = config?.kotlin?.package
171
- let kotlinSerializer: 'kotlinx' | 'none' | undefined = config?.kotlin?.serializer
172
- let swiftSerializer: 'codable' | 'none' | undefined = config?.swift?.serializer
173
- let swiftAccessLevel: 'public' | 'internal' | undefined = config?.swift?.accessLevel
174
- let unsupportedUnions: 'throw' | 'fallback' | undefined = config?.unsupportedUnions
175
- let shareModels = config?.shareModels ?? true
271
+ const state: ParseState = {
272
+ url: config?.url,
273
+ file: config?.file,
274
+ outDir: config?.outDir,
275
+ watch: config?.watch ?? false,
276
+ interval: config?.interval ?? 3000,
277
+ ajsc: { jsdoc: true, ...config?.ajsc },
278
+ clientImportPath: config?.clientImportPath,
279
+ dryRun: config?.dryRun ?? false,
280
+ namespaceTypes: config?.namespaceTypes ?? true,
281
+ selfContained: config?.selfContained ?? true,
282
+ serviceName: config?.serviceName,
283
+ cleanOutDir: config?.cleanOutDir ?? true,
284
+ target: config?.target,
285
+ kotlinPackage: config?.kotlin?.package,
286
+ kotlinSerializer: config?.kotlin?.serializer,
287
+ swiftSerializer: config?.swift?.serializer,
288
+ swiftAccessLevel: config?.swift?.accessLevel,
289
+ unsupportedUnions: config?.unsupportedUnions,
290
+ shareModels: config?.shareModels ?? true,
291
+ sharedModelsModule: config?.sharedModelsModule,
292
+ strictSharedModels: config?.strictSharedModels ?? false,
293
+ configPath: undefined,
294
+ }
176
295
  const sharedTypesImport = config?.sharedTypesImport
177
- let sharedModelsModule: string | undefined = config?.sharedModelsModule
178
- let strictSharedModels = config?.strictSharedModels ?? false
179
- let configPath: string | undefined
180
296
 
181
297
  for (let i = 0; i < argv.length; i++) {
182
298
  const arg = argv[i]
299
+ // Non-flag tokens (positionals) are silently ignored, matching the
300
+ // historical behavior. Value tokens never reach this check — they are
301
+ // consumed via ++i by the flag that owns them.
302
+ if (arg === undefined || !arg.startsWith('--')) continue
183
303
 
184
- if (arg === '--url') {
185
- url = argv[++i]
186
- } else if (arg === '--file') {
187
- file = argv[++i]
188
- } else if (arg === '--out') {
189
- outDir = argv[++i]
190
- } else if (arg === '--watch') {
191
- watch = true
192
- } else if (arg === '--interval') {
193
- interval = parseInt(argv[++i] ?? '3000', 10)
194
- } else if (arg === '--enum-style') {
195
- const val = argv[++i]
196
- if (val === 'union' || val === 'enum') {
197
- ajsc.enumStyle = val
198
- }
199
- } else if (arg === '--depluralize') {
200
- ajsc.depluralize = true
201
- } else if (arg === '--array-item-naming') {
202
- const val = argv[++i]
203
- ajsc.arrayItemNaming = val === 'false' ? false : val
204
- } else if (arg === '--uncountable-words') {
205
- ajsc.uncountableWords = (argv[++i] ?? '').split(',').map((w) => w.trim()).filter(Boolean)
206
- } else if (arg === '--jsdoc') {
207
- ajsc.jsdoc = true
208
- } else if (arg === '--no-jsdoc') {
209
- ajsc.jsdoc = false
210
- } else if (arg === '--client-import-path') {
211
- clientImportPath = argv[++i]
212
- } else if (arg === '--dry-run') {
213
- dryRun = true
214
- } else if (arg === '--namespace-types') {
215
- namespaceTypes = true
216
- } else if (arg === '--no-namespace-types') {
217
- namespaceTypes = false
218
- } else if (arg === '--self-contained') {
219
- selfContained = true
220
- } else if (arg === '--no-self-contained') {
221
- selfContained = false
222
- } else if (arg === '--service-name') {
223
- serviceName = argv[++i]
224
- } else if (arg === '--clean-out-dir') {
225
- cleanOutDir = true
226
- } else if (arg === '--no-clean-out-dir') {
227
- cleanOutDir = false
228
- } else if (arg === '--target') {
229
- const val = argv[++i]
230
- if (val === 'ts' || val === 'kotlin' || val === 'swift') {
231
- target = val
232
- } else {
233
- throw new Error(`Invalid --target value: ${val ?? '(missing)'} (expected 'ts', 'kotlin', or 'swift')`)
234
- }
235
- } else if (arg === '--kotlin-package') {
236
- kotlinPackage = argv[++i]
237
- } else if (arg === '--kotlin-serializer') {
238
- const val = argv[++i]
239
- if (val === 'kotlinx' || val === 'none') {
240
- kotlinSerializer = val
241
- } else {
242
- throw new Error(`Invalid --kotlin-serializer value: ${val ?? '(missing)'} (expected 'kotlinx' or 'none')`)
243
- }
244
- } else if (arg === '--swift-serializer') {
245
- const val = argv[++i]
246
- if (val === 'codable' || val === 'none') {
247
- swiftSerializer = val
248
- } else {
249
- throw new Error(`Invalid --swift-serializer value: ${val ?? '(missing)'} (expected 'codable' or 'none')`)
250
- }
251
- } else if (arg === '--swift-access-level') {
252
- const val = argv[++i]
253
- if (val === 'public' || val === 'internal') {
254
- swiftAccessLevel = val
255
- } else {
256
- throw new Error(`Invalid --swift-access-level value: ${val ?? '(missing)'} (expected 'public' or 'internal')`)
257
- }
258
- } else if (arg === '--unsupported-unions') {
259
- const val = argv[++i]
260
- if (val === 'throw' || val === 'fallback') {
261
- unsupportedUnions = val
262
- } else {
263
- throw new Error(`Invalid --unsupported-unions value: ${val ?? '(missing)'} (expected 'throw' or 'fallback')`)
264
- }
265
- } else if (arg === '--share-models') {
266
- shareModels = true
267
- } else if (arg === '--no-share-models') {
268
- shareModels = false
269
- } else if (arg === '--shared-models-module') {
270
- sharedModelsModule = argv[++i]
271
- } else if (arg === '--strict-shared-models') {
272
- strictSharedModels = true
273
- } else if (arg === '--config') {
274
- configPath = argv[++i]
275
- } else if (arg !== undefined && arg.startsWith('--')) {
304
+ const spec = FLAG_SPEC_BY_NAME.get(arg)
305
+ if (spec === undefined) {
276
306
  // Reject unknown flags loudly. Silently ignoring them led to a downstream
277
307
  // bug where a misspelled `--targt` produced an empty envelope with no
278
308
  // user-visible cause. Suggest the closest known flag when the typo is
@@ -283,10 +313,17 @@ export function parseArgs(argv: string[], config?: CodegenConfig): ParsedArgs {
283
313
  : ' See the README for the supported flag set.'
284
314
  throw new Error(`[ts-procedures-codegen] Unknown CLI flag: ${arg}.${tail}`)
285
315
  }
286
- }
287
316
 
288
- // configPath is consumed by the caller (main) before parseArgs is called with the loaded config.
289
- // When called from main, config is already loaded. When called directly (tests), configPath is ignored.
317
+ const handler = FLAG_HANDLERS[arg]
318
+ if (handler === undefined) {
319
+ // Drift guard: the flag is cataloged (so --help and did-you-mean know it)
320
+ // but nobody taught parseArgs what it does. The parity test catches this
321
+ // in CI; this throw catches it if the test is skipped.
322
+ throw new Error(`[ts-procedures-codegen] Internal error: ${arg} is cataloged in FLAG_SPECS but has no parseArgs handler.`)
323
+ }
324
+
325
+ handler(state, spec.takesValue ? argv[++i] : undefined)
326
+ }
290
327
 
291
328
  // ---------------------------------------------------------------------------
292
329
  // Validation — fails fast on user-controllable errors before envelope resolve.
@@ -296,16 +333,16 @@ export function parseArgs(argv: string[], config?: CodegenConfig): ParsedArgs {
296
333
  // see flag-shape errors from this block, not pipeline-internal throws.
297
334
  // ---------------------------------------------------------------------------
298
335
 
299
- if (outDir === undefined) {
336
+ if (state.outDir === undefined) {
300
337
  throw new Error('Missing required argument: --out <dir>')
301
338
  }
302
339
 
303
- if (url === undefined && file === undefined) {
340
+ if (state.url === undefined && state.file === undefined) {
304
341
  throw new Error('Missing required input source: provide --url <url> or --file <path>')
305
342
  }
306
343
 
307
344
  // Kotlin target requires a package; surface this before any I/O happens.
308
- if (target === 'kotlin' && (kotlinPackage === undefined || kotlinPackage === '')) {
345
+ if (state.target === 'kotlin' && (state.kotlinPackage === undefined || state.kotlinPackage === '')) {
309
346
  throw new Error('Missing required argument: --kotlin-package <pkg> (required when --target kotlin)')
310
347
  }
311
348
 
@@ -314,40 +351,40 @@ export function parseArgs(argv: string[], config?: CodegenConfig): ParsedArgs {
314
351
  // from parseArgs; runtime/emitter wiring errors fire from pipeline.ts).
315
352
 
316
353
  return {
317
- url,
318
- file,
319
- outDir,
320
- watch,
321
- interval,
322
- ajsc,
323
- ...(clientImportPath !== undefined ? { clientImportPath } : {}),
324
- dryRun,
325
- namespaceTypes,
326
- selfContained,
327
- ...(serviceName !== undefined ? { serviceName } : {}),
328
- cleanOutDir,
329
- ...(target !== undefined ? { target } : {}),
330
- ...(kotlinPackage !== undefined
354
+ url: state.url,
355
+ file: state.file,
356
+ outDir: state.outDir,
357
+ watch: state.watch,
358
+ interval: state.interval,
359
+ ajsc: state.ajsc,
360
+ ...(state.clientImportPath !== undefined ? { clientImportPath: state.clientImportPath } : {}),
361
+ dryRun: state.dryRun,
362
+ namespaceTypes: state.namespaceTypes,
363
+ selfContained: state.selfContained,
364
+ ...(state.serviceName !== undefined ? { serviceName: state.serviceName } : {}),
365
+ cleanOutDir: state.cleanOutDir,
366
+ ...(state.target !== undefined ? { target: state.target } : {}),
367
+ ...(state.kotlinPackage !== undefined
331
368
  ? {
332
369
  kotlin: {
333
- package: kotlinPackage,
334
- ...(kotlinSerializer !== undefined ? { serializer: kotlinSerializer } : {}),
370
+ package: state.kotlinPackage,
371
+ ...(state.kotlinSerializer !== undefined ? { serializer: state.kotlinSerializer } : {}),
335
372
  },
336
373
  }
337
374
  : {}),
338
- ...(swiftSerializer !== undefined || swiftAccessLevel !== undefined
375
+ ...(state.swiftSerializer !== undefined || state.swiftAccessLevel !== undefined
339
376
  ? {
340
377
  swift: {
341
- ...(swiftSerializer !== undefined ? { serializer: swiftSerializer } : {}),
342
- ...(swiftAccessLevel !== undefined ? { accessLevel: swiftAccessLevel } : {}),
378
+ ...(state.swiftSerializer !== undefined ? { serializer: state.swiftSerializer } : {}),
379
+ ...(state.swiftAccessLevel !== undefined ? { accessLevel: state.swiftAccessLevel } : {}),
343
380
  },
344
381
  }
345
382
  : {}),
346
- ...(unsupportedUnions !== undefined ? { unsupportedUnions } : {}),
347
- shareModels,
383
+ ...(state.unsupportedUnions !== undefined ? { unsupportedUnions: state.unsupportedUnions } : {}),
384
+ shareModels: state.shareModels,
348
385
  ...(sharedTypesImport !== undefined ? { sharedTypesImport } : {}),
349
- ...(sharedModelsModule !== undefined ? { sharedModelsModule } : {}),
350
- strictSharedModels,
386
+ ...(state.sharedModelsModule !== undefined ? { sharedModelsModule: state.sharedModelsModule } : {}),
387
+ strictSharedModels: state.strictSharedModels,
351
388
  }
352
389
  }
353
390
 
@@ -1,5 +1,5 @@
1
1
  import { describe, it, expect } from 'vitest'
2
- import { FLAG_SPECS, KNOWN_FLAGS, formatHelp } from './flag-specs.js'
2
+ import { FLAG_SPECS, FLAG_SPEC_BY_NAME, KNOWN_FLAGS, formatHelp } from './flag-specs.js'
3
3
 
4
4
  describe('flag-specs', () => {
5
5
  it('derives KNOWN_FLAGS from the spec table', () => {
@@ -35,4 +35,19 @@ describe('flag-specs', () => {
35
35
  expect(help).toContain('--shared-models-module')
36
36
  expect(help).toContain('--strict-shared-models')
37
37
  })
38
+
39
+ it('keeps takesValue (parse arity) consistent with arg (help placeholder)', () => {
40
+ // `takesValue` drives parsing; `arg` drives help rendering. They describe
41
+ // the same fact, so a spec where they disagree is a bug in the catalog.
42
+ for (const spec of FLAG_SPECS) {
43
+ expect(spec.takesValue, `${spec.name}: takesValue must match presence of arg`).toBe(spec.arg !== undefined)
44
+ }
45
+ })
46
+
47
+ it('FLAG_SPEC_BY_NAME indexes every spec exactly once', () => {
48
+ expect(FLAG_SPEC_BY_NAME.size).toBe(FLAG_SPECS.length)
49
+ for (const spec of FLAG_SPECS) {
50
+ expect(FLAG_SPEC_BY_NAME.get(spec.name)).toBe(spec)
51
+ }
52
+ })
38
53
  })