@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
@@ -287,6 +287,7 @@ const ticket09ReceiptFilePaths = [
287
287
  "package/dist/cli-command-identity.js",
288
288
  "package/dist/cli.js",
289
289
  "package/dist/main.js",
290
+ "package/dist/standard-schema.js",
290
291
  "package/package.json",
291
292
  ] as const;
292
293
 
@@ -315,23 +316,94 @@ function hasCanonicalReceiptFiles(files: Receipt["files"]): boolean {
315
316
  return true;
316
317
  }
317
318
 
319
+ /** 发布消费检查与交接校验共用原生结果的业务判据。 */
320
+ export function hasLookupEvidence(source: string): boolean {
321
+ try {
322
+ const result: unknown = JSON.parse(source);
323
+ return (
324
+ isRecord(result) &&
325
+ result.schemaVersion === "1" &&
326
+ result.command === "lookup" &&
327
+ result.kind === "data" &&
328
+ result.variant === "found" &&
329
+ isRecord(result.data) &&
330
+ result.data.name === "ada" &&
331
+ result.data.title === "Ada Lovelace"
332
+ );
333
+ } catch {
334
+ return false;
335
+ }
336
+ }
337
+
338
+ export function hasSchemaEvidence(
339
+ source: string,
340
+ commandName: string,
341
+ ): boolean {
342
+ try {
343
+ const result: unknown = JSON.parse(source);
344
+ if (
345
+ !isRecord(result) ||
346
+ result.schemaVersion !== "1" ||
347
+ result.command !== "schema" ||
348
+ result.kind !== "data" ||
349
+ result.variant !== "exported" ||
350
+ !isRecord(result.data)
351
+ )
352
+ return false;
353
+ const manifest = result.data.manifest;
354
+ if (
355
+ !isRecord(manifest) ||
356
+ !isRecord(manifest.commands) ||
357
+ typeof manifest.root !== "string"
358
+ )
359
+ return false;
360
+ const root = manifest.commands[manifest.root];
361
+ const lookup = manifest.commands.lookup;
362
+ const schema = manifest.commands.schema;
363
+ return (
364
+ isRecord(root) &&
365
+ root.name === commandName &&
366
+ isRecord(schema) &&
367
+ schema.name === "schema" &&
368
+ isRecord(lookup) &&
369
+ lookup.name === "lookup" &&
370
+ isRecord(lookup.input) &&
371
+ isRecord(lookup.input.inputSchema) &&
372
+ isRecord(lookup.success) &&
373
+ isRecord(lookup.success.variants) &&
374
+ isRecord(lookup.success.variants.found) &&
375
+ lookup.success.variants.found.exitCode === 0 &&
376
+ isRecord(lookup.failures) &&
377
+ isRecord(lookup.failures.notFound) &&
378
+ lookup.failures.notFound.exitCode === 1
379
+ );
380
+ } catch {
381
+ return false;
382
+ }
383
+ }
384
+
318
385
  function hasStableSmokeEvidence(receipt: Receipt): boolean {
319
- const [runtimeImport, help, version, greet] = receipt.smokes;
386
+ const [runtimeImport, help, version, lookup, schema] = receipt.smokes;
320
387
  return (
321
- receipt.smokes.length === 4 &&
388
+ receipt.smokes.length === 5 &&
322
389
  runtimeImport?.name === "runtime-import" &&
323
390
  sameArguments(runtimeImport.args, []) &&
324
391
  runtimeImport.stdout === "" &&
325
392
  help?.name === "help" &&
326
393
  sameArguments(help.args, ["--help"]) &&
327
- help.stdout.includes(`Usage: ${receipt.publication.commandName}`) &&
328
- help.stdout.includes("greet") &&
394
+ help.stdout.includes(receipt.publication.commandName) &&
395
+ help.stdout.includes("lookup") &&
396
+ help.stdout.includes("schema") &&
329
397
  version?.name === "version" &&
330
398
  sameArguments(version.args, ["--version"]) &&
331
- version.stdout === `${receipt.publication.version}\n` &&
332
- greet?.name === "greet" &&
333
- sameArguments(greet.args, ["greet", " Ada Lovelace "]) &&
334
- greet.stdout === "Hello, Ada Lovelace\n"
399
+ version.stdout ===
400
+ `${receipt.publication.commandName} ${receipt.publication.version}\n` &&
401
+ lookup?.name === "lookup" &&
402
+ sameArguments(lookup.args, ["lookup", "ada"]) &&
403
+ hasLookupEvidence(lookup.stdout) &&
404
+ schema?.name === "schema" &&
405
+ sameArguments(schema.args, ["schema"]) &&
406
+ hasSchemaEvidence(schema.stdout, receipt.publication.commandName)
335
407
  );
336
408
  }
