@optique/core 1.1.0-dev.2096 → 1.1.0-dev.2146

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 (69) hide show
  1. package/dist/annotation-state.cjs +26 -26
  2. package/dist/annotation-state.d.cts +133 -1
  3. package/dist/annotation-state.d.ts +133 -1
  4. package/dist/annotations.cjs +2 -2
  5. package/dist/constructs.cjs +141 -73
  6. package/dist/constructs.js +70 -2
  7. package/dist/dependency-metadata.cjs +12 -12
  8. package/dist/dependency-metadata.d.cts +34 -3
  9. package/dist/dependency-metadata.d.ts +34 -3
  10. package/dist/dependency-runtime.cjs +37 -13
  11. package/dist/dependency-runtime.d.cts +197 -2
  12. package/dist/dependency-runtime.d.ts +197 -2
  13. package/dist/dependency-runtime.js +22 -1
  14. package/dist/dependency.cjs +7 -7
  15. package/dist/displaywidth.d.cts +12 -0
  16. package/dist/displaywidth.d.ts +12 -0
  17. package/dist/execution-context.d.cts +23 -0
  18. package/dist/execution-context.d.ts +23 -0
  19. package/dist/extension.cjs +14 -14
  20. package/dist/facade.cjs +46 -36
  21. package/dist/facade.js +31 -21
  22. package/dist/index.cjs +22 -21
  23. package/dist/index.d.cts +2 -2
  24. package/dist/index.d.ts +2 -2
  25. package/dist/index.js +3 -3
  26. package/dist/input-trace.d.cts +2 -1
  27. package/dist/input-trace.d.ts +2 -1
  28. package/dist/internal/annotations.cjs +3 -0
  29. package/dist/internal/annotations.d.cts +47 -5
  30. package/dist/internal/annotations.d.ts +47 -5
  31. package/dist/internal/annotations.js +1 -1
  32. package/dist/internal/command-alias.cjs +16 -0
  33. package/dist/internal/command-alias.js +14 -0
  34. package/dist/internal/dependency.cjs +131 -0
  35. package/dist/internal/dependency.d.cts +311 -2
  36. package/dist/internal/dependency.d.ts +311 -2
  37. package/dist/internal/dependency.js +119 -1
  38. package/dist/internal/parser.cjs +35 -13
  39. package/dist/internal/parser.d.cts +44 -3
  40. package/dist/internal/parser.d.ts +44 -3
  41. package/dist/internal/parser.js +28 -6
  42. package/dist/modifiers.cjs +41 -41
  43. package/dist/parser.cjs +11 -11
  44. package/dist/phase2-seed.cjs +2 -2
  45. package/dist/phase2-seed.d.cts +50 -0
  46. package/dist/phase2-seed.d.ts +50 -0
  47. package/dist/primitives.cjs +74 -33
  48. package/dist/primitives.d.cts +10 -0
  49. package/dist/primitives.d.ts +10 -0
  50. package/dist/primitives.js +54 -13
  51. package/dist/suggestion.cjs +72 -2
  52. package/dist/suggestion.d.cts +188 -0
  53. package/dist/suggestion.d.ts +188 -0
  54. package/dist/suggestion.js +71 -3
  55. package/dist/usage-internals.cjs +5 -1
  56. package/dist/usage-internals.js +5 -1
  57. package/dist/usage.cjs +9 -1
  58. package/dist/usage.d.cts +14 -0
  59. package/dist/usage.d.ts +14 -0
  60. package/dist/usage.js +9 -1
  61. package/dist/validate.cjs +1 -0
  62. package/dist/validate.d.cts +99 -0
  63. package/dist/validate.d.ts +99 -0
  64. package/dist/validate.js +1 -1
  65. package/dist/valueparser.cjs +333 -79
  66. package/dist/valueparser.d.cts +197 -1
  67. package/dist/valueparser.d.ts +197 -1
  68. package/dist/valueparser.js +334 -81
  69. package/package.json +19 -4
@@ -1,11 +1,12 @@
1
- const require_annotations = require('./internal/annotations.cjs');
1
+ const require_internal_annotations = require('./internal/annotations.cjs');
2
2
  const require_message = require('./message.cjs');
3
3
  const require_validate = require('./validate.cjs');
4
4
  const require_usage = require('./usage.cjs');
5
5
  const require_mode_dispatch = require('./internal/mode-dispatch.cjs');
