@ykdz/template 0.0.38 → 0.0.41

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 (227) hide show
  1. package/dist/application.d.ts +1 -52
  2. package/dist/application.js +0 -81
  3. package/dist/cli.js +14 -28
  4. package/dist/input-schemas.d.ts +30 -0
  5. package/dist/input-schemas.js +75 -0
  6. package/dist/main.d.ts +3 -14
  7. package/dist/main.js +496 -378
  8. package/dist/result-schemas.d.ts +222 -0
  9. package/dist/result-schemas.js +74 -0
  10. package/dist/standard-schema.d.ts +3 -0
  11. package/dist/standard-schema.js +29 -0
  12. package/node_modules/@standard-schema/spec/LICENSE +21 -0
  13. package/node_modules/@standard-schema/spec/README.md +198 -0
  14. package/node_modules/@standard-schema/spec/dist/index.cjs +18 -0
  15. package/node_modules/@standard-schema/spec/dist/index.d.cts +119 -0
  16. package/node_modules/@standard-schema/spec/dist/index.d.ts +119 -0
  17. package/node_modules/@standard-schema/spec/dist/index.js +0 -0
  18. package/node_modules/@standard-schema/spec/package.json +52 -0
  19. package/node_modules/@types/node/LICENSE +21 -0
  20. package/node_modules/@types/node/README.md +15 -0
  21. package/node_modules/@types/node/assert/strict.d.ts +111 -0
  22. package/node_modules/@types/node/assert.d.ts +1115 -0
  23. package/node_modules/@types/node/async_hooks.d.ts +619 -0
  24. package/node_modules/@types/node/buffer.buffer.d.ts +477 -0
  25. package/node_modules/@types/node/buffer.d.ts +1941 -0
  26. package/node_modules/@types/node/child_process.d.ts +1480 -0
  27. package/node_modules/@types/node/cluster.d.ts +578 -0
  28. package/node_modules/@types/node/compatibility/iterators.d.ts +21 -0
  29. package/node_modules/@types/node/console.d.ts +453 -0
  30. package/node_modules/@types/node/constants.d.ts +21 -0
  31. package/node_modules/@types/node/crypto.d.ts +5484 -0
  32. package/node_modules/@types/node/dgram.d.ts +715 -0
  33. package/node_modules/@types/node/diagnostics_channel.d.ts +576 -0
  34. package/node_modules/@types/node/dns/promises.d.ts +503 -0
  35. package/node_modules/@types/node/dns.d.ts +923 -0
  36. package/node_modules/@types/node/domain.d.ts +166 -0
  37. package/node_modules/@types/node/events.d.ts +967 -0
  38. package/node_modules/@types/node/fs/promises.d.ts +1354 -0
  39. package/node_modules/@types/node/fs.d.ts +4777 -0
  40. package/node_modules/@types/node/globals.d.ts +170 -0
  41. package/node_modules/@types/node/globals.typedarray.d.ts +41 -0
  42. package/node_modules/@types/node/http.d.ts +2246 -0
  43. package/node_modules/@types/node/http2.d.ts +2882 -0
  44. package/node_modules/@types/node/https.d.ts +585 -0
  45. package/node_modules/@types/node/index.d.ts +101 -0
  46. package/node_modules/@types/node/inspector.d.ts +323 -0
  47. package/node_modules/@types/node/inspector.generated.d.ts +4406 -0
  48. package/node_modules/@types/node/module.d.ts +844 -0
  49. package/node_modules/@types/node/net.d.ts +1197 -0
  50. package/node_modules/@types/node/os.d.ts +507 -0
  51. package/node_modules/@types/node/package.json +140 -0
  52. package/node_modules/@types/node/path.d.ts +200 -0
  53. package/node_modules/@types/node/perf_hooks.d.ts +1054 -0
  54. package/node_modules/@types/node/process.d.ts +2101 -0
  55. package/node_modules/@types/node/punycode.d.ts +117 -0
  56. package/node_modules/@types/node/querystring.d.ts +152 -0
  57. package/node_modules/@types/node/readline/promises.d.ts +161 -0
  58. package/node_modules/@types/node/readline.d.ts +600 -0
  59. package/node_modules/@types/node/repl.d.ts +438 -0
  60. package/node_modules/@types/node/sea.d.ts +162 -0
  61. package/node_modules/@types/node/sqlite.d.ts +1182 -0
  62. package/node_modules/@types/node/stream/consumers.d.ts +45 -0
  63. package/node_modules/@types/node/stream/promises.d.ts +90 -0
  64. package/node_modules/@types/node/stream/web.d.ts +596 -0
  65. package/node_modules/@types/node/stream.d.ts +1707 -0
  66. package/node_modules/@types/node/string_decoder.d.ts +67 -0
  67. package/node_modules/@types/node/test.d.ts +2656 -0
  68. package/node_modules/@types/node/timers/promises.d.ts +108 -0
  69. package/node_modules/@types/node/timers.d.ts +285 -0
  70. package/node_modules/@types/node/tls.d.ts +1314 -0
  71. package/node_modules/@types/node/trace_events.d.ts +197 -0
  72. package/node_modules/@types/node/ts5.6/buffer.buffer.d.ts +473 -0
  73. package/node_modules/@types/node/ts5.6/compatibility/float16array.d.ts +71 -0
  74. package/node_modules/@types/node/ts5.6/globals.typedarray.d.ts +36 -0
  75. package/node_modules/@types/node/ts5.6/index.d.ts +103 -0
  76. package/node_modules/@types/node/ts5.7/compatibility/float16array.d.ts +72 -0
  77. package/node_modules/@types/node/ts5.7/index.d.ts +103 -0
  78. package/node_modules/@types/node/tty.d.ts +209 -0
  79. package/node_modules/@types/node/url.d.ts +1103 -0
  80. package/node_modules/@types/node/util.d.ts +2376 -0
  81. package/node_modules/@types/node/v8.d.ts +1010 -0
  82. package/node_modules/@types/node/vm.d.ts +1160 -0
  83. package/node_modules/@types/node/wasi.d.ts +202 -0
  84. package/node_modules/@types/node/web-globals/abortcontroller.d.ts +34 -0
  85. package/node_modules/@types/node/web-globals/crypto.d.ts +32 -0
  86. package/node_modules/@types/node/web-globals/domexception.d.ts +68 -0
  87. package/node_modules/@types/node/web-globals/events.d.ts +97 -0
  88. package/node_modules/@types/node/web-globals/fetch.d.ts +62 -0
  89. package/node_modules/@types/node/web-globals/navigator.d.ts +25 -0
  90. package/node_modules/@types/node/web-globals/storage.d.ts +24 -0
  91. package/node_modules/@types/node/web-globals/streams.d.ts +22 -0
  92. package/node_modules/@types/node/worker_threads.d.ts +894 -0
  93. package/node_modules/@types/node/zlib.d.ts +768 -0
  94. package/node_modules/@valibot/to-json-schema/LICENSE.md +9 -0
  95. package/node_modules/@valibot/to-json-schema/README.md +475 -0
  96. package/node_modules/@valibot/to-json-schema/dist/index.cjs +800 -0
  97. package/node_modules/@valibot/to-json-schema/dist/index.d.cts +991 -0
  98. package/node_modules/@valibot/to-json-schema/dist/index.d.mts +991 -0
  99. package/node_modules/@valibot/to-json-schema/dist/index.min.cjs +1 -0
  100. package/node_modules/@valibot/to-json-schema/dist/index.min.mjs +1 -0
  101. package/node_modules/@valibot/to-json-schema/dist/index.mjs +795 -0
  102. package/node_modules/@valibot/to-json-schema/package.json +75 -0
  103. package/node_modules/@ykdz/cli-contract/LICENSE +21 -0
  104. package/node_modules/@ykdz/cli-contract/dist/atomic-variant-compiler.d.ts +17 -0
  105. package/node_modules/@ykdz/cli-contract/dist/atomic-variant-compiler.js +55 -0
  106. package/node_modules/@ykdz/cli-contract/dist/cli-contract.d.ts +1164 -0
  107. package/node_modules/@ykdz/cli-contract/dist/cli-contract.js +1408 -0
  108. package/node_modules/@ykdz/cli-contract/dist/cli-execution.d.ts +54 -0
  109. package/node_modules/@ykdz/cli-contract/dist/cli-execution.js +708 -0
  110. package/node_modules/@ykdz/cli-contract/dist/cli-invocation.d.ts +174 -0
  111. package/node_modules/@ykdz/cli-contract/dist/cli-invocation.js +524 -0
  112. package/node_modules/@ykdz/cli-contract/dist/cli-write-error.d.ts +10 -0
  113. package/node_modules/@ykdz/cli-contract/dist/cli-write-error.js +14 -0
  114. package/node_modules/@ykdz/cli-contract/dist/contract-definition-error.d.ts +260 -0
  115. package/node_modules/@ykdz/cli-contract/dist/contract-definition-error.js +130 -0
  116. package/node_modules/@ykdz/cli-contract/dist/contract-execution-error.d.ts +63 -0
  117. package/node_modules/@ykdz/cli-contract/dist/contract-execution-error.js +17 -0
  118. package/node_modules/@ykdz/cli-contract/dist/contract-schema-compiler.d.ts +6 -0
  119. package/node_modules/@ykdz/cli-contract/dist/contract-schema-compiler.js +532 -0
  120. package/node_modules/@ykdz/cli-contract/dist/contract-schema.d.ts +14 -0
  121. package/node_modules/@ykdz/cli-contract/dist/contract-schema.js +1 -0
  122. package/node_modules/@ykdz/cli-contract/dist/description.d.ts +2 -0
  123. package/node_modules/@ykdz/cli-contract/dist/description.js +13 -0
  124. package/node_modules/@ykdz/cli-contract/dist/index.d.ts +8 -0
  125. package/node_modules/@ykdz/cli-contract/dist/index.js +7 -0
  126. package/node_modules/@ykdz/cli-contract/dist/json-value.d.ts +4 -0
  127. package/node_modules/@ykdz/cli-contract/dist/json-value.js +64 -0
  128. package/node_modules/@ykdz/cli-contract/dist/node.d.ts +21 -0
  129. package/node_modules/@ykdz/cli-contract/dist/node.js +109 -0
  130. package/node_modules/@ykdz/cli-contract/dist/outcome-fact.d.ts +162 -0
  131. package/node_modules/@ykdz/cli-contract/dist/outcome-fact.js +113 -0
  132. package/node_modules/@ykdz/cli-contract/dist/outcome-wire.d.ts +53 -0
  133. package/node_modules/@ykdz/cli-contract/dist/outcome-wire.js +189 -0
  134. package/node_modules/@ykdz/cli-contract/dist/stream-variant-compiler.d.ts +16 -0
  135. package/node_modules/@ykdz/cli-contract/dist/stream-variant-compiler.js +45 -0
  136. package/node_modules/@ykdz/cli-contract/package.json +69 -0
  137. package/node_modules/@ykdz/template-builtin-presets/dist/src/foundation.d.ts +20 -0
  138. package/node_modules/@ykdz/template-builtin-presets/dist/src/foundation.js +38 -2
  139. package/node_modules/@ykdz/template-builtin-presets/dist/src/generation-record.d.ts +19 -0
  140. package/node_modules/@ykdz/template-builtin-presets/dist/src/generation-record.js +1 -1
  141. package/node_modules/@ykdz/template-builtin-presets/dist/src/index.d.ts +2 -1
  142. package/node_modules/@ykdz/template-builtin-presets/dist/src/index.js +2 -1
  143. package/node_modules/@ykdz/template-builtin-presets/dist/src/ts-cli/definition.js +9 -5
  144. package/node_modules/@ykdz/template-builtin-presets/package.json +2 -1
  145. package/node_modules/@ykdz/template-builtin-presets/templates/foundation/AGENTS.md.template +5 -3
  146. package/node_modules/@ykdz/template-builtin-presets/templates/{shared/devcontainer/node-pnpm.Dockerfile → foundation/devcontainer/base.Dockerfile} +14 -1
  147. package/node_modules/@ykdz/template-builtin-presets/templates/rust-bin/devcontainer/rust.Dockerfile +1 -5
  148. package/node_modules/@ykdz/template-builtin-presets/templates/shared/devcontainer/github-cli.Dockerfile +1 -3
  149. package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/publication/artifact.ts +128 -59
  150. package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/publication/handoff.ts +80 -8
  151. package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/publication/readiness.ts +7 -3
  152. package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/src/cli.ts +9 -19
  153. package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/src/main.ts +144 -60
  154. package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/src/standard-schema.ts +37 -0
  155. package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/test/e2e/journeys/lookup.journey.ts +71 -0
  156. package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/test/integration/command.test.ts +118 -59
  157. package/node_modules/@ykdz/template-core/dist/dependency-catalog.d.ts +0 -1
  158. package/node_modules/@ykdz/template-core/dist/dependency-catalog.js +0 -19
  159. package/node_modules/@ykdz/template-core/dist/pnpm-workspace.yaml +3 -2
  160. package/node_modules/@ykdz/template-core/dist/project-blueprint.d.ts +15 -0
  161. package/node_modules/@ykdz/template-core/dist/project-blueprint.js +1 -1
  162. package/node_modules/@ykdz/template-core/dist/project-github.d.ts +0 -59
  163. package/node_modules/@ykdz/template-core/dist/project-github.js +0 -67
  164. package/node_modules/undici-types/LICENSE +21 -0
  165. package/node_modules/undici-types/README.md +6 -0
  166. package/node_modules/undici-types/agent.d.ts +32 -0
  167. package/node_modules/undici-types/api.d.ts +43 -0
  168. package/node_modules/undici-types/balanced-pool.d.ts +30 -0
  169. package/node_modules/undici-types/cache-interceptor.d.ts +179 -0
  170. package/node_modules/undici-types/cache.d.ts +36 -0
  171. package/node_modules/undici-types/client-stats.d.ts +15 -0
  172. package/node_modules/undici-types/client.d.ts +123 -0
  173. package/node_modules/undici-types/connector.d.ts +36 -0
  174. package/node_modules/undici-types/content-type.d.ts +21 -0
  175. package/node_modules/undici-types/cookies.d.ts +30 -0
  176. package/node_modules/undici-types/diagnostics-channel.d.ts +74 -0
  177. package/node_modules/undici-types/dispatcher.d.ts +279 -0
  178. package/node_modules/undici-types/env-http-proxy-agent.d.ts +22 -0
  179. package/node_modules/undici-types/errors.d.ts +177 -0
  180. package/node_modules/undici-types/eventsource.d.ts +66 -0
  181. package/node_modules/undici-types/fetch.d.ts +211 -0
  182. package/node_modules/undici-types/formdata.d.ts +108 -0
  183. package/node_modules/undici-types/global-dispatcher.d.ts +9 -0
  184. package/node_modules/undici-types/global-origin.d.ts +7 -0
  185. package/node_modules/undici-types/h2c-client.d.ts +73 -0
  186. package/node_modules/undici-types/handlers.d.ts +15 -0
  187. package/node_modules/undici-types/header.d.ts +160 -0
  188. package/node_modules/undici-types/index.d.ts +91 -0
  189. package/node_modules/undici-types/interceptors.d.ts +80 -0
  190. package/node_modules/undici-types/mock-agent.d.ts +68 -0
  191. package/node_modules/undici-types/mock-call-history.d.ts +111 -0
  192. package/node_modules/undici-types/mock-client.d.ts +27 -0
  193. package/node_modules/undici-types/mock-errors.d.ts +12 -0
  194. package/node_modules/undici-types/mock-interceptor.d.ts +94 -0
  195. package/node_modules/undici-types/mock-pool.d.ts +27 -0
  196. package/node_modules/undici-types/package.json +55 -0
  197. package/node_modules/undici-types/patch.d.ts +29 -0
  198. package/node_modules/undici-types/pool-stats.d.ts +19 -0
  199. package/node_modules/undici-types/pool.d.ts +41 -0
  200. package/node_modules/undici-types/proxy-agent.d.ts +29 -0
  201. package/node_modules/undici-types/readable.d.ts +68 -0
  202. package/node_modules/undici-types/retry-agent.d.ts +8 -0
  203. package/node_modules/undici-types/retry-handler.d.ts +125 -0
  204. package/node_modules/undici-types/round-robin-pool.d.ts +41 -0
  205. package/node_modules/undici-types/snapshot-agent.d.ts +109 -0
  206. package/node_modules/undici-types/socks5-proxy-agent.d.ts +25 -0
  207. package/node_modules/undici-types/util.d.ts +18 -0
  208. package/node_modules/undici-types/utility.d.ts +7 -0
  209. package/node_modules/undici-types/webidl.d.ts +347 -0
  210. package/node_modules/undici-types/websocket.d.ts +188 -0
  211. package/package.json +5 -3
  212. package/dist/command-result.d.ts +0 -9
  213. package/dist/command-result.js +0 -231
  214. package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/test/e2e/journeys/greet.journey.ts +0 -46
  215. package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/test/unit/greet.test.ts +0 -15
  216. package/node_modules/commander/LICENSE +0 -22
  217. package/node_modules/commander/Readme.md +0 -1172
  218. package/node_modules/commander/index.js +0 -21
  219. package/node_modules/commander/lib/argument.js +0 -147
  220. package/node_modules/commander/lib/command.js +0 -2790
  221. package/node_modules/commander/lib/error.js +0 -36
  222. package/node_modules/commander/lib/help.js +0 -731
  223. package/node_modules/commander/lib/option.js +0 -377
  224. package/node_modules/commander/lib/suggestSimilar.js +0 -99
  225. package/node_modules/commander/package-support.json +0 -19
  226. package/node_modules/commander/package.json +0 -64
  227. package/node_modules/commander/typings/index.d.ts +0 -1113