337
409
 
@@ -584,15 +584,19 @@ function addManifestBlockers(options: {
584
584
  "imports",
585
585
  "provenance",
586
586
  ].filter((field) => Object.hasOwn(manifest, field));
587
+ const runtimeDependencies = manifest.dependencies;
587
588
  const manifestContractValid =
588
589
  manifest.type === "module" &&
589
590
  exactStringSet(manifest.files, publicationFiles) &&
590
591
  expectedPublicNodeRangeFromRoot !== undefined &&
591
592
  isObject(manifest.engines) &&
592
593
  manifest.engines.node === expectedPublicNodeRangeFromRoot &&
593
- validRecordOfStrings(manifest.dependencies) &&
594
- typeof manifest.dependencies.commander === "string" &&
595
- manifest.dependencies.commander.trim().length > 0 &&
594
+ validRecordOfStrings(runtimeDependencies) &&
595
+ ["@ykdz/cli-contract", "@valibot/to-json-schema", "valibot"].every(
596
+ (name) =>
597
+ typeof runtimeDependencies[name] === "string" &&
598
+ runtimeDependencies[name].trim().length > 0,
599
+ ) &&
596
600
  isObject(scripts) &&
597
601
  typeof scripts.build === "string" &&
598
602
  scripts.build.length > 0 &&
@@ -1,27 +1,17 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from "node:module";
3
3
 
4
+ import { nodeCliOutput } from "@ykdz/cli-contract/node";
5
+
4
6
  import { cliCommandIdentity } from "./cli-command-identity.ts";
5
- import { runCli, type CliRuntime } from "./main.ts";
7
+ import { runCli } from "./main.ts";
6
8
 
7
9
  const require = createRequire(import.meta.url);
8
- const packageManifest = require("../package.json") as unknown;
9
-
10
- const runtime: CliRuntime = {
10
+ process.exitCode = await runCli({
11
11
  argv: process.argv,
12
- streams: {
13
- stdin: process.stdin,
14
- stdout: process.stdout,
15
- stderr: process.stderr,
12
+ write: nodeCliOutput({ stdout: process.stdout, stderr: process.stderr }),
13
+ // 身份读取和校验也使用 runCli 的意外故障出口。
14
+ get identity() {
15
+ return cliCommandIdentity(require("../package.json"));
16
16
  },
17
- cwd: process.cwd(),
18
- env: process.env,
19
- tty: {
20
- stdin: Boolean(process.stdin.isTTY),
21
- stdout: Boolean(process.stdout.isTTY),
22
- stderr: Boolean(process.stderr.isTTY),
23
- },
24
- identity: cliCommandIdentity(packageManifest),
25
- };
26
-
27
- process.exitCode = await runCli(runtime);
17
+ });
@@ -1,78 +1,162 @@
1
- import { Command, CommanderError } from "commander";
1
+ import {
2
+ CliWriteError,
3
+ defineCli,
4
+ executeCli,
5
+ helpCapability,
6
+ outputCapability,
7
+ parseCliInvocation,
8
+ text,
9
+ versionCapability,
10
+ type WriteCliOutput,
11
+ } from "@ykdz/cli-contract";
12
+ import { isNodeBrokenPipe } from "@ykdz/cli-contract/node";
13
+ import * as v from "valibot";
2
14
 
3
15
  import type { CliCommandIdentity } from "./cli-command-identity.ts";
4
-
5
- export type Greeting = {
6
- readonly message: string;
7
- };
16
+ import { standardSchema } from "./standard-schema.ts";
8
17
 
9
18
  export type CliRuntime = {
10
19
  readonly argv: readonly string[];
11
- readonly streams: {
12
- readonly stdin: object;
13
- readonly stdout: { write(chunk: string): unknown };
14
- readonly stderr: { write(chunk: string): unknown };
15
- };
16
- readonly cwd: string;
17
- readonly env: Readonly<Record<string, string | undefined>>;
18
- readonly tty: {
19
- readonly stdin: boolean;
20
- readonly stdout: boolean;
21
- readonly stderr: boolean;
22
- };
20
+ readonly write: WriteCliOutput;
23
21
  readonly identity: CliCommandIdentity;
24
22
  };
25
23
 
26
- export function greet(name: string): Greeting {
27
- const normalizedName = name.trim();
28
- if (normalizedName.length === 0) {
29
- throw new Error("Name must not be empty");
30
- }
31
- return { message: `Hello, ${normalizedName}` };
32
- }
24
+ const records = [
25
+ { name: "ada", title: "Ada Lovelace" },
26
+ { name: "grace", title: "Grace Hopper" },
27
+ ];
33
28
 
34
- function errorMessage(error: unknown): string {
35
- return error instanceof Error ? error.message : String(error);
36
- }
37
-
38
- export function createCliCommand(runtime: CliRuntime): Command {
39
- const command = new Command()
40
- .name(runtime.identity.commandName)
41
- .description("A TypeScript command-line tool.")
42
- .version(runtime.identity.version)
43
- .configureOutput({
44
- writeOut: (text) => runtime.streams.stdout.write(text),
45
- writeErr: (text) => runtime.streams.stderr.write(text),
46
- })
47
- .showHelpAfterError()
48
- .exitOverride();
49
- const greetCommand = command
50
- .command("greet")
51
- .description("Greet a person.")
52
- .argument("<name>", "name to greet")
53
- .action((name: string) => {
54
- try {
55
- runtime.streams.stdout.write(`${greet(name).message}\n`);
56
- } catch (error) {
57
- greetCommand.error(`error: ${errorMessage(error)}`, {
58
- exitCode: 1,
59
- code: "cli.greet.invalid-name",
60
- });
61
- }
62
- });
63
- return command;
29
+ function createCliContract(identity: CliCommandIdentity) {
30
+ const define = defineCli<{ getManifest(): Record<string, unknown> }>();
31
+ return define({
32
+ root: "cli",
33
+ help: helpCapability({
34
+ shortAlias: "-h",
35
+ headings: {
36
+ usage: "用法",
37
+ commands: "命令",
38
+ arguments: "参数",
39
+ options: "选项",
40
+ },
41
+ wording: {
42
+ commandPlaceholder: "命令",
43
+ choices: "候选:{choices}",
44
+ default: "默认:{value}",
45
+ },
46
+ }),
47
+ version: versionCapability({
48
+ value: text.line(`${identity.commandName} ${identity.version}`),
49
+ shortAlias: "-V",
50
+ }),
51
+ output: outputCapability({ defaultFormat: "structured", text: true }),
52
+ usageFailureExitCode: 2,
53
+ commands: {
54
+ cli: {
55
+ kind: "rootGroup",
56
+ name: identity.commandName,
57
+ description: "查询示例记录。",
58
+ },
59
+ ...define.command("lookup")({
60
+ kind: "command",
61
+ parent: "cli",
62
+ name: "lookup",
63
+ description: "按名称查询内存记录。",
64
+ fields: {
65
+ name: { kind: "positional", description: "小写字母组成的记录名称" },
66
+ },
67
+ input: standardSchema(
68
+ v.object({
69
+ name: v.pipe(
70
+ v.string(),
71
+ v.regex(/^[a-z]+$/, "名称只能包含小写字母。"),
72
+ ),
73
+ }),
74
+ ),
75
+ success: {
76
+ kind: "data",
77
+ variants: {
78
+ found: {
79
+ description: "找到记录",
80
+ exitCode: 0,
81
+ schema: standardSchema(
82
+ v.object({ name: v.string(), title: v.string() }),
83
+ ),
84
+ text: ({ name, title }) =>
85
+ text.line(`找到记录:${title}(${name})`),
86
+ },
87
+ },
88
+ },
89
+ failures: {
90
+ notFound: {
91
+ description: "记录不存在",
92
+ exitCode: 1,
93
+ schema: standardSchema(v.object({ name: v.string() })),
94
+ text: ({ name }) => text.line(`未找到记录:${name}。`),
95
+ },
96
+ },
97
+ handler({ input, outcome }) {
98
+ const record = records.find(({ name }) => name === input.name);
99
+ return record === undefined
100
+ ? outcome.failure.notFound({ name: input.name })
101
+ : outcome.data.found(record);
102
+ },
103
+ }),
104
+ ...define.command("schema")({
105
+ kind: "command",
106
+ parent: "cli",
107
+ name: "schema",
108
+ description: "导出完整 CLI 契约及输入/输出 Schema。",
109
+ input: standardSchema(v.object({})),
110
+ fields: {},
111
+ success: {
112
+ kind: "data",
113
+ variants: {
114
+ exported: {
115
+ description: "完整 CLI 契约",
116
+ exitCode: 0,
117
+ schema: standardSchema(
118
+ v.object({ manifest: v.record(v.string(), v.unknown()) }),
119
+ ),
120
+ text: ({ manifest }) =>
121
+ text.lines([
122
+ "CLI 契约",
123
+ ...JSON.stringify(manifest, null, 2).split("\n"),
124
+ ]),
125
+ },
126
+ },
127
+ },
128
+ failures: {},
129
+ handler: ({ dependencies, outcome }) =>
130
+ outcome.data.exported({ manifest: dependencies.getManifest() }),
131
+ }),
132
+ },
133
+ });
64
134
  }
65
135
 
66
136
  export async function runCli(runtime: CliRuntime): Promise<number> {
67
137
  try {
68
- await createCliCommand(runtime).parseAsync([...runtime.argv], {
69
- from: "node",
138
+ const contract = createCliContract(runtime.identity);
139
+ const result = await executeCli(contract, {
140
+ invocation: parseCliInvocation(contract, runtime.argv.slice(2)),
141
+ dependencies: { getManifest: () => ({ ...contract.manifest }) },
142
+ write: runtime.write,
70
143
  });
71
- return 0;
144
+ return result.exitCode;
72
145
  } catch (error) {
73
- if (error instanceof CommanderError) {
74
- return error.exitCode;
146
+ if (
147
+ error instanceof CliWriteError &&
148
+ error.destination === "stdout" &&
149
+ isNodeBrokenPipe(error)
150
+ )
151
+ return 0;
152
+ try {
153
+ await runtime.write({
154
+ destination: "stderr",
155
+ chunk: "命令执行失败;请检查运行环境或报告此问题。\n",
156
+ });
157
+ } catch {
158
+ // 诊断通道故障不能改变原定退出类别。
75
159
  }
76
- throw error;
160
+ return 70;
77
161
  }
78
162
  }
@@ -0,0 +1,37 @@
1
+ import { toStandardJsonSchema } from "@valibot/to-json-schema";
2
+ import type * as v from "valibot";
3
+
4
+ /** 上游执行器要求失败结果不带 value;保留 Valibot 的验证及官方 JSON Schema 投影。 */
5
+ export function standardSchema<
6
+ T extends v.BaseSchema<unknown, unknown, v.BaseIssue<unknown>>,
7
+ >(schema: T, ignoreActions: string[] = []) {
8
+ const converted = toStandardJsonSchema(schema);
9
+ const adapted: typeof converted = {
10
+ "~standard": {
11
+ ...converted["~standard"],
12
+ jsonSchema: {
13
+ input: (options) =>
14
+ converted["~standard"].jsonSchema.input({
15
+ ...options,
16
+ libraryOptions: { ...options.libraryOptions, ignoreActions },
17
+ }),
18
+ output: (options) =>
19
+ converted["~standard"].jsonSchema.output({
20
+ ...options,
21
+ libraryOptions: { ...options.libraryOptions, ignoreActions },
22
+ }),
23
+ },
24
+ validate(value) {
25
+ const result = converted["~standard"].validate(value);
26
+ if (result instanceof Promise)
27
+ return result.then((resolved) =>
28
+ resolved.issues === undefined
29
+ ? resolved
30
+ : { issues: resolved.issues },
31
+ );
32
+ return result.issues === undefined ? result : { issues: result.issues };
33
+ },
34
+ },
35
+ };
36
+ return adapted;
37
+ }
@@ -0,0 +1,71 @@
1
+ import assert from "node:assert/strict";
2
+ import { readFile } from "node:fs/promises";
3
+ import path from "node:path";
4
+
5
+ import { cliCommandIdentity } from "../../../src/cli-command-identity.ts";
6
+ import type { CliJourney } from "../journey.ts";
7
+
8
+ const journey: CliJourney = {
9
+ name: "lookup",
10
+ modes: ["source", "distribution", "packed"],
11
+ async setup() {},
12
+ commands() {
13
+ return [
14
+ { name: "查询记录", args: ["lookup", "ada"] },
15
+ { name: "文本查询", args: ["lookup", "ada", "--output-format", "text"] },
16
+ { name: "未找到记录", args: ["lookup", "missing"] },
17
+ {
18
+ name: "文本失败",
19
+ args: ["lookup", "missing", "--output-format", "text"],
20
+ },
21
+ { name: "非法输入", args: ["lookup", " ", "--output-format", "text"] },
22
+ { name: "完整契约", args: ["schema"] },
23
+ { name: "帮助", args: ["--help"] },
24
+ { name: "版本", args: ["--version"] },
25
+ ];
26
+ },
27
+ async assertions({ context, results }) {
28
+ const identity = cliCommandIdentity(
29
+ JSON.parse(
30
+ await readFile(path.join(context.packageRoot, "package.json"), "utf8"),
31
+ ),
32
+ );
33
+ assert.equal(results[0]?.exitCode, 0);
34
+ assert.equal(results[0]?.stderr, "");
35
+ assert.deepEqual(JSON.parse(results[0]?.stdout ?? ""), {
36
+ schemaVersion: "1",
37
+ command: "lookup",
38
+ kind: "data",
39
+ variant: "found",
40
+ data: { name: "ada", title: "Ada Lovelace" },
41
+ });
42
+ assert.equal(results[1]?.exitCode, 0);
43
+ assert.equal(results[1]?.stderr, "");
44
+ assert.match(results[1]?.stdout ?? "", /找到记录:Ada Lovelace/u);
45
+ for (const result of results.slice(2, 4)) {
46
+ assert.equal(result.exitCode, 1);
47
+ assert.equal(result.stdout, "");
48
+ }
49
+ assert.equal(JSON.parse(results[2]?.stderr ?? "").variant, "notFound");
50
+ assert.match(results[3]?.stderr ?? "", /未找到记录:missing/u);
51
+ assert.equal(results[4]?.exitCode, 2);
52
+ assert.equal(results[4]?.stdout, "");
53
+ assert.equal(JSON.parse(results[4]?.stderr ?? "").kind, "usageFailure");
54
+ for (const result of results.slice(5)) {
55
+ assert.equal(result.exitCode, 0);
56
+ assert.equal(result.stderr, "");
57
+ }
58
+ const schema = JSON.parse(results[5]?.stdout ?? "");
59
+ assert.equal(schema.command, "schema");
60
+ for (const fact of ["lookup", "schema", "found", "notFound"])
61
+ assert.ok(JSON.stringify(schema.data.manifest).includes(fact));
62
+ assert.match(results[6]?.stdout ?? "", /lookup/u);
63
+ assert.ok(results[6]?.stdout.includes(identity.commandName));
64
+ assert.equal(
65
+ results[7]?.stdout,
66
+ `${identity.commandName} ${identity.version}\n`,
67
+ );
68
+ },
69
+ };
70
+
71
+ export default journey;