6
- const require_dependency = require('./internal/dependency.cjs');
6
+ const require_internal_dependency = require('./internal/dependency.cjs');
7
7
  const require_dependency_runtime = require('./dependency-runtime.cjs');
8
8
  const require_annotation_state = require('./annotation-state.cjs');
9
+ const require_command_alias = require('./internal/command-alias.cjs');
9
10
  const require_execution_context = require('./execution-context.cjs');
10
11
  const require_phase2_seed = require('./phase2-seed.cjs');
11
12
  const require_suggestion = require('./suggestion.cjs');
@@ -46,8 +47,8 @@ function buildTraceEntry(kind, rawInput, consumed, valueParser, parseResult, opt
46
47
  ...optionNames$1 != null ? { optionNames: optionNames$1 } : {},
47
48
  metavar: valueParser.metavar
48
49
  };
49
- if (require_dependency.isDerivedValueParser(valueParser)) {
50
- const defaults = require_dependency.getSnapshottedDefaultDependencyValues(parseResult);
50
+ if (require_internal_dependency.isDerivedValueParser(valueParser)) {
51
+ const defaults = require_internal_dependency.getSnapshottedDefaultDependencyValues(parseResult);
51
52
  if (defaults != null) return {
52
53
  ...entry,
53
54
  defaultDependencyValues: defaults
@@ -211,13 +212,13 @@ function fail() {
211
212
  */
212
213
  function* getSuggestionsWithDependency(valueParser, prefix, dependencyRegistry, exec) {
213
214
  if (!valueParser.suggest) return;
214
- if (require_dependency.isDerivedValueParser(valueParser) && require_dependency.suggestWithDependency in valueParser) {
215
+ if (require_internal_dependency.isDerivedValueParser(valueParser) && require_internal_dependency.suggestWithDependency in valueParser) {
215
216
  const derived = valueParser;
216
- const suggestWithDep = derived[require_dependency.suggestWithDependency];
217
+ const suggestWithDep = derived[require_internal_dependency.suggestWithDependency];
217
218
  if (suggestWithDep && dependencyRegistry) {
218
219
  const dependencyRuntime = exec?.dependencyRuntime;
219
- const depIds = require_dependency.getDependencyIds(derived);
220
- const defaultsFn = require_dependency.getDefaultValuesFunction(derived);
220
+ const depIds = require_internal_dependency.getDependencyIds(derived);
221
+ const defaultsFn = require_internal_dependency.getDefaultValuesFunction(derived);
221
222
  const defaults = defaultsFn?.();
222
223
  const dependencyValues = [];
223
224
  let hasAnyValue = false;
@@ -295,13 +296,13 @@ function* suggestOptionSync(optionNames$1, valueParser, hidden, context, prefix)
295
296
  */
296
297
  async function* getSuggestionsWithDependencyAsync(valueParser, prefix, dependencyRegistry, exec) {
297
298
  if (!valueParser.suggest) return;
298
- if (require_dependency.isDerivedValueParser(valueParser) && require_dependency.suggestWithDependency in valueParser) {
299
+ if (require_internal_dependency.isDerivedValueParser(valueParser) && require_internal_dependency.suggestWithDependency in valueParser) {
299
300
  const derived = valueParser;
300
- const suggestWithDep = derived[require_dependency.suggestWithDependency];
301
+ const suggestWithDep = derived[require_internal_dependency.suggestWithDependency];
301
302
  if (suggestWithDep && dependencyRegistry) {
302
303
  const dependencyRuntime = exec?.dependencyRuntime;
303
- const depIds = require_dependency.getDependencyIds(derived);
304
- const defaultsFn = require_dependency.getDefaultValuesFunction(derived);
304
+ const depIds = require_internal_dependency.getDependencyIds(derived);
305
+ const defaultsFn = require_internal_dependency.getDefaultValuesFunction(derived);
305
306
  const defaults = defaultsFn?.();
306
307
  const dependencyValues = [];
307
308
  let hasAnyValue = false;
@@ -690,7 +691,7 @@ function option(...args) {
690
691
  configurable: true,
691
692
  enumerable: false
692
693
  });
693
- else if (!require_dependency.isDerivedValueParser(valueParser)) {
694
+ else if (!require_internal_dependency.isDerivedValueParser(valueParser)) {
694
695
  const vp = valueParser;
695
696
  const wrapParseResult = (parsed) => parsed.success ? parsed : {
696
697
  success: false,
@@ -698,6 +699,7 @@ function option(...args) {
698
699
  };
699
700
  Object.defineProperty(result, "validateValue", {
700
701
  value(v) {
702
+ if (typeof vp.validate === "function") return require_mode_dispatch.wrapForMode(mode, wrapParseResult(vp.validate(v)));
701
703
  let stringified;
702
704
  try {
703
705
  stringified = vp.format(v);
@@ -1324,7 +1326,7 @@ function argument(valueParser, options = {}) {
1324
1326
  enumerable: false
1325
1327
  });
1326
1328
  }
1327
- if (!require_dependency.isDerivedValueParser(valueParser)) {
1329
+ if (!require_internal_dependency.isDerivedValueParser(valueParser)) {
1328
1330
  const vp = valueParser;
1329
1331
  const vpMode = valueParser.mode;
1330
1332
  const wrapParseResult = (parsed) => parsed.success ? parsed : {
@@ -1333,6 +1335,7 @@ function argument(valueParser, options = {}) {
1333
1335
  };
1334
1336
  Object.defineProperty(result, "validateValue", {
1335
1337
  value(v) {
1338
+ if (typeof vp.validate === "function") return require_mode_dispatch.wrapForMode(vpMode, wrapParseResult(vp.validate(v)));
1336
1339
  let stringified;
1337
1340
  try {
1338
1341
  stringified = vp.format(v);
@@ -1380,7 +1383,7 @@ function getCommandChildState(commandState, childState, parser) {
1380
1383
  return require_annotation_state.getWrappedChildState(commandState, childState, parser);
1381
1384
  }
1382
1385
  function createCommandState(sourceState, state) {
1383
- return require_annotations.annotateFreshArray(sourceState, state);
1386
+ return require_internal_annotations.annotateFreshArray(sourceState, state);
1384
1387
  }
1385
1388
  function appendCommandPath(exec, name) {
1386
1389
  if (exec == null) return void 0;
@@ -1389,25 +1392,56 @@ function appendCommandPath(exec, name) {
1389
1392
  commandPath: [...exec.commandPath ?? [], name]
1390
1393
  };
1391
1394
  }
1392
- function* suggestCommandSync(context, prefix, name, parser, options) {
1395
+ function getCommandNames(name, options) {
1396
+ return [
1397
+ name,
1398
+ ...getVisibleCommandAliases(options),
1399
+ ...getHiddenCommandAliases(options)
1400
+ ];
1401
+ }
1402
+ function getVisibleCommandAliases(options) {
1403
+ const aliases = options.aliases;
1404
+ if (aliases == null) return [];
1405
+ if (!isNonEmptyStringArray(aliases)) throw new TypeError("Command aliases must be a non-empty array of strings.");
1406
+ return aliases;
1407
+ }
1408
+ function getHiddenCommandAliases(options) {
1409
+ const hiddenAliases = options[require_command_alias.hiddenCommandAliasesKey];
1410
+ if (hiddenAliases == null) return [];
1411
+ if (!isNonEmptyStringArray(hiddenAliases)) throw new TypeError("Hidden command aliases must be a non-empty array of strings.");
1412
+ return hiddenAliases;
1413
+ }
1414
+ function isNonEmptyStringArray(value) {
1415
+ if (!Array.isArray(value) || value.length === 0) return false;
1416
+ for (let i = 0; i < value.length; i++) if (typeof value[i] !== "string") return false;
1417
+ return true;
1418
+ }
1419
+ function validateUniqueCommandNames(names) {
1420
+ const seen = /* @__PURE__ */ new Set();
1421
+ for (const name of names) {
1422
+ if (seen.has(name)) throw new TypeError(`Command has a duplicate name: "${name}".`);
1423
+ seen.add(name);
1424
+ }
1425
+ }
1426
+ function* suggestCommandSync(context, prefix, name, aliases, parser, options) {
1393
1427
  if (require_usage.isSuggestionHidden(options.hidden)) return;
1394
1428
  const state = normalizeCommandState(context.state);
1395
1429
  if (state === void 0) {
1396
- if (name.startsWith(prefix)) yield {
1430
+ for (const commandName of [name, ...aliases]) if (commandName.startsWith(prefix)) yield {
1397
1431
  kind: "literal",
1398
- text: name,
1432
+ text: commandName,
1399
1433
  ...options.description && { description: options.description }
1400
1434
  };
1401
1435
  } else if (state[0] === "matched") yield* parser.suggest(require_execution_context.withChildContext(context, name, getCommandChildState(context.state, parser.initialState, parser), parser.usage), prefix);
1402
1436
  else if (state[0] === "parsing") yield* parser.suggest(require_execution_context.withChildContext(context, name, getCommandChildState(context.state, state[1], parser), parser.usage), prefix);
1403
1437
  }
1404
- async function* suggestCommandAsync(context, prefix, name, parser, options) {
1438
+ async function* suggestCommandAsync(context, prefix, name, aliases, parser, options) {
1405
1439
  if (require_usage.isSuggestionHidden(options.hidden)) return;
1406
1440
  const state = normalizeCommandState(context.state);
1407
1441
  if (state === void 0) {
1408
- if (name.startsWith(prefix)) yield {
1442
+ for (const commandName of [name, ...aliases]) if (commandName.startsWith(prefix)) yield {
1409
1443
  kind: "literal",
1410
- text: name,
1444
+ text: commandName,
1411
1445
  ...options.description && { description: options.description }
1412
1446
  };
1413
1447
  } else if (state[0] === "matched") {
@@ -1435,7 +1469,11 @@ async function* suggestCommandAsync(context, prefix, name, parser, options) {
1435
1469
  * embedded whitespace, or contains control characters.
1436
1470
  */
1437
1471
  function command(name, parser, options = {}) {
1438
- require_validate.validateCommandNames([name], "Command");
1472
+ const commandNames = getCommandNames(name, options);
1473
+ const aliases = getVisibleCommandAliases(options);
1474
+ const hiddenAliases = getHiddenCommandAliases(options);
1475
+ require_validate.validateCommandNames(commandNames, "Command");
1476
+ validateUniqueCommandNames(commandNames);
1439
1477
  const isAsync = parser.mode === "async";
1440
1478
  const syncInnerParser = parser;
1441
1479
  const asyncInnerParser = parser;
@@ -1448,10 +1486,12 @@ function command(name, parser, options = {}) {
1448
1486
  usage: [{
1449
1487
  type: "command",
1450
1488
  name,
1489
+ ...aliases.length > 0 && { aliases },
1490
+ ...hiddenAliases.length > 0 && { hiddenAliases },
1451
1491
  ...options.usageLine != null && { usageLine: options.usageLine },
1452
1492
  ...options.hidden != null && { hidden: options.hidden }
1453
1493
  }, ...parser.usage],
1454
- leadingNames: new Set([name]),
1494
+ leadingNames: new Set(commandNames),
1455
1495
  acceptingAnyToken: false,
1456
1496
  initialState: void 0,
1457
1497
  canSkip(state, exec) {
@@ -1474,10 +1514,11 @@ function command(name, parser, options = {}) {
1474
1514
  parse(context) {
1475
1515
  const state = normalizeCommandState(context.state);
1476
1516
  if (state === void 0) {
1477
- if (context.buffer.length < 1 || context.buffer[0] !== name) {
1517
+ if (context.buffer.length < 1 || !commandNames.includes(context.buffer[0])) {
1478
1518
  const actual = context.buffer.length > 0 ? context.buffer[0] : null;
1479
1519
  const leadingCmds = require_usage_internals.extractLeadingCommandNames(context.usage);
1480
- const suggestions = actual ? require_suggestion.findSimilar(actual, leadingCmds, require_suggestion.DEFAULT_FIND_SIMILAR_OPTIONS) : [];
1520
+ const rawSuggestions = actual ? require_suggestion.findSimilar(actual, leadingCmds, require_suggestion.DEFAULT_FIND_SIMILAR_OPTIONS) : [];
1521
+ const suggestions = require_suggestion.expandCommandAliasSuggestions(context.usage, rawSuggestions);
1481
1522
  if (options.errors?.notMatched) {
1482
1523
  const errorMessage = options.errors.notMatched;
1483
1524
  return {
@@ -1608,8 +1649,8 @@ function command(name, parser, options = {}) {
1608
1649
  return require_mode_dispatch.wrapForMode(parser.mode, null);
1609
1650
  },
1610
1651
  suggest(context, prefix) {
1611
- if (isAsync) return suggestCommandAsync(context, prefix, name, parser, options);
1612
- return suggestCommandSync(context, prefix, name, parser, options);
1652
+ if (isAsync) return suggestCommandAsync(context, prefix, name, aliases, parser, options);
1653
+ return suggestCommandSync(context, prefix, name, aliases, parser, options);
1613
1654
  },
1614
1655
  getDocFragments(state, defaultValue) {
1615
1656
  const commandState = state.kind === "available" ? normalizeCommandState(state.state) : void 0;
@@ -1740,7 +1781,7 @@ function passThrough(options = {}) {
1740
1781
  next: {
1741
1782
  ...context,
1742
1783
  buffer: [],
1743
- state: require_annotations.annotateFreshArray(context.state, [...context.state, ...captured])
1784
+ state: require_internal_annotations.annotateFreshArray(context.state, [...context.state, ...captured])
1744
1785
  },
1745
1786
  consumed: captured
1746
1787
  };
@@ -1761,7 +1802,7 @@ function passThrough(options = {}) {
1761
1802
  next: {
1762
1803
  ...context,
1763
1804
  buffer: context.buffer.slice(1),
1764
- state: require_annotations.annotateFreshArray(context.state, [...context.state, token])
1805
+ state: require_internal_annotations.annotateFreshArray(context.state, [...context.state, token])
1765
1806
  },
1766
1807
  consumed: [token]
1767
1808
  };
@@ -1777,7 +1818,7 @@ function passThrough(options = {}) {
1777
1818
  next: {
1778
1819
  ...context,
1779
1820
  buffer: context.buffer.slice(1),
1780
- state: require_annotations.annotateFreshArray(context.state, [...context.state, token])
1821
+ state: require_internal_annotations.annotateFreshArray(context.state, [...context.state, token])
1781
1822
  },
1782
1823
  consumed: [token]
1783
1824
  };
@@ -1787,7 +1828,7 @@ function passThrough(options = {}) {
1787
1828
  next: {
1788
1829
  ...context,
1789
1830
  buffer: context.buffer.slice(2),
1790
- state: require_annotations.annotateFreshArray(context.state, [
1831
+ state: require_internal_annotations.annotateFreshArray(context.state, [
1791
1832
  ...context.state,
1792
1833
  token,
1793
1834
  nextToken
@@ -1800,7 +1841,7 @@ function passThrough(options = {}) {
1800
1841
  next: {
1801
1842
  ...context,
1802
1843
  buffer: context.buffer.slice(1),
1803
- state: require_annotations.annotateFreshArray(context.state, [...context.state, token])
1844
+ state: require_internal_annotations.annotateFreshArray(context.state, [...context.state, token])
1804
1845
  },
1805
1846
  consumed: [token]
1806
1847
  };
@@ -1812,7 +1853,7 @@ function passThrough(options = {}) {
1812
1853
  };
1813
1854
  },
1814
1855
  complete(state, _exec) {
1815
- if (require_annotations.getAnnotations(state) == null) return {
1856
+ if (require_internal_annotations.getAnnotations(state) == null) return {
1816
1857
  success: true,
1817
1858
  value: state
1818
1859
  };
@@ -448,6 +448,16 @@ interface CommandOptions {
448
448
  * @since 0.9.0
449
449
  */
450
450
  readonly hidden?: HiddenVisibility;
451
+ /**
452
+ * Additional names that invoke this command.
453
+ *
454
+ * Aliases are functional at runtime and are suggested by shell completion,
455
+ * but are hidden from usage and documentation output. The `name` parameter
456
+ * passed to {@link command} remains the canonical display name.
457
+ *
458
+ * @since 1.1.0
459
+ */
460
+ readonly aliases?: readonly [string, ...string[]];
451
461
  /**
452
462
  * Error messages customization.
453
463
  * @since 0.5.0
@@ -448,6 +448,16 @@ interface CommandOptions {
448
448
  * @since 0.9.0
449
449
  */
450
450
  readonly hidden?: HiddenVisibility;
451
+ /**
452
+ * Additional names that invoke this command.
453
+ *
454
+ * Aliases are functional at runtime and are suggested by shell completion,
455
+ * but are hidden from usage and documentation output. The `name` parameter
456
+ * passed to {@link command} remains the canonical display name.
457
+ *
458
+ * @since 1.1.0
459
+ */
460
+ readonly aliases?: readonly [string, ...string[]];
451
461
  /**
452
462
  * Error messages customization.
453
463
  * @since 0.5.0
@@ -6,9 +6,10 @@ import { dispatchByMode, dispatchIterableByMode, wrapForMode } from "./internal/
6
6
  import { getDefaultValuesFunction, getDependencyIds, getSnapshottedDefaultDependencyValues, isDerivedValueParser, suggestWithDependency } from "./internal/dependency.js";
7
7
  import { replayDerivedParser, replayDerivedParserAsync } from "./dependency-runtime.js";
8
8
  import { getWrappedChildParseState, getWrappedChildState, isAnnotationWrappedInitialState, normalizeInjectedAnnotationState } from "./annotation-state.js";
9
+ import { hiddenCommandAliasesKey } from "./internal/command-alias.js";
9
10
  import { mergeChildExec, withChildContext, withChildExecPath } from "./execution-context.js";
10
11
  import { completeOrExtractPhase2Seed, extractPhase2SeedKey } from "./phase2-seed.js";
11
- import { DEFAULT_FIND_SIMILAR_OPTIONS, createErrorWithSuggestions, createSuggestionMessage, findSimilar } from "./suggestion.js";
12
+ import { DEFAULT_FIND_SIMILAR_OPTIONS, createErrorWithSuggestions, createSuggestionMessage, expandCommandAliasSuggestions, findSimilar } from "./suggestion.js";
12
13
  import { extractLeadingCommandNames } from "./usage-internals.js";
13
14
  import { extractDependencyMetadata } from "./dependency-metadata.js";
14
15
  import { isValueParser } from "./valueparser.js";
@@ -698,6 +699,7 @@ function option(...args) {
698
699
  };
699
700
  Object.defineProperty(result, "validateValue", {
700
701
  value(v) {
702
+ if (typeof vp.validate === "function") return wrapForMode(mode, wrapParseResult(vp.validate(v)));
701
703
  let stringified;
702
704
  try {
703
705
  stringified = vp.format(v);
@@ -1333,6 +1335,7 @@ function argument(valueParser, options = {}) {
1333
1335
  };
1334
1336
  Object.defineProperty(result, "validateValue", {
1335
1337
  value(v) {
1338
+ if (typeof vp.validate === "function") return wrapForMode(vpMode, wrapParseResult(vp.validate(v)));
1336
1339
  let stringified;
1337
1340
  try {
1338
1341
  stringified = vp.format(v);
@@ -1389,25 +1392,56 @@ function appendCommandPath(exec, name) {
1389
1392
  commandPath: [...exec.commandPath ?? [], name]
1390
1393
  };
1391
1394
  }
1392
- function* suggestCommandSync(context, prefix, name, parser, options) {
1395
+ function getCommandNames(name, options) {
1396
+ return [
1397
+ name,
1398
+ ...getVisibleCommandAliases(options),
1399
+ ...getHiddenCommandAliases(options)
1400
+ ];
1401
+ }
1402
+ function getVisibleCommandAliases(options) {
1403
+ const aliases = options.aliases;
1404
+ if (aliases == null) return [];
1405
+ if (!isNonEmptyStringArray(aliases)) throw new TypeError("Command aliases must be a non-empty array of strings.");
1406
+ return aliases;
1407
+ }
1408
+ function getHiddenCommandAliases(options) {
1409
+ const hiddenAliases = options[hiddenCommandAliasesKey];
1410
+ if (hiddenAliases == null) return [];
1411
+ if (!isNonEmptyStringArray(hiddenAliases)) throw new TypeError("Hidden command aliases must be a non-empty array of strings.");
1412
+ return hiddenAliases;
1413
+ }
1414
+ function isNonEmptyStringArray(value) {
1415
+ if (!Array.isArray(value) || value.length === 0) return false;
1416
+ for (let i = 0; i < value.length; i++) if (typeof value[i] !== "string") return false;
1417
+ return true;
1418
+ }
1419
+ function validateUniqueCommandNames(names) {
1420
+ const seen = /* @__PURE__ */ new Set();
1421
+ for (const name of names) {
1422
+ if (seen.has(name)) throw new TypeError(`Command has a duplicate name: "${name}".`);
1423
+ seen.add(name);
1424
+ }
1425
+ }
1426
+ function* suggestCommandSync(context, prefix, name, aliases, parser, options) {
1393
1427
  if (isSuggestionHidden(options.hidden)) return;
1394
1428
  const state = normalizeCommandState(context.state);
1395
1429
  if (state === void 0) {
1396
- if (name.startsWith(prefix)) yield {
1430
+ for (const commandName of [name, ...aliases]) if (commandName.startsWith(prefix)) yield {
1397
1431
  kind: "literal",
1398
- text: name,
1432
+ text: commandName,
1399
1433
  ...options.description && { description: options.description }
1400
1434
  };
1401
1435
  } else if (state[0] === "matched") yield* parser.suggest(withChildContext(context, name, getCommandChildState(context.state, parser.initialState, parser), parser.usage), prefix);
1402
1436
  else if (state[0] === "parsing") yield* parser.suggest(withChildContext(context, name, getCommandChildState(context.state, state[1], parser), parser.usage), prefix);
1403
1437
  }
1404
- async function* suggestCommandAsync(context, prefix, name, parser, options) {
1438
+ async function* suggestCommandAsync(context, prefix, name, aliases, parser, options) {
1405
1439
  if (isSuggestionHidden(options.hidden)) return;
1406
1440
  const state = normalizeCommandState(context.state);
1407
1441
  if (state === void 0) {
1408
- if (name.startsWith(prefix)) yield {
1442
+ for (const commandName of [name, ...aliases]) if (commandName.startsWith(prefix)) yield {
1409
1443
  kind: "literal",
1410
- text: name,
1444
+ text: commandName,
1411
1445
  ...options.description && { description: options.description }
1412
1446
  };
1413
1447
  } else if (state[0] === "matched") {
@@ -1435,7 +1469,11 @@ async function* suggestCommandAsync(context, prefix, name, parser, options) {
1435
1469
  * embedded whitespace, or contains control characters.
1436
1470
  */
1437
1471
  function command(name, parser, options = {}) {
1438
- validateCommandNames([name], "Command");
1472
+ const commandNames = getCommandNames(name, options);
1473
+ const aliases = getVisibleCommandAliases(options);
1474
+ const hiddenAliases = getHiddenCommandAliases(options);
1475
+ validateCommandNames(commandNames, "Command");
1476
+ validateUniqueCommandNames(commandNames);
1439
1477
  const isAsync = parser.mode === "async";
1440
1478
  const syncInnerParser = parser;
1441
1479
  const asyncInnerParser = parser;
@@ -1448,10 +1486,12 @@ function command(name, parser, options = {}) {
1448
1486
  usage: [{
1449
1487
  type: "command",
1450
1488
  name,
1489
+ ...aliases.length > 0 && { aliases },
1490
+ ...hiddenAliases.length > 0 && { hiddenAliases },
1451
1491
  ...options.usageLine != null && { usageLine: options.usageLine },
1452
1492
  ...options.hidden != null && { hidden: options.hidden }
1453
1493
  }, ...parser.usage],
1454
- leadingNames: new Set([name]),
1494
+ leadingNames: new Set(commandNames),
1455
1495
  acceptingAnyToken: false,
1456
1496
  initialState: void 0,
1457
1497
  canSkip(state, exec) {
@@ -1474,10 +1514,11 @@ function command(name, parser, options = {}) {
1474
1514
  parse(context) {
1475
1515
  const state = normalizeCommandState(context.state);
1476
1516
  if (state === void 0) {
1477
- if (context.buffer.length < 1 || context.buffer[0] !== name) {
1517
+ if (context.buffer.length < 1 || !commandNames.includes(context.buffer[0])) {
1478
1518
  const actual = context.buffer.length > 0 ? context.buffer[0] : null;
1479
1519
  const leadingCmds = extractLeadingCommandNames(context.usage);
1480
- const suggestions = actual ? findSimilar(actual, leadingCmds, DEFAULT_FIND_SIMILAR_OPTIONS) : [];
1520
+ const rawSuggestions = actual ? findSimilar(actual, leadingCmds, DEFAULT_FIND_SIMILAR_OPTIONS) : [];
1521
+ const suggestions = expandCommandAliasSuggestions(context.usage, rawSuggestions);
1481
1522
  if (options.errors?.notMatched) {
1482
1523
  const errorMessage = options.errors.notMatched;
1483
1524
  return {
@@ -1608,8 +1649,8 @@ function command(name, parser, options = {}) {
1608
1649
  return wrapForMode(parser.mode, null);
1609
1650
  },
1610
1651
  suggest(context, prefix) {
1611
- if (isAsync) return suggestCommandAsync(context, prefix, name, parser, options);
1612
- return suggestCommandSync(context, prefix, name, parser, options);
1652
+ if (isAsync) return suggestCommandAsync(context, prefix, name, aliases, parser, options);
1653
+ return suggestCommandSync(context, prefix, name, aliases, parser, options);
1613
1654
  },
1614
1655
  getDocFragments(state, defaultValue) {
1615
1656
  const commandState = state.kind === "available" ? normalizeCommandState(state.state) : void 0;
@@ -141,6 +141,73 @@ function createSuggestionMessage(suggestions) {
141
141
  return messageParts;
142
142
  }
143
143
  /**
144
+ * Expands command alias suggestions so an alias typo can point at both the
145
+ * canonical command and the alias that matched.
146
+ *
147
+ * @param usage Usage terms that define command aliases.
148
+ * @param suggestions Candidate suggestions returned by {@link findSimilar}.
149
+ * @returns Suggestions with alias hits expanded to canonical name + alias.
150
+ * @internal
151
+ */
152
+ function expandCommandAliasSuggestions(usage, suggestions) {
153
+ if (suggestions.length === 0) return suggestions;
154
+ const commandAliasTargets = collectCommandAliasTargets(usage);
155
+ const expanded = [];
156
+ const seen = /* @__PURE__ */ new Set();
157
+ for (const suggestion of suggestions) {
158
+ const targets = commandAliasTargets.get(suggestion) ?? [suggestion];
159
+ for (const target of targets) {
160
+ if (seen.has(target)) continue;
161
+ seen.add(target);
162
+ expanded.push(target);
163
+ }
164
+ }
165
+ return expanded;
166
+ }
167
+ function collectCommandAliasTargets(usage) {
168
+ const targets = /* @__PURE__ */ new Map();
169
+ function traverse(terms) {
170
+ if (!terms || !Array.isArray(terms)) return true;
171
+ for (const term of terms) {
172
+ if (term.type === "option") continue;
173
+ if (term.type === "argument") return false;
174
+ if (term.type === "command") {
175
+ if (require_usage.isSuggestionHidden(term.hidden)) return false;
176
+ if (!targets.has(term.name)) targets.set(term.name, [term.name]);
177
+ for (const alias of term.aliases ?? []) if (!targets.has(alias)) targets.set(alias, [term.name, alias]);
178
+ for (const alias of term.hiddenAliases ?? []) if (!targets.has(alias)) targets.set(alias, [term.name]);
179
+ return false;
180
+ }
181
+ if (term.type === "optional") {
182
+ traverse(term.terms);
183
+ continue;
184
+ }
185
+ if (term.type === "multiple") {
186
+ const termsSkippable = traverse(term.terms);
187
+ if (term.min === 0 || termsSkippable) continue;
188
+ return false;
189
+ }
190
+ if (term.type === "sequence") {
191
+ if (traverse(term.terms)) continue;
192
+ return false;
193
+ }
194
+ if (term.type === "exclusive") {
195
+ let anySkippable = false;
196
+ for (const branch of term.terms) {
197
+ const branchSkippable = traverse(branch);
198
+ anySkippable = anySkippable || branchSkippable;
199
+ }
200
+ if (anySkippable) continue;
201
+ return false;
202
+ }
203
+ return false;
204
+ }
205
+ return true;
206
+ }
207
+ traverse(usage);
208
+ return targets;
209
+ }
210
+ /**
144
211
  * Creates an error message with suggestions for similar options or commands.
145
212
  *
146
213
  * This is a convenience function that combines the functionality of
@@ -175,7 +242,8 @@ function createErrorWithSuggestions(baseError, invalidInput, usage, type = "both
175
242
  if (type === "option" || type === "both") for (const name of require_usage.extractOptionNames(usage)) candidates.add(name);
176
243
  if (type === "command" || type === "both") for (const name of require_usage.extractCommandNames(usage)) candidates.add(name);
177
244
  const suggestions = findSimilar(invalidInput, candidates, DEFAULT_FIND_SIMILAR_OPTIONS);
178
- const suggestionMsg = customFormatter ? customFormatter(suggestions) : createSuggestionMessage(suggestions);
245
+ const displaySuggestions = type === "option" ? suggestions : expandCommandAliasSuggestions(usage, suggestions);
246
+ const suggestionMsg = customFormatter ? customFormatter(displaySuggestions) : createSuggestionMessage(displaySuggestions);
179
247
  return suggestionMsg.length > 0 ? [
180
248
  ...baseError,
181
249
  require_message.text("\n\n"),
@@ -248,4 +316,6 @@ exports.DEFAULT_FIND_SIMILAR_OPTIONS = DEFAULT_FIND_SIMILAR_OPTIONS;
248
316
  exports.createErrorWithSuggestions = createErrorWithSuggestions;
249
317
  exports.createSuggestionMessage = createSuggestionMessage;
250
318
  exports.deduplicateSuggestions = deduplicateSuggestions;
251
- exports.findSimilar = findSimilar;
319
+ exports.expandCommandAliasSuggestions = expandCommandAliasSuggestions;
320
+ exports.findSimilar = findSimilar;
321
+ exports.levenshteinDistance = levenshteinDistance;