politty 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/dist/cli.js +1 -1
  2. package/dist/docs/index.js +1 -1
  3. package/dist/index.js +1 -1
  4. package/dist/{runner-D43SkHt5.js → runner-APRZYXUS.js} +74 -3
  5. package/package.json +20 -65
  6. package/dist/arg-registry-DDJpsUea.d.cts +0 -942
  7. package/dist/arg-registry-DDJpsUea.d.cts.map +0 -1
  8. package/dist/arg-registry-DDJpsUea.d.ts.map +0 -1
  9. package/dist/augment.cjs +0 -0
  10. package/dist/augment.d.cts +0 -17
  11. package/dist/augment.d.cts.map +0 -1
  12. package/dist/augment.d.ts.map +0 -1
  13. package/dist/cli.cjs +0 -54
  14. package/dist/cli.cjs.map +0 -1
  15. package/dist/cli.d.cts +0 -1
  16. package/dist/cli.js.map +0 -1
  17. package/dist/completion/index.cjs +0 -23
  18. package/dist/completion/index.d.cts +0 -3
  19. package/dist/completion-CLHO3Xaz.cjs +0 -5769
  20. package/dist/completion-CLHO3Xaz.cjs.map +0 -1
  21. package/dist/completion-DHnVx9Zk.js.map +0 -1
  22. package/dist/docs/index.cjs +0 -3127
  23. package/dist/docs/index.cjs.map +0 -1
  24. package/dist/docs/index.d.cts +0 -752
  25. package/dist/docs/index.d.cts.map +0 -1
  26. package/dist/docs/index.d.ts.map +0 -1
  27. package/dist/docs/index.js.map +0 -1
  28. package/dist/index-DKGn3lIl.d.ts.map +0 -1
  29. package/dist/index-WyViqW59.d.cts +0 -663
  30. package/dist/index-WyViqW59.d.cts.map +0 -1
  31. package/dist/index.cjs +0 -45
  32. package/dist/index.d.cts +0 -685
  33. package/dist/index.d.cts.map +0 -1
  34. package/dist/index.d.ts.map +0 -1
  35. package/dist/log-collector-DK32-73m.js.map +0 -1
  36. package/dist/log-collector-DUqC427m.cjs +0 -185
  37. package/dist/log-collector-DUqC427m.cjs.map +0 -1
  38. package/dist/prompt/clack/index.cjs +0 -33
  39. package/dist/prompt/clack/index.cjs.map +0 -1
  40. package/dist/prompt/clack/index.d.cts +0 -18
  41. package/dist/prompt/clack/index.d.cts.map +0 -1
  42. package/dist/prompt/clack/index.d.ts.map +0 -1
  43. package/dist/prompt/clack/index.js.map +0 -1
  44. package/dist/prompt/index.cjs +0 -7
  45. package/dist/prompt/index.d.cts +0 -108
  46. package/dist/prompt/index.d.cts.map +0 -1
  47. package/dist/prompt/index.d.ts.map +0 -1
  48. package/dist/prompt/inquirer/index.cjs +0 -48
  49. package/dist/prompt/inquirer/index.cjs.map +0 -1
  50. package/dist/prompt/inquirer/index.d.cts +0 -18
  51. package/dist/prompt/inquirer/index.d.cts.map +0 -1
  52. package/dist/prompt/inquirer/index.d.ts.map +0 -1
  53. package/dist/prompt/inquirer/index.js.map +0 -1
  54. package/dist/prompt-Bs9e-Em3.cjs +0 -196
  55. package/dist/prompt-Bs9e-Em3.cjs.map +0 -1
  56. package/dist/prompt-Cc8Tfmdv.js.map +0 -1
  57. package/dist/runner-D43SkHt5.js.map +0 -1
  58. package/dist/runner-DvFvokV6.cjs +0 -2865
  59. package/dist/runner-DvFvokV6.cjs.map +0 -1
  60. package/dist/schema-extractor-BxSRwLrx.cjs +0 -710
  61. package/dist/schema-extractor-BxSRwLrx.cjs.map +0 -1
  62. package/dist/schema-extractor-Dqe7_kyQ.js.map +0 -1