@@ -0,0 +1,1408 @@
1
+ import { compileAtomicVariants, } from "#/atomic-variant-compiler";
2
+ import { ContractDefinitionError, } from "#/contract-definition-error";
3
+ import { ContractExecutionError } from "#/contract-execution-error";
4
+ import { compileContractSchema, compileInputFields, projectFieldDescriptions, } from "#/contract-schema-compiler";
5
+ import { copySingleLineText, isSingleLineText } from "#/description";
6
+ import { deepFreeze } from "#/json-value";
7
+ import { isIssuedTextProjection } from "#/outcome-fact";
8
+ import { createCompletionWireSchema, createDataWireSchema, createFailureWireSchema, createStreamHeaderWireSchema, createStreamRecordWireSchema, createStreamSuccessWireSchema, createUsageFailureWireSchema, } from "#/outcome-wire";
9
+ import { compileStreamRecords, } from "#/stream-variant-compiler";
10
+ export { createCompletionFact, createDataFact, createFailureFact, createStreamRecordFact, createStreamSuccessFact, isIssuedOutcomeFact, text, } from "#/outcome-fact";
11
+ const cliContractType = Symbol("CliContract.type");
12
+ const executableCommandType = Symbol("ExecutableCommand.type");
13
+ const helpCapabilities = new WeakSet();
14
+ const outputCapabilities = new WeakSet();
15
+ const versionCapabilities = new WeakSet();
16
+ const compiledCliContracts = new WeakMap();
17
+ // 类型约束与定义期检查共用已知属性,避免拼错的声明被静默丢弃。
18
+ const definitionPropertyKeys = {
19
+ cli: [
20
+ "root",
21
+ "help",
22
+ "version",
23
+ "output",
24
+ "usageFailureExitCode",
25
+ "commands",
26
+ ],
27
+ group: [
28
+ "kind",
29
+ "parent",
30
+ "name",
31
+ "aliases",
32
+ "description",
33
+ "helpSupplement",
34
+ "handler",
35
+ ],
36
+ command: [
37
+ "kind",
38
+ "parent",
39
+ "name",
40
+ "aliases",
41
+ "description",
42
+ "helpSupplement",
43
+ "fields",
44
+ "input",
45
+ "success",
46
+ "failures",
47
+ "handler",
48
+ "usageConstraints",
49
+ ],
50
+ field: {
51
+ flag: [
52
+ "kind",
53
+ "longOption",
54
+ "shortAlias",
55
+ "negatedLongOption",
56
+ "description",
57
+ ],
58
+ option: ["kind", "longOption", "shortAlias", "description"],
59
+ positional: ["kind", "description"],
60
+ },
61
+ usage: {
62
+ requires: ["kind", "field", "requires"],
63
+ exclusive: ["kind", "fields"],
64
+ forbiddenCombination: ["kind", "values"],
65
+ value: ["field", "value"],
66
+ },
67
+ success: {
68
+ completion: ["kind", "text"],
69
+ data: ["kind", "variants"],
70
+ stream: ["kind", "records", "text"],
71
+ },
72
+ atomicVariant: ["description", "schema", "exitCode", "text"],
73
+ streamRecord: ["description", "schema", "text"],
74
+ capability: {
75
+ help: ["shortAlias", "headings", "wording"],
76
+ version: ["value", "description", "shortAlias"],
77
+ output: ["defaultFormat", "text", "compatibilityFlags"],
78
+ },
79
+ };
80
+ export function isCompiledExecutable(command) {
81
+ return command.kind === "rootCommand" || command.kind === "command";
82
+ }
83
+ export function helpCapability(definition = {}) {
84
+ assertKnownCapabilityOptions(definition, definitionPropertyKeys.capability.help, "help");
85
+ if (definition.shortAlias !== undefined && definition.shortAlias !== "-h") {
86
+ throw new TypeError("帮助短别名必须是 -h");
87
+ }
88
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- helpCapability 由本工厂签发并登记到 WeakSet,类型品牌不在运行时输出。
89
+ const capability = Object.freeze({
90
+ kind: "helpCapability",
91
+ longOption: "--help",
92
+ ...(definition.shortAlias === undefined
93
+ ? {}
94
+ : { shortAlias: definition.shortAlias }),
95
+ ...(definition.headings === undefined
96
+ ? {}
97
+ : { headings: copyHelpHeadings(definition.headings) }),
98
+ ...(definition.wording === undefined
99
+ ? {}
100
+ : { wording: copyHelpFactWording(definition.wording) }),
101
+ });
102
+ helpCapabilities.add(capability);
103
+ return capability;
104
+ }
105
+ function copyHelpHeadings(value) {
106
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
107
+ throw new TypeError("帮助标题必须是对象");
108
+ }
109
+ const allowed = new Set([
110
+ "usage",
111
+ "commands",
112
+ "arguments",
113
+ "options",
114
+ "constraints",
115
+ "supplement",
116
+ ]);
117
+ const headings = {};
118
+ for (const [key, heading] of Object.entries(value)) {
119
+ if (!allowed.has(key))
120
+ throw new TypeError("帮助标题包含未知段落");
121
+ headings[key] = copySingleLineText(heading, `帮助标题 ${key}`);
122
+ }
123
+ return Object.freeze(headings);
124
+ }
125
+ const helpTemplatePlaceholders = {
126
+ choices: ["choices"],
127
+ default: ["value"],
128
+ requires: ["field", "requires"],
129
+ exclusive: ["fields"],
130
+ forbiddenCombination: ["values"],
131
+ };
132
+ function copyHelpFactWording(value) {
133
+ if (!isPlainRecord(value)) {
134
+ throw new TypeError("帮助事实文案必须是对象");
135
+ }
136
+ const wording = {};
137
+ for (const [slot, raw] of Object.entries(value)) {
138
+ if (slot === "commandPlaceholder") {
139
+ const label = copySingleLineText(raw, "命令组占位词");
140
+ if (label.trim().length === 0 ||
141
+ label.includes("<") ||
142
+ label.includes(">")) {
143
+ throw new TypeError("命令组占位词无效");
144
+ }
145
+ wording[slot] = label;
146
+ continue;
147
+ }
148
+ if (!isHelpTemplateSlot(slot)) {
149
+ throw new TypeError(`帮助事实文案包含未知槽位 ${slot}`);
150
+ }
151
+ const template = copySingleLineText(raw, `帮助事实模板 ${slot}`);
152
+ const required = new Set(helpTemplatePlaceholders[slot]);
153
+ const placeholders = [
154
+ ...template.matchAll(/\{([A-Za-z][A-Za-z0-9]*)\}/g),
155
+ ].map((match) => match[1] ?? "");
156
+ const outside = template.replace(/\{[A-Za-z][A-Za-z0-9]*\}/g, "");
157
+ if (template.trim().length === 0 ||
158
+ outside.includes("{") ||
159
+ outside.includes("}") ||
160
+ placeholders.some((placeholder) => !required.has(placeholder)) ||
161
+ [...required].some((placeholder) => !placeholders.includes(placeholder))) {
162
+ throw new TypeError(`帮助事实模板 ${slot} 的占位符无效`);
163
+ }
164
+ wording[slot] = template;
165
+ }
166
+ return Object.freeze(wording);
167
+ }
168
+ function isHelpTemplateSlot(slot) {
169
+ return Object.hasOwn(helpTemplatePlaceholders, slot);
170
+ }
171
+ /**
172
+ * 在根契约显式装配版本提前请求,并自动覆盖整棵命令树。`value` 与可选
173
+ * `description` 均由消费者拥有;核心只注册固定 spelling、投影帮助并写出 value,
174
+ * 不读取包元数据、不添加前缀或默认说明,也不把版本当作命令输入。
175
+ */
176
+ export function versionCapability(definition) {
177
+ assertKnownCapabilityOptions(definition, definitionPropertyKeys.capability.version, "version");
178
+ const value = copyVersionTextLine(definition.value);
179
+ const description = definition.description === undefined
180
+ ? undefined
181
+ : copyVersionLine(definition.description);
182
+ if (definition.shortAlias !== undefined && definition.shortAlias !== "-V") {
183
+ throw new TypeError("版本短别名必须是 -V");
184
+ }
185
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- versionCapability 由本工厂签发并登记到 WeakSet,类型品牌不在运行时输出。
186
+ const capability = Object.freeze({
187
+ kind: "versionCapability",
188
+ value,
189
+ ...(description === undefined ? {} : { description }),
190
+ ...(definition.shortAlias === undefined
191
+ ? {}
192
+ : { shortAlias: definition.shortAlias }),
193
+ });
194
+ versionCapabilities.add(capability);
195
+ return capability;
196
+ }
197
+ function copyVersionTextLine(value) {
198
+ if (typeof value !== "object" ||
199
+ value === null ||
200
+ !("kind" in value) ||
201
+ value.kind !== "line" ||
202
+ !("value" in value)) {
203
+ throw new TypeError("版本值必须是合法单行文本");
204
+ }
205
+ return copySingleLineText(value.value, "版本值");
206
+ }
207
+ function copyVersionLine(value, label = "版本描述") {
208
+ return copySingleLineText(value, label);
209
+ }
210
+ function compileHelpSupplement(command, value, issues) {
211
+ if (value === undefined)
212
+ return undefined;
213
+ if (!isIssuedTextProjection(value) || value.kind !== "lines") {
214
+ issues.push({ code: "invalidHelpSupplement", command });
215
+ return undefined;
216
+ }
217
+ return deepFreeze([...value.lines]);
218
+ }
219
+ function compileControls(definition) {
220
+ const version = isVersionCapability(definition.version)
221
+ ? definition.version
222
+ : undefined;
223
+ return deepFreeze({
224
+ help: {
225
+ longOption: definition.help.longOption,
226
+ ...(definition.help.shortAlias === undefined
227
+ ? {}
228
+ : { shortAlias: definition.help.shortAlias }),
229
+ ...(definition.help.headings === undefined
230
+ ? {}
231
+ : { headings: definition.help.headings }),
232
+ ...(definition.help.wording === undefined
233
+ ? {}
234
+ : { wording: definition.help.wording }),
235
+ },
236
+ ...(version === undefined
237
+ ? {}
238
+ : {
239
+ version: {
240
+ longOption: "--version",
241
+ ...(version.shortAlias === undefined
242
+ ? {}
243
+ : { shortAlias: version.shortAlias }),
244
+ value: version.value,
245
+ ...(version.description === undefined
246
+ ? {}
247
+ : { description: version.description }),
248
+ },
249
+ }),
250
+ output: {
251
+ defaultFormat: definition.output.defaultFormat,
252
+ formats: definition.output.formats,
253
+ ...(definition.output.formats.includes("text")
254
+ ? { selector: "--output-format" }
255
+ : {}),
256
+ ...(Object.keys(definition.output.compatibilityFlags).length === 0
257
+ ? {}
258
+ : { compatibilityFlags: definition.output.compatibilityFlags }),
259
+ },
260
+ });
261
+ }
262
+ /**
263
+ * 在根契约显式装配一次应用结果的输出格式能力,并自动覆盖整棵命令树。
264
+ * 它把 selector 与兼容 flag 保留为调用控制,不进入命令 input 或 handler;帮助、版本
265
+ * 与用法失败使用各自固定投影。`defaultFormat`、text 支持和兼容映射必须由消费者
266
+ * 显式选择,不能在命令节点另行装配格式或输出 profile。
267
+ */
268
+ export function outputCapability(definition) {
269
+ assertKnownCapabilityOptions(definition, definitionPropertyKeys.capability.output, "output");
270
+ const formats = definition.defaultFormat === "text" || definition.text === true
271
+ ? ["structured", "text"]
272
+ : ["structured"];
273
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- outputCapability 由本工厂签发并登记到 WeakSet,类型品牌不在运行时输出。
274
+ const capability = Object.freeze({
275
+ kind: "outputCapability",
276
+ defaultFormat: definition.defaultFormat,
277
+ formats: Object.freeze(formats),
278
+ compatibilityFlags: Object.freeze(definition.compatibilityFlags === undefined
279
+ ? {}
280
+ : definition.compatibilityFlags),
281
+ });
282
+ outputCapabilities.add(capability);
283
+ return capability;
284
+ }
285
+ function assertKnownCapabilityOptions(definition, allowed, capability) {
286
+ for (const property of Object.keys(definition)) {
287
+ if (!allowed.includes(property)) {
288
+ throw new TypeError(`${capability} 能力包含未知属性 ${property}`);
289
+ }
290
+ }
291
+ }
292
+ /**
293
+ * 定义一个闭合 CLI 契约。根级 help 与 output 能力在此处一次装配,并据此约束根命令的结果投影;
294
+ * 动态声明仍会在定义期报告 `ContractDefinitionError`,不能以它替代静态可判定声明的类型检查。
295
+ * @see {@link https://github.com/YKDZ/cli-contract/tree/v0.2.1/packages/example | 端到端示例}
296
+ */
297
+ export function defineCli() {
298
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- 公共泛型定义接口由此运行时编译入口实现,类型参数只约束调用方。
299
+ const define = ((definition) => compileCli(definition));
300
+ Object.defineProperty(define, "command", {
301
+ configurable: true,
302
+ value: (command) => (definition) => Object.freeze({
303
+ [command]: Object.freeze({
304
+ ...definition,
305
+ [executableCommandType]: true,
306
+ }),
307
+ }),
308
+ });
309
+ return define;
310
+ }
311
+ function compileCli(definition) {
312
+ const issues = [];
313
+ collectUnknownDefinitionProperties(definition, issues);
314
+ const isHierarchy = definition.commands[definition.root]?.kind === "rootGroup";
315
+ const helpWording = helpCapabilities.has(definition.help)
316
+ ? definition.help.wording
317
+ : undefined;
318
+ if (isTextOutputEnabled(definition.output)) {
319
+ for (const slot of ["choices", "default"]) {
320
+ if (helpWording?.[slot] === undefined) {
321
+ reportMissingHelpFactTemplate(issues, definition.root, slot);
322
+ }
323
+ }
324
+ }
325
+ let orderedIds;
326
+ if (isHierarchy) {
327
+ collectCliBaseIssues(definition, issues);
328
+ orderedIds = orderHierarchy(definition, issues);
329
+ if (helpWording?.commandPlaceholder === undefined) {
330
+ reportMissingHelpFactTemplate(issues, definition.root, "commandPlaceholder");
331
+ }
332
+ }
333
+ else {
334
+ orderedIds =
335
+ collectRootDefinitionIssues(definition, issues) === undefined
336
+ ? []
337
+ : [definition.root];
338
+ }
339
+ if (isHierarchy && issues.length > 0) {
340
+ throw new ContractDefinitionError(
341
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- issues.length 已检查为正数,动态数组在此恢复非空元组类型。
342
+ issues);
343
+ }
344
+ const runtimeCommands = {};
345
+ const grammarNodes = [];
346
+ const manifestCommands = {};
347
+ const wire = {};
348
+ for (const id of orderedIds) {
349
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- orderedIds 来自已验证的命令键或根键。
350
+ const node = definition.commands[id];
351
+ const aliases = Object.freeze([...(node.aliases ?? [])]);
352
+ const helpSupplement = compileHelpSupplement(id, node.helpSupplement, issues);
353
+ if (node.kind === "rootGroup" || node.kind === "commandGroup") {
354
+ const usage = deepFreeze({
355
+ command: id,
356
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- 层级契约已在进入循环前检查 commandPlaceholder 必填。
357
+ synopsis: `${commandPath(id, definition.commands)} <${helpWording?.commandPlaceholder}>`,
358
+ });
359
+ const compiledNode = {
360
+ id,
361
+ kind: node.kind,
362
+ ...(node.parent === undefined ? {} : { parent: node.parent }),
363
+ name: node.name,
364
+ aliases,
365
+ description: node.description,
366
+ ...(helpSupplement === undefined ? {} : { helpSupplement }),
367
+ usage,
368
+ };
369
+ runtimeCommands[id] = compiledNode;
370
+ if (id !== definition.root) {
371
+ grammarNodes.push(deepFreeze({
372
+ kind: "commandGroup",
373
+ id,
374
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- 非根命令组的 parent 已在层级定义检查中确认。
375
+ parent: node.parent,
376
+ name: node.name,
377
+ aliases,
378
+ description: node.description,
379
+ ...(helpSupplement === undefined ? {} : { helpSupplement }),
380
+ usage,
381
+ }));
382
+ }
383
+ manifestCommands[id] = deepFreeze({
384
+ kind: node.kind,
385
+ ...(node.parent === undefined ? {} : { parent: node.parent }),
386
+ name: node.name,
387
+ aliases,
388
+ description: node.description,
389
+ ...(helpSupplement === undefined ? {} : { helpSupplement }),
390
+ });
391
+ continue;
392
+ }
393
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- 命令组分支已继续下一轮;其余节点按可执行定义编译。
394
+ const executable = node;
395
+ if (typeof executable.handler !== "function") {
396
+ issues.push({ code: "invalidCommandHandler", command: id });
397
+ }
398
+ const input = compileContractSchema(executable.input, { command: id, location: "input" }, issues);
399
+ const compiledSuccess = compileSuccess(id, executable.success, isTextOutputEnabled(definition.output), issues);
400
+ const compiledFailures = compileFailures(id, executable.failures, issues);
401
+ collectTextPresenterIssues(id, isTextOutputEnabled(definition.output), executable.success, executable.failures, issues);
402
+ const fields = input === undefined
403
+ ? []
404
+ : compileInputFields(executable.fields ?? {}, input.inputSchema, id, issues);
405
+ if (fields.some((field) => field.choices !== undefined) &&
406
+ helpWording?.choices === undefined) {
407
+ reportMissingHelpFactTemplate(issues, id, "choices");
408
+ }
409
+ if (fields.some((field) => field.default !== undefined) &&
410
+ helpWording?.default === undefined) {
411
+ reportMissingHelpFactTemplate(issues, id, "default");
412
+ }
413
+ const describedInput = input === undefined ? undefined : projectFieldDescriptions(input, fields);
414
+ collectControlFieldConflicts(id, fields, definition, issues);
415
+ const usageConstraints = compileUsageConstraints(executable.usageConstraints === undefined
416
+ ? []
417
+ : executable.usageConstraints, fields, id, issues);
418
+ for (const slot of new Set(usageConstraints.map((constraint) => constraint.kind))) {
419
+ if (helpWording?.[slot] === undefined) {
420
+ reportMissingHelpFactTemplate(issues, id, slot);
421
+ }
422
+ }
423
+ const usage = deepFreeze({
424
+ command: id,
425
+ synopsis: createUsageSynopsis(commandPath(id, definition.commands), fields),
426
+ });
427
+ runtimeCommands[id] = Object.freeze({
428
+ id,
429
+ kind: executable.kind,
430
+ ...(executable.parent === undefined ? {} : { parent: executable.parent }),
431
+ name: executable.name,
432
+ aliases,
433
+ description: executable.description,
434
+ ...(helpSupplement === undefined ? {} : { helpSupplement }),
435
+ fields,
436
+ usageConstraints,
437
+ usage,
438
+ input: executable.input,
439
+ success: compiledSuccess.runtime,
440
+ failures: compiledFailures.runtime,
441
+ handler: executable.handler,
442
+ });
443
+ if (executable.kind === "command") {
444
+ grammarNodes.push(deepFreeze({
445
+ kind: "command",
446
+ id,
447
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- kind 为 command 时,层级定义检查已确认 parent。
448
+ parent: executable.parent,
449
+ name: executable.name,
450
+ aliases,
451
+ description: executable.description,
452
+ ...(helpSupplement === undefined ? {} : { helpSupplement }),
453
+ fields,
454
+ ...(usageConstraints.length === 0 ? {} : { usageConstraints }),
455
+ usage,
456
+ }));
457
+ }
458
+ if (describedInput === undefined)
459
+ continue;
460
+ const manifestDetails = {
461
+ description: executable.description,
462
+ ...(helpSupplement === undefined ? {} : { helpSupplement }),
463
+ fields,
464
+ ...(usageConstraints.length === 0 ? {} : { usageConstraints }),
465
+ input: projectUsageConstraints(describedInput, usageConstraints),
466
+ success: compiledSuccess.manifest,
467
+ failures: compiledFailures.manifest,
468
+ };
469
+ manifestCommands[id] =
470
+ executable.kind === "command"
471
+ ? deepFreeze({
472
+ kind: "command",
473
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- kind 为 command 时,层级定义检查已确认 parent。
474
+ parent: executable.parent,
475
+ name: executable.name,
476
+ aliases,
477
+ ...manifestDetails,
478
+ })
479
+ : deepFreeze({
480
+ kind: "rootCommand",
481
+ name: executable.name,
482
+ ...manifestDetails,
483
+ });
484
+ wire[id] = deepFreeze({
485
+ ...compiledSuccess.wire,
486
+ ...(Object.keys(compiledFailures.wire).length === 0
487
+ ? {}
488
+ : { failure: compiledFailures.wire }),
489
+ });
490
+ }
491
+ if (issues.length > 0)
492
+ throw new ContractDefinitionError(
493
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- issues.length 已检查为正数,动态数组在此恢复非空元组类型。
494
+ issues);
495
+ const controls = compileControls(definition);
496
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- 定义检查通过后根命令已在编译循环写入映射。
497
+ const rootCommand = runtimeCommands[definition.root];
498
+ const rootGrammar = isCompiledExecutable(rootCommand)
499
+ ? {
500
+ kind: "rootCommand",
501
+ id: rootCommand.id,
502
+ name: rootCommand.name,
503
+ description: rootCommand.description,
504
+ ...(rootCommand.helpSupplement === undefined
505
+ ? {}
506
+ : { helpSupplement: rootCommand.helpSupplement }),
507
+ fields: rootCommand.fields,
508
+ ...(rootCommand.usageConstraints.length === 0
509
+ ? {}
510
+ : { usageConstraints: rootCommand.usageConstraints }),
511
+ usage: rootCommand.usage,
512
+ }
513
+ : {
514
+ kind: "rootGroup",
515
+ id: rootCommand.id,
516
+ name: rootCommand.name,
517
+ aliases: rootCommand.aliases,
518
+ description: rootCommand.description,
519
+ ...(rootCommand.helpSupplement === undefined
520
+ ? {}
521
+ : { helpSupplement: rootCommand.helpSupplement }),
522
+ usage: rootCommand.usage,
523
+ };
524
+ const grammar = deepFreeze({
525
+ root: rootGrammar,
526
+ nodes: grammarNodes,
527
+ controls,
528
+ });
529
+ const manifest = deepFreeze({
530
+ schemaVersion: "3",
531
+ root: definition.root,
532
+ commands: manifestCommands,
533
+ controls,
534
+ usageFailure: {
535
+ exitCode: definition.usageFailureExitCode,
536
+ wire: createUsageFailureWireSchema(Object.values(runtimeCommands).map((command) => ({
537
+ command: command.id,
538
+ usage: command.usage.synopsis,
539
+ helpArgv: usageFailureHelpArgv(runtimeCommands, command.id),
540
+ }))),
541
+ },
542
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- 根命令的 wire 已由同一编译循环写入映射。
543
+ wire: isHierarchy ? wire : wire[definition.root],
544
+ });
545
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- 契约类型品牌由编译器签发,并登记到 compiledCliContracts。
546
+ const contract = Object.freeze({
547
+ grammar,
548
+ manifest,
549
+ });
550
+ compiledCliContracts.set(contract, {
551
+ contract,
552
+ root: definition.root,
553
+ usageFailureExitCode: definition.usageFailureExitCode,
554
+ commands: deepFreeze(runtimeCommands),
555
+ });
556
+ return contract;
557
+ }
558
+ function reportMissingHelpFactTemplate(issues, command, slot) {
559
+ if (issues.some((issue) => issue.code === "missingHelpFactTemplate" &&
560
+ issue.command === command &&
561
+ issue.slot === slot)) {
562
+ return;
563
+ }
564
+ issues.push({ code: "missingHelpFactTemplate", command, slot });
565
+ }
566
+ function collectUnknownProperties(value, path, allowed, issues) {
567
+ if (!isPlainRecord(value))
568
+ return;
569
+ for (const property of Object.keys(value)) {
570
+ if (!allowed.includes(property)) {
571
+ issues.push({
572
+ code: "unknownDefinitionProperty",
573
+ path: [...path, property],
574
+ });
575
+ }
576
+ }
577
+ }
578
+ function collectUnknownVariantProperties(definitions, path, allowed, issues) {
579
+ if (!isPlainRecord(definitions))
580
+ return;
581
+ for (const [variant, definition] of Object.entries(definitions)) {
582
+ collectUnknownProperties(definition, [...path, variant], allowed, issues);
583
+ }
584
+ }
585
+ function collectUnknownDefinitionProperties(definition, issues) {
586
+ collectUnknownProperties(definition, [], definitionPropertyKeys.cli, issues);
587
+ if (!isPlainRecord(definition.commands))
588
+ return;
589
+ for (const [command, node] of Object.entries(definition.commands)) {
590
+ const path = ["commands", command];
591
+ if (!isPlainRecord(node))
592
+ continue;
593
+ if (node.kind === "rootGroup" || node.kind === "commandGroup") {
594
+ collectUnknownProperties(node, path, definitionPropertyKeys.group, issues);
595
+ continue;
596
+ }
597
+ if (node.kind !== "rootCommand" && node.kind !== "command")
598
+ continue;
599
+ collectUnknownProperties(node, path, definitionPropertyKeys.command, issues);
600
+ if (isPlainRecord(node.fields)) {
601
+ for (const [field, fieldDefinition] of Object.entries(node.fields)) {
602
+ const fieldPath = [...path, "fields", field];
603
+ if (!isPlainRecord(fieldDefinition))
604
+ continue;
605
+ const allowed = fieldDefinition.kind === "flag"
606
+ ? definitionPropertyKeys.field.flag
607
+ : fieldDefinition.kind === "valueOption" ||
608
+ fieldDefinition.kind === "repeatableOption"
609
+ ? definitionPropertyKeys.field.option
610
+ : fieldDefinition.kind === "positional" ||
611
+ fieldDefinition.kind === "variadicPositional"
612
+ ? definitionPropertyKeys.field.positional
613
+ : undefined;
614
+ if (allowed !== undefined) {
615
+ collectUnknownProperties(fieldDefinition, fieldPath, allowed, issues);
616
+ }
617
+ }
618
+ }
619
+ if (Array.isArray(node.usageConstraints)) {
620
+ node.usageConstraints.forEach((constraint, index) => {
621
+ const constraintPath = [...path, "usageConstraints", String(index)];
622
+ if (!isPlainRecord(constraint))
623
+ return;
624
+ const allowed = constraint.kind === "requires"
625
+ ? definitionPropertyKeys.usage.requires
626
+ : constraint.kind === "exclusive"
627
+ ? definitionPropertyKeys.usage.exclusive
628
+ : constraint.kind === "forbiddenCombination"
629
+ ? definitionPropertyKeys.usage.forbiddenCombination
630
+ : undefined;
631
+ if (allowed === undefined)
632
+ return;
633
+ collectUnknownProperties(constraint, constraintPath, allowed, issues);
634
+ if (constraint.kind === "forbiddenCombination" &&
635
+ Array.isArray(constraint.values)) {
636
+ constraint.values.forEach((value, valueIndex) => collectUnknownProperties(value, [...constraintPath, "values", String(valueIndex)], definitionPropertyKeys.usage.value, issues));
637
+ }
638
+ });
639
+ }
640
+ if (isPlainRecord(node.success)) {
641
+ const successPath = [...path, "success"];
642
+ const success = node.success;
643
+ if (success.kind === "completion") {
644
+ collectUnknownProperties(success, successPath, definitionPropertyKeys.success.completion, issues);
645
+ }
646
+ else if (success.kind === "data") {
647
+ collectUnknownProperties(success, successPath, definitionPropertyKeys.success.data, issues);
648
+ collectUnknownVariantProperties(success.variants, [...successPath, "variants"], definitionPropertyKeys.atomicVariant, issues);
649
+ }
650
+ else if (success.kind === "stream") {
651
+ collectUnknownProperties(success, successPath, definitionPropertyKeys.success.stream, issues);
652
+ collectUnknownVariantProperties(success.records, [...successPath, "records"], definitionPropertyKeys.streamRecord, issues);
653
+ }
654
+ }
655
+ collectUnknownVariantProperties(node.failures, [...path, "failures"], definitionPropertyKeys.atomicVariant, issues);
656
+ }
657
+ }
658
+ export function getCompiledCli(contract) {
659
+ const compiled = compiledCliContracts.get(contract);
660
+ if (compiled === undefined) {
661
+ throw new ContractExecutionError([{ code: "invalidCliContract" }]);
662
+ }
663
+ return compiled;
664
+ }
665
+ function collectRootDefinitionIssues(definition, issues) {
666
+ collectCliBaseIssues(definition, issues);
667
+ const commandKeys = Object.keys(definition.commands);
668
+ const command = definition.commands[definition.root];
669
+ if (command === undefined || commandKeys.length !== 1) {
670
+ issues.push({
671
+ code: "invalidRootCommandSet",
672
+ root: definition.root,
673
+ // oxlint-disable-next-line unicorn/no-array-sort -- commandKeys 来自 Object.keys 且不再复用,原地排序不会影响命令对象。
674
+ receivedCommands: commandKeys.sort(),
675
+ });
676
+ }
677
+ if (command === undefined)
678
+ return undefined;
679
+ if (command.kind !== "rootCommand") {
680
+ issues.push({
681
+ code: "invalidRootCommandKind",
682
+ command: definition.root,
683
+ received: typeof command.kind === "string" ? command.kind : null,
684
+ });
685
+ return undefined;
686
+ }
687
+ if (command.name.length === 0) {
688
+ issues.push({
689
+ code: "missingCommandText",
690
+ command: definition.root,
691
+ field: "name",
692
+ });
693
+ }
694
+ if (!isSingleLineText(command.description)) {
695
+ issues.push({
696
+ code: "invalidDescription",
697
+ command: definition.root,
698
+ location: "command",
699
+ received: typeof command.description === "string" ? command.description : null,
700
+ });
701
+ }
702
+ return command;
703
+ }
704
+ function collectCliBaseIssues(definition, issues) {
705
+ if (!/^[a-z][A-Za-z0-9]*$/.test(definition.root)) {
706
+ issues.push({
707
+ code: "invalidCommandIdentity",
708
+ command: definition.root,
709
+ });
710
+ }
711
+ if (!helpCapabilities.has(definition.help)) {
712
+ issues.push({ code: "invalidCapability", capability: "help" });
713
+ }
714
+ if (definition.version !== undefined &&
715
+ !versionCapabilities.has(definition.version)) {
716
+ issues.push({ code: "invalidCapability", capability: "version" });
717
+ }
718
+ if (!outputCapabilities.has(definition.output)) {
719
+ issues.push({ code: "invalidCapability", capability: "output" });
720
+ }
721
+ else if (!definition.output.formats.includes(definition.output.defaultFormat)) {
722
+ issues.push({
723
+ code: "invalidOutputFormat",
724
+ expected: "structured|text",
725
+ received: typeof definition.output.defaultFormat === "string"
726
+ ? definition.output.defaultFormat
727
+ : null,
728
+ });
729
+ }
730
+ else if (!isPlainRecord(definition.output.compatibilityFlags)) {
731
+ issues.push({
732
+ code: "invalidOutputCompatibilityFlags",
733
+ received: outputCompatibilityFlagsKind(definition.output.compatibilityFlags),
734
+ });
735
+ }
736
+ else {
737
+ for (const [flag, format] of Object.entries(definition.output.compatibilityFlags)) {
738
+ if (!/^--[a-z0-9]+(?:-[a-z0-9]+)*$/.test(flag) ||
739
+ flag === "--help" ||
740
+ flag === "--output-format" ||
741
+ (isVersionCapability(definition.version) && flag === "--version") ||
742
+ !definition.output.formats.includes(format)) {
743
+ issues.push({
744
+ code: "invalidOutputCompatibilityFlag",
745
+ flag,
746
+ received: typeof format === "string" ? format : null,
747
+ });
748
+ }
749
+ }
750
+ }
751
+ if (!Number.isInteger(definition.usageFailureExitCode) ||
752
+ definition.usageFailureExitCode < 1 ||
753
+ definition.usageFailureExitCode > 255) {
754
+ issues.push({
755
+ code: "invalidUsageFailureExitCode",
756
+ received: typeof definition.usageFailureExitCode === "number"
757
+ ? definition.usageFailureExitCode
758
+ : null,
759
+ minimum: 1,
760
+ maximum: 255,
761
+ });
762
+ }
763
+ }
764
+ function orderHierarchy(definition, issues) {
765
+ const ids = Object.keys(definition.commands);
766
+ if (!ids.some((id) => definition.commands[id]?.parent === definition.root)) {
767
+ issues.push({ code: "rootGroupWithoutChildren", command: definition.root });
768
+ }
769
+ for (const id of ids) {
770
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- id 来自 Object.keys(definition.commands),此处读取同一对象。
771
+ const node = definition.commands[id];
772
+ if (!/^[a-z][A-Za-z0-9]*$/.test(id))
773
+ issues.push({ code: "invalidCommandIdentity", command: id });
774
+ if (node.name.length === 0)
775
+ issues.push({ code: "missingCommandText", command: id, field: "name" });
776
+ if (!isSingleLineText(node.description))
777
+ issues.push({
778
+ code: "invalidDescription",
779
+ command: id,
780
+ location: "command",
781
+ received: typeof node.description === "string" ? node.description : null,
782
+ });
783
+ if (id === definition.root && node.kind !== "rootGroup") {
784
+ issues.push({
785
+ code: "invalidRootCommandKind",
786
+ command: id,
787
+ received: node.kind,
788
+ });
789
+ }
790
+ if (id === definition.root && node.parent !== undefined) {
791
+ issues.push({
792
+ code: "invalidCommandParent",
793
+ command: id,
794
+ parent: node.parent,
795
+ });
796
+ }
797
+ if (id !== definition.root &&
798
+ (node.kind === "rootGroup" || node.kind === "rootCommand")) {
799
+ issues.push({
800
+ code: "invalidCommandNodeKind",
801
+ command: id,
802
+ received: node.kind,
803
+ });
804
+ }
805
+ if ((node.kind === "rootGroup" || node.kind === "commandGroup") &&
806
+ "handler" in node) {
807
+ issues.push({ code: "invalidCommandHandler", command: id });
808
+ }
809
+ if (id !== definition.root &&
810
+ (node.parent === undefined ||
811
+ definition.commands[node.parent] === undefined)) {
812
+ issues.push({
813
+ code: "invalidCommandParent",
814
+ command: id,
815
+ parent: node.parent ?? null,
816
+ });
817
+ }
818
+ else if (id !== definition.root &&
819
+ node.parent !== undefined &&
820
+ definition.commands[node.parent]?.kind !== "rootGroup" &&
821
+ definition.commands[node.parent]?.kind !== "commandGroup") {
822
+ issues.push({
823
+ code: "invalidCommandParent",
824
+ command: id,
825
+ parent: node.parent,
826
+ });
827
+ }
828
+ for (const spelling of [node.name, ...(node.aliases ?? [])]) {
829
+ if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(spelling)) {
830
+ issues.push({ code: "invalidCommandSpelling", command: id, spelling });
831
+ }
832
+ }
833
+ }
834
+ const cycleMembers = ids.filter((id) => {
835
+ const visited = new Set();
836
+ let current = id;
837
+ while (current !== undefined &&
838
+ definition.commands[current] !== undefined) {
839
+ if (visited.has(current))
840
+ return true;
841
+ visited.add(current);
842
+ current = definition.commands[current]?.parent;
843
+ }
844
+ return false;
845
+ });
846
+ if (cycleMembers.length > 0)
847
+ issues.push({
848
+ code: "commandHierarchyCycle",
849
+ commands: Object.freeze(cycleMembers),
850
+ });
851
+ for (const parent of ids) {
852
+ const spellings = new Map();
853
+ for (const child of ids.filter((id) => definition.commands[id]?.parent === parent)) {
854
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- child 来自同一命令对象的键集合。
855
+ const node = definition.commands[child];
856
+ for (const spelling of [node.name, ...(node.aliases ?? [])]) {
857
+ const owners = spellings.get(spelling) ?? [];
858
+ owners.push(child);
859
+ spellings.set(spelling, owners);
860
+ }
861
+ }
862
+ for (const [spelling, commands] of spellings) {
863
+ if (commands.length > 1) {
864
+ issues.push({
865
+ code: "duplicateCommandSpelling",
866
+ parent,
867
+ spelling,
868
+ commands: Object.freeze(commands),
869
+ });
870
+ }
871
+ }
872
+ }
873
+ const ordered = [];
874
+ const visit = (id) => {
875
+ if (ordered.includes(id))
876
+ return;
877
+ ordered.push(id);
878
+ for (const child of ids)
879
+ if (definition.commands[child]?.parent === id)
880
+ visit(child);
881
+ };
882
+ if (definition.commands[definition.root] !== undefined)
883
+ visit(definition.root);
884
+ return [...ordered, ...ids.filter((id) => !ordered.includes(id))];
885
+ }
886
+ function commandPath(id, commands) {
887
+ return commandPathTokens(id, commands).join(" ");
888
+ }
889
+ function commandPathTokens(id, commands) {
890
+ const names = [];
891
+ let current = id;
892
+ while (current !== undefined) {
893
+ const node = commands[current];
894
+ if (node === undefined)
895
+ break;
896
+ names.unshift(node.name);
897
+ current = node.parent;
898
+ }
899
+ return names;
900
+ }
901
+ export function usageFailureHelpArgv(commands, command) {
902
+ const path = [];
903
+ let current = commands[command];
904
+ while (current !== undefined) {
905
+ path.unshift(current.name);
906
+ current =
907
+ current.parent === undefined ? undefined : commands[current.parent];
908
+ }
909
+ return [...path, "--help"];
910
+ }
911
+ function compileUsageConstraints(definitions, fields, command, issues) {
912
+ const fieldsByKey = new Map(fields.map((field) => [field.key, field]));
913
+ const compiled = [];
914
+ if (!Array.isArray(definitions)) {
915
+ issues.push({
916
+ code: "invalidUsageConstraint",
917
+ command,
918
+ index: null,
919
+ aspect: "collection",
920
+ received: null,
921
+ });
922
+ return deepFreeze(compiled);
923
+ }
924
+ for (const [index, definition] of definitions.entries()) {
925
+ if (typeof definition !== "object" || definition === null) {
926
+ issues.push({
927
+ code: "invalidUsageConstraint",
928
+ command,
929
+ index,
930
+ aspect: "entry",
931
+ received: null,
932
+ });
933
+ continue;
934
+ }
935
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- 定义条目已通过非 null 对象检查,此处读取动态属性。
936
+ const candidate = definition;
937
+ if (candidate.kind !== "requires" &&
938
+ candidate.kind !== "exclusive" &&
939
+ candidate.kind !== "forbiddenCombination") {
940
+ issues.push({
941
+ code: "invalidUsageConstraint",
942
+ command,
943
+ index,
944
+ aspect: "kind",
945
+ received: typeof candidate.kind === "string" ? candidate.kind : null,
946
+ });
947
+ continue;
948
+ }
949
+ if (candidate.kind === "requires") {
950
+ if (typeof candidate.field !== "string" ||
951
+ typeof candidate.requires !== "string") {
952
+ issues.push({
953
+ code: "invalidUsageConstraint",
954
+ command,
955
+ index,
956
+ aspect: "members",
957
+ received: "requires",
958
+ });
959
+ continue;
960
+ }
961
+ const field = fieldsByKey.get(candidate.field);
962
+ const required = fieldsByKey.get(candidate.requires);
963
+ if (field === undefined)
964
+ issues.push({
965
+ code: "unknownUsageConstraintField",
966
+ command,
967
+ constraint: "requires",
968
+ index,
969
+ member: "field",
970
+ field: candidate.field,
971
+ });
972
+ if (required === undefined)
973
+ issues.push({
974
+ code: "unknownUsageConstraintField",
975
+ command,
976
+ constraint: "requires",
977
+ index,
978
+ member: "requires",
979
+ field: candidate.requires,
980
+ });
981
+ if (field !== undefined && required !== undefined) {
982
+ if (!isOptionalOption(field) || !isOptionalOption(required)) {
983
+ for (const offendingField of [field, required])
984
+ if (!isOptionalOption(offendingField))
985
+ issues.push({
986
+ code: "inapplicableUsageConstraintField",
987
+ command,
988
+ constraint: "requires",
989
+ field: offendingField.key,
990
+ kind: offendingField.kind,
991
+ });
992
+ }
993
+ if (field.key === required.key)
994
+ issues.push({
995
+ code: "contradictoryUsageConstraint",
996
+ command,
997
+ constraint: "requires",
998
+ fields: Object.freeze([field.key]),
999
+ });
1000
+ compiled.push(deepFreeze({
1001
+ kind: "requires",
1002
+ field: field.key,
1003
+ requires: required.key,
1004
+ }));
1005
+ }
1006
+ continue;
1007
+ }
1008
+ if (candidate.kind === "exclusive") {
1009
+ if (!Array.isArray(candidate.fields) ||
1010
+ candidate.fields.length < 2 ||
1011
+ !candidate.fields.every((field) => typeof field === "string")) {
1012
+ issues.push({
1013
+ code: "invalidUsageConstraint",
1014
+ command,
1015
+ index,
1016
+ aspect: "members",
1017
+ received: "exclusive",
1018
+ });
1019
+ continue;
1020
+ }
1021
+ const selected = candidate.fields.map((key) => fieldsByKey.get(key));
1022
+ for (const [fieldIndex, field] of selected.entries()) {
1023
+ if (field === undefined) {
1024
+ issues.push({
1025
+ code: "unknownUsageConstraintField",
1026
+ command,
1027
+ constraint: "exclusive",
1028
+ index,
1029
+ member: "fields",
1030
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- fields 已逐项检查为字符串,索引来自对应 selected 数组。
1031
+ field: candidate.fields[fieldIndex],
1032
+ });
1033
+ }
1034
+ else if (!isOptionalOption(field)) {
1035
+ issues.push({
1036
+ code: "inapplicableUsageConstraintField",
1037
+ command,
1038
+ constraint: "exclusive",
1039
+ field: field.key,
1040
+ kind: field.kind,
1041
+ });
1042
+ }
1043
+ }
1044
+ const fieldsInConstraint = candidate.fields;
1045
+ if (new Set(fieldsInConstraint).size !== fieldsInConstraint.length)
1046
+ issues.push({
1047
+ code: "contradictoryUsageConstraint",
1048
+ command,
1049
+ constraint: "exclusive",
1050
+ fields: Object.freeze([...fieldsInConstraint]),
1051
+ });
1052
+ if (selected.every((field) => field !== undefined))
1053
+ compiled.push(deepFreeze({
1054
+ kind: "exclusive",
1055
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- fields 已检查至少两个字符串,冻结后保留非空元组长度。
1056
+ fields: Object.freeze([...fieldsInConstraint]),
1057
+ }));
1058
+ continue;
1059
+ }
1060
+ if (candidate.kind === "forbiddenCombination") {
1061
+ if (!Array.isArray(candidate.values) ||
1062
+ candidate.values.length < 2 ||
1063
+ !candidate.values.every((value) => typeof value === "object" &&
1064
+ value !== null &&
1065
+ typeof value.field === "string" &&
1066
+ (typeof value.value === "string" ||
1067
+ typeof value.value === "boolean"))) {
1068
+ issues.push({
1069
+ code: "invalidUsageConstraint",
1070
+ command,
1071
+ index,
1072
+ aspect: "members",
1073
+ received: "forbiddenCombination",
1074
+ });
1075
+ continue;
1076
+ }
1077
+ const values = [];
1078
+ for (const value of candidate.values) {
1079
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- values 已逐项检查 field 和 value 的运行时类型。
1080
+ const discreteValue = value;
1081
+ const field = fieldsByKey.get(discreteValue.field);
1082
+ if (field === undefined) {
1083
+ issues.push({
1084
+ code: "unknownUsageConstraintField",
1085
+ command,
1086
+ constraint: "forbiddenCombination",
1087
+ index,
1088
+ member: "values.field",
1089
+ field: discreteValue.field,
1090
+ });
1091
+ continue;
1092
+ }
1093
+ if (field.kind !== "flag" && field.kind !== "valueOption") {
1094
+ issues.push({
1095
+ code: "inapplicableUsageConstraintField",
1096
+ command,
1097
+ constraint: "forbiddenCombination",
1098
+ field: field.key,
1099
+ kind: field.kind,
1100
+ });
1101
+ continue;
1102
+ }
1103
+ if ((field.kind === "flag" && typeof discreteValue.value !== "boolean") ||
1104
+ (field.kind === "valueOption" &&
1105
+ typeof discreteValue.value !== "string")) {
1106
+ issues.push({
1107
+ code: "invalidUsageConstraintValue",
1108
+ command,
1109
+ field: field.key,
1110
+ expected: field.kind === "flag" ? "boolean" : "string",
1111
+ });
1112
+ continue;
1113
+ }
1114
+ values.push({ field: field.key, value: discreteValue.value });
1115
+ }
1116
+ const duplicateValueField = values.some((value, valueIndex) => values.findIndex(({ field }) => field === value.field) !== valueIndex);
1117
+ if (duplicateValueField)
1118
+ issues.push({
1119
+ code: "contradictoryUsageConstraint",
1120
+ command,
1121
+ constraint: "forbiddenCombination",
1122
+ fields: Object.freeze(values.map(({ field }) => field)),
1123
+ });
1124
+ if (values.length === candidate.values.length)
1125
+ compiled.push(deepFreeze({
1126
+ kind: "forbiddenCombination",
1127
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- 源 values 至少两项,且映射长度相等才进入此分支。
1128
+ values: Object.freeze(values),
1129
+ }));
1130
+ continue;
1131
+ }
1132
+ issues.push({
1133
+ code: "invalidUsageConstraint",
1134
+ command,
1135
+ index,
1136
+ aspect: "kind",
1137
+ received: null,
1138
+ });
1139
+ }
1140
+ for (const constraint of compiled) {
1141
+ if (constraint.kind !== "requires")
1142
+ continue;
1143
+ if (constraint.field === constraint.requires)
1144
+ continue;
1145
+ const exclusive = compiled.find((candidate) => candidate.kind === "exclusive" &&
1146
+ candidate.fields.includes(constraint.field) &&
1147
+ candidate.fields.includes(constraint.requires));
1148
+ if (exclusive !== undefined)
1149
+ issues.push({
1150
+ code: "contradictoryUsageConstraint",
1151
+ command,
1152
+ constraint: "requires",
1153
+ fields: Object.freeze([constraint.field, constraint.requires]),
1154
+ });
1155
+ }
1156
+ return deepFreeze(compiled);
1157
+ }
1158
+ function isOptionalOption(field) {
1159
+ return (!field.required &&
1160
+ field.kind !== "positional" &&
1161
+ field.kind !== "variadicPositional");
1162
+ }
1163
+ function projectUsageConstraints(schema, constraints) {
1164
+ if (constraints.length === 0)
1165
+ return schema;
1166
+ const constraintSchemas = [];
1167
+ for (const constraint of constraints) {
1168
+ if (constraint.kind === "requires") {
1169
+ constraintSchemas.push(Object.fromEntries([
1170
+ ["if", { required: [constraint.field] }],
1171
+ // oxlint-disable-next-line unicorn/no-thenable -- Draft 2020-12 固定关键字。
1172
+ ["then", { required: [constraint.requires] }],
1173
+ ]));
1174
+ continue;
1175
+ }
1176
+ if (constraint.kind === "exclusive") {
1177
+ constraintSchemas.push({
1178
+ not: {
1179
+ anyOf: constraint.fields.flatMap((field, index) => constraint.fields.slice(index + 1).map((other) => ({
1180
+ required: [field, other],
1181
+ }))),
1182
+ },
1183
+ });
1184
+ continue;
1185
+ }
1186
+ constraintSchemas.push({
1187
+ not: {
1188
+ allOf: constraint.values.map(({ field, value }) => ({
1189
+ properties: { [field]: { const: value } },
1190
+ required: [field],
1191
+ })),
1192
+ },
1193
+ });
1194
+ }
1195
+ return deepFreeze({
1196
+ inputSchema: {
1197
+ ...schema.inputSchema,
1198
+ allOf: [
1199
+ ...(Array.isArray(schema.inputSchema.allOf)
1200
+ ? schema.inputSchema.allOf
1201
+ : []),
1202
+ ...constraintSchemas,
1203
+ ],
1204
+ },
1205
+ outputSchema: schema.outputSchema,
1206
+ });
1207
+ }
1208
+ function compileFailures(command, failures, issues) {
1209
+ const compiled = compileAtomicVariants(command, "failure", failures, 1, createFailureWireSchema, issues);
1210
+ return {
1211
+ runtime: compiled.runtime,
1212
+ manifest: compiled.manifest,
1213
+ wire: compiled.wire,
1214
+ };
1215
+ }
1216
+ function collectTextPresenterIssues(command, textEnabled, success, failures, issues) {
1217
+ if (success.kind === "completion") {
1218
+ collectTextPresenterIssue(command, textEnabled, "completion", success, issues);
1219
+ }
1220
+ else if (success.kind === "data") {
1221
+ for (const [variant, definition] of Object.entries(success.variants)) {
1222
+ collectTextPresenterIssue(command, textEnabled, "data", definition, issues, variant);
1223
+ }
1224
+ }
1225
+ else {
1226
+ collectTextPresenterIssue(command, textEnabled, "streamSuccess", success, issues);
1227
+ for (const [variant, definition] of Object.entries(success.records)) {
1228
+ collectTextPresenterIssue(command, textEnabled, "record", definition, issues, variant);
1229
+ }
1230
+ }
1231
+ for (const [variant, definition] of Object.entries(failures)) {
1232
+ collectTextPresenterIssue(command, textEnabled, "failure", definition, issues, variant);
1233
+ }
1234
+ }
1235
+ function isTextOutputEnabled(output) {
1236
+ return isOutputCapability(output) && output.formats.includes("text");
1237
+ }
1238
+ function isOutputCapability(value) {
1239
+ return (typeof value === "object" && value !== null && outputCapabilities.has(value));
1240
+ }
1241
+ function isVersionCapability(value) {
1242
+ return (typeof value === "object" &&
1243
+ value !== null &&
1244
+ versionCapabilities.has(value));
1245
+ }
1246
+ function collectControlFieldConflicts(command, fields, definition, issues) {
1247
+ const controls = new Map([
1248
+ ["--help", "help"],
1249
+ ]);
1250
+ if (helpCapabilities.has(definition.help) &&
1251
+ definition.help.shortAlias !== undefined) {
1252
+ controls.set(definition.help.shortAlias, "help");
1253
+ }
1254
+ if (outputCapabilities.has(definition.output) &&
1255
+ isPlainRecord(definition.output.compatibilityFlags)) {
1256
+ for (const spelling of Object.keys(definition.output.compatibilityFlags)) {
1257
+ controls.set(spelling, "outputFormat");
1258
+ }
1259
+ if (definition.output.formats.includes("text")) {
1260
+ controls.set("--output-format", "outputFormat");
1261
+ }
1262
+ }
1263
+ if (isVersionCapability(definition.version)) {
1264
+ const version = definition.version;
1265
+ controls.set("--version", "version");
1266
+ if (version.shortAlias !== undefined) {
1267
+ controls.set(version.shortAlias, "version");
1268
+ }
1269
+ }
1270
+ for (const field of fields) {
1271
+ if (field.kind === "positional" || field.kind === "variadicPositional") {
1272
+ continue;
1273
+ }
1274
+ for (const spelling of [
1275
+ field.longOption,
1276
+ field.shortAlias,
1277
+ ...(field.kind === "flag" ? [field.negatedLongOption] : []),
1278
+ ]) {
1279
+ const control = spelling === undefined ? undefined : controls.get(spelling);
1280
+ if (spelling !== undefined && control !== undefined) {
1281
+ issues.push({
1282
+ code: "fieldOptionConflictsWithControl",
1283
+ command,
1284
+ field: field.key,
1285
+ spelling,
1286
+ control,
1287
+ });
1288
+ }
1289
+ }
1290
+ }
1291
+ }
1292
+ function isPlainRecord(value) {
1293
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
1294
+ return false;
1295
+ }
1296
+ const prototype = Object.getPrototypeOf(value);
1297
+ return prototype === Object.prototype || prototype === null;
1298
+ }
1299
+ function outputCompatibilityFlagsKind(value) {
1300
+ if (value === null)
1301
+ return "null";
1302
+ if (Array.isArray(value))
1303
+ return "array";
1304
+ return typeof value;
1305
+ }
1306
+ function collectTextPresenterIssue(command, textEnabled, location, definition, issues, variant) {
1307
+ const hasPresenter = typeof definition.text === "function";
1308
+ if (textEnabled === hasPresenter)
1309
+ return;
1310
+ issues.push({
1311
+ code: textEnabled ? "missingTextPresenter" : "unexpectedTextPresenter",
1312
+ command,
1313
+ location,
1314
+ ...(variant === undefined ? {} : { variant }),
1315
+ });
1316
+ }
1317
+ function compileSuccess(command, success, textEnabled, issues) {
1318
+ if (success.kind === "completion") {
1319
+ return {
1320
+ runtime: Object.freeze({
1321
+ kind: "completion",
1322
+ ...("text" in success ? { text: success.text } : {}),
1323
+ }),
1324
+ manifest: Object.freeze({ kind: "completion" }),
1325
+ wire: Object.freeze({
1326
+ completion: deepFreeze(createCompletionWireSchema(command)),
1327
+ }),
1328
+ };
1329
+ }
1330
+ if (success.kind === "stream") {
1331
+ const records = compileStreamRecords(command, success.records, issues);
1332
+ if (Object.keys(records.runtime).length === 0) {
1333
+ issues.push({ code: "missingStreamRecord", command });
1334
+ }
1335
+ const header = deepFreeze(createStreamHeaderWireSchema(command));
1336
+ const terminal = deepFreeze(createStreamSuccessWireSchema());
1337
+ const recordWire = Object.fromEntries(Object.entries(records.wire).map(([variant, schema]) => [
1338
+ variant,
1339
+ deepFreeze(createStreamRecordWireSchema(variant, schema)),
1340
+ ]));
1341
+ return deepFreeze({
1342
+ runtime: {
1343
+ kind: "stream",
1344
+ records: records.runtime,
1345
+ ...("text" in success ? { text: success.text } : {}),
1346
+ },
1347
+ manifest: {
1348
+ kind: "stream",
1349
+ records: records.manifest,
1350
+ ...(textEnabled
1351
+ ? { text: { recordFraming: "fragment" } }
1352
+ : {}),
1353
+ },
1354
+ wire: {
1355
+ stream: {
1356
+ header,
1357
+ records: recordWire,
1358
+ terminal,
1359
+ line: deepFreeze({
1360
+ $schema: "https://json-schema.org/draft/2020-12/schema",
1361
+ oneOf: [header, ...Object.values(recordWire), terminal],
1362
+ }),
1363
+ },
1364
+ },
1365
+ });
1366
+ }
1367
+ const variants = compileAtomicVariants(command, "data", success.variants, 0, createDataWireSchema, issues);
1368
+ if (Object.keys(variants.runtime).length === 0) {
1369
+ issues.push({ code: "missingDataVariant", command });
1370
+ }
1371
+ return deepFreeze({
1372
+ runtime: { kind: "data", variants: variants.runtime },
1373
+ manifest: {
1374
+ kind: "data",
1375
+ variants: variants.manifest,
1376
+ },
1377
+ wire: { data: variants.wire },
1378
+ });
1379
+ }
1380
+ function createUsageSynopsis(commandName, fields) {
1381
+ return [commandName, ...fields.map((field) => formatFieldUsage(field))].join(" ");
1382
+ }
1383
+ export function formatFieldUsage(field, includeShortAlias = false) {
1384
+ const value = field.kind === "positional"
1385
+ ? `<${field.key}>`
1386
+ : field.kind === "variadicPositional"
1387
+ ? `<${field.key}...>`
1388
+ : field.kind === "flag"
1389
+ ? [
1390
+ field.longOption,
1391
+ ...(includeShortAlias && field.shortAlias !== undefined
1392
+ ? [field.shortAlias]
1393
+ : []),
1394
+ field.negatedLongOption,
1395
+ ]
1396
+ .filter((spelling) => spelling !== undefined)
1397
+ .join(includeShortAlias ? ", " : "|")
1398
+ : `${[
1399
+ field.longOption,
1400
+ ...(includeShortAlias && field.shortAlias !== undefined
1401
+ ? [field.shortAlias]
1402
+ : []),
1403
+ ].join(", ")} <${field.key}>`;
1404
+ if (field.kind === "repeatableOption") {
1405
+ return field.required ? `(${value})...` : `[${value}]...`;
1406
+ }
1407
+ return field.required ? value : `[${value}]`;
1408
+ }