ts-procedures 8.5.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 (635) 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-index.js +13 -0
  103. package/build/codegen/emit-index.js.map +1 -1
  104. package/build/codegen/emit-index.test.js +25 -0
  105. package/build/codegen/emit-index.test.js.map +1 -1
  106. package/build/codegen/emit-scope.d.ts +13 -30
  107. package/build/codegen/emit-scope.js +15 -807
  108. package/build/codegen/emit-scope.js.map +1 -1
  109. package/build/codegen/emit-scope.test.js +86 -4
  110. package/build/codegen/emit-scope.test.js.map +1 -1
  111. package/build/codegen/goldens.test.js +69 -0
  112. package/build/codegen/goldens.test.js.map +1 -0
  113. package/build/codegen/group-routes.d.ts +1 -1
  114. package/build/codegen/pipeline.d.ts +1 -1
  115. package/build/codegen/resolve-envelope.d.ts +1 -1
  116. package/build/codegen/targets/_shared/error-schemas.d.ts +1 -1
  117. package/build/codegen/targets/_shared/route-slots.d.ts +1 -1
  118. package/build/codegen/targets/_shared/target-run.d.ts +1 -1
  119. package/build/codegen/targets/kotlin/emit-route-kotlin.d.ts +1 -1
  120. package/build/codegen/targets/swift/emit-route-swift.d.ts +1 -1
  121. package/build/core/create-http-stream.d.ts +50 -0
  122. package/build/core/create-http-stream.js +108 -0
  123. package/build/core/create-http-stream.js.map +1 -0
  124. package/build/{create-http-stream.test.js → core/create-http-stream.test.js} +1 -1
  125. package/build/core/create-http-stream.test.js.map +1 -0
  126. package/build/core/create-http.d.ts +51 -0
  127. package/build/core/create-http.js +65 -0
  128. package/build/core/create-http.js.map +1 -0
  129. package/build/{create-http.test.js → core/create-http.test.js} +13 -4
  130. package/build/core/create-http.test.js.map +1 -0
  131. package/build/core/create-stream.d.ts +26 -0
  132. package/build/core/create-stream.js +80 -0
  133. package/build/core/create-stream.js.map +1 -0
  134. package/build/{create-stream.test.js → core/create-stream.test.js} +23 -28
  135. package/build/core/create-stream.test.js.map +1 -0
  136. package/build/core/create.d.ts +22 -0
  137. package/build/core/create.js +71 -0
  138. package/build/core/create.js.map +1 -0
  139. package/build/{create.test.js → core/create.test.js} +25 -46
  140. package/build/core/create.test.js.map +1 -0
  141. package/build/core/definition-site.d.ts +24 -0
  142. package/build/{stack-utils.js → core/definition-site.js} +20 -20
  143. package/build/core/definition-site.js.map +1 -0
  144. package/build/{stack-utils.test.js → core/definition-site.test.js} +12 -3
  145. package/build/core/definition-site.test.js.map +1 -0
  146. package/build/{errors.d.ts → core/errors.d.ts} +19 -8
  147. package/build/{errors.js → core/errors.js} +21 -26
  148. package/build/core/errors.js.map +1 -0
  149. package/build/core/errors.test.js.map +1 -0
  150. package/build/core/factory-options.test.js +82 -0
  151. package/build/core/factory-options.test.js.map +1 -0
  152. package/build/core/http-route.d.ts +13 -0
  153. package/build/core/http-route.js +54 -0
  154. package/build/core/http-route.js.map +1 -0
  155. package/build/core/internal.d.ts +72 -0
  156. package/build/core/internal.js +128 -0
  157. package/build/core/internal.js.map +1 -0
  158. package/build/{migration.test.js → core/migration.test.js} +17 -1
  159. package/build/core/migration.test.js.map +1 -0
  160. package/build/core/procedures.d.ts +143 -0
  161. package/build/core/procedures.js +64 -0
  162. package/build/core/procedures.js.map +1 -0
  163. package/build/{index.test.js → core/procedures.test.js} +14 -11
  164. package/build/core/procedures.test.js.map +1 -0
  165. package/build/core/types.d.ts +182 -0
  166. package/build/{schema → core}/types.js.map +1 -1
  167. package/build/exports.d.ts +31 -11
  168. package/build/exports.js +23 -8
  169. package/build/exports.js.map +1 -1
  170. package/build/schema/adapter.d.ts +35 -0
  171. package/build/schema/adapter.js +13 -0
  172. package/build/schema/adapter.js.map +1 -0
  173. package/build/schema/adapter.test.js +53 -0
  174. package/build/schema/adapter.test.js.map +1 -0
  175. package/build/schema/compile.d.ts +37 -0
  176. package/build/schema/compile.js +38 -0
  177. package/build/schema/compile.js.map +1 -0
  178. package/build/schema/compile.test.js +78 -0
  179. package/build/schema/compile.test.js.map +1 -0
  180. package/build/schema/compute-schema.d.ts +47 -37
  181. package/build/schema/compute-schema.js +86 -29
  182. package/build/schema/compute-schema.js.map +1 -1
  183. package/build/schema/compute-schema.test.js +158 -40
  184. package/build/schema/compute-schema.test.js.map +1 -1
  185. package/build/schema/json-schema.d.ts +17 -0
  186. package/build/schema/json-schema.js +2 -0
  187. package/build/schema/json-schema.js.map +1 -0
  188. package/build/schema/typebox.d.ts +11 -0
  189. package/build/schema/typebox.js +24 -0
  190. package/build/schema/typebox.js.map +1 -0
  191. package/build/schema/typebox.test.js +34 -0
  192. package/build/schema/typebox.test.js.map +1 -0
  193. package/build/server/context.d.ts +8 -0
  194. package/build/server/context.js +7 -0
  195. package/build/server/context.js.map +1 -0
  196. package/build/server/context.test.js +16 -0
  197. package/build/server/context.test.js.map +1 -0
  198. package/build/{doc-envelope.d.ts → server/doc-envelope.d.ts} +1 -1
  199. package/build/server/doc-envelope.js.map +1 -0
  200. package/build/server/doc-envelope.test.d.ts +1 -0
  201. package/build/server/doc-envelope.test.js.map +1 -0
  202. package/build/{implementations/http → server}/doc-registry.d.ts +7 -2
  203. package/build/{implementations/http → server}/doc-registry.js +9 -5
  204. package/build/server/doc-registry.js.map +1 -0
  205. package/build/server/doc-registry.test.d.ts +1 -0
  206. package/build/{implementations/http → server}/doc-registry.test.js +27 -24
  207. package/build/server/doc-registry.test.js.map +1 -0
  208. package/build/server/docs/docs.test.d.ts +1 -0
  209. package/build/server/docs/docs.test.js +237 -0
  210. package/build/server/docs/docs.test.js.map +1 -0
  211. package/build/{implementations/http/hono → server}/docs/http-doc.d.ts +2 -2
  212. package/build/{implementations/http/hono → server}/docs/http-doc.js +1 -1
  213. package/build/server/docs/http-doc.js.map +1 -0
  214. package/build/{implementations/http/hono → server}/docs/http-stream-doc.d.ts +2 -2
  215. package/build/{implementations/http/hono → server}/docs/http-stream-doc.js +1 -1
  216. package/build/server/docs/http-stream-doc.js.map +1 -0
  217. package/build/{implementations/http/hono → server}/docs/rpc-doc.d.ts +2 -2
  218. package/build/{implementations/http/hono → server}/docs/rpc-doc.js +1 -1
  219. package/build/server/docs/rpc-doc.js.map +1 -0
  220. package/build/{implementations/http/hono → server}/docs/stream-doc.d.ts +2 -2
  221. package/build/{implementations/http/hono → server}/docs/stream-doc.js +1 -1
  222. package/build/server/docs/stream-doc.js.map +1 -0
  223. package/build/server/errors/dispatch.d.ts +96 -0
  224. package/build/{implementations/http/error-dispatch.js → server/errors/dispatch.js} +20 -10
  225. package/build/server/errors/dispatch.js.map +1 -0
  226. package/build/server/errors/dispatch.test.d.ts +1 -0
  227. package/build/server/errors/dispatch.test.js +418 -0
  228. package/build/server/errors/dispatch.test.js.map +1 -0
  229. package/build/{implementations/http/error-taxonomy.d.ts → server/errors/taxonomy.d.ts} +8 -17
  230. package/build/{implementations/http/error-taxonomy.js → server/errors/taxonomy.js} +6 -15
  231. package/build/server/errors/taxonomy.js.map +1 -0
  232. package/build/server/errors/taxonomy.test.d.ts +1 -0
  233. package/build/{implementations/http/error-taxonomy.test.js → server/errors/taxonomy.test.js} +45 -39
  234. package/build/server/errors/taxonomy.test.js.map +1 -0
  235. package/build/server/index.d.ts +29 -0
  236. package/build/server/index.js +27 -0
  237. package/build/server/index.js.map +1 -0
  238. package/build/server/no-framework-imports.test.d.ts +1 -0
  239. package/build/server/no-framework-imports.test.js +40 -0
  240. package/build/server/no-framework-imports.test.js.map +1 -0
  241. package/build/{implementations/http/hono/path.d.ts → server/paths.d.ts} +2 -3
  242. package/build/{implementations/http/hono/path.js → server/paths.js} +1 -1
  243. package/build/server/paths.js.map +1 -0
  244. package/build/server/paths.test.d.ts +1 -0
  245. package/build/server/paths.test.js +111 -0
  246. package/build/server/paths.test.js.map +1 -0
  247. package/build/server/request/params.d.ts +29 -0
  248. package/build/server/request/params.js +43 -0
  249. package/build/server/request/params.js.map +1 -0
  250. package/build/server/request/params.test.d.ts +1 -0
  251. package/build/server/request/params.test.js +91 -0
  252. package/build/server/request/params.test.js.map +1 -0
  253. package/build/server/request/query.d.ts +9 -0
  254. package/build/server/request/query.js +22 -0
  255. package/build/server/request/query.js.map +1 -0
  256. package/build/server/request/query.test.d.ts +1 -0
  257. package/build/server/request/query.test.js +60 -0
  258. package/build/server/request/query.test.js.map +1 -0
  259. package/build/server/sse.d.ts +70 -0
  260. package/build/server/sse.js +94 -0
  261. package/build/server/sse.js.map +1 -0
  262. package/build/server/sse.test.d.ts +1 -0
  263. package/build/server/sse.test.js +98 -0
  264. package/build/server/sse.test.js.map +1 -0
  265. package/build/{implementations → server}/types.d.ts +17 -15
  266. package/build/{implementations → server}/types.js.map +1 -1
  267. package/docs/astro-adapter.md +8 -9
  268. package/docs/client-and-codegen.md +4 -4
  269. package/docs/client-error-handling.md +92 -5
  270. package/docs/codegen-kotlin.md +2 -3
  271. package/docs/codegen-swift.md +1 -2
  272. package/docs/core.md +135 -54
  273. package/docs/http-integrations.md +83 -6
  274. package/docs/migration-v8-to-v9.md +192 -0
  275. package/docs/plans/2026-06-09-v9-rewrite.md +130 -0
  276. package/docs/specs/2026-06-09-v9-rewrite-design.md +221 -0
  277. package/docs/streaming.md +12 -0
  278. package/package.json +23 -47
  279. package/src/{implementations/http → adapters}/astro/index.test.ts +2 -2
  280. package/src/adapters/hono/__fixtures__/parity-envelope.json +389 -0
  281. package/src/adapters/hono/envelope-parity.test.ts +126 -0
  282. package/src/{implementations/http → adapters}/hono/handlers/http-stream.test.ts +1 -1
  283. package/src/adapters/hono/handlers/http-stream.ts +73 -0
  284. package/src/{implementations/http → adapters}/hono/handlers/http.test.ts +1 -1
  285. package/src/adapters/hono/handlers/http.ts +70 -0
  286. package/src/{implementations/http → adapters}/hono/handlers/rpc.test.ts +2 -2
  287. package/src/adapters/hono/handlers/rpc.ts +39 -0
  288. package/src/{implementations/http → adapters}/hono/handlers/stream.test.ts +4 -3
  289. package/src/{implementations/http → adapters}/hono/handlers/stream.ts +19 -92
  290. package/src/{implementations/http → adapters}/hono/index.test.ts +14 -16
  291. package/src/{implementations/http → adapters}/hono/index.ts +35 -30
  292. package/src/{implementations/http → adapters/hono}/on-request-error.test.ts +3 -3
  293. package/src/adapters/hono/request.ts +28 -0
  294. package/src/{implementations/http → adapters/hono}/route-errors.test.ts +5 -5
  295. package/src/{implementations/http → adapters}/hono/types.ts +43 -20
  296. package/src/client/freeze.test.ts +41 -0
  297. package/src/client/typed-error-dispatch.test.ts +3 -3
  298. package/src/codegen/__fixtures__/make-envelope.ts +1 -1
  299. package/src/codegen/__fixtures__/models-envelope.json +310 -0
  300. package/src/codegen/__fixtures__/users-envelope.json +9 -0
  301. package/src/codegen/__goldens__/MANIFEST.json +85 -0
  302. package/src/codegen/__goldens__/kotlin-default--models/Billing.kt +112 -0
  303. package/src/codegen/__goldens__/kotlin-default--models/BillingReports.kt +26 -0
  304. package/src/codegen/__goldens__/kotlin-default--models/Orders.kt +88 -0
  305. package/src/codegen/__goldens__/kotlin-default--users/Users.kt +189 -0
  306. package/src/codegen/__goldens__/swift-default--models/Billing.swift +97 -0
  307. package/src/codegen/__goldens__/swift-default--models/BillingReports.swift +20 -0
  308. package/src/codegen/__goldens__/swift-default--models/Orders.swift +81 -0
  309. package/src/codegen/__goldens__/swift-default--users/Users.swift +204 -0
  310. package/src/codegen/__goldens__/ts-default--models/_client.ts +1319 -0
  311. package/src/codegen/__goldens__/ts-default--models/_errors.ts +90 -0
  312. package/src/codegen/__goldens__/ts-default--models/_models.ts +10 -0
  313. package/src/codegen/__goldens__/ts-default--models/_types.ts +502 -0
  314. package/src/codegen/__goldens__/ts-default--models/billing-reports.ts +29 -0
  315. package/src/codegen/__goldens__/ts-default--models/billing.ts +67 -0
  316. package/src/codegen/__goldens__/ts-default--models/index.ts +48 -0
  317. package/src/codegen/__goldens__/ts-default--models/orders.ts +80 -0
  318. package/src/codegen/__goldens__/ts-default--users/_client.ts +1319 -0
  319. package/src/codegen/__goldens__/ts-default--users/_errors.ts +90 -0
  320. package/src/codegen/__goldens__/ts-default--users/_types.ts +502 -0
  321. package/src/codegen/__goldens__/ts-default--users/index.ts +38 -0
  322. package/src/codegen/__goldens__/ts-default--users/users.ts +169 -0
  323. package/src/codegen/__goldens__/ts-external-runtime--models/_errors.ts +90 -0
  324. package/src/codegen/__goldens__/ts-external-runtime--models/_models.ts +10 -0
  325. package/src/codegen/__goldens__/ts-external-runtime--models/billing-reports.ts +29 -0
  326. package/src/codegen/__goldens__/ts-external-runtime--models/billing.ts +67 -0
  327. package/src/codegen/__goldens__/ts-external-runtime--models/index.ts +48 -0
  328. package/src/codegen/__goldens__/ts-external-runtime--models/orders.ts +80 -0
  329. package/src/codegen/__goldens__/ts-external-runtime--users/_errors.ts +90 -0
  330. package/src/codegen/__goldens__/ts-external-runtime--users/index.ts +38 -0
  331. package/src/codegen/__goldens__/ts-external-runtime--users/users.ts +169 -0
  332. package/src/codegen/__goldens__/ts-flat--models/_client.ts +1319 -0
  333. package/src/codegen/__goldens__/ts-flat--models/_errors.ts +87 -0
  334. package/src/codegen/__goldens__/ts-flat--models/_models.ts +10 -0
  335. package/src/codegen/__goldens__/ts-flat--models/_types.ts +502 -0
  336. package/src/codegen/__goldens__/ts-flat--models/billing-reports.ts +28 -0
  337. package/src/codegen/__goldens__/ts-flat--models/billing.ts +51 -0
  338. package/src/codegen/__goldens__/ts-flat--models/index.ts +42 -0
  339. package/src/codegen/__goldens__/ts-flat--models/orders.ts +73 -0
  340. package/src/codegen/__goldens__/ts-flat--users/_client.ts +1319 -0
  341. package/src/codegen/__goldens__/ts-flat--users/_errors.ts +87 -0
  342. package/src/codegen/__goldens__/ts-flat--users/_types.ts +502 -0
  343. package/src/codegen/__goldens__/ts-flat--users/index.ts +34 -0
  344. package/src/codegen/__goldens__/ts-flat--users/users.ts +126 -0
  345. package/src/codegen/__goldens__/ts-no-share-models--models/_client.ts +1319 -0
  346. package/src/codegen/__goldens__/ts-no-share-models--models/_errors.ts +90 -0
  347. package/src/codegen/__goldens__/ts-no-share-models--models/_types.ts +502 -0
  348. package/src/codegen/__goldens__/ts-no-share-models--models/billing-reports.ts +29 -0
  349. package/src/codegen/__goldens__/ts-no-share-models--models/billing.ts +111 -0
  350. package/src/codegen/__goldens__/ts-no-share-models--models/index.ts +48 -0
  351. package/src/codegen/__goldens__/ts-no-share-models--models/orders.ts +112 -0
  352. package/src/codegen/__goldens__/ts-no-share-models--users/_client.ts +1319 -0
  353. package/src/codegen/__goldens__/ts-no-share-models--users/_errors.ts +90 -0
  354. package/src/codegen/__goldens__/ts-no-share-models--users/_types.ts +502 -0
  355. package/src/codegen/__goldens__/ts-no-share-models--users/index.ts +38 -0
  356. package/src/codegen/__goldens__/ts-no-share-models--users/users.ts +169 -0
  357. package/src/codegen/__goldens__/ts-shared-models-module--models/_client.ts +1319 -0
  358. package/src/codegen/__goldens__/ts-shared-models-module--models/_errors.ts +90 -0
  359. package/src/codegen/__goldens__/ts-shared-models-module--models/_models.ts +7 -0
  360. package/src/codegen/__goldens__/ts-shared-models-module--models/_types.ts +502 -0
  361. package/src/codegen/__goldens__/ts-shared-models-module--models/billing-reports.ts +29 -0
  362. package/src/codegen/__goldens__/ts-shared-models-module--models/billing.ts +67 -0
  363. package/src/codegen/__goldens__/ts-shared-models-module--models/index.ts +48 -0
  364. package/src/codegen/__goldens__/ts-shared-models-module--models/orders.ts +80 -0
  365. package/src/codegen/bin/cli.test.ts +13 -2
  366. package/src/codegen/bin/cli.ts +181 -144
  367. package/src/codegen/bin/flag-specs.test.ts +16 -1
  368. package/src/codegen/bin/flag-specs.ts +43 -31
  369. package/src/codegen/bundle-size.test.ts +1 -1
  370. package/src/codegen/collect-models.ts +1 -1
  371. package/src/codegen/e2e.test.ts +1 -1
  372. package/src/codegen/emit/api-route.ts +184 -0
  373. package/src/codegen/emit/context.ts +32 -0
  374. package/src/codegen/emit/declarations.ts +49 -0
  375. package/src/codegen/emit/format-types.ts +232 -0
  376. package/src/codegen/emit/http-stream-route.ts +162 -0
  377. package/src/codegen/emit/route-shared.ts +102 -0
  378. package/src/codegen/emit/rpc-route.ts +49 -0
  379. package/src/codegen/emit/scope-file.ts +226 -0
  380. package/src/codegen/emit/stream-route.ts +81 -0
  381. package/src/codegen/emit-errors.integration.test.ts +2 -2
  382. package/src/codegen/emit-errors.test.ts +1 -1
  383. package/src/codegen/emit-errors.ts +1 -1
  384. package/src/codegen/emit-index.test.ts +34 -0
  385. package/src/codegen/emit-index.ts +19 -0
  386. package/src/codegen/emit-scope.test.ts +96 -6
  387. package/src/codegen/emit-scope.ts +15 -1003
  388. package/src/codegen/goldens.test.ts +89 -0
  389. package/src/codegen/group-routes.test.ts +1 -1
  390. package/src/codegen/group-routes.ts +1 -1
  391. package/src/codegen/pipeline.test.ts +1 -1
  392. package/src/codegen/pipeline.ts +1 -1
  393. package/src/codegen/resolve-envelope.test.ts +1 -1
  394. package/src/codegen/resolve-envelope.ts +1 -1
  395. package/src/codegen/targets/_shared/error-schemas.test.ts +1 -1
  396. package/src/codegen/targets/_shared/error-schemas.ts +1 -1
  397. package/src/codegen/targets/_shared/route-slots.test.ts +1 -1
  398. package/src/codegen/targets/_shared/route-slots.ts +1 -1
  399. package/src/codegen/targets/_shared/target-run.ts +1 -1
  400. package/src/codegen/targets/kotlin/__fixtures__/users-golden.kt +6 -0
  401. package/src/codegen/targets/kotlin/emit-route-kotlin.test.ts +1 -1
  402. package/src/codegen/targets/kotlin/emit-route-kotlin.ts +1 -1
  403. package/src/codegen/targets/kotlin/emit-scope-kotlin.test.ts +1 -1
  404. package/src/codegen/targets/swift/__fixtures__/users-golden.swift +6 -0
  405. package/src/codegen/targets/swift/access-level.test.ts +1 -1
  406. package/src/codegen/targets/swift/emit-route-swift.test.ts +1 -1
  407. package/src/codegen/targets/swift/emit-route-swift.ts +1 -1
  408. package/src/codegen/targets/swift/emit-scope-swift.test.ts +1 -1
  409. package/src/codegen/targets/ts/shared-models.test.ts +1 -1
  410. package/src/{create-http-stream.test.ts → core/create-http-stream.test.ts} +1 -1
  411. package/src/core/create-http-stream.ts +207 -0
  412. package/src/{create-http.test.ts → core/create-http.test.ts} +15 -4
  413. package/src/core/create-http.ts +126 -0
  414. package/src/{create-stream.test.ts → core/create-stream.test.ts} +28 -31
  415. package/src/core/create-stream.ts +142 -0
  416. package/src/{create.test.ts → core/create.test.ts} +25 -57
  417. package/src/core/create.ts +121 -0
  418. package/src/{stack-utils.test.ts → core/definition-site.test.ts} +14 -3
  419. package/src/{stack-utils.ts → core/definition-site.ts} +20 -23
  420. package/src/{errors.test.ts → core/errors.test.ts} +1 -1
  421. package/src/{errors.ts → core/errors.ts} +30 -28
  422. package/src/core/factory-options.test.ts +112 -0
  423. package/src/core/http-route.ts +73 -0
  424. package/src/core/internal.ts +203 -0
  425. package/src/{migration.test.ts → core/migration.test.ts} +23 -1
  426. package/src/{index.test.ts → core/procedures.test.ts} +13 -11
  427. package/src/core/procedures.ts +75 -0
  428. package/src/core/types.ts +195 -0
  429. package/src/exports.ts +60 -11
  430. package/src/schema/adapter.test.ts +58 -0
  431. package/src/schema/adapter.ts +45 -0
  432. package/src/schema/compile.test.ts +95 -0
  433. package/src/schema/compile.ts +64 -0
  434. package/src/schema/compute-schema.test.ts +222 -41
  435. package/src/schema/compute-schema.ts +145 -71
  436. package/src/schema/json-schema.ts +21 -0
  437. package/src/schema/typebox.test.ts +40 -0
  438. package/src/schema/typebox.ts +27 -0
  439. package/src/server/context.test.ts +22 -0
  440. package/src/server/context.ts +18 -0
  441. package/src/{doc-envelope.test.ts → server/doc-envelope.test.ts} +2 -2
  442. package/src/{doc-envelope.ts → server/doc-envelope.ts} +1 -1
  443. package/src/{implementations/http → server}/doc-registry.test.ts +32 -26
  444. package/src/{implementations/http → server}/doc-registry.ts +11 -7
  445. package/src/server/docs/docs.test.ts +287 -0
  446. package/src/{implementations/http/hono → server}/docs/http-doc.ts +3 -3
  447. package/src/{implementations/http/hono → server}/docs/http-stream-doc.ts +3 -3
  448. package/src/{implementations/http/hono → server}/docs/rpc-doc.ts +3 -3
  449. package/src/{implementations/http/hono → server}/docs/stream-doc.ts +3 -3
  450. package/src/server/errors/dispatch.test.ts +450 -0
  451. package/src/server/errors/dispatch.ts +189 -0
  452. package/src/{implementations/http/error-taxonomy.test.ts → server/errors/taxonomy.test.ts} +45 -39
  453. package/src/{implementations/http/error-taxonomy.ts → server/errors/taxonomy.ts} +8 -17
  454. package/src/server/index.ts +29 -0
  455. package/src/server/no-framework-imports.test.ts +43 -0
  456. package/src/server/paths.test.ts +141 -0
  457. package/src/{implementations/http/hono/path.ts → server/paths.ts} +2 -13
  458. package/src/server/request/params.test.ts +143 -0
  459. package/src/server/request/params.ts +68 -0
  460. package/src/server/request/query.test.ts +70 -0
  461. package/src/server/request/query.ts +24 -0
  462. package/src/server/sse.test.ts +113 -0
  463. package/src/server/sse.ts +117 -0
  464. package/src/{implementations → server}/types.ts +17 -16
  465. package/build/create-http-stream.d.ts +0 -58
  466. package/build/create-http-stream.js +0 -122
  467. package/build/create-http-stream.js.map +0 -1
  468. package/build/create-http-stream.test.js.map +0 -1
  469. package/build/create-http.d.ts +0 -49
  470. package/build/create-http.js +0 -108
  471. package/build/create-http.js.map +0 -1
  472. package/build/create-http.test.js.map +0 -1
  473. package/build/create-stream.d.ts +0 -35
  474. package/build/create-stream.js +0 -123
  475. package/build/create-stream.js.map +0 -1
  476. package/build/create-stream.test.js.map +0 -1
  477. package/build/create.d.ts +0 -28
  478. package/build/create.js +0 -82
  479. package/build/create.js.map +0 -1
  480. package/build/create.test.js.map +0 -1
  481. package/build/doc-envelope.js.map +0 -1
  482. package/build/doc-envelope.test.js.map +0 -1
  483. package/build/errors.js.map +0 -1
  484. package/build/errors.test.js.map +0 -1
  485. package/build/implementations/http/astro/astro-context.js.map +0 -1
  486. package/build/implementations/http/astro/create-handler.js.map +0 -1
  487. package/build/implementations/http/astro/index.js.map +0 -1
  488. package/build/implementations/http/astro/index.test.js.map +0 -1
  489. package/build/implementations/http/astro/rewrite-request.js.map +0 -1
  490. package/build/implementations/http/doc-registry.js.map +0 -1
  491. package/build/implementations/http/doc-registry.test.js.map +0 -1
  492. package/build/implementations/http/error-dispatch.d.ts +0 -76
  493. package/build/implementations/http/error-dispatch.js.map +0 -1
  494. package/build/implementations/http/error-dispatch.test.js +0 -254
  495. package/build/implementations/http/error-dispatch.test.js.map +0 -1
  496. package/build/implementations/http/error-taxonomy.js.map +0 -1
  497. package/build/implementations/http/error-taxonomy.test.js.map +0 -1
  498. package/build/implementations/http/hono/docs/http-doc.js.map +0 -1
  499. package/build/implementations/http/hono/docs/http-stream-doc.js.map +0 -1
  500. package/build/implementations/http/hono/docs/rpc-doc.js.map +0 -1
  501. package/build/implementations/http/hono/docs/stream-doc.js.map +0 -1
  502. package/build/implementations/http/hono/handlers/http-stream.js +0 -123
  503. package/build/implementations/http/hono/handlers/http-stream.js.map +0 -1
  504. package/build/implementations/http/hono/handlers/http-stream.test.js.map +0 -1
  505. package/build/implementations/http/hono/handlers/http.js +0 -110
  506. package/build/implementations/http/hono/handlers/http.js.map +0 -1
  507. package/build/implementations/http/hono/handlers/http.test.js.map +0 -1
  508. package/build/implementations/http/hono/handlers/rpc.js +0 -32
  509. package/build/implementations/http/hono/handlers/rpc.js.map +0 -1
  510. package/build/implementations/http/hono/handlers/rpc.test.js.map +0 -1
  511. package/build/implementations/http/hono/handlers/stream.d.ts +0 -23
  512. package/build/implementations/http/hono/handlers/stream.js +0 -147
  513. package/build/implementations/http/hono/handlers/stream.js.map +0 -1
  514. package/build/implementations/http/hono/handlers/stream.test.js.map +0 -1
  515. package/build/implementations/http/hono/index.js.map +0 -1
  516. package/build/implementations/http/hono/index.test.js.map +0 -1
  517. package/build/implementations/http/hono/path.js.map +0 -1
  518. package/build/implementations/http/hono/path.test.js +0 -83
  519. package/build/implementations/http/hono/path.test.js.map +0 -1
  520. package/build/implementations/http/hono/types.d.ts +0 -51
  521. package/build/implementations/http/hono/types.js.map +0 -1
  522. package/build/implementations/http/on-request-error.test.js.map +0 -1
  523. package/build/implementations/http/route-errors.test.js.map +0 -1
  524. package/build/index.d.ts +0 -175
  525. package/build/index.js +0 -47
  526. package/build/index.js.map +0 -1
  527. package/build/index.test.js.map +0 -1
  528. package/build/migration.test.js.map +0 -1
  529. package/build/schema/extract-json-schema.d.ts +0 -2
  530. package/build/schema/extract-json-schema.js +0 -12
  531. package/build/schema/extract-json-schema.js.map +0 -1
  532. package/build/schema/extract-json-schema.test.js +0 -23
  533. package/build/schema/extract-json-schema.test.js.map +0 -1
  534. package/build/schema/parser.d.ts +0 -36
  535. package/build/schema/parser.js +0 -210
  536. package/build/schema/parser.js.map +0 -1
  537. package/build/schema/parser.test.js +0 -120
  538. package/build/schema/parser.test.js.map +0 -1
  539. package/build/schema/resolve-schema-lib.d.ts +0 -12
  540. package/build/schema/resolve-schema-lib.js +0 -11
  541. package/build/schema/resolve-schema-lib.js.map +0 -1
  542. package/build/schema/resolve-schema-lib.test.js +0 -17
  543. package/build/schema/resolve-schema-lib.test.js.map +0 -1
  544. package/build/schema/types.d.ts +0 -8
  545. package/build/schema/types.js +0 -2
  546. package/build/stack-utils.d.ts +0 -25
  547. package/build/stack-utils.js.map +0 -1
  548. package/build/stack-utils.test.js.map +0 -1
  549. package/build/types.d.ts +0 -142
  550. package/build/types.js +0 -2
  551. package/build/types.js.map +0 -1
  552. package/docs/decisions/2026-06-02-monorepo-split-evaluation.md +0 -80
  553. package/docs/handoffs/ajsc-named-type-collision.md +0 -134
  554. package/docs/handoffs/ajsc-named-type-support.md +0 -181
  555. package/docs/handoffs/shared-models-auto-resolve-response.md +0 -181
  556. package/docs/npm-workspaces-migration-plan.md +0 -611
  557. package/docs/superpowers/plans/2026-04-24-doc-registry-simplification.md +0 -886
  558. package/docs/superpowers/plans/2026-04-24-kotlin-codegen-target.md +0 -1265
  559. package/docs/superpowers/plans/2026-04-25-ajsc-v7-kotlin-polish.md +0 -1993
  560. package/docs/superpowers/plans/2026-04-29-safe-result-api.md +0 -2293
  561. package/docs/superpowers/plans/2026-05-07-astro-adapter.md +0 -1391
  562. package/docs/superpowers/plans/2026-05-08-create-http.md +0 -3355
  563. package/docs/superpowers/plans/2026-05-08-hono-app-builder-convergence.md +0 -3365
  564. package/docs/superpowers/plans/2026-06-05-dx-feedback-round.md +0 -1292
  565. package/docs/superpowers/plans/2026-06-06-shared-models-convention-and-diagnostics.md +0 -659
  566. package/docs/superpowers/specs/2026-04-24-kotlin-swift-codegen-design.md +0 -401
  567. package/docs/superpowers/specs/2026-04-25-ajsc-v7-kotlin-polish-design.md +0 -314
  568. package/docs/superpowers/specs/2026-04-25-swift-codegen-design.md +0 -264
  569. package/docs/superpowers/specs/2026-04-29-safe-result-api-design.md +0 -324
  570. package/docs/superpowers/specs/2026-05-07-astro-adapter-design.md +0 -252
  571. package/docs/superpowers/specs/2026-05-08-create-http-design.md +0 -409
  572. package/docs/superpowers/specs/2026-05-08-hono-app-builder-convergence-design.md +0 -411
  573. package/docs/superpowers/specs/2026-06-05-dx-feedback-round-design.md +0 -285
  574. package/src/create-http-stream.ts +0 -191
  575. package/src/create-http.ts +0 -210
  576. package/src/create-stream.ts +0 -228
  577. package/src/create.ts +0 -172
  578. package/src/implementations/http/README.md +0 -390
  579. package/src/implementations/http/error-dispatch.test.ts +0 -283
  580. package/src/implementations/http/error-dispatch.ts +0 -176
  581. package/src/implementations/http/hono/handlers/http-stream.ts +0 -152
  582. package/src/implementations/http/hono/handlers/http.ts +0 -145
  583. package/src/implementations/http/hono/handlers/rpc.ts +0 -54
  584. package/src/implementations/http/hono/path.test.ts +0 -96
  585. package/src/index.ts +0 -101
  586. package/src/schema/extract-json-schema.test.ts +0 -25
  587. package/src/schema/extract-json-schema.ts +0 -15
  588. package/src/schema/parser.test.ts +0 -182
  589. package/src/schema/parser.ts +0 -265
  590. package/src/schema/resolve-schema-lib.test.ts +0 -19
  591. package/src/schema/resolve-schema-lib.ts +0 -29
  592. package/src/schema/types.ts +0 -20
  593. package/src/types.ts +0 -133
  594. /package/build/{implementations/http → adapters}/astro/astro-context.d.ts +0 -0
  595. /package/build/{implementations/http → adapters}/astro/astro-context.js +0 -0
  596. /package/build/{implementations/http → adapters}/astro/create-handler.d.ts +0 -0
  597. /package/build/{implementations/http → adapters}/astro/create-handler.js +0 -0
  598. /package/build/{implementations/http → adapters}/astro/index.d.ts +0 -0
  599. /package/build/{implementations/http → adapters}/astro/index.js +0 -0
  600. /package/build/{implementations/http → adapters}/astro/index.test.d.ts +0 -0
  601. /package/build/{implementations/http → adapters}/astro/rewrite-request.d.ts +0 -0
  602. /package/build/{implementations/http → adapters}/astro/rewrite-request.js +0 -0
  603. /package/build/{create-http-stream.test.d.ts → adapters/hono/envelope-parity.test.d.ts} +0 -0
  604. /package/build/{implementations/http → adapters}/hono/handlers/http-stream.test.d.ts +0 -0
  605. /package/build/{implementations/http → adapters}/hono/handlers/http.test.d.ts +0 -0
  606. /package/build/{implementations/http → adapters}/hono/handlers/rpc.test.d.ts +0 -0
  607. /package/build/{implementations/http → adapters}/hono/handlers/stream.test.d.ts +0 -0
  608. /package/build/{implementations/http → adapters}/hono/index.test.d.ts +0 -0
  609. /package/build/{implementations/http → adapters/hono}/on-request-error.test.d.ts +0 -0
  610. /package/build/{implementations/http → adapters/hono}/route-errors.test.d.ts +0 -0
  611. /package/build/{create-http.test.d.ts → client/freeze.test.d.ts} +0 -0
  612. /package/build/{create-stream.test.d.ts → codegen/goldens.test.d.ts} +0 -0
  613. /package/build/{create.test.d.ts → core/create-http-stream.test.d.ts} +0 -0
  614. /package/build/{doc-envelope.test.d.ts → core/create-http.test.d.ts} +0 -0
  615. /package/build/{errors.test.d.ts → core/create-stream.test.d.ts} +0 -0
  616. /package/build/{implementations/http/doc-registry.test.d.ts → core/create.test.d.ts} +0 -0
  617. /package/build/{implementations/http/error-dispatch.test.d.ts → core/definition-site.test.d.ts} +0 -0
  618. /package/build/{implementations/http/error-taxonomy.test.d.ts → core/errors.test.d.ts} +0 -0
  619. /package/build/{errors.test.js → core/errors.test.js} +0 -0
  620. /package/build/{implementations/http/hono/path.test.d.ts → core/factory-options.test.d.ts} +0 -0
  621. /package/build/{migration.test.d.ts → core/migration.test.d.ts} +0 -0
  622. /package/build/{index.test.d.ts → core/procedures.test.d.ts} +0 -0
  623. /package/build/{implementations/http/hono → core}/types.js +0 -0
  624. /package/build/schema/{extract-json-schema.test.d.ts → adapter.test.d.ts} +0 -0
  625. /package/build/schema/{parser.test.d.ts → compile.test.d.ts} +0 -0
  626. /package/build/schema/{resolve-schema-lib.test.d.ts → typebox.test.d.ts} +0 -0
  627. /package/build/{stack-utils.test.d.ts → server/context.test.d.ts} +0 -0
  628. /package/build/{doc-envelope.js → server/doc-envelope.js} +0 -0
  629. /package/build/{doc-envelope.test.js → server/doc-envelope.test.js} +0 -0
  630. /package/build/{implementations → server}/types.js +0 -0
  631. /package/src/{implementations/http → adapters}/astro/README.md +0 -0
  632. /package/src/{implementations/http → adapters}/astro/astro-context.ts +0 -0
  633. /package/src/{implementations/http → adapters}/astro/create-handler.ts +0 -0
  634. /package/src/{implementations/http → adapters}/astro/index.ts +0 -0
  635. /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
  })