@@ -1,2865 +0,0 @@
1
- const require_log_collector = require('./log-collector-DUqC427m.cjs');
2
- const require_schema_extractor = require('./schema-extractor-BxSRwLrx.cjs');
3
- let node_util = require("node:util");
4
- let string_width = require("string-width");
5
- string_width = require_log_collector.__toESM(string_width, 1);
6
-
7
- //#region src/core/case-proxy.ts
8
- /**
9
- * Wrap an args object with a Proxy that allows dual-case access.
10
- *
11
- * Given `{ "my-option": "value" }`, both `obj["my-option"]` and `obj.myOption`
12
- * will return `"value"`.
13
- *
14
- * - `Object.keys()`, `JSON.stringify()`, and spread return only the original keys.
15
- * - The `in` operator detects both case variants.
16
- */
17
- function createDualCaseProxy(obj) {
18
- return new Proxy(obj, {
19
- get(target, prop, receiver) {
20
- if (typeof prop === "string") {
21
- if (prop in target) return Reflect.get(target, prop, receiver);
22
- const camel = require_schema_extractor.toCamelCase(prop);
23
- if (camel !== prop && camel in target) return Reflect.get(target, camel, receiver);
24
- const kebab = require_schema_extractor.toKebabCase(prop);
25
- if (kebab !== prop && kebab in target) return Reflect.get(target, kebab, receiver);
26
- }
27
- return Reflect.get(target, prop, receiver);
28
- },
29
- has(target, prop) {
30
- if (typeof prop === "string") {
31
- if (prop in target) return true;
32
- const camel = require_schema_extractor.toCamelCase(prop);
33
- if (camel !== prop && camel in target) return true;
34
- const kebab = require_schema_extractor.toKebabCase(prop);
35
- if (kebab !== prop && kebab in target) return true;
36
- }
37
- return Reflect.has(target, prop);
38
- }
39
- });
40
- }
41
-
42
- //#endregion
43
- //#region src/executor/command-runner.ts
44
- /**
45
- * Execute a command lifecycle: setup → run → cleanup
46
- *
47
- * This is an internal function that executes the command's lifecycle hooks.
48
- * For running commands with argument parsing, use `runCommand` instead.
49
- *
50
- * @param command - The command to execute
51
- * @param args - Already validated arguments
52
- * @param options - Lifecycle options
53
- * @returns The result of command execution
54
- * @internal
55
- */
56
- async function executeLifecycle(command, args, _options = {}) {
57
- let error;
58
- let result;
59
- const collector = _options.captureLogs ?? false ? require_log_collector.createLogCollector() : null;
60
- collector?.start();
61
- const setupContext = { args };
62
- const cleanupContext = {
63
- args,
64
- error
65
- };
66
- let signalHandler;
67
- if (_options.handleSignals) {
68
- signalHandler = async (_signal) => {
69
- if (signalHandler) {
70
- process.off("SIGINT", signalHandler);
71
- process.off("SIGTERM", signalHandler);
72
- }
73
- const signalError = /* @__PURE__ */ new Error("Process interrupted");
74
- cleanupContext.error = signalError;
75
- if (command.cleanup) try {
76
- await command.cleanup(cleanupContext);
77
- } catch (e) {
78
- console.error("Error during signal cleanup:", e);
79
- }
80
- if (_options.globalCleanup) try {
81
- await _options.globalCleanup({ error: signalError });
82
- } catch (e) {
83
- console.error("Error during global signal cleanup:", e);
84
- }
85
- collector?.stop();
86
- if (process.stdout.writableNeedDrain) await new Promise((resolve) => {
87
- const timeout = setTimeout(() => {
88
- process.stdout.off("drain", onDrain);
89
- resolve();
90
- }, 200);
91
- const onDrain = () => {
92
- clearTimeout(timeout);
93
- resolve();
94
- };
95
- process.stdout.once("drain", onDrain);
96
- });
97
- process.exit(1);
98
- };
99
- process.on("SIGINT", signalHandler);
100
- process.on("SIGTERM", signalHandler);
101
- }
102
- try {
103
- if (command.setup) await command.setup(setupContext);
104
- if (command.run) result = await command.run(args);
105
- } catch (e) {
106
- error = e instanceof Error ? e : new Error(String(e));
107
- } finally {
108
- if (signalHandler) {
109
- process.off("SIGINT", signalHandler);
110
- process.off("SIGTERM", signalHandler);
111
- }
112
- }
113
- if (command.cleanup) {
114
- cleanupContext.error = error;
115
- try {
116
- await command.cleanup(cleanupContext);
117
- } catch (cleanupError) {
118
- if (!error) error = cleanupError instanceof Error ? cleanupError : new Error(String(cleanupError));
119
- }
120
- }
121
- collector?.stop();
122
- const logs = require_log_collector.mergeLogs(_options.existingLogs ?? require_log_collector.emptyLogs(), collector?.getLogs() ?? require_log_collector.emptyLogs());
123
- if (error) return {
124
- success: false,
125
- error,
126
- exitCode: 1,
127
- logs
128
- };
129
- return {
130
- success: true,
131
- result,
132
- exitCode: 0,
133
- logs
134
- };
135
- }
136
-
137
- //#endregion
138
- //#region src/output/logger.ts
139
- /**
140
- * Check if color output should be disabled
141
- */
142
- function shouldDisableColor() {
143
- if (process.env.NO_COLOR !== void 0) return true;
144
- if (process.env.FORCE_COLOR === "0") return true;
145
- if (process.env.FORCE_COLOR) return false;
146
- if (process.env.CI) return true;
147
- if (!process.stdout.isTTY) return true;
148
- return false;
149
- }
150
- /**
151
- * Global flag to control color output
152
- */
153
- let colorDisabled = shouldDisableColor();
154
- /**
155
- * Enable or disable color output programmatically
156
- */
157
- function setColorEnabled(enabled) {
158
- colorDisabled = !enabled;
159
- }
160
- /**
161
- * Check if color output is currently enabled
162
- */
163
- function isColorEnabled() {
164
- return !colorDisabled;
165
- }
166
- /**
167
- * Create a style function that applies the given styles
168
- */
169
- function createStyleFn(...styleArgs) {
170
- return (text) => {
171
- if (colorDisabled) return text;
172
- let result = text;
173
- for (const style of styleArgs) result = (0, node_util.styleText)(style, result);
174
- return result;
175
- };
176
- }
177
- /**
178
- * Semantic style functions for inline text styling
179
- */
180
- const styles = {
181
- success: createStyleFn("green"),
182
- error: createStyleFn("red"),
183
- warning: createStyleFn("yellow"),
184
- info: createStyleFn("cyan"),
185
- bold: createStyleFn("bold"),
186
- dim: createStyleFn("dim"),
187
- italic: createStyleFn("italic"),
188
- underline: createStyleFn("underline"),
189
- red: createStyleFn("red"),
190
- green: createStyleFn("green"),
191
- yellow: createStyleFn("yellow"),
192
- blue: createStyleFn("blue"),
193
- magenta: createStyleFn("magenta"),
194
- cyan: createStyleFn("cyan"),
195
- white: createStyleFn("white"),
196
- gray: createStyleFn("gray"),
197
- command: createStyleFn("bold"),
198
- commandName: createStyleFn("bold", "underline", "cyan"),
199
- option: createStyleFn("cyan"),
200
- optionName: createStyleFn("bold"),
201
- placeholder: createStyleFn("dim"),
202
- defaultValue: createStyleFn("dim"),
203
- required: createStyleFn("yellow"),
204
- description: (text) => text,
205
- sectionHeader: createStyleFn("bold", "underline"),
206
- version: createStyleFn("dim")
207
- };
208
- /**
209
- * Standardized symbols for CLI output
210
- */
211
- const symbols = {
212
- success: styles.green("✓"),
213
- error: styles.red("✖"),
214
- warning: styles.yellow("⚠"),
215
- info: styles.cyan("ℹ"),
216
- bullet: styles.gray("•"),
217
- arrow: styles.gray("→")
218
- };
219
- /**
220
- * Logger for CLI output
221
- */
222
- const logger = {
223
- /**
224
- * Log informational message
225
- */
226
- info(message) {
227
- console.log(message);
228
- },
229
- /**
230
- * Log success message
231
- */
232
- success(message) {
233
- console.log(`${symbols.success} ${styles.success(message)}`);
234
- },
235
- /**
236
- * Log warning message
237
- */
238
- warn(message) {
239
- console.warn(`${symbols.warning} ${styles.warning(message)}`);
240
- },
241
- /**
242
- * Log error message
243
- */
244
- error(message) {
245
- console.error(`${symbols.error} ${styles.error(message)}`);
246
- },
247
- /**
248
- * Log raw message without prefix
249
- */
250
- log(message) {
251
- console.log(message);
252
- },
253
- /**
254
- * Log empty line
255
- */
256
- newline() {
257
- console.log("");
258
- },
259
- /**
260
- * Log debug message with dim color
261
- */
262
- debug(message) {
263
- console.log(styles.dim(message));
264
- }
265
- };
266
-
267
- //#endregion
268
- //#region src/output/markdown-renderer.ts
269
- /**
270
- * Lightweight Markdown-to-terminal renderer.
271
- *
272
- * Supports a subset of Markdown tailored for CLI help notes:
273
- * - Inline: bold, italic, inline code, links
274
- * - Block: paragraphs, unordered/ordered lists, blockquotes, headings,
275
- * horizontal rules, fenced code blocks
276
- */
277
- /**
278
- * Apply inline Markdown formatting to a string.
279
- *
280
- * Processing order matters to avoid conflicts:
281
- * 1. Inline code (backticks) — content inside is literal, no further processing
282
- * 2. Bold (**text**)
283
- * 3. Italic (*text* or _text_)
284
- * 4. Links [text](url)
285
- */
286
- function renderInline(text) {
287
- const codeSpans = [];
288
- let result = text.replace(/`([^`]+)`/g, (_match, code) => {
289
- const index = codeSpans.length;
290
- codeSpans.push(styles.cyan(code));
291
- return `\x00CODE${index}\x00`;
292
- });
293
- result = result.replace(/\*\*(.+?)\*\*/g, (_match, content) => styles.bold(content));
294
- result = result.replace(/__(.+?)__/g, (_match, content) => styles.bold(content));
295
- result = result.replace(/\*(.+?)\*/g, (_match, content) => styles.italic(content));
296
- result = result.replace(/(?<!\w)_(.+?)_(?!\w)/g, (_match, content) => styles.italic(content));
297
- result = result.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_match, linkText, url) => `${styles.underline(linkText)} ${styles.dim(`(${url})`)}`);
298
- result = result.replace(/\x00CODE(\d+)\x00/g, (_match, index) => codeSpans[Number(index)]);
299
- return result;
300
- }
301
- /**
302
- * Render a Markdown string to styled terminal output.
303
- *
304
- * Block-level processing:
305
- * - Splits input into blocks separated by blank lines
306
- * - Detects headings, horizontal rules, blockquotes, lists, code blocks, and paragraphs
307
- * - Applies inline formatting within each block
308
- */
309
- function renderMarkdown(markdown) {
310
- return splitIntoBlocks(markdown.split("\n")).map(renderBlock).join("\n\n");
311
- }
312
- const HEADING_RE = /^(#{1,6})\s+(.+)$/;
313
- const HR_RE = /^(?:---+|\*\*\*+|___+)\s*$/;
314
- const BLOCKQUOTE_RE = /^>\s?(.*)$/;
315
- const UL_RE = /^-\s+(.+)$/;
316
- const OL_RE = /^(\d+)[.)]\s+(.+)$/;
317
- const FENCE_OPEN_RE = /^(`{3,}|~{3,})(\S*)\s*$/;
318
- const ALERT_RE = /^\[!(NOTE|TIP|IMPORTANT|WARNING|CAUTION)\]\s*$/;
319
- const TABLE_ROW_RE = /^\|(.+)\|$/;
320
- const TABLE_SEP_RE = /^\|(\s*:?-+:?\s*\|)+$/;
321
- /**
322
- * Split lines into logical blocks separated by blank lines.
323
- * Consecutive lines of the same block type are grouped together.
324
- */
325
- function splitIntoBlocks(lines) {
326
- const blocks = [];
327
- let i = 0;
328
- while (i < lines.length) {
329
- const line = lines[i];
330
- if (line.trim() === "") {
331
- i++;
332
- continue;
333
- }
334
- const fenceMatch = line.match(FENCE_OPEN_RE);
335
- if (fenceMatch) {
336
- const fence = fenceMatch[1];
337
- const lang = fenceMatch[2] ?? "";
338
- const codeLines = [];
339
- i++;
340
- while (i < lines.length) {
341
- if (lines[i].startsWith(fence.charAt(0).repeat(fence.length)) && lines[i].trim() === fence.charAt(0).repeat(Math.max(fence.length, lines[i].trim().length))) {
342
- i++;
343
- break;
344
- }
345
- codeLines.push(lines[i]);
346
- i++;
347
- }
348
- blocks.push({
349
- type: "code",
350
- lang,
351
- lines: codeLines
352
- });
353
- continue;
354
- }
355
- const headingMatch = line.match(HEADING_RE);
356
- if (headingMatch) {
357
- blocks.push({
358
- type: "heading",
359
- level: headingMatch[1].length,
360
- content: headingMatch[2]
361
- });
362
- i++;
363
- continue;
364
- }
365
- if (HR_RE.test(line)) {
366
- blocks.push({ type: "hr" });
367
- i++;
368
- continue;
369
- }
370
- if (BLOCKQUOTE_RE.test(line)) {
371
- const bqLines = [];
372
- while (i < lines.length) {
373
- const bqMatch = lines[i].match(BLOCKQUOTE_RE);
374
- if (bqMatch) {
375
- bqLines.push(bqMatch[1]);
376
- i++;
377
- } else break;
378
- }
379
- if (bqLines.length > 0) {
380
- const alertMatch = bqLines[0].match(ALERT_RE);
381
- if (alertMatch) {
382
- const contentLines = bqLines.slice(1).filter((l) => l !== "");
383
- blocks.push({
384
- type: "alert",
385
- alertType: alertMatch[1],
386
- lines: contentLines
387
- });
388
- continue;
389
- }
390
- }
391
- blocks.push({
392
- type: "blockquote",
393
- lines: bqLines
394
- });
395
- continue;
396
- }
397
- if (UL_RE.test(line)) {
398
- const items = [];
399
- while (i < lines.length) {
400
- const ulMatch = lines[i].match(UL_RE);
401
- if (ulMatch) {
402
- items.push(ulMatch[1]);
403
- i++;
404
- } else break;
405
- }
406
- blocks.push({
407
- type: "ul",
408
- items
409
- });
410
- continue;
411
- }
412
- const olMatch = line.match(OL_RE);
413
- if (olMatch) {
414
- const start = Number(olMatch[1]);
415
- const items = [];
416
- while (i < lines.length) {
417
- const match = lines[i].match(OL_RE);
418
- if (match) {
419
- items.push(match[2]);
420
- i++;
421
- } else break;
422
- }
423
- blocks.push({
424
- type: "ol",
425
- items,
426
- start
427
- });
428
- continue;
429
- }
430
- if (TABLE_ROW_RE.test(line) && i + 1 < lines.length && TABLE_SEP_RE.test(lines[i + 1])) {
431
- const headers = parseCells(line);
432
- const alignments = parseAlignments(lines[i + 1]);
433
- i += 2;
434
- const rows = [];
435
- while (i < lines.length && TABLE_ROW_RE.test(lines[i])) {
436
- rows.push(parseCells(lines[i]));
437
- i++;
438
- }
439
- blocks.push({
440
- type: "table",
441
- headers,
442
- alignments,
443
- rows
444
- });
445
- continue;
446
- }
447
- const paraLines = [];
448
- while (i < lines.length) {
449
- const l = lines[i];
450
- if (l.trim() === "" || HEADING_RE.test(l) || HR_RE.test(l) || BLOCKQUOTE_RE.test(l) || UL_RE.test(l) || OL_RE.test(l) || FENCE_OPEN_RE.test(l) || TABLE_ROW_RE.test(l) && i + 1 < lines.length && TABLE_SEP_RE.test(lines[i + 1])) break;
451
- paraLines.push(l);
452
- i++;
453
- }
454
- if (paraLines.length > 0) blocks.push({
455
- type: "paragraph",
456
- lines: paraLines
457
- });
458
- }
459
- return blocks;
460
- }
461
- /**
462
- * Parse cells from a table row: `| a | b | c |` → `["a", "b", "c"]`
463
- */
464
- function parseCells(row) {
465
- return row.slice(1, -1).split("|").map((cell) => cell.trim());
466
- }
467
- /**
468
- * Parse alignment from separator row: `|:---|:---:|---:|` → `["left", "center", "right"]`
469
- */
470
- function parseAlignments(sepRow) {
471
- return sepRow.slice(1, -1).split("|").map((cell) => {
472
- const trimmed = cell.trim();
473
- if (trimmed.startsWith(":") && trimmed.endsWith(":")) return "center";
474
- if (trimmed.endsWith(":")) return "right";
475
- return "left";
476
- });
477
- }
478
- /**
479
- * Pad a string to a given width with the specified alignment.
480
- */
481
- function alignText(text, width, alignment) {
482
- const visualWidth = (0, string_width.default)(text);
483
- const total = Math.max(0, width - visualWidth);
484
- if (alignment === "right") return " ".repeat(total) + text;
485
- if (alignment === "center") {
486
- const left = Math.floor(total / 2);
487
- return " ".repeat(left) + text + " ".repeat(total - left);
488
- }
489
- return text + " ".repeat(total);
490
- }
491
- /**
492
- * Style configuration for GitHub-style alert blocks.
493
- */
494
- const alertStyles = {
495
- NOTE: {
496
- icon: "ℹ",
497
- label: "Note",
498
- styleFn: styles.cyan
499
- },
500
- TIP: {
501
- icon: "💡",
502
- label: "Tip",
503
- styleFn: styles.green
504
- },
505
- IMPORTANT: {
506
- icon: "❗",
507
- label: "Important",
508
- styleFn: styles.magenta
509
- },
510
- WARNING: {
511
- icon: "⚠",
512
- label: "Warning",
513
- styleFn: styles.yellow
514
- },
515
- CAUTION: {
516
- icon: "🔴",
517
- label: "Caution",
518
- styleFn: styles.red
519
- }
520
- };
521
- /**
522
- * Render a single block to styled terminal output.
523
- */
524
- function renderBlock(block) {
525
- switch (block.type) {
526
- case "heading": return styles.green(styles.bold(renderInline(block.content)));
527
- case "hr": return styles.dim("─".repeat(40));
528
- case "blockquote": {
529
- const prefix = styles.dim("│ ");
530
- return block.lines.map((line) => `${prefix}${renderInline(line)}`).join("\n");
531
- }
532
- case "alert": {
533
- const { icon, label, styleFn } = alertStyles[block.alertType];
534
- const prefix = styleFn(styles.bold("│")) + " ";
535
- const header = `${prefix}${styleFn(icon)} ${styleFn(label)}`;
536
- if (block.lines.length === 0) return header;
537
- return `${header}\n${block.lines.map((line) => `${prefix}${renderInline(line)}`).join("\n")}`;
538
- }
539
- case "ul": return block.items.map((item) => `${styles.dim("•")} ${renderInline(item)}`).join("\n");
540
- case "ol": {
541
- const maxNum = block.start + block.items.length - 1;
542
- const width = String(maxNum).length;
543
- return block.items.map((item, i) => {
544
- const num = String(block.start + i).padStart(width, " ");
545
- return `${styles.dim(`${num}.`)} ${renderInline(item)}`;
546
- }).join("\n");
547
- }
548
- case "table": {
549
- const colCount = block.headers.length;
550
- const renderedHeaders = block.headers.map((h) => renderInline(h));
551
- const renderedRows = block.rows.map((row) => Array.from({ length: colCount }, (_, i) => renderInline(row[i] ?? "")));
552
- const colWidths = renderedHeaders.map((h, i) => {
553
- const headerWidth = (0, string_width.default)(h);
554
- const cellWidths = renderedRows.map((row) => (0, string_width.default)(row[i]));
555
- return Math.max(headerWidth, ...cellWidths);
556
- });
557
- const pipe = styles.dim("│");
558
- const topBorder = styles.dim(`┌─${colWidths.map((w) => "─".repeat(w)).join("─┬─")}─┐`);
559
- const midBorder = styles.dim(`├─${colWidths.map((w) => "─".repeat(w)).join("─┼─")}─┤`);
560
- const botBorder = styles.dim(`└─${colWidths.map((w) => "─".repeat(w)).join("─┴─")}─┘`);
561
- return [
562
- topBorder,
563
- `${pipe} ${renderedHeaders.map((h, i) => styles.bold(alignText(h, colWidths[i], block.alignments[i] ?? "left"))).join(` ${pipe} `)} ${pipe}`,
564
- midBorder,
565
- ...renderedRows.map((row) => {
566
- return `${pipe} ${row.map((cell, i) => alignText(cell, colWidths[i], block.alignments[i] ?? "left")).join(` ${pipe} `)} ${pipe}`;
567
- }),
568
- botBorder
569
- ].join("\n");
570
- }
571
- case "code": return block.lines.map((line) => ` ${styles.yellow(line)}`).join("\n");
572
- case "paragraph": return renderInline(block.lines.join(" "));
573
- }
574
- }
575
-
576
- //#endregion
577
- //#region src/output/help-generator.ts
578
- /**
579
- * Default descriptions for built-in options
580
- */
581
- const defaultBuiltinDescriptions = {
582
- help: "Show help",
583
- helpAll: "Show help with all subcommand options",
584
- version: "Show version"
585
- };
586
- /**
587
- * Internal subcommands are reserved for framework internals and hidden from help output.
588
- */
589
- function isVisibleSubcommand(name) {
590
- return !name.startsWith("__");
591
- }
592
- function getVisibleSubcommandEntries(subCommands) {
593
- return Object.entries(subCommands).filter(([name]) => isVisibleSubcommand(name));
594
- }
595
- /**
596
- * Build full command name from context
597
- */
598
- function buildFullCommandName(command, context) {
599
- if (context?.rootName && context.commandPath && context.commandPath.length > 0) return context.commandPath.join(" ");
600
- return command.name ?? "command";
601
- }
602
- /**
603
- * Build usage command name (includes root name for subcommands)
604
- */
605
- function buildUsageCommandName(command, context) {
606
- if (context?.rootName && context.commandPath && context.commandPath.length > 0) return `${context.rootName} ${context.commandPath.join(" ")}`;
607
- return command.name ?? "command";
608
- }
609
- /**
610
- * Render the usage line for a command
611
- */
612
- function renderUsageLine(command, context) {
613
- const parts = [];
614
- const name = buildUsageCommandName(command, context);
615
- parts.push(styles.commandName(name));
616
- if (context?.globalExtracted?.fields.length) parts.push(styles.placeholder("[global options]"));
617
- const extracted = require_schema_extractor.getExtractedFields(command);
618
- if (extracted) {
619
- const positionals = extracted.fields.filter((a) => a.positional);
620
- if (extracted.fields.filter((a) => !a.positional).length > 0) parts.push(styles.placeholder("[options]"));
621
- if (command.subCommands && getVisibleSubcommandEntries(command.subCommands).length > 0) parts.push(styles.placeholder("[command]"));
622
- for (const arg of positionals) if (arg.required) parts.push(styles.option(`<${arg.name}>`));
623
- else parts.push(styles.placeholder(`[${arg.name}]`));
624
- } else if (command.subCommands && getVisibleSubcommandEntries(command.subCommands).length > 0) parts.push(styles.placeholder("[command]"));
625
- return parts.join(" ");
626
- }
627
- /**
628
- * Render the options section
629
- */
630
- function renderOptions(command, descriptions = {}, context) {
631
- const lines = [];
632
- const desc = {
633
- help: descriptions.help ?? defaultBuiltinDescriptions.help,
634
- helpAll: descriptions.helpAll ?? defaultBuiltinDescriptions.helpAll,
635
- version: descriptions.version ?? defaultBuiltinDescriptions.version
636
- };
637
- const extracted = require_schema_extractor.getExtractedFields(command);
638
- const hasUserDefinedh = extracted?.fields.some((f) => f.overrideBuiltinAlias === true && require_schema_extractor.getAllAliases(f).includes("h")) ?? false;
639
- const hasUserDefinedH = extracted?.fields.some((f) => f.overrideBuiltinAlias === true && require_schema_extractor.getAllAliases(f).includes("H")) ?? false;
640
- if (hasUserDefinedh) lines.push(formatOption(styles.option("--help"), desc.help));
641
- else lines.push(formatOption(`${styles.option("-h")}, ${styles.option("--help")}`, desc.help));
642
- if (hasUserDefinedH) lines.push(formatOption(styles.option("--help-all"), desc.helpAll));
643
- else lines.push(formatOption(`${styles.option("-H")}, ${styles.option("--help-all")}`, desc.helpAll));
644
- if (context?.rootVersion) lines.push(formatOption(styles.option("--version"), desc.version));
645
- if (!extracted) return lines.join("\n");
646
- if (extracted.schemaType === "discriminatedUnion" && extracted.discriminator) return renderDiscriminatedUnionOptions(extracted, command, lines);
647
- if (extracted.schemaType === "union" && extracted.unionOptions) return renderUnionOptions(extracted, command, lines);
648
- if (extracted.schemaType === "xor" && extracted.unionOptions) return renderUnionOptions(extracted, command, lines);
649
- const options = extracted.fields.filter((a) => !a.positional);
650
- for (const opt of options) {
651
- const flags = formatFlags(opt);
652
- let desc = opt.description ?? "";
653
- if (opt.defaultValue !== void 0) desc += ` ${styles.defaultValue(`(default: ${JSON.stringify(opt.defaultValue)})`)}`;
654
- if (opt.required) desc += ` ${styles.required("(required)")}`;
655
- const envInfo = formatEnvInfo(opt.env);
656
- if (envInfo) desc += ` ${envInfo}`;
657
- lines.push(formatOption(flags, desc));
658
- const negationLine = formatNegationLine(opt);
659
- if (negationLine) lines.push(negationLine);
660
- }
661
- return lines.join("\n");
662
- }
663
- /**
664
- * Render a separate line for the custom negation option when a
665
- * `negationDescription` is provided. When no description is given, the
666
- * negation is shown inline by `formatFlags`.
667
- */
668
- function formatNegationLine(opt, indent = 0, extraDescPadding = 0) {
669
- if (!opt.negationDisplay || !opt.negationDescription) return null;
670
- return formatOption(styles.option(`--${opt.negationDisplay}`), `${opt.negationDescription} ${styles.dim(`(↔ --${opt.cliName})`)}`, indent, extraDescPadding);
671
- }
672
- /**
673
- * Render options for discriminated union with variants
674
- */
675
- function renderDiscriminatedUnionOptions(extracted, _command, lines) {
676
- const discriminator = extracted.discriminator;
677
- const variants = extracted.variants ?? [];
678
- const discriminatorField = extracted.fields.find((f) => f.name === discriminator);
679
- if (discriminatorField) {
680
- const variantValues = variants.map((v) => v.discriminatorValue).join("|");
681
- const flags = `${styles.option(`--${discriminator}`)} ${styles.placeholder(`<${variantValues}>`)}`;
682
- const description = extracted.description ?? discriminatorField.description ?? "Action to perform";
683
- lines.push(formatOption(flags, description));
684
- }
685
- const commonFields = /* @__PURE__ */ new Set();
686
- const allFieldNames = /* @__PURE__ */ new Set();
687
- for (const variant of variants) for (const field of variant.fields) allFieldNames.add(field.name);
688
- for (const fieldName of allFieldNames) {
689
- if (fieldName === discriminator) continue;
690
- if (variants.every((v) => v.fields.some((f) => f.name === fieldName))) commonFields.add(fieldName);
691
- }
692
- for (const fieldName of commonFields) {
693
- const field = extracted.fields.find((f) => f.name === fieldName);
694
- if (field && !field.positional) {
695
- const flags = formatFlags(field);
696
- let desc = field.description ?? "";
697
- if (field.defaultValue !== void 0) desc += ` ${styles.defaultValue(`(default: ${JSON.stringify(field.defaultValue)})`)}`;
698
- const envInfo = formatEnvInfo(field.env);
699
- if (envInfo) desc += ` ${envInfo}`;
700
- lines.push(formatOption(flags, desc));
701
- const negationLine = formatNegationLine(field);
702
- if (negationLine) lines.push(negationLine);
703
- }
704
- }
705
- for (const variant of variants) {
706
- const variantFields = variant.fields.filter((f) => f.name !== discriminator && !commonFields.has(f.name) && !f.positional);
707
- if (variantFields.length > 0) {
708
- lines.push("");
709
- const variantLabel = variant.description ? `${styles.dim("When")} ${styles.option(discriminator)}=${styles.bold(variant.discriminatorValue)}: ${variant.description}` : `${styles.dim("When")} ${styles.option(discriminator)}=${styles.bold(variant.discriminatorValue)}:`;
710
- lines.push(variantLabel);
711
- for (const field of variantFields) {
712
- const flags = formatFlags(field);
713
- let desc = field.description ?? "";
714
- if (field.defaultValue !== void 0) desc += ` ${styles.defaultValue(`(default: ${JSON.stringify(field.defaultValue)})`)}`;
715
- if (field.required) desc += ` ${styles.required("(required)")}`;
716
- const envInfo = formatEnvInfo(field.env);
717
- if (envInfo) desc += ` ${envInfo}`;
718
- lines.push(formatOption(flags, desc, 1));
719
- const negationLine = formatNegationLine(field, 1);
720
- if (negationLine) lines.push(negationLine);
721
- }
722
- }
723
- }
724
- return lines.join("\n");
725
- }
726
- /**
727
- * Render options for union with multiple options
728
- */
729
- function renderUnionOptions(extracted, _command, lines) {
730
- const unionOptions = extracted.unionOptions ?? [];
731
- const commonFields = /* @__PURE__ */ new Set();
732
- const allFieldNames = /* @__PURE__ */ new Set();
733
- for (const option of unionOptions) for (const field of option.fields) allFieldNames.add(field.name);
734
- for (const fieldName of allFieldNames) if (unionOptions.every((o) => o.fields.some((f) => f.name === fieldName))) commonFields.add(fieldName);
735
- for (const fieldName of commonFields) {
736
- const field = extracted.fields.find((f) => f.name === fieldName);
737
- if (field && !field.positional) {
738
- const flags = formatFlags(field);
739
- let desc = field.description ?? "";
740
- if (field.defaultValue !== void 0) desc += ` ${styles.defaultValue(`(default: ${JSON.stringify(field.defaultValue)})`)}`;
741
- const envInfo = formatEnvInfo(field.env);
742
- if (envInfo) desc += ` ${envInfo}`;
743
- lines.push(formatOption(flags, desc));
744
- const negationLine = formatNegationLine(field);
745
- if (negationLine) lines.push(negationLine);
746
- }
747
- }
748
- for (let i = 0; i < unionOptions.length; i++) {
749
- const option = unionOptions[i];
750
- if (!option) continue;
751
- const uniqueFields = option.fields.filter((f) => !commonFields.has(f.name) && !f.positional);
752
- const label = option.description ?? `Variant ${i + 1}`;
753
- if (uniqueFields.length > 0) {
754
- lines.push("");
755
- lines.push(` ${styles.bold(`${label}:`)}`);
756
- for (const field of uniqueFields) {
757
- const flags = formatFlags(field);
758
- let desc = field.description ?? "";
759
- if (field.defaultValue !== void 0) desc += ` ${styles.defaultValue(`(default: ${JSON.stringify(field.defaultValue)})`)}`;
760
- if (field.required) desc += ` ${styles.required("(required)")}`;
761
- const envInfo = formatEnvInfo(field.env);
762
- if (envInfo) desc += ` ${envInfo}`;
763
- lines.push(formatOption(flags, desc, 1));
764
- const negationLine = formatNegationLine(field, 1);
765
- if (negationLine) lines.push(negationLine);
766
- }
767
- } else {
768
- lines.push("");
769
- lines.push(` ${styles.bold(`${label}:`)}`);
770
- lines.push(` ${styles.dim(styles.italic("no options"))}`);
771
- }
772
- }
773
- return lines.join("\n");
774
- }
775
- /**
776
- * Format option flags (-v, --verbose <VALUE>)
777
- * Uses cliName (kebab-case) for display
778
- */
779
- function formatFlags(opt) {
780
- const aliasParts = [];
781
- if (opt.alias) {
782
- for (const alias of opt.alias) if (alias.length === 1) aliasParts.push(styles.option(`-${alias}`));
783
- }
784
- let longFlag = styles.option(`--${opt.cliName}`);
785
- if (opt.type !== "boolean") {
786
- const placeholder = opt.placeholder ?? opt.cliName.toUpperCase();
787
- longFlag += ` ${styles.placeholder(`<${placeholder}>`)}`;
788
- }
789
- aliasParts.push(longFlag);
790
- if (opt.alias) {
791
- for (const alias of opt.alias) if (alias.length > 1) {
792
- let longAlias = styles.option(`--${alias}`);
793
- if (opt.type !== "boolean") {
794
- const placeholder = opt.placeholder ?? opt.cliName.toUpperCase();
795
- longAlias += ` ${styles.placeholder(`<${placeholder}>`)}`;
796
- }
797
- aliasParts.push(longAlias);
798
- }
799
- }
800
- const aliasStr = aliasParts.join(", ");
801
- if (opt.type === "boolean" && opt.negationDisplay && !opt.negationDescription) return `${aliasStr} / ${styles.option(`--${opt.negationDisplay}`)}`;
802
- return aliasStr;
803
- }
804
- /**
805
- * Format environment variable info for help display
806
- */
807
- function formatEnvInfo(env) {
808
- if (!env) return "";
809
- const envNames = Array.isArray(env) ? env : [env];
810
- return styles.dim(`[env: ${envNames.join(", ")}]`);
811
- }
812
- /**
813
- * Strip ANSI escape codes from a string to get visual length
814
- */
815
- function stripAnsi(str) {
816
- return str.replace(/\x1B\[[0-9;]*m/g, "");
817
- }
818
- /**
819
- * Pad a string that may contain ANSI codes to a visual width
820
- */
821
- function padEndVisual(str, width) {
822
- const visualLength = stripAnsi(str).length;
823
- const padding = Math.max(0, width - visualLength);
824
- return str + " ".repeat(padding);
825
- }
826
- /**
827
- * Format a single option line
828
- * If flags exceed the column width, description is moved to the next line
829
- */
830
- function formatOption(flags, description, indent = 0, extraDescPadding = 0) {
831
- const flagWidth = 32;
832
- const indentStr = " ".repeat(indent);
833
- const visualFlagLength = stripAnsi(flags).length;
834
- const effectiveFlagWidth = flagWidth - indent * 2 + extraDescPadding;
835
- if (visualFlagLength >= effectiveFlagWidth) return `${indentStr} ${flags}\n${" ".repeat(effectiveFlagWidth + 2 + indent * 2)}${description}`;
836
- return `${indentStr} ${padEndVisual(flags, effectiveFlagWidth)}${description}`;
837
- }
838
- /**
839
- * Format a single option field as a help line
840
- */
841
- function formatFieldLine(opt, indent = 0, extraDescPadding = 0) {
842
- const flags = formatFlags(opt);
843
- let desc = opt.description ?? "";
844
- if (opt.defaultValue !== void 0) desc += ` ${styles.defaultValue(`(default: ${JSON.stringify(opt.defaultValue)})`)}`;
845
- if (opt.required) desc += ` ${styles.required("(required)")}`;
846
- const envInfo = formatEnvInfo(opt.env);
847
- if (envInfo) desc += ` ${envInfo}`;
848
- return formatOption(flags, desc, indent, extraDescPadding);
849
- }
850
- /**
851
- * Render global options section
852
- */
853
- function renderGlobalOptions(globalExtracted) {
854
- const lines = [];
855
- for (const opt of globalExtracted.fields) {
856
- if (opt.positional) continue;
857
- lines.push(formatFieldLine(opt));
858
- const negationLine = formatNegationLine(opt);
859
- if (negationLine) lines.push(negationLine);
860
- }
861
- return lines.join("\n");
862
- }
863
- /**
864
- * Render options for a subcommand (used by showSubcommandOptions)
865
- */
866
- function renderSubcommandOptionsCompact(command, indent) {
867
- const lines = [];
868
- const extracted = require_schema_extractor.getExtractedFields(command);
869
- if (extracted) {
870
- const options = extracted.fields.filter((a) => !a.positional);
871
- for (const opt of options) {
872
- const flags = formatFlags(opt);
873
- let desc = opt.description ?? "";
874
- if (opt.defaultValue !== void 0) desc += ` ${styles.defaultValue(`(default: ${JSON.stringify(opt.defaultValue)})`)}`;
875
- const envInfo = formatEnvInfo(opt.env);
876
- if (envInfo) desc += ` ${envInfo}`;
877
- lines.push(formatOption(flags, desc, indent, 2));
878
- const negationLine = formatNegationLine(opt, indent, 2);
879
- if (negationLine) lines.push(negationLine);
880
- }
881
- }
882
- return lines;
883
- }
884
- /**
885
- * Render subcommands recursively with their options (flat style)
886
- */
887
- function renderSubcommandsWithOptions(subCommands, parentPath, baseIndent) {
888
- const lines = [];
889
- for (const [name, subCmd] of getVisibleSubcommandEntries(subCommands)) {
890
- const cmd = require_schema_extractor.resolveSubCommandMeta(subCmd);
891
- const fullPath = parentPath ? `${parentPath} ${name}` : name;
892
- const desc = cmd?.description ?? "";
893
- const aliases = cmd?.aliases;
894
- const displayName = aliases && aliases.length > 0 ? `${fullPath}, ${aliases.join(", ")}` : fullPath;
895
- lines.push(formatOption(styles.command(displayName), desc, baseIndent));
896
- if (cmd) {
897
- const optionLines = renderSubcommandOptionsCompact(cmd, baseIndent + 1);
898
- lines.push(...optionLines);
899
- const visibleNestedSubCommands = cmd.subCommands ? Object.fromEntries(getVisibleSubcommandEntries(cmd.subCommands)) : void 0;
900
- if (visibleNestedSubCommands && Object.keys(visibleNestedSubCommands).length > 0) {
901
- const nestedLines = renderSubcommandsWithOptions(visibleNestedSubCommands, fullPath, baseIndent);
902
- lines.push(...nestedLines);
903
- }
904
- }
905
- }
906
- return lines;
907
- }
908
- /**
909
- * Generate help text for a command
910
- *
911
- * @param command - The command to generate help for
912
- * @param options - Help generation options
913
- * @returns Formatted help text
914
- */
915
- function generateHelp(command, options) {
916
- const sections = [];
917
- const context = options.context;
918
- const displayName = buildFullCommandName(command, context);
919
- if (displayName) {
920
- let header = styles.commandName(displayName);
921
- if (context?.rootName && context.commandPath && context.commandPath.length > 0) if (context.rootVersion) header += ` ${styles.version(`(${context.rootName} v${context.rootVersion})`)}`;
922
- else header += ` ${styles.version(`(${context.rootName})`)}`;
923
- else if (context?.rootVersion) header += ` ${styles.version(`v${context.rootVersion}`)}`;
924
- sections.push(header);
925
- }
926
- if (context?.aliasFor) sections.push(styles.dim(`Alias for ${styles.commandName(context.aliasFor)}`));
927
- if (command.description) sections.push(command.description);
928
- if (!context?.aliasFor && command.aliases && command.aliases.length > 0) sections.push(`${styles.sectionHeader("Aliases:")} ${command.aliases.map((a) => styles.command(a)).join(", ")}`);
929
- sections.push(`${styles.sectionHeader("Usage:")} ${renderUsageLine(command, context)}`);
930
- const optionsText = renderOptions(command, options.descriptions, context);
931
- if (optionsText) sections.push(`${styles.sectionHeader("Options:")}\n${optionsText}`);
932
- if (context?.globalExtracted?.fields.length) sections.push(`${styles.sectionHeader("Global Options:")}\n${renderGlobalOptions(context.globalExtracted)}`);
933
- if (options.showSubcommands !== false && command.subCommands && getVisibleSubcommandEntries(command.subCommands).length > 0) {
934
- const currentPath = context?.commandPath?.join(" ") ?? "";
935
- const visibleSubCommands = Object.fromEntries(getVisibleSubcommandEntries(command.subCommands));
936
- if (options.showSubcommandOptions) {
937
- const subLines = renderSubcommandsWithOptions(visibleSubCommands, currentPath, 0);
938
- sections.push(`${styles.sectionHeader("Commands:")}\n${subLines.join("\n")}`);
939
- } else {
940
- const subLines = [];
941
- for (const [name, subCmd] of Object.entries(visibleSubCommands)) {
942
- const cmd = require_schema_extractor.resolveSubCommandMeta(subCmd);
943
- const desc = cmd?.description ?? "";
944
- const fullName = currentPath ? `${currentPath} ${name}` : name;
945
- const aliases = cmd?.aliases;
946
- const displayName = aliases && aliases.length > 0 ? `${fullName}, ${aliases.join(", ")}` : fullName;
947
- subLines.push(formatOption(styles.command(displayName), desc));
948
- }
949
- sections.push(`${styles.sectionHeader("Commands:")}\n${subLines.join("\n")}`);
950
- }
951
- }
952
- if (command.examples && command.examples.length > 0) {
953
- const exampleLines = renderExamplesForHelp(command.examples, context);
954
- sections.push(`${styles.sectionHeader("Examples:")}\n${exampleLines}`);
955
- }
956
- if (command.notes) {
957
- const indented = renderMarkdown(command.notes).split("\n").map((line) => line === "" ? "" : ` ${line}`).join("\n");
958
- sections.push(`${styles.sectionHeader("Notes:")}\n${indented}`);
959
- }
960
- return `\n${sections.join("\n\n")}\n`;
961
- }
962
- /**
963
- * Render examples for CLI help output
964
- */
965
- function renderExamplesForHelp(examples, context) {
966
- const lines = [];
967
- const cmdPrefix = context?.rootName ? `${context.rootName} ` : "";
968
- const cmdPath = context?.commandPath?.join(" ") ?? "";
969
- const fullPrefix = cmdPath ? `${cmdPrefix}${cmdPath} ` : cmdPrefix;
970
- for (const example of examples) {
971
- lines.push(` ${styles.dim(example.desc)}`);
972
- lines.push(` ${styles.dim("$")} ${fullPrefix}${example.cmd}`);
973
- if (example.output) for (const line of example.output.split("\n")) lines.push(` ${line}`);
974
- lines.push("");
975
- }
976
- if (lines.length > 0 && lines[lines.length - 1] === "") lines.pop();
977
- return lines.join("\n");
978
- }
979
-
980
- //#endregion
981
- //#region src/validator/validation-errors.ts
982
- /**
983
- * Error thrown when positional argument configuration is invalid
984
- */
985
- var PositionalConfigError = class extends Error {
986
- constructor(message) {
987
- super(message);
988
- this.name = "PositionalConfigError";
989
- }
990
- };
991
- /**
992
- * Error thrown when a reserved alias is used
993
- */
994
- var ReservedAliasError = class extends Error {
995
- constructor(message) {
996
- super(message);
997
- this.name = "ReservedAliasError";
998
- }
999
- };
1000
- /**
1001
- * Error thrown when duplicate field names are detected
1002
- */
1003
- var DuplicateFieldError = class extends Error {
1004
- constructor(message) {
1005
- super(message);
1006
- this.name = "DuplicateFieldError";
1007
- }
1008
- };
1009
- /**
1010
- * Error thrown when duplicate aliases are detected
1011
- */
1012
- var DuplicateAliasError = class extends Error {
1013
- constructor(message) {
1014
- super(message);
1015
- this.name = "DuplicateAliasError";
1016
- }
1017
- };
1018
- /**
1019
- * Error thrown when fields are case variants of each other (e.g. "my-option" and "myOption")
1020
- */
1021
- var CaseVariantCollisionError = class extends Error {
1022
- constructor(message) {
1023
- super(message);
1024
- this.name = "CaseVariantCollisionError";
1025
- }
1026
- };
1027
- /**
1028
- * Error thrown when a custom boolean negation name collides with another
1029
- * field's name, cliName, alias, or another field's negation (including
1030
- * derived camelCase variants).
1031
- */
1032
- var DuplicateNegationError = class extends Error {
1033
- constructor(message) {
1034
- super(message);
1035
- this.name = "DuplicateNegationError";
1036
- }
1037
- };
1038
-
1039
- //#endregion
1040
- //#region src/validator/command-validator.ts
1041
- /**
1042
- * Check for duplicate field names
1043
- */
1044
- function checkDuplicateFields(extracted, commandPath) {
1045
- const errors = [];
1046
- const seenNames = /* @__PURE__ */ new Map();
1047
- for (const field of extracted.fields) {
1048
- if (seenNames.has(field.name)) errors.push({
1049
- commandPath,
1050
- type: "duplicate_field",
1051
- message: `Duplicate field name "${field.name}" detected.`,
1052
- field: field.name
1053
- });
1054
- seenNames.set(field.name, field.name);
1055
- }
1056
- return errors;
1057
- }
1058
- /**
1059
- * Check for case-variant collisions (e.g. "my-option" and "myOption" defined simultaneously)
1060
- */
1061
- function checkCaseVariantCollisions(extracted, commandPath) {
1062
- const errors = [];
1063
- const canonicalMap = /* @__PURE__ */ new Map();
1064
- for (const field of extracted.fields) {
1065
- const camel = require_schema_extractor.toCamelCase(field.name);
1066
- const existing = canonicalMap.get(camel);
1067
- if (existing && existing !== field.name) errors.push({
1068
- commandPath,
1069
- type: "case_variant_collision",
1070
- message: `Fields "${existing}" and "${field.name}" are case variants of each other and would collide.`,
1071
- field: field.name
1072
- });
1073
- canonicalMap.set(camel, field.name);
1074
- }
1075
- return errors;
1076
- }
1077
- /**
1078
- * Check for duplicate aliases and alias-field name conflicts
1079
- */
1080
- function checkDuplicateAliases(extracted, commandPath) {
1081
- const errors = [];
1082
- const seenAliases = /* @__PURE__ */ new Map();
1083
- const fieldNames = new Set(extracted.fields.map((f) => f.name));
1084
- const cliNames = new Set(extracted.fields.map((f) => f.cliName));
1085
- const registerAlias = (alias, fieldName, isDerived) => {
1086
- if (fieldNames.has(alias) || cliNames.has(alias)) errors.push({
1087
- commandPath,
1088
- type: "duplicate_alias",
1089
- message: `Alias "${alias}" for field "${fieldName}" conflicts with existing field name or CLI name "${alias}".`,
1090
- field: fieldName
1091
- });
1092
- const existingField = seenAliases.get(alias);
1093
- if (existingField && existingField !== fieldName) {
1094
- const qualifier = isDerived ? " (derived camelCase variant)" : "";
1095
- errors.push({
1096
- commandPath,
1097
- type: "duplicate_alias",
1098
- message: `Duplicate alias "${alias}"${qualifier} detected. Both "${existingField}" and "${fieldName}" use the same alias.`,
1099
- field: fieldName
1100
- });
1101
- }
1102
- seenAliases.set(alias, fieldName);
1103
- };
1104
- for (const field of extracted.fields) {
1105
- const allAliases = require_schema_extractor.getAllAliases(field);
1106
- if (allAliases.length === 0) continue;
1107
- for (const alias of allAliases) {
1108
- registerAlias(alias, field.name, false);
1109
- if (alias.length > 1 && alias.includes("-")) {
1110
- const camelVariant = require_schema_extractor.toCamelCase(alias);
1111
- if (camelVariant !== alias && !fieldNames.has(camelVariant)) registerAlias(camelVariant, field.name, true);
1112
- }
1113
- }
1114
- }
1115
- return errors;
1116
- }
1117
- /**
1118
- * Check for collisions involving custom boolean `negation` names
1119
- */
1120
- function checkDuplicateNegations(extracted, commandPath) {
1121
- const errors = [];
1122
- const claimed = /* @__PURE__ */ new Map();
1123
- const claim = (name, fieldName, kind) => {
1124
- if (!claimed.has(name)) claimed.set(name, {
1125
- field: fieldName,
1126
- kind
1127
- });
1128
- };
1129
- for (const field of extracted.fields) {
1130
- claim(field.name, field.name, "field name");
1131
- if (field.name.includes("-")) {
1132
- const camelName = require_schema_extractor.toCamelCase(field.name);
1133
- if (camelName !== field.name) claim(camelName, field.name, "field name");
1134
- }
1135
- if (field.cliName !== field.name) claim(field.cliName, field.name, "CLI name");
1136
- if (field.cliName.includes("-")) {
1137
- const camelCli = require_schema_extractor.toCamelCase(field.cliName);
1138
- if (camelCli !== field.cliName) claim(camelCli, field.name, "CLI name");
1139
- }
1140
- for (const alias of require_schema_extractor.getAllAliases(field)) {
1141
- claim(alias, field.name, "alias");
1142
- if (alias.length > 1 && alias.includes("-")) {
1143
- const camelVariant = require_schema_extractor.toCamelCase(alias);
1144
- if (camelVariant !== alias) claim(camelVariant, field.name, "alias");
1145
- }
1146
- }
1147
- if (field.type === "boolean" && field.negation !== false && typeof field.negation !== "string") {
1148
- const defaultKebab = `no-${field.cliName}`;
1149
- claim(defaultKebab, field.name, "default negation");
1150
- const camelBase = require_schema_extractor.toCamelCase(field.cliName);
1151
- const defaultCamel = `no${camelBase[0]?.toUpperCase() ?? ""}${camelBase.slice(1)}`;
1152
- if (defaultCamel !== defaultKebab) claim(defaultCamel, field.name, "default negation");
1153
- }
1154
- }
1155
- const seenNegations = /* @__PURE__ */ new Map();
1156
- const register = (name, fieldName, isDerived) => {
1157
- const claim = claimed.get(name);
1158
- if (claim) {
1159
- const qualifier = isDerived ? " (derived camelCase variant)" : "";
1160
- const conflict = claim.field === fieldName ? `the same field's own ${claim.kind} "${name}"` : `${claim.kind} "${name}" of field "${claim.field}"`;
1161
- errors.push({
1162
- commandPath,
1163
- type: "duplicate_negation",
1164
- message: `Negation "${name}"${qualifier} for field "${fieldName}" conflicts with ${conflict}.`,
1165
- field: fieldName
1166
- });
1167
- }
1168
- const existing = seenNegations.get(name);
1169
- if (existing && existing !== fieldName) {
1170
- const qualifier = isDerived ? " (derived camelCase variant)" : "";
1171
- errors.push({
1172
- commandPath,
1173
- type: "duplicate_negation",
1174
- message: `Duplicate negation "${name}"${qualifier} detected. Both "${existing}" and "${fieldName}" use the same negation name.`,
1175
- field: fieldName
1176
- });
1177
- }
1178
- seenNegations.set(name, fieldName);
1179
- };
1180
- for (const field of extracted.fields) {
1181
- if (typeof field.negation !== "string") continue;
1182
- register(field.negation, field.name, false);
1183
- if (field.negation.includes("-")) {
1184
- const camelVariant = require_schema_extractor.toCamelCase(field.negation);
1185
- if (camelVariant !== field.negation) register(camelVariant, field.name, true);
1186
- }
1187
- }
1188
- return errors;
1189
- }
1190
- /**
1191
- * Check positional argument configuration
1192
- */
1193
- function checkPositionalConfig(extracted, commandPath) {
1194
- const errors = [];
1195
- const positionalFields = extracted.fields.filter((f) => f.positional);
1196
- let foundArrayPositional = null;
1197
- let foundOptionalPositional = null;
1198
- for (const field of positionalFields) {
1199
- if (foundArrayPositional !== null) errors.push({
1200
- commandPath,
1201
- type: "positional_config",
1202
- message: `Positional argument "${field.name}" cannot follow array positional argument "${foundArrayPositional}".`,
1203
- field: field.name
1204
- });
1205
- if (field.type === "array" && foundOptionalPositional !== null) errors.push({
1206
- commandPath,
1207
- type: "positional_config",
1208
- message: `Array positional "${field.name}" cannot be used with optional positional "${foundOptionalPositional}" (ambiguous parsing).`,
1209
- field: field.name
1210
- });
1211
- if (foundOptionalPositional !== null && field.required) errors.push({
1212
- commandPath,
1213
- type: "positional_config",
1214
- message: `Required positional "${field.name}" cannot follow optional positional "${foundOptionalPositional}".`,
1215
- field: field.name
1216
- });
1217
- if (field.type === "array") foundArrayPositional = field.name;
1218
- if (!field.required) foundOptionalPositional = field.name;
1219
- }
1220
- return errors;
1221
- }
1222
- /**
1223
- * Check for reserved aliases used without override flag
1224
- */
1225
- function checkReservedAliases(extracted, commandPath) {
1226
- const errors = [];
1227
- for (const field of extracted.fields) {
1228
- if (field.overrideBuiltinAlias === true) continue;
1229
- for (const alias of require_schema_extractor.getAllAliases(field)) if (alias === "h" || alias === "H") errors.push({
1230
- commandPath,
1231
- type: "reserved_alias",
1232
- message: `Alias "${alias}" is reserved for --${alias === "h" ? "help" : "help-all"}.`,
1233
- field: field.name
1234
- });
1235
- }
1236
- return errors;
1237
- }
1238
- /**
1239
- * Validate that no duplicate field names exist
1240
- *
1241
- * @param extracted - Extracted fields from schema
1242
- * @throws {DuplicateFieldError} If duplicate field names are found
1243
- */
1244
- function validateDuplicateFields(extracted) {
1245
- const errors = checkDuplicateFields(extracted, []);
1246
- if (errors.length > 0) throw new DuplicateFieldError(`Duplicate field name "${errors[0]?.field ?? "unknown"}" detected. Each field must have a unique name.`);
1247
- }
1248
- /**
1249
- * Validate that no duplicate aliases exist
1250
- *
1251
- * Also checks for conflicts between aliases and field names
1252
- *
1253
- * @param extracted - Extracted fields from schema
1254
- * @throws {DuplicateAliasError} If duplicate aliases are found or alias conflicts with field name
1255
- */
1256
- function validateDuplicateAliases(extracted) {
1257
- const errors = checkDuplicateAliases(extracted, []);
1258
- if (errors.length > 0) {
1259
- const err = errors[0];
1260
- throw new DuplicateAliasError(err.message);
1261
- }
1262
- }
1263
- /**
1264
- * Validate positional argument configuration
1265
- *
1266
- * Rules:
1267
- * - Array positional arguments must be the last positional
1268
- * - No positional arguments can follow an array positional
1269
- * - Required positional arguments cannot follow optional positional arguments
1270
- * - Array positional and optional positional cannot be used together (ambiguous parsing)
1271
- *
1272
- * @param extracted - Extracted fields from schema
1273
- * @throws {PositionalConfigError} If configuration is invalid
1274
- */
1275
- function validatePositionalConfig(extracted) {
1276
- const errors = checkPositionalConfig(extracted, []);
1277
- if (errors.length > 0) {
1278
- const err = errors[0];
1279
- throw new PositionalConfigError(err.message);
1280
- }
1281
- }
1282
- /**
1283
- * Validate that no reserved aliases are used without explicit override
1284
- *
1285
- * Reserved aliases:
1286
- * - 'h' is reserved for --help
1287
- * - 'H' is reserved for --help-all
1288
- *
1289
- * Users can override these by setting overrideBuiltinAlias: true
1290
- *
1291
- * @param extracted - Extracted fields from schema
1292
- * @param _hasSubCommands - Whether the command has subcommands (reserved for future use)
1293
- * @throws {ReservedAliasError} If a reserved alias is used without override flag
1294
- */
1295
- function validateReservedAliases(extracted, _hasSubCommands) {
1296
- const errors = checkReservedAliases(extracted, []);
1297
- if (errors.length > 0) {
1298
- const field = errors[0].field ?? "unknown";
1299
- const found = extracted.fields.find((f) => f.name === field);
1300
- const alias = (found ? require_schema_extractor.getAllAliases(found) : []).find((a) => a === "h" || a === "H") ?? "h";
1301
- throw new ReservedAliasError(`Alias "${alias}" is reserved for --${alias === "h" ? "help" : "help-all"}. To override this, set { overrideBuiltinAlias: true } for "${field}" and keep the alias where it is currently defined (in alias or hiddenAlias).`);
1302
- }
1303
- }
1304
- /**
1305
- * Validate that custom boolean negation names do not collide with anything
1306
- *
1307
- * @param extracted - Extracted fields from schema
1308
- * @throws {DuplicateNegationError} If a colliding negation is found
1309
- */
1310
- function validateDuplicateNegations(extracted) {
1311
- const errors = checkDuplicateNegations(extracted, []);
1312
- if (errors.length > 0) {
1313
- const err = errors[0];
1314
- throw new DuplicateNegationError(err.message);
1315
- }
1316
- }
1317
- /**
1318
- * Validate that no case-variant collisions exist
1319
- *
1320
- * @param extracted - Extracted fields from schema
1321
- * @throws {CaseVariantCollisionError} If case-variant collisions are found
1322
- */
1323
- function validateCaseVariantCollisions(extracted) {
1324
- const errors = checkCaseVariantCollisions(extracted, []);
1325
- if (errors.length > 0) {
1326
- const err = errors[0];
1327
- throw new CaseVariantCollisionError(err.message);
1328
- }
1329
- }
1330
- /**
1331
- * Validate that no case-variant collisions exist between two schemas
1332
- * (e.g., global args and command args).
1333
- *
1334
- * @param extractedA - Extracted fields from first schema (e.g., global args)
1335
- * @param extractedB - Extracted fields from second schema (e.g., command args)
1336
- * @throws {CaseVariantCollisionError} If cross-schema case-variant collisions are found
1337
- */
1338
- function validateCrossSchemaCollisions(extractedA, extractedB) {
1339
- const canonicalMap = /* @__PURE__ */ new Map();
1340
- for (const field of extractedA.fields) canonicalMap.set(require_schema_extractor.toCamelCase(field.name), field.name);
1341
- for (const field of extractedB.fields) {
1342
- const camel = require_schema_extractor.toCamelCase(field.name);
1343
- const existing = canonicalMap.get(camel);
1344
- if (existing && existing !== field.name) throw new CaseVariantCollisionError(`Global field "${existing}" and command field "${field.name}" are case variants of each other and would collide.`);
1345
- }
1346
- }
1347
- /**
1348
- * Collect validation errors for a single command's schema (non-throwing)
1349
- */
1350
- function collectSchemaErrors(extracted, _hasSubCommands, commandPath) {
1351
- return [
1352
- ...checkDuplicateFields(extracted, commandPath),
1353
- ...checkCaseVariantCollisions(extracted, commandPath),
1354
- ...checkDuplicateAliases(extracted, commandPath),
1355
- ...checkDuplicateNegations(extracted, commandPath),
1356
- ...checkPositionalConfig(extracted, commandPath),
1357
- ...checkReservedAliases(extracted, commandPath)
1358
- ];
1359
- }
1360
- /**
1361
- * Check for alias conflicts within subcommands
1362
- * - Aliases must not conflict with subcommand names
1363
- * - Aliases must not conflict with other aliases
1364
- */
1365
- function checkSubCommandAliasConflicts(command, commandPath) {
1366
- const errors = [];
1367
- if (!command.subCommands) return errors;
1368
- const nameToOwner = /* @__PURE__ */ new Map();
1369
- for (const [name] of Object.entries(command.subCommands)) nameToOwner.set(name, name);
1370
- for (const [name, subCmdValue] of Object.entries(command.subCommands)) {
1371
- const resolved = require_schema_extractor.isLazyCommand(subCmdValue) ? subCmdValue.meta : typeof subCmdValue !== "function" ? subCmdValue : null;
1372
- if (!resolved?.aliases) continue;
1373
- const subCommandPath = [...commandPath, name];
1374
- for (const alias of resolved.aliases) {
1375
- if (!/^[a-zA-Z0-9][a-zA-Z0-9_-]*$/.test(alias)) {
1376
- errors.push({
1377
- commandPath: subCommandPath,
1378
- type: "invalid_alias",
1379
- message: `Alias "${alias}" is invalid. Aliases must start with an alphanumeric character and contain only alphanumeric characters, hyphens, or underscores.`,
1380
- field: name
1381
- });
1382
- continue;
1383
- }
1384
- if (alias === name) {
1385
- errors.push({
1386
- commandPath: subCommandPath,
1387
- type: "duplicate_alias",
1388
- message: `Alias "${alias}" conflicts with its own name.`,
1389
- field: name
1390
- });
1391
- continue;
1392
- }
1393
- const existing = nameToOwner.get(alias);
1394
- if (existing) if (existing === name) errors.push({
1395
- commandPath: subCommandPath,
1396
- type: "duplicate_alias",
1397
- message: `Alias "${alias}" is duplicated within the alias list.`,
1398
- field: name
1399
- });
1400
- else errors.push({
1401
- commandPath: subCommandPath,
1402
- type: "duplicate_alias",
1403
- message: `Alias "${alias}" conflicts with existing subcommand or alias "${existing}".`,
1404
- field: name
1405
- });
1406
- else nameToOwner.set(alias, name);
1407
- }
1408
- }
1409
- return errors;
1410
- }
1411
- /**
1412
- * Validate a command and all its subcommands recursively
1413
- *
1414
- * This function collects all validation errors without throwing,
1415
- * making it suitable for test assertions.
1416
- *
1417
- * @param command - The command to validate
1418
- * @param options - Validation options
1419
- * @returns Validation result with all errors collected
1420
- *
1421
- * @example
1422
- * ```ts
1423
- * const result = await validateCommand(myCommand);
1424
- * if (!result.valid) {
1425
- * console.error(result.errors);
1426
- * }
1427
- * ```
1428
- */
1429
- async function validateCommand(command, options = {}) {
1430
- const commandPath = options.commandPath ?? [command.name];
1431
- const errors = [];
1432
- const hasSubCommands = command.subCommands ? Object.keys(command.subCommands).length > 0 : false;
1433
- if (command.args) {
1434
- const extracted = require_schema_extractor.extractFields(command.args);
1435
- errors.push(...collectSchemaErrors(extracted, hasSubCommands, commandPath));
1436
- }
1437
- errors.push(...checkSubCommandAliasConflicts(command, commandPath));
1438
- if (command.subCommands) for (const [name, subCmd] of Object.entries(command.subCommands)) {
1439
- const subResult = await validateCommand(await require_schema_extractor.resolveLazyCommand(subCmd), { commandPath: [...commandPath, name] });
1440
- if (!subResult.valid) errors.push(...subResult.errors);
1441
- }
1442
- if (errors.length === 0) return { valid: true };
1443
- return {
1444
- valid: false,
1445
- errors
1446
- };
1447
- }
1448
- /**
1449
- * Format command validation errors for display
1450
- *
1451
- * @param errors - Array of validation errors
1452
- * @returns Formatted error message
1453
- */
1454
- function formatCommandValidationErrors(errors) {
1455
- if (errors.length === 0) return "";
1456
- const lines = ["Command definition errors:"];
1457
- for (const error of errors) {
1458
- const path = error.commandPath.join(" > ");
1459
- lines.push(` - [${path}] ${error.message}`);
1460
- }
1461
- return lines.join("\n");
1462
- }
1463
-
1464
- //#endregion
1465
- //#region src/parser/argv-parser.ts
1466
- /**
1467
- * Parse argv into a flat record
1468
- *
1469
- * Supports:
1470
- * - Long options: --flag, --flag=value, --flag value
1471
- * - Short options: -f, -f=value, -f value
1472
- * - Combined short options: -abc (treated as -a -b -c if all are boolean)
1473
- * - Positional arguments
1474
- * - -- to stop parsing options
1475
- * - Boolean negation: --no-flag, --noFlag (requires `booleanFlags`)
1476
- *
1477
- * **Note:** When using negation detection (`--noFlag` / `--no-flag`),
1478
- * supply `definedNames` so that options whose names happen to start with
1479
- * "no" (e.g. `noDryRun`) are not mistaken for negation of another flag.
1480
- * Without `definedNames`, all `--noX` forms matching a boolean flag will
1481
- * be treated as negation.
1482
- *
1483
- * @param argv - Command line arguments
1484
- * @param options - Parser options
1485
- * @returns Parsed arguments
1486
- */
1487
- function parseArgv(argv, options = {}) {
1488
- const { aliasMap = /* @__PURE__ */ new Map(), booleanFlags = /* @__PURE__ */ new Set(), arrayFlags = /* @__PURE__ */ new Set(), definedNames = /* @__PURE__ */ new Set(), negationMap = /* @__PURE__ */ new Map(), customNegatedFields = /* @__PURE__ */ new Set() } = options;
1489
- const result = {
1490
- options: {},
1491
- positionals: [],
1492
- rest: []
1493
- };
1494
- let i = 0;
1495
- let stopParsing = false;
1496
- const setOption = (name, value) => {
1497
- const resolvedName = aliasMap.get(name) ?? name;
1498
- if (arrayFlags.has(resolvedName)) {
1499
- const existing = result.options[resolvedName];
1500
- if (Array.isArray(existing)) existing.push(value);
1501
- else if (existing !== void 0) result.options[resolvedName] = [existing, value];
1502
- else result.options[resolvedName] = [value];
1503
- } else result.options[resolvedName] = value;
1504
- };
1505
- while (i < argv.length) {
1506
- const arg = argv[i];
1507
- if (stopParsing) {
1508
- result.rest.push(arg);
1509
- i++;
1510
- continue;
1511
- }
1512
- if (arg === "--") {
1513
- stopParsing = true;
1514
- i++;
1515
- continue;
1516
- }
1517
- if (arg.startsWith("--")) {
1518
- const withoutDashes = arg.slice(2);
1519
- if (!withoutDashes.includes("=")) {
1520
- const negatedField = negationMap.get(withoutDashes);
1521
- if (negatedField && booleanFlags.has(negatedField)) {
1522
- setOption(negatedField, false);
1523
- i++;
1524
- continue;
1525
- }
1526
- }
1527
- if (withoutDashes.startsWith("no-")) {
1528
- const flagName = withoutDashes.slice(3);
1529
- if (flagName === flagName.toLowerCase()) {
1530
- const resolvedName = aliasMap.get(flagName) ?? flagName;
1531
- if (booleanFlags.has(resolvedName) && !customNegatedFields.has(resolvedName)) {
1532
- const asIsResolved = aliasMap.get(withoutDashes) ?? withoutDashes;
1533
- if (!definedNames.has(asIsResolved)) {
1534
- setOption(flagName, false);
1535
- i++;
1536
- continue;
1537
- }
1538
- }
1539
- }
1540
- }
1541
- if (withoutDashes.length > 2 && withoutDashes.startsWith("no") && /[A-Z]/.test(withoutDashes[2])) {
1542
- const camelFlagName = withoutDashes[2].toLowerCase() + withoutDashes.slice(3);
1543
- const resolvedName = aliasMap.get(camelFlagName) ?? camelFlagName;
1544
- if (booleanFlags.has(resolvedName) && !customNegatedFields.has(resolvedName)) {
1545
- const asIsResolved = aliasMap.get(withoutDashes) ?? withoutDashes;
1546
- if (!definedNames.has(asIsResolved)) {
1547
- setOption(camelFlagName, false);
1548
- i++;
1549
- continue;
1550
- }
1551
- }
1552
- }
1553
- const eqIndex = withoutDashes.indexOf("=");
1554
- if (eqIndex !== -1) {
1555
- setOption(withoutDashes.slice(0, eqIndex), withoutDashes.slice(eqIndex + 1));
1556
- i++;
1557
- } else {
1558
- const name = withoutDashes;
1559
- const resolvedName = aliasMap.get(name) ?? name;
1560
- if (booleanFlags.has(resolvedName)) {
1561
- setOption(name, true);
1562
- i++;
1563
- } else {
1564
- const nextArg = argv[i + 1];
1565
- if (nextArg !== void 0 && !nextArg.startsWith("-")) {
1566
- setOption(name, nextArg);
1567
- i += 2;
1568
- } else {
1569
- setOption(name, true);
1570
- i++;
1571
- }
1572
- }
1573
- }
1574
- continue;
1575
- }
1576
- if (arg.startsWith("-") && arg.length > 1 && !arg.startsWith("--")) {
1577
- const withoutDash = arg.slice(1);
1578
- const eqIndex = withoutDash.indexOf("=");
1579
- if (eqIndex !== -1) {
1580
- setOption(withoutDash.slice(0, eqIndex), withoutDash.slice(eqIndex + 1));
1581
- i++;
1582
- } else if (withoutDash.length === 1) {
1583
- const name = withoutDash;
1584
- const resolvedName = aliasMap.get(name) ?? name;
1585
- if (booleanFlags.has(resolvedName)) {
1586
- setOption(name, true);
1587
- i++;
1588
- } else {
1589
- const nextArg = argv[i + 1];
1590
- if (nextArg !== void 0 && !nextArg.startsWith("-")) {
1591
- setOption(name, nextArg);
1592
- i += 2;
1593
- } else {
1594
- setOption(name, true);
1595
- i++;
1596
- }
1597
- }
1598
- } else {
1599
- for (const char of withoutDash) setOption(char, true);
1600
- i++;
1601
- }
1602
- continue;
1603
- }
1604
- result.positionals.push(arg);
1605
- i++;
1606
- }
1607
- return result;
1608
- }
1609
- /**
1610
- * Build parser options from extracted fields
1611
- */
1612
- function buildParserOptions(extracted) {
1613
- const aliasMap = /* @__PURE__ */ new Map();
1614
- const booleanFlags = /* @__PURE__ */ new Set();
1615
- const arrayFlags = /* @__PURE__ */ new Set();
1616
- const definedNames = /* @__PURE__ */ new Set();
1617
- const negationMap = /* @__PURE__ */ new Map();
1618
- const customNegatedFields = /* @__PURE__ */ new Set();
1619
- for (const field of extracted.fields) definedNames.add(field.name);
1620
- for (const field of extracted.fields) {
1621
- if (field.cliName !== field.name) aliasMap.set(field.cliName, field.name);
1622
- for (const alias of require_schema_extractor.getAllAliases(field)) {
1623
- aliasMap.set(alias, field.name);
1624
- if (alias.length > 1 && alias.includes("-")) {
1625
- const camelAlias = require_schema_extractor.toCamelCase(alias);
1626
- if (camelAlias !== alias && !definedNames.has(camelAlias) && !aliasMap.has(camelAlias)) aliasMap.set(camelAlias, field.name);
1627
- }
1628
- }
1629
- const camelVariant = require_schema_extractor.toCamelCase(field.name);
1630
- if (camelVariant !== field.name && !definedNames.has(camelVariant) && !aliasMap.has(camelVariant)) aliasMap.set(camelVariant, field.name);
1631
- if (field.type === "boolean") booleanFlags.add(field.name);
1632
- if (field.type === "array") arrayFlags.add(field.name);
1633
- if (field.type === "boolean" && (typeof field.negation === "string" || field.negation === false)) {
1634
- customNegatedFields.add(field.name);
1635
- if (typeof field.negation === "string") {
1636
- negationMap.set(field.negation, field.name);
1637
- if (field.negation.includes("-")) {
1638
- const camelNegation = require_schema_extractor.toCamelCase(field.negation);
1639
- if (camelNegation !== field.negation) negationMap.set(camelNegation, field.name);
1640
- }
1641
- }
1642
- }
1643
- }
1644
- return {
1645
- aliasMap,
1646
- booleanFlags,
1647
- arrayFlags,
1648
- definedNames,
1649
- negationMap,
1650
- customNegatedFields
1651
- };
1652
- }
1653
- /**
1654
- * Merge parsed argv with positional fields to create a flat record
1655
- */
1656
- function mergeWithPositionals(parsed, extracted) {
1657
- const result = { ...parsed.options };
1658
- const positionalFields = extracted.fields.filter((f) => f.positional);
1659
- const allPositionals = parsed.rest.length > 0 ? [...parsed.positionals, ...parsed.rest] : parsed.positionals;
1660
- let positionalIndex = 0;
1661
- for (const field of positionalFields) {
1662
- if (positionalIndex >= allPositionals.length) break;
1663
- if (field.type === "array") {
1664
- result[field.name] = allPositionals.slice(positionalIndex);
1665
- break;
1666
- } else {
1667
- result[field.name] = allPositionals[positionalIndex];
1668
- positionalIndex++;
1669
- }
1670
- }
1671
- return result;
1672
- }
1673
-
1674
- //#endregion
1675
- //#region src/parser/subcommand-scanner.ts
1676
- /**
1677
- * Build lookup tables from extracted global schema fields.
1678
- * Shared by scanForSubcommand, separateGlobalArgs, and findFirstPositional.
1679
- */
1680
- function buildGlobalFlagLookup(globalExtracted) {
1681
- const { aliasMap = /* @__PURE__ */ new Map(), booleanFlags = /* @__PURE__ */ new Set(), negationMap = /* @__PURE__ */ new Map(), customNegatedFields = /* @__PURE__ */ new Set() } = buildParserOptions(globalExtracted);
1682
- const shortAliases = /* @__PURE__ */ new Set();
1683
- for (const field of globalExtracted.fields) for (const alias of require_schema_extractor.getAllAliases(field)) if (alias.length === 1) shortAliases.add(alias);
1684
- return {
1685
- aliasMap,
1686
- booleanFlags,
1687
- flagNames: new Set(globalExtracted.fields.map((f) => f.name)),
1688
- cliNames: new Set(globalExtracted.fields.map((f) => f.cliName)),
1689
- aliases: shortAliases,
1690
- negationMap,
1691
- customNegatedFields
1692
- };
1693
- }
1694
- /**
1695
- * Resolve a long option (--flag, --flag=value, --no-flag, --custom-negation)
1696
- * against global flag lookup. Returns the resolved camelCase name and whether
1697
- * it is a known global flag.
1698
- *
1699
- * `isSuppressedNegation` is true when the token matches a default `--no-X`
1700
- * form that has been suppressed by a custom `negation` on the target field.
1701
- * The caller may use this to keep argv scanning past such tokens (so a
1702
- * trailing subcommand is still detected) even though they no longer negate.
1703
- */
1704
- function resolveGlobalLongOption(arg, lookup) {
1705
- const withoutDashes = arg.includes("=") ? arg.slice(2, arg.indexOf("=")) : arg.slice(2);
1706
- const customNegated = !arg.includes("=") ? lookup.negationMap.get(withoutDashes) : void 0;
1707
- if (customNegated) return {
1708
- resolvedName: customNegated,
1709
- withoutDashes,
1710
- isNegated: true,
1711
- isGlobal: lookup.flagNames.has(customNegated),
1712
- isSuppressedNegation: false
1713
- };
1714
- const kebabNegated = withoutDashes.startsWith("no-");
1715
- const camelNegated = !kebabNegated && withoutDashes.length > 2 && withoutDashes.startsWith("no") && /[A-Z]/.test(withoutDashes[2]);
1716
- if (kebabNegated || camelNegated) {
1717
- const literalResolved = lookup.aliasMap.get(withoutDashes) ?? withoutDashes;
1718
- if (lookup.flagNames.has(literalResolved) || lookup.cliNames.has(withoutDashes)) return {
1719
- resolvedName: literalResolved,
1720
- withoutDashes,
1721
- isNegated: false,
1722
- isGlobal: true,
1723
- isSuppressedNegation: false
1724
- };
1725
- }
1726
- const defaultIsNegated = kebabNegated || camelNegated;
1727
- const flagName = kebabNegated ? withoutDashes.slice(3) : camelNegated ? withoutDashes[2].toLowerCase() + withoutDashes.slice(3) : withoutDashes;
1728
- const resolvedName = lookup.aliasMap.get(flagName) ?? flagName;
1729
- const suppressDefaultNegation = defaultIsNegated && lookup.customNegatedFields.has(resolvedName);
1730
- return {
1731
- resolvedName,
1732
- withoutDashes,
1733
- isNegated: defaultIsNegated && !suppressDefaultNegation,
1734
- isGlobal: !suppressDefaultNegation && (lookup.flagNames.has(resolvedName) || lookup.cliNames.has(withoutDashes) || lookup.cliNames.has(flagName)),
1735
- isSuppressedNegation: suppressDefaultNegation
1736
- };
1737
- }
1738
- /**
1739
- * Check whether a non-boolean flag should consume the next argv token as its value.
1740
- * Returns true when the next token exists, is not a flag, and the current flag
1741
- * is not boolean / negated / using = syntax.
1742
- */
1743
- function shouldConsumeValue(arg, resolvedName, isNegated, nextArg, booleanFlags) {
1744
- return !arg.includes("=") && !booleanFlags.has(resolvedName) && !isNegated && nextArg !== void 0 && !nextArg.startsWith("-");
1745
- }
1746
- /**
1747
- * Collect a recognized global flag (and its value if applicable) into `dest`,
1748
- * returning how many argv positions were consumed (1 or 2).
1749
- */
1750
- function collectGlobalFlag(argv, i, resolvedName, isNegated, booleanFlags, dest) {
1751
- const arg = argv[i];
1752
- dest.push(arg);
1753
- if (shouldConsumeValue(arg, resolvedName, isNegated, argv[i + 1], booleanFlags)) {
1754
- dest.push(argv[i + 1]);
1755
- return 2;
1756
- }
1757
- return 1;
1758
- }
1759
- /**
1760
- * Scan argv to find the subcommand position, skipping over global flags.
1761
- *
1762
- * Walks argv and recognizes global flags (long, short, --no-*) so that
1763
- * `my-cli --verbose build --output dist` correctly identifies `build` as
1764
- * the subcommand (index 1) rather than treating `--verbose` as the subcommand.
1765
- *
1766
- * Limitation: flags appearing before the subcommand name are matched only
1767
- * against the global schema. If a flag is defined in both global and a
1768
- * subcommand's local schema, the pre-subcommand occurrence is always treated
1769
- * as global because the local schema is not available until the subcommand is
1770
- * identified (lazy-loaded commands make eager checking infeasible). Place
1771
- * colliding flags after the subcommand name so that `separateGlobalArgs` can
1772
- * apply local-precedence logic.
1773
- *
1774
- * @param argv - Command line arguments
1775
- * @param subCommandNames - Valid subcommand names
1776
- * @param globalExtracted - Extracted fields from global args schema
1777
- * @returns Scan result with subcommand position and token separation
1778
- */
1779
- function scanForSubcommand(argv, subCommandNames, globalExtracted) {
1780
- const lookup = buildGlobalFlagLookup(globalExtracted);
1781
- const subCommandNameSet = new Set(subCommandNames);
1782
- const globalTokensBefore = [];
1783
- const suppressedTokens = [];
1784
- let i = 0;
1785
- while (i < argv.length) {
1786
- const arg = argv[i];
1787
- if (arg === "--" || BUILTIN_FLAGS.has(arg)) break;
1788
- if (!arg.startsWith("-") && subCommandNameSet.has(arg)) return {
1789
- subCommandIndex: i,
1790
- globalTokensBefore,
1791
- tokensAfterSubcommand: argv.slice(i + 1),
1792
- suppressedTokens
1793
- };
1794
- if (arg.startsWith("--")) {
1795
- const { resolvedName, isNegated, isGlobal, isSuppressedNegation } = resolveGlobalLongOption(arg, lookup);
1796
- if (isGlobal) {
1797
- i += collectGlobalFlag(argv, i, resolvedName, isNegated, lookup.booleanFlags, globalTokensBefore);
1798
- continue;
1799
- }
1800
- if (isSuppressedNegation) {
1801
- suppressedTokens.push(arg.includes("=") ? arg.slice(2, arg.indexOf("=")) : arg.slice(2));
1802
- i++;
1803
- continue;
1804
- }
1805
- break;
1806
- }
1807
- if (arg.startsWith("-") && arg.length > 1) {
1808
- const withoutDash = arg.includes("=") ? arg.slice(1, arg.indexOf("=")) : arg.slice(1);
1809
- if (withoutDash.length === 1) {
1810
- const resolvedName = lookup.aliasMap.get(withoutDash) ?? withoutDash;
1811
- if (lookup.aliases.has(withoutDash) || lookup.flagNames.has(resolvedName)) {
1812
- i += collectGlobalFlag(argv, i, resolvedName, false, lookup.booleanFlags, globalTokensBefore);
1813
- continue;
1814
- }
1815
- }
1816
- break;
1817
- }
1818
- break;
1819
- }
1820
- return {
1821
- subCommandIndex: -1,
1822
- globalTokensBefore,
1823
- tokensAfterSubcommand: [],
1824
- suppressedTokens
1825
- };
1826
- }
1827
- const BUILTIN_FLAGS = new Set([
1828
- "--help",
1829
- "-h",
1830
- "--help-all",
1831
- "-H",
1832
- "--version"
1833
- ]);
1834
- /**
1835
- * Find the index of the first positional argument in argv, properly skipping
1836
- * global flag values. Returns -1 when no positional is present.
1837
- *
1838
- * Mirrors `scanForSubcommand`'s conservative stop conditions: the scan stops
1839
- * (returning -1) on a `--` terminator, a builtin flag (`--help`/`--version`),
1840
- * an unknown long flag, or an unknown/combined short flag. Past such tokens we
1841
- * can't tell a flag *value* from a positional, so continuing would misclassify
1842
- * e.g. `--help plugin` or `--unknown value` and wrongly trip plugin dispatch.
1843
- *
1844
- * Without globalExtracted, no flag is global, so any leading flag halts the
1845
- * scan and a positional is only found when it precedes every flag.
1846
- */
1847
- function findFirstPositionalIndex(argv, globalExtracted) {
1848
- const lookup = globalExtracted ? buildGlobalFlagLookup(globalExtracted) : {
1849
- aliasMap: /* @__PURE__ */ new Map(),
1850
- booleanFlags: /* @__PURE__ */ new Set(),
1851
- flagNames: /* @__PURE__ */ new Set(),
1852
- cliNames: /* @__PURE__ */ new Set(),
1853
- aliases: /* @__PURE__ */ new Set(),
1854
- negationMap: /* @__PURE__ */ new Map(),
1855
- customNegatedFields: /* @__PURE__ */ new Set()
1856
- };
1857
- for (let i = 0; i < argv.length; i++) {
1858
- const arg = argv[i];
1859
- if (!arg.startsWith("-")) return i;
1860
- if (arg === "--" || BUILTIN_FLAGS.has(arg)) return -1;
1861
- if (arg.startsWith("--")) {
1862
- const { resolvedName, isNegated, isGlobal, isSuppressedNegation } = resolveGlobalLongOption(arg, lookup);
1863
- if (isGlobal) {
1864
- if (shouldConsumeValue(arg, resolvedName, isNegated, argv[i + 1], lookup.booleanFlags)) i++;
1865
- continue;
1866
- }
1867
- if (isSuppressedNegation) continue;
1868
- return -1;
1869
- }
1870
- const withoutDash = arg.includes("=") ? arg.slice(1, arg.indexOf("=")) : arg.slice(1);
1871
- if (withoutDash.length === 1) {
1872
- const resolvedName = lookup.aliasMap.get(withoutDash) ?? withoutDash;
1873
- if (lookup.aliases.has(withoutDash) || lookup.flagNames.has(resolvedName)) {
1874
- if (shouldConsumeValue(arg, resolvedName, false, argv[i + 1], lookup.booleanFlags)) i++;
1875
- continue;
1876
- }
1877
- }
1878
- return -1;
1879
- }
1880
- return -1;
1881
- }
1882
- /**
1883
- * Find the first positional argument in argv, properly skipping global flag
1884
- * values. Thin wrapper over {@link findFirstPositionalIndex} — see that
1885
- * function for the scan and stop conditions. Returns `undefined` when none.
1886
- */
1887
- function findFirstPositional(argv, globalExtracted) {
1888
- const index = findFirstPositionalIndex(argv, globalExtracted);
1889
- return index >= 0 ? argv[index] : void 0;
1890
- }
1891
-
1892
- //#endregion
1893
- //#region src/parser/arg-parser.ts
1894
- /**
1895
- * Parse CLI arguments for a command
1896
- *
1897
- * @param argv - Command line arguments
1898
- * @param command - The command to parse for
1899
- * @param options - Parse options
1900
- * @returns Parse result
1901
- */
1902
- function parseArgs(argv, command, options = {}) {
1903
- const subCommandNameSet = require_schema_extractor.listSubCommandNamesWithAliases(command);
1904
- const subCommandNames = [...subCommandNameSet];
1905
- const hasSubCommands = subCommandNames.length > 0;
1906
- if (hasSubCommands && argv.length > 0) if (options.globalExtracted) {
1907
- const scanResult = scanForSubcommand(argv, subCommandNames, options.globalExtracted);
1908
- if (scanResult.subCommandIndex >= 0) {
1909
- const rawGlobalArgs = parseGlobalArgs(scanResult.globalTokensBefore, options.globalExtracted);
1910
- return {
1911
- helpRequested: false,
1912
- helpAllRequested: false,
1913
- versionRequested: false,
1914
- subCommand: argv[scanResult.subCommandIndex],
1915
- remainingArgs: scanResult.tokensAfterSubcommand,
1916
- rawArgs: {},
1917
- positionals: [],
1918
- unknownFlags: scanResult.suppressedTokens,
1919
- rawGlobalArgs
1920
- };
1921
- }
1922
- } else {
1923
- const firstArg = argv[0];
1924
- if (firstArg && !firstArg.startsWith("-") && subCommandNameSet.has(firstArg)) return {
1925
- helpRequested: false,
1926
- helpAllRequested: false,
1927
- versionRequested: false,
1928
- subCommand: firstArg,
1929
- remainingArgs: argv.slice(1),
1930
- rawArgs: {},
1931
- positionals: [],
1932
- unknownFlags: []
1933
- };
1934
- }
1935
- let extracted;
1936
- if (command.args) {
1937
- extracted = require_schema_extractor.extractFields(command.args);
1938
- if (!options.skipValidation) {
1939
- validateDuplicateFields(extracted);
1940
- validateCaseVariantCollisions(extracted);
1941
- validateDuplicateAliases(extracted);
1942
- validateDuplicateNegations(extracted);
1943
- validatePositionalConfig(extracted);
1944
- validateReservedAliases(extracted, hasSubCommands);
1945
- if (options.globalExtracted) validateCrossSchemaCollisions(options.globalExtracted, extracted);
1946
- }
1947
- }
1948
- const ddIdx = argv.indexOf("--");
1949
- const flagScanArgv = ddIdx >= 0 ? argv.slice(0, ddIdx) : argv;
1950
- const hasUserDefinedH = extracted?.fields.some((f) => f.overrideBuiltinAlias === true && require_schema_extractor.getAllAliases(f).includes("H")) ?? false;
1951
- const hasUserDefinedh = extracted?.fields.some((f) => f.overrideBuiltinAlias === true && require_schema_extractor.getAllAliases(f).includes("h")) ?? false;
1952
- const helpAllRequested = flagScanArgv.includes("--help-all") || !hasUserDefinedH && flagScanArgv.includes("-H");
1953
- const helpRequested = !helpAllRequested && (flagScanArgv.includes("--help") || !hasUserDefinedh && flagScanArgv.includes("-h"));
1954
- const versionRequested = flagScanArgv.includes("--version");
1955
- if (helpRequested || helpAllRequested || versionRequested) return {
1956
- helpRequested,
1957
- helpAllRequested,
1958
- versionRequested,
1959
- subCommand: void 0,
1960
- remainingArgs: [],
1961
- rawArgs: {},
1962
- positionals: [],
1963
- unknownFlags: []
1964
- };
1965
- let commandArgv = argv;
1966
- let rawGlobalArgs;
1967
- if (options.globalExtracted) {
1968
- const { separated, globalParsed } = separateGlobalArgs(argv, options.globalExtracted, extracted);
1969
- commandArgv = separated;
1970
- rawGlobalArgs = globalParsed;
1971
- }
1972
- if (!extracted) return {
1973
- helpRequested: false,
1974
- helpAllRequested: false,
1975
- versionRequested: false,
1976
- subCommand: void 0,
1977
- remainingArgs: [],
1978
- rawArgs: {},
1979
- positionals: [],
1980
- unknownFlags: [],
1981
- rawGlobalArgs
1982
- };
1983
- const parserOptions = buildParserOptions(extracted);
1984
- const parsed = parseArgv(commandArgv, parserOptions);
1985
- const rawArgs = mergeWithPositionals(parsed, extracted);
1986
- for (const field of extracted.fields) if (field.env && rawArgs[field.name] === void 0) {
1987
- const envNames = Array.isArray(field.env) ? field.env : [field.env];
1988
- for (const envName of envNames) {
1989
- const envValue = process.env[envName];
1990
- if (envValue !== void 0) {
1991
- rawArgs[field.name] = envValue;
1992
- break;
1993
- }
1994
- }
1995
- }
1996
- const knownFlags = new Set(extracted.fields.map((f) => f.name));
1997
- const knownCliNames = new Set(extracted.fields.map((f) => f.cliName));
1998
- const knownAliases = /* @__PURE__ */ new Set();
1999
- for (const f of extracted.fields) for (const alias of require_schema_extractor.getAllAliases(f)) knownAliases.add(alias);
2000
- if (options.globalExtracted) for (const f of options.globalExtracted.fields) {
2001
- knownFlags.add(f.name);
2002
- knownCliNames.add(f.cliName);
2003
- for (const alias of require_schema_extractor.getAllAliases(f)) knownAliases.add(alias);
2004
- }
2005
- const unknownFlags = [];
2006
- for (const key of Object.keys(parsed.options)) if (!knownFlags.has(key) && !knownCliNames.has(key) && !knownAliases.has(key)) unknownFlags.push(key);
2007
- return {
2008
- helpRequested: false,
2009
- helpAllRequested: false,
2010
- versionRequested: false,
2011
- subCommand: void 0,
2012
- remainingArgs: [],
2013
- rawArgs,
2014
- positionals: parsed.positionals,
2015
- unknownFlags,
2016
- extractedFields: extracted,
2017
- rawGlobalArgs
2018
- };
2019
- }
2020
- /**
2021
- * Parse global args from a list of tokens (e.g., tokens before the subcommand).
2022
- * Env fallbacks are applied later in the runner on the accumulated global args.
2023
- */
2024
- function parseGlobalArgs(tokens, globalExtracted) {
2025
- if (tokens.length === 0) return {};
2026
- return mergeWithPositionals(parseArgv(tokens, buildParserOptions(globalExtracted)), globalExtracted);
2027
- }
2028
- /**
2029
- * Separate global flags from command-local args in argv.
2030
- * Global flags mixed with command args (e.g., `build --verbose --output dist`)
2031
- * are extracted and returned separately.
2032
- * When a flag is defined in both global and local schemas, the local definition
2033
- * takes precedence (the flag stays in the command tokens).
2034
- *
2035
- * Note: Combined short flags (e.g., `-vq`) are not decomposed here; only
2036
- * single-character short options are recognized as global. The underlying
2037
- * `parseArgv` handles combined shorts for command-local parsing.
2038
- */
2039
- function separateGlobalArgs(argv, globalExtracted, localExtracted) {
2040
- const lookup = buildGlobalFlagLookup(globalExtracted);
2041
- const localFieldNames = new Set(localExtracted?.fields.map((f) => f.name) ?? []);
2042
- const localCliNames = new Set(localExtracted?.fields.map((f) => f.cliName) ?? []);
2043
- const localAliasMapKeys = localExtracted ? new Set(buildParserOptions(localExtracted).aliasMap?.keys() ?? []) : /* @__PURE__ */ new Set();
2044
- const globalTokens = [];
2045
- const commandTokens = [];
2046
- for (let i = 0; i < argv.length; i++) {
2047
- const arg = argv[i];
2048
- if (arg === "--") {
2049
- commandTokens.push(...argv.slice(i));
2050
- break;
2051
- }
2052
- if (arg.startsWith("--")) {
2053
- const { resolvedName, withoutDashes, isNegated, isGlobal } = resolveGlobalLongOption(arg, lookup);
2054
- const flagName = resolvedName;
2055
- const isLocalCollision = localFieldNames.has(withoutDashes) || localFieldNames.has(flagName) || localCliNames.has(withoutDashes) || localCliNames.has(flagName) || localAliasMapKeys.has(withoutDashes) || localAliasMapKeys.has(flagName);
2056
- if (isGlobal && !isLocalCollision) {
2057
- i += collectGlobalFlag(argv, i, resolvedName, isNegated, lookup.booleanFlags, globalTokens) - 1;
2058
- continue;
2059
- }
2060
- } else if (arg.startsWith("-") && arg.length > 1) {
2061
- const withoutDash = arg.includes("=") ? arg.slice(1, arg.indexOf("=")) : arg.slice(1);
2062
- if (withoutDash.length === 1) {
2063
- const resolvedName = lookup.aliasMap.get(withoutDash) ?? withoutDash;
2064
- if ((lookup.aliases.has(withoutDash) || lookup.flagNames.has(resolvedName)) && !localAliasMapKeys.has(withoutDash)) {
2065
- i += collectGlobalFlag(argv, i, resolvedName, false, lookup.booleanFlags, globalTokens) - 1;
2066
- continue;
2067
- }
2068
- }
2069
- }
2070
- commandTokens.push(arg);
2071
- }
2072
- return {
2073
- separated: commandTokens,
2074
- globalParsed: parseGlobalArgs(globalTokens, globalExtracted)
2075
- };
2076
- }
2077
-
2078
- //#endregion
2079
- //#region src/validator/error-formatter.ts
2080
- /**
2081
- * Calculate Levenshtein distance between two strings
2082
- */
2083
- function levenshteinDistance(a, b) {
2084
- const matrix = [];
2085
- for (let i = 0; i <= b.length; i++) matrix[i] = [i];
2086
- for (let j = 0; j <= a.length; j++) matrix[0][j] = j;
2087
- for (let i = 1; i <= b.length; i++) for (let j = 1; j <= a.length; j++) if (b.charAt(i - 1) === a.charAt(j - 1)) matrix[i][j] = matrix[i - 1][j - 1];
2088
- else matrix[i][j] = Math.min(matrix[i - 1][j - 1] + 1, matrix[i][j - 1] + 1, matrix[i - 1][j] + 1);
2089
- return matrix[b.length][a.length];
2090
- }
2091
- /**
2092
- * Find similar strings from a list
2093
- */
2094
- function findSimilar(target, candidates) {
2095
- const threshold = Math.max(2, Math.floor(target.length / 2));
2096
- return candidates.map((candidate) => ({
2097
- candidate,
2098
- distance: levenshteinDistance(target.toLowerCase(), candidate.toLowerCase())
2099
- })).filter(({ distance }) => distance <= threshold).sort((a, b) => a.distance - b.distance).map(({ candidate }) => candidate).slice(0, 3);
2100
- }
2101
- /**
2102
- * Format unknown flag warning with suggestions (for strip mode)
2103
- *
2104
- * @param flag - The unknown flag (e.g., "--verbos")
2105
- * @param knownFlags - List of known flag names
2106
- * @returns Formatted warning message with suggestions
2107
- */
2108
- function formatUnknownFlagWarning(flag, knownFlags) {
2109
- const similar = findSimilar(flag.replace(/^-{1,2}/, ""), knownFlags);
2110
- let message = `${styles.warning("Warning: Unknown option:")} ${styles.bold(flag)}`;
2111
- if (similar.length > 0) {
2112
- message += `\n\n${styles.info("Did you mean?")}`;
2113
- for (const suggestion of similar) message += `\n ${symbols.arrow} ${styles.option(`--${suggestion}`)}`;
2114
- }
2115
- return message;
2116
- }
2117
- /**
2118
- * Format runtime error
2119
- *
2120
- * @param error - The error that occurred
2121
- * @param debug - Whether to include stack trace
2122
- * @returns Formatted error message
2123
- */
2124
- function formatRuntimeError(error, debug) {
2125
- if (debug && error.stack) return `${styles.error("Error:")} ${error.message}\n\n${styles.dim(error.stack)}`;
2126
- return `${styles.error("Error:")} ${error.message}`;
2127
- }
2128
-
2129
- //#endregion
2130
- //#region src/validator/zod-validator.ts
2131
- /**
2132
- * Convert ZodError to ValidationError array (zod v4 compatible)
2133
- */
2134
- function formatZodErrors(error) {
2135
- return error.issues.map((issue) => ({
2136
- path: issue.path.map(String),
2137
- message: issue.message,
2138
- code: issue.code,
2139
- received: "received" in issue ? issue.received : void 0,
2140
- expected: "expected" in issue ? String(issue.expected) : void 0
2141
- }));
2142
- }
2143
- /**
2144
- * Validate raw arguments against a schema
2145
- *
2146
- * @param rawArgs - Parsed but unvalidated arguments
2147
- * @param schema - Zod schema (ZodObject, ZodDiscriminatedUnion, etc.)
2148
- * @returns Validation result with typed data or errors
2149
- */
2150
- function validateArgs(rawArgs, schema) {
2151
- const result = schema.safeParse(rawArgs);
2152
- if (result.success) return {
2153
- success: true,
2154
- data: result.data
2155
- };
2156
- return {
2157
- success: false,
2158
- errors: formatZodErrors(result.error)
2159
- };
2160
- }
2161
- /**
2162
- * Format validation errors for display
2163
- */
2164
- function formatValidationErrors(errors) {
2165
- return errors.map((e) => {
2166
- return `${e.path.length > 0 ? `${e.path.join(".")}: ` : ""}${e.message}`;
2167
- }).join("\n");
2168
- }
2169
-
2170
- //#endregion
2171
- //#region src/core/effect-runner.ts
2172
- /**
2173
- * Execute all registered effect callbacks for validated args.
2174
- *
2175
- * Effects run sequentially in field-definition order.
2176
- * Only fires for fields that have an `effect` callback defined.
2177
- *
2178
- * @param validatedArgs - The validated (post-Zod) argument values
2179
- * @param extracted - The extracted fields from the schema
2180
- * @param globalArgs - The validated global args (passed to command arg effects)
2181
- */
2182
- async function runEffects(validatedArgs, extracted, globalArgs) {
2183
- for (const field of extracted.fields) {
2184
- if (!field.effect) continue;
2185
- await field.effect(validatedArgs[field.name], {
2186
- name: field.name,
2187
- args: validatedArgs,
2188
- ...globalArgs != null && { globalArgs }
2189
- });
2190
- }
2191
- }
2192
-
2193
- //#endregion
2194
- //#region src/core/runner.ts
2195
- var runner_exports = /* @__PURE__ */ require_log_collector.__exportAll({
2196
- runCommand: () => runCommand,
2197
- runMain: () => runMain
2198
- });
2199
- /**
2200
- * Default logger using console
2201
- */
2202
- const defaultLogger = {
2203
- log: (message) => console.log(message),
2204
- error: (message) => console.error(message)
2205
- };
2206
- /**
2207
- * Run a command with the given arguments (programmatic/test usage)
2208
- *
2209
- * This function parses arguments, validates them, routes to subcommands,
2210
- * and executes the command. It does NOT call process.exit.
2211
- *
2212
- * @param command - The command to run
2213
- * @param argv - Command line arguments to parse
2214
- * @param options - Run options
2215
- * @returns The result of command execution
2216
- *
2217
- * @example
2218
- * ```ts
2219
- * import { defineCommand, runCommand } from "politty";
2220
- *
2221
- * const command = defineCommand({
2222
- * name: "my-cli",
2223
- * args: z.object({ name: z.string() }),
2224
- * run: ({ name }) => console.log(`Hello, ${name}!`),
2225
- * });
2226
- *
2227
- * // In tests
2228
- * const result = await runCommand(command, ["--name", "World"]);
2229
- * expect(result.exitCode).toBe(0);
2230
- * ```
2231
- */
2232
- async function runCommand(command, argv, options = {}) {
2233
- const globalExtracted = extractAndValidateGlobal(options);
2234
- const shouldCaptureLogs = options.captureLogs ?? false;
2235
- const globalCollector = shouldCaptureLogs ? require_log_collector.createLogCollector() : null;
2236
- if (options.setup) {
2237
- globalCollector?.start();
2238
- try {
2239
- await options.setup({});
2240
- } catch (e) {
2241
- const error = e instanceof Error ? e : new Error(String(e));
2242
- if (options.cleanup) try {
2243
- await options.cleanup({ error });
2244
- } catch {}
2245
- globalCollector?.stop();
2246
- return {
2247
- success: false,
2248
- error,
2249
- exitCode: 1,
2250
- logs: globalCollector?.getLogs() ?? require_log_collector.emptyLogs()
2251
- };
2252
- }
2253
- globalCollector?.stop();
2254
- }
2255
- const result = await runCommandInternal(command, argv, {
2256
- ...options,
2257
- handleSignals: false,
2258
- _globalExtracted: globalExtracted,
2259
- _globalCleanup: options.cleanup,
2260
- _existingLogs: globalCollector?.getLogs()
2261
- });
2262
- if (options.cleanup) {
2263
- const cleanupCollector = shouldCaptureLogs ? require_log_collector.createLogCollector() : null;
2264
- cleanupCollector?.start();
2265
- const cleanupCtx = { error: !result.success ? result.error : void 0 };
2266
- try {
2267
- await options.cleanup(cleanupCtx);
2268
- } catch (e) {
2269
- if (result.success) {
2270
- const error = e instanceof Error ? e : new Error(String(e));
2271
- cleanupCollector?.stop();
2272
- return {
2273
- success: false,
2274
- error,
2275
- exitCode: 1,
2276
- logs: require_log_collector.mergeLogs(result.logs, cleanupCollector?.getLogs() ?? require_log_collector.emptyLogs())
2277
- };
2278
- }
2279
- }
2280
- cleanupCollector?.stop();
2281
- const cleanupLogs = cleanupCollector?.getLogs() ?? require_log_collector.emptyLogs();
2282
- if (cleanupLogs.entries.length > 0) return {
2283
- ...result,
2284
- logs: require_log_collector.mergeLogs(result.logs, cleanupLogs)
2285
- };
2286
- }
2287
- return result;
2288
- }
2289
- /**
2290
- * Hidden internal subcommands (e.g. `__refresh-completion`) are spawned
2291
- * by background hooks and must not run user-provided
2292
- * `setup`/`cleanup`/`prompt` or required `globalArgs`. Those exist for
2293
- * the foreground CLI run; replaying them in a detached child causes
2294
- * duplicate side effects, stuck prompts, and validation failures the
2295
- * user never opted into.
2296
- *
2297
- * We treat any registered subcommand whose name starts with `__` as
2298
- * internal. We use `findFirstPositional` (schema-aware) instead of the
2299
- * naive "first non-flag token" so an option *value* like
2300
- * `--name __refresh-completion` doesn't trip the bypass — that would
2301
- * silently skip lifecycle hooks for ordinary invocations.
2302
- */
2303
- function isInternalSubcommandInvocation(command, argv, globalExtracted) {
2304
- const firstPositional = findFirstPositional(argv, globalExtracted);
2305
- if (!firstPositional || !firstPositional.startsWith("__")) return false;
2306
- return Boolean(command.subCommands?.[firstPositional]);
2307
- }
2308
- /**
2309
- * Run a CLI command as the main entry point
2310
- *
2311
- * This function:
2312
- * - Uses process.argv for arguments
2313
- * - Handles SIGINT/SIGTERM signals
2314
- * - Calls process.exit with the appropriate exit code
2315
- * - Invokes `command.runMainHook` once before parsing if set, so plug-ins
2316
- * like `withCompletionCommand` can fire detached background work
2317
- * - Bypasses user `setup`/`cleanup`/`prompt` and required `globalArgs`
2318
- * for registered hidden subcommands whose name starts with `__`
2319
- * (e.g. `__refresh-completion`)
2320
- *
2321
- * @param command - The command to run
2322
- * @param options - Main options (version, debug)
2323
- *
2324
- * @example
2325
- * ```ts
2326
- * import { defineCommand, runMain } from "politty";
2327
- *
2328
- * const command = defineCommand({
2329
- * name: "my-cli",
2330
- * run: () => console.log("Hello!"),
2331
- * });
2332
- *
2333
- * runMain(command, { version: "1.0.0" });
2334
- * ```
2335
- */
2336
- async function runMain(command, options = {}) {
2337
- if (command.runMainHook) try {
2338
- command.runMainHook(process.argv.slice(2));
2339
- } catch {}
2340
- const argv = process.argv.slice(2);
2341
- let globalExtractedForBypass;
2342
- if (options.globalArgs) try {
2343
- globalExtractedForBypass = require_schema_extractor.extractFields(options.globalArgs);
2344
- } catch {}
2345
- let effectiveOptions = options;
2346
- if (isInternalSubcommandInvocation(command, argv, globalExtractedForBypass)) {
2347
- const { setup: _s, cleanup: _c, prompt: _p, globalArgs: _g, ...rest } = options;
2348
- effectiveOptions = rest;
2349
- }
2350
- const globalExtracted = extractAndValidateGlobal(effectiveOptions);
2351
- if (effectiveOptions.onUnknownSubcommand && !isInternalSubcommandInvocation(command, argv, globalExtractedForBypass)) {
2352
- const knownSubCommands = require_schema_extractor.listSubCommandNamesWithAliases(command);
2353
- if (knownSubCommands.size > 0) {
2354
- const positionalIndex = findFirstPositionalIndex(argv, globalExtracted);
2355
- const name = positionalIndex >= 0 ? argv[positionalIndex] : void 0;
2356
- if (name && !knownSubCommands.has(name)) {
2357
- const forwardArgs = argv.slice(positionalIndex + 1);
2358
- const exitCode = await effectiveOptions.onUnknownSubcommand({
2359
- commandPath: [],
2360
- name,
2361
- args: forwardArgs
2362
- });
2363
- if (typeof exitCode === "number") {
2364
- await flushStandardStreams();
2365
- return process.exit(exitCode);
2366
- }
2367
- }
2368
- }
2369
- }
2370
- if (effectiveOptions.setup) try {
2371
- await effectiveOptions.setup({});
2372
- } catch (e) {
2373
- const error = e instanceof Error ? e : new Error(String(e));
2374
- if (effectiveOptions.cleanup) try {
2375
- await effectiveOptions.cleanup({ error });
2376
- } catch {}
2377
- process.exit(1);
2378
- }
2379
- const result = await runCommandInternal(command, argv, {
2380
- debug: effectiveOptions.debug,
2381
- captureLogs: effectiveOptions.captureLogs,
2382
- skipValidation: effectiveOptions.skipValidation,
2383
- handleSignals: true,
2384
- logger: effectiveOptions.logger,
2385
- globalArgs: effectiveOptions.globalArgs,
2386
- prompt: effectiveOptions.prompt,
2387
- onUnknownSubcommand: effectiveOptions.onUnknownSubcommand,
2388
- _globalExtracted: globalExtracted,
2389
- _globalCleanup: effectiveOptions.cleanup,
2390
- _context: {
2391
- commandPath: [],
2392
- rootName: command.name,
2393
- rootVersion: effectiveOptions.version,
2394
- globalExtracted
2395
- }
2396
- });
2397
- if ((effectiveOptions.displayErrors ?? true) && !result.success && result.error) (effectiveOptions.logger ?? defaultLogger).error(formatRuntimeError(result.error, effectiveOptions.debug ?? false));
2398
- if (effectiveOptions.cleanup) {
2399
- const cleanupCtx = { error: !result.success ? result.error : void 0 };
2400
- try {
2401
- await effectiveOptions.cleanup(cleanupCtx);
2402
- } catch {}
2403
- }
2404
- await flushStandardStreams();
2405
- process.exit(result.exitCode);
2406
- }
2407
- /**
2408
- * Flush stdout/stderr before exit to prevent truncated output when piped
2409
- * (pipe writes are buffered asynchronously, so exiting early loses data).
2410
- *
2411
- * We await a zero-byte write's callback rather than a `drain` event: `drain`
2412
- * only fires after a `write()` returned `false` (backpressure), so buffered
2413
- * writes that never tripped it would hang. The write callback is ordered after
2414
- * all pending writes, so it resolves once the buffer is flushed.
2415
- */
2416
- async function flushStandardStreams() {
2417
- await Promise.all([process.stdout, process.stderr].map((stream) => stream.writableLength > 0 ? new Promise((resolve) => stream.write("", () => resolve())) : Promise.resolve()));
2418
- }
2419
- /**
2420
- * Internal implementation of command running
2421
- */
2422
- async function runCommandInternal(command, argv, options = {}) {
2423
- const logger = options.logger ?? defaultLogger;
2424
- const context = options._context ?? {
2425
- commandPath: [],
2426
- rootName: command.name,
2427
- globalExtracted: options._globalExtracted
2428
- };
2429
- const collector = options.captureLogs ?? false ? require_log_collector.createLogCollector() : null;
2430
- collector?.start();
2431
- const getCurrentLogs = () => {
2432
- return require_log_collector.mergeLogs(options._existingLogs ?? require_log_collector.emptyLogs(), collector?.getLogs() ?? require_log_collector.emptyLogs());
2433
- };
2434
- try {
2435
- const parseResult = parseArgs(argv, command, {
2436
- skipValidation: options.skipValidation,
2437
- globalExtracted: options._globalExtracted
2438
- });
2439
- const accumulatedGlobalArgs = {
2440
- ...options._parsedGlobalArgs,
2441
- ...parseResult.rawGlobalArgs
2442
- };
2443
- const nestedCommandPath = context.commandPath ?? [];
2444
- if (options.onUnknownSubcommand && nestedCommandPath.length > 0) {
2445
- const knownSubCommands = require_schema_extractor.listSubCommandNamesWithAliases(command);
2446
- if (knownSubCommands.size > 0) {
2447
- const positionalIndex = findFirstPositionalIndex(argv, options._globalExtracted);
2448
- const name = positionalIndex >= 0 ? argv[positionalIndex] : void 0;
2449
- if (name && !knownSubCommands.has(name)) {
2450
- const forwardArgs = argv.slice(positionalIndex + 1);
2451
- const exitCode = await options.onUnknownSubcommand({
2452
- commandPath: nestedCommandPath,
2453
- name,
2454
- args: forwardArgs
2455
- });
2456
- if (typeof exitCode === "number") {
2457
- collector?.stop();
2458
- if (options.handleSignals) {
2459
- if (options._globalCleanup) try {
2460
- await options._globalCleanup({ error: void 0 });
2461
- } catch {}
2462
- await flushStandardStreams();
2463
- process.exit(exitCode);
2464
- }
2465
- return exitCode === 0 ? {
2466
- success: true,
2467
- result: void 0,
2468
- exitCode: 0,
2469
- logs: getCurrentLogs()
2470
- } : {
2471
- success: false,
2472
- error: /* @__PURE__ */ new Error(`Plugin "${[...nestedCommandPath, name].join(" ")}" exited with code ${exitCode}`),
2473
- exitCode,
2474
- logs: getCurrentLogs()
2475
- };
2476
- }
2477
- }
2478
- }
2479
- }
2480
- if (parseResult.helpRequested || parseResult.helpAllRequested) {
2481
- let hasUnknownSubcommand = false;
2482
- const subCmdNames = require_schema_extractor.listSubCommands(command);
2483
- const allSubCmdNameSet = require_schema_extractor.listSubCommandNamesWithAliases(command);
2484
- if (subCmdNames.length > 0) {
2485
- const potentialSubCmd = findFirstPositional(argv, context.globalExtracted);
2486
- if (potentialSubCmd && !allSubCmdNameSet.has(potentialSubCmd)) hasUnknownSubcommand = true;
2487
- }
2488
- const help = generateHelp(command, {
2489
- showSubcommands: options.showSubcommands ?? true,
2490
- showSubcommandOptions: parseResult.helpAllRequested || options.showSubcommandOptions,
2491
- context
2492
- });
2493
- logger.log(help);
2494
- collector?.stop();
2495
- if (hasUnknownSubcommand) {
2496
- const unknownCmd = findFirstPositional(argv, context.globalExtracted) ?? "";
2497
- const similar = findSimilar(unknownCmd, [...allSubCmdNameSet]);
2498
- const suggestion = similar.length > 0 ? ` Did you mean: ${similar.join(", ")}?` : "";
2499
- return {
2500
- success: false,
2501
- error: /* @__PURE__ */ new Error(`Unknown subcommand: ${unknownCmd}${suggestion ? `.${suggestion}` : ""}`),
2502
- exitCode: 1,
2503
- logs: getCurrentLogs()
2504
- };
2505
- }
2506
- return {
2507
- success: true,
2508
- result: void 0,
2509
- exitCode: 0,
2510
- logs: getCurrentLogs()
2511
- };
2512
- }
2513
- if (parseResult.versionRequested) {
2514
- const version = context.rootVersion;
2515
- if (version) logger.log(version);
2516
- collector?.stop();
2517
- return {
2518
- success: true,
2519
- result: void 0,
2520
- exitCode: 0,
2521
- logs: getCurrentLogs()
2522
- };
2523
- }
2524
- if (parseResult.subCommand) {
2525
- if (parseResult.unknownFlags.length > 0) {
2526
- const globalMode = context.globalExtracted?.unknownKeysMode ?? "strip";
2527
- if (globalMode === "strict") {
2528
- collector?.stop();
2529
- return {
2530
- success: false,
2531
- error: /* @__PURE__ */ new Error(`Unknown flags: ${parseResult.unknownFlags.join(", ")}`),
2532
- exitCode: 1,
2533
- logs: getCurrentLogs()
2534
- };
2535
- }
2536
- if (globalMode === "strip") {
2537
- const knownGlobalFlags = context.globalExtracted?.fields.map((f) => f.name) ?? [];
2538
- for (const flag of parseResult.unknownFlags) logger.error(formatUnknownFlagWarning(flag, knownGlobalFlags));
2539
- }
2540
- }
2541
- const resolved = await require_schema_extractor.resolveSubcommandWithAlias(command, parseResult.subCommand);
2542
- if (resolved) {
2543
- const subContext = {
2544
- commandPath: [...context.commandPath ?? [], parseResult.subCommand],
2545
- rootName: context.rootName,
2546
- rootVersion: context.rootVersion,
2547
- globalExtracted: context.globalExtracted,
2548
- aliasFor: resolved.aliasFor
2549
- };
2550
- collector?.stop();
2551
- return runCommandInternal(resolved.command, parseResult.remainingArgs, {
2552
- ...options,
2553
- _context: subContext,
2554
- _existingLogs: getCurrentLogs(),
2555
- _parsedGlobalArgs: accumulatedGlobalArgs
2556
- });
2557
- }
2558
- }
2559
- if (require_schema_extractor.listSubCommands(command).length > 0 && !parseResult.subCommand && !command.run) {
2560
- const help = generateHelp(command, {
2561
- showSubcommands: options.showSubcommands ?? true,
2562
- context
2563
- });
2564
- logger.log(help);
2565
- collector?.stop();
2566
- return {
2567
- success: true,
2568
- result: void 0,
2569
- exitCode: 0,
2570
- logs: getCurrentLogs()
2571
- };
2572
- }
2573
- if (parseResult.unknownFlags.length > 0) {
2574
- const unknownKeysMode = parseResult.extractedFields?.unknownKeysMode ?? "strip";
2575
- const knownFlags = parseResult.extractedFields?.fields.map((f) => f.name) ?? [];
2576
- if (unknownKeysMode === "strict") {
2577
- collector?.stop();
2578
- return {
2579
- success: false,
2580
- error: /* @__PURE__ */ new Error(`Unknown flags: ${parseResult.unknownFlags.join(", ")}`),
2581
- exitCode: 1,
2582
- logs: getCurrentLogs()
2583
- };
2584
- } else if (unknownKeysMode === "strip") for (const flag of parseResult.unknownFlags) logger.error(formatUnknownFlagWarning(flag, knownFlags));
2585
- }
2586
- let validatedGlobalArgs = {};
2587
- const isCompletionInvocation = command.name === "__complete";
2588
- if (options.globalArgs && options._globalExtracted && !isCompletionInvocation) {
2589
- for (const field of options._globalExtracted.fields) if (field.env && accumulatedGlobalArgs[field.name] === void 0) {
2590
- const envNames = Array.isArray(field.env) ? field.env : [field.env];
2591
- for (const envName of envNames) {
2592
- const envValue = process.env[envName];
2593
- if (envValue !== void 0) {
2594
- accumulatedGlobalArgs[field.name] = envValue;
2595
- break;
2596
- }
2597
- }
2598
- }
2599
- if (options.prompt) {
2600
- const resolved = await options.prompt(accumulatedGlobalArgs, options._globalExtracted);
2601
- Object.assign(accumulatedGlobalArgs, resolved);
2602
- }
2603
- const globalValidation = validateArgs(accumulatedGlobalArgs, options.globalArgs);
2604
- if (!globalValidation.success) {
2605
- collector?.stop();
2606
- return {
2607
- success: false,
2608
- error: new Error(formatValidationErrors(globalValidation.errors)),
2609
- exitCode: 1,
2610
- logs: getCurrentLogs()
2611
- };
2612
- }
2613
- validatedGlobalArgs = globalValidation.data;
2614
- }
2615
- if (!command.args) {
2616
- const proxiedGlobalArgs = createDualCaseProxy(validatedGlobalArgs);
2617
- if (options._globalExtracted && !isCompletionInvocation) await runEffects(proxiedGlobalArgs, options._globalExtracted, proxiedGlobalArgs);
2618
- collector?.stop();
2619
- return await executeLifecycle(command, proxiedGlobalArgs, {
2620
- handleSignals: options.handleSignals,
2621
- captureLogs: options.captureLogs,
2622
- existingLogs: getCurrentLogs(),
2623
- globalCleanup: options._globalCleanup
2624
- });
2625
- }
2626
- let argsToValidate = parseResult.rawArgs;
2627
- if (options.prompt && parseResult.extractedFields) {
2628
- const resolved = await options.prompt(argsToValidate, parseResult.extractedFields);
2629
- argsToValidate = {
2630
- ...argsToValidate,
2631
- ...resolved
2632
- };
2633
- }
2634
- const validationResult = validateArgs(argsToValidate, command.args);
2635
- if (!validationResult.success) {
2636
- collector?.stop();
2637
- return {
2638
- success: false,
2639
- error: new Error(formatValidationErrors(validationResult.errors)),
2640
- exitCode: 1,
2641
- logs: getCurrentLogs()
2642
- };
2643
- }
2644
- const proxiedCommandArgs = createDualCaseProxy(validationResult.data);
2645
- const proxiedGlobalArgs = createDualCaseProxy(validatedGlobalArgs);
2646
- if (options._globalExtracted && !isCompletionInvocation) await runEffects(proxiedGlobalArgs, options._globalExtracted, proxiedGlobalArgs);
2647
- if (parseResult.extractedFields && !isCompletionInvocation) await runEffects(proxiedCommandArgs, parseResult.extractedFields, proxiedGlobalArgs);
2648
- const mergedArgs = createDualCaseProxy({
2649
- ...proxiedGlobalArgs,
2650
- ...proxiedCommandArgs
2651
- });
2652
- collector?.stop();
2653
- return await executeLifecycle(command, mergedArgs, {
2654
- handleSignals: options.handleSignals,
2655
- captureLogs: options.captureLogs,
2656
- existingLogs: getCurrentLogs(),
2657
- globalCleanup: options._globalCleanup
2658
- });
2659
- } catch (error) {
2660
- const err = error instanceof Error ? error : new Error(String(error));
2661
- collector?.stop();
2662
- return {
2663
- success: false,
2664
- error: err,
2665
- exitCode: 1,
2666
- logs: getCurrentLogs()
2667
- };
2668
- }
2669
- }
2670
- /**
2671
- * Extract global fields from options.globalArgs and validate the schema upfront.
2672
- * Rejects positional fields since global options must be flags.
2673
- * Returns undefined when no globalArgs is provided.
2674
- */
2675
- function extractAndValidateGlobal(options) {
2676
- if (!options.globalArgs) return void 0;
2677
- const extracted = require_schema_extractor.extractFields(options.globalArgs);
2678
- if (!options.skipValidation) {
2679
- validateDuplicateFields(extracted);
2680
- validateCaseVariantCollisions(extracted);
2681
- validateDuplicateAliases(extracted);
2682
- validateDuplicateNegations(extracted);
2683
- validateReservedAliases(extracted, true);
2684
- const positionalNames = extracted.fields.filter((f) => f.positional).map((f) => f.name);
2685
- if (positionalNames.length > 0) throw new Error(`Global options schema must not contain positional arguments. Found: ${positionalNames.join(", ")}`);
2686
- }
2687
- return extracted;
2688
- }
2689
-
2690
- //#endregion
2691
- Object.defineProperty(exports, 'CaseVariantCollisionError', {
2692
- enumerable: true,
2693
- get: function () {
2694
- return CaseVariantCollisionError;
2695
- }
2696
- });
2697
- Object.defineProperty(exports, 'DuplicateAliasError', {
2698
- enumerable: true,
2699
- get: function () {
2700
- return DuplicateAliasError;
2701
- }
2702
- });
2703
- Object.defineProperty(exports, 'DuplicateFieldError', {
2704
- enumerable: true,
2705
- get: function () {
2706
- return DuplicateFieldError;
2707
- }
2708
- });
2709
- Object.defineProperty(exports, 'DuplicateNegationError', {
2710
- enumerable: true,
2711
- get: function () {
2712
- return DuplicateNegationError;
2713
- }
2714
- });
2715
- Object.defineProperty(exports, 'PositionalConfigError', {
2716
- enumerable: true,
2717
- get: function () {
2718
- return PositionalConfigError;
2719
- }
2720
- });
2721
- Object.defineProperty(exports, 'ReservedAliasError', {
2722
- enumerable: true,
2723
- get: function () {
2724
- return ReservedAliasError;
2725
- }
2726
- });
2727
- Object.defineProperty(exports, 'createDualCaseProxy', {
2728
- enumerable: true,
2729
- get: function () {
2730
- return createDualCaseProxy;
2731
- }
2732
- });
2733
- Object.defineProperty(exports, 'formatCommandValidationErrors', {
2734
- enumerable: true,
2735
- get: function () {
2736
- return formatCommandValidationErrors;
2737
- }
2738
- });
2739
- Object.defineProperty(exports, 'formatValidationErrors', {
2740
- enumerable: true,
2741
- get: function () {
2742
- return formatValidationErrors;
2743
- }
2744
- });
2745
- Object.defineProperty(exports, 'generateHelp', {
2746
- enumerable: true,
2747
- get: function () {
2748
- return generateHelp;
2749
- }
2750
- });
2751
- Object.defineProperty(exports, 'isColorEnabled', {
2752
- enumerable: true,
2753
- get: function () {
2754
- return isColorEnabled;
2755
- }
2756
- });
2757
- Object.defineProperty(exports, 'logger', {
2758
- enumerable: true,
2759
- get: function () {
2760
- return logger;
2761
- }
2762
- });
2763
- Object.defineProperty(exports, 'parseArgv', {
2764
- enumerable: true,
2765
- get: function () {
2766
- return parseArgv;
2767
- }
2768
- });
2769
- Object.defineProperty(exports, 'renderInline', {
2770
- enumerable: true,
2771
- get: function () {
2772
- return renderInline;
2773
- }
2774
- });
2775
- Object.defineProperty(exports, 'renderMarkdown', {
2776
- enumerable: true,
2777
- get: function () {
2778
- return renderMarkdown;
2779
- }
2780
- });
2781
- Object.defineProperty(exports, 'runCommand', {
2782
- enumerable: true,
2783
- get: function () {
2784
- return runCommand;
2785
- }
2786
- });
2787
- Object.defineProperty(exports, 'runMain', {
2788
- enumerable: true,
2789
- get: function () {
2790
- return runMain;
2791
- }
2792
- });
2793
- Object.defineProperty(exports, 'runner_exports', {
2794
- enumerable: true,
2795
- get: function () {
2796
- return runner_exports;
2797
- }
2798
- });
2799
- Object.defineProperty(exports, 'setColorEnabled', {
2800
- enumerable: true,
2801
- get: function () {
2802
- return setColorEnabled;
2803
- }
2804
- });
2805
- Object.defineProperty(exports, 'styles', {
2806
- enumerable: true,
2807
- get: function () {
2808
- return styles;
2809
- }
2810
- });
2811
- Object.defineProperty(exports, 'symbols', {
2812
- enumerable: true,
2813
- get: function () {
2814
- return symbols;
2815
- }
2816
- });
2817
- Object.defineProperty(exports, 'validateCaseVariantCollisions', {
2818
- enumerable: true,
2819
- get: function () {
2820
- return validateCaseVariantCollisions;
2821
- }
2822
- });
2823
- Object.defineProperty(exports, 'validateCommand', {
2824
- enumerable: true,
2825
- get: function () {
2826
- return validateCommand;
2827
- }
2828
- });
2829
- Object.defineProperty(exports, 'validateCrossSchemaCollisions', {
2830
- enumerable: true,
2831
- get: function () {
2832
- return validateCrossSchemaCollisions;
2833
- }
2834
- });
2835
- Object.defineProperty(exports, 'validateDuplicateAliases', {
2836
- enumerable: true,
2837
- get: function () {
2838
- return validateDuplicateAliases;
2839
- }
2840
- });
2841
- Object.defineProperty(exports, 'validateDuplicateFields', {
2842
- enumerable: true,
2843
- get: function () {
2844
- return validateDuplicateFields;
2845
- }
2846
- });
2847
- Object.defineProperty(exports, 'validateDuplicateNegations', {
2848
- enumerable: true,
2849
- get: function () {
2850
- return validateDuplicateNegations;
2851
- }
2852
- });
2853
- Object.defineProperty(exports, 'validatePositionalConfig', {
2854
- enumerable: true,
2855
- get: function () {
2856
- return validatePositionalConfig;
2857
- }
2858
- });
2859
- Object.defineProperty(exports, 'validateReservedAliases', {
2860
- enumerable: true,
2861
- get: function () {
2862
- return validateReservedAliases;
2863
- }
2864
- });
2865
- //# sourceMappingURL=runner-DvFvokV6.cjs.map