@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
@@ -0,0 +1,188 @@
1
+ import { Message } from "./message.cjs";
2
+ import { Usage } from "./usage.cjs";
3
+ import { Suggestion } from "./internal/parser.cjs";
4
+
5
+ //#region src/suggestion.d.ts
6
+
7
+ /**
8
+ * Calculates the Levenshtein distance between two strings.
9
+ *
10
+ * The Levenshtein distance is the minimum number of single-character edits
11
+ * (insertions, deletions, or substitutions) required to transform one string
12
+ * into another.
13
+ *
14
+ * @param source The source string
15
+ * @param target The target string
16
+ * @returns The edit distance (number of insertions, deletions, substitutions)
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * levenshteinDistance("kitten", "sitting"); // returns 3
21
+ * levenshteinDistance("--verbos", "--verbose"); // returns 1
22
+ * levenshteinDistance("hello", "hello"); // returns 0
23
+ * ```
24
+ */
25
+ declare function levenshteinDistance(source: string, target: string): number;
26
+ /**
27
+ * Options for finding similar strings.
28
+ */
29
+ interface FindSimilarOptions {
30
+ /**
31
+ * Maximum edit distance to consider a match.
32
+ * Strings with a distance greater than this value will not be suggested.
33
+ * @default 3
34
+ */
35
+ readonly maxDistance?: number;
36
+ /**
37
+ * Maximum distance ratio (distance / input length).
38
+ * Prevents suggesting long strings for very short inputs.
39
+ * For example, with maxDistanceRatio=0.5, an input of length 2
40
+ * will only suggest strings within distance 1.
41
+ * @default 0.5
42
+ */
43
+ readonly maxDistanceRatio?: number;
44
+ /**
45
+ * Maximum number of suggestions to return.
46
+ * @default 3
47
+ */
48
+ readonly maxSuggestions?: number;
49
+ /**
50
+ * Case-sensitive comparison.
51
+ * If false, strings are compared case-insensitively.
52
+ * @default false
53
+ */
54
+ readonly caseSensitive?: boolean;
55
+ }
56
+ /**
57
+ * Default options for finding similar strings.
58
+ * These values are optimized for command-line option/command name suggestions.
59
+ *
60
+ * @since 0.7.0
61
+ */
62
+ declare const DEFAULT_FIND_SIMILAR_OPTIONS: Required<FindSimilarOptions>;
63
+ /**
64
+ * Finds similar strings from a list of candidates.
65
+ *
66
+ * This function uses Levenshtein distance to find strings that are similar
67
+ * to the input string. Results are sorted by similarity (most similar first).
68
+ *
69
+ * @param input The input string to find matches for
70
+ * @param candidates List of candidate strings to compare against
71
+ * @param options Configuration options
72
+ * @returns Array of similar strings, sorted by similarity (most similar first)
73
+ *
74
+ * @example
75
+ * ```typescript
76
+ * const candidates = ["--verbose", "--version", "--verify", "--help"];
77
+ * findSimilar("--verbos", candidates);
78
+ * // returns ["--verbose"]
79
+ *
80
+ * findSimilar("--ver", candidates, { maxDistance: 5 });
81
+ * // returns ["--verify", "--version", "--verbose"]
82
+ *
83
+ * findSimilar("--xyz", candidates);
84
+ * // returns [] (no similar matches)
85
+ * ```
86
+ */
87
+ declare function findSimilar(input: string, candidates: Iterable<string>, options?: FindSimilarOptions): string[];
88
+ /**
89
+ * Creates a suggestion message for a mismatched option/command.
90
+ *
91
+ * This function formats suggestions in a user-friendly way:
92
+ * - No suggestions: returns empty message
93
+ * - One suggestion: "Did you mean `option`?"
94
+ * - Multiple suggestions: "Did you mean one of these?\n option1\n option2"
95
+ *
96
+ * @param suggestions List of similar valid options/commands
97
+ * @returns A Message array with suggestion text
98
+ *
99
+ * @example
100
+ * ```typescript
101
+ * createSuggestionMessage(["--verbose", "--version"]);
102
+ * // returns message parts for:
103
+ * // "Did you mean one of these?
104
+ * // --verbose
105
+ * // --version"
106
+ *
107
+ * createSuggestionMessage(["--verbose"]);
108
+ * // returns message parts for:
109
+ * // "Did you mean `--verbose`?"
110
+ *
111
+ * createSuggestionMessage([]);
112
+ * // returns []
113
+ * ```
114
+ */
115
+ declare function createSuggestionMessage(suggestions: readonly string[]): Message;
116
+ /**
117
+ * Expands command alias suggestions so an alias typo can point at both the
118
+ * canonical command and the alias that matched.
119
+ *
120
+ * @param usage Usage terms that define command aliases.
121
+ * @param suggestions Candidate suggestions returned by {@link findSimilar}.
122
+ * @returns Suggestions with alias hits expanded to canonical name + alias.
123
+ * @internal
124
+ */
125
+ declare function expandCommandAliasSuggestions(usage: Usage, suggestions: readonly string[]): readonly string[];
126
+ /**
127
+ * Creates an error message with suggestions for similar options or commands.
128
+ *
129
+ * This is a convenience function that combines the functionality of
130
+ * `findSimilar()` and `createSuggestionMessage()` to generate user-friendly
131
+ * error messages with "Did you mean?" suggestions.
132
+ *
133
+ * @param baseError The base error message to display
134
+ * @param invalidInput The invalid option or command name that the user typed
135
+ * @param usage The usage information to extract available options/commands from
136
+ * @param type What type of names to suggest ("option", "command", or "both")
137
+ * @param customFormatter Optional custom function to format suggestions instead
138
+ * of using the default "Did you mean?" formatting
139
+ * @returns A message combining the base error with suggestions, or just the
140
+ * base error if no similar names are found
141
+ *
142
+ * @example
143
+ * ```typescript
144
+ * const baseError = message`No matched option for ${optionName("--verbos")}.`;
145
+ * const error = createErrorWithSuggestions(
146
+ * baseError,
147
+ * "--verbos",
148
+ * context.usage,
149
+ * "option"
150
+ * );
151
+ * // Returns: "No matched option for `--verbos`.\nDid you mean `--verbose`?"
152
+ * ```
153
+ *
154
+ * @since 0.7.0
155
+ */
156
+ declare function createErrorWithSuggestions(baseError: Message, invalidInput: string, usage: Usage, type?: "option" | "command" | "both", customFormatter?: (suggestions: readonly string[]) => Message): Message;
157
+ /**
158
+ * Removes duplicate suggestions from an array while preserving order.
159
+ *
160
+ * Suggestions are considered duplicates if they have the same key:
161
+ * - Literal suggestions: same text
162
+ * - File suggestions: same type, extensions, and pattern
163
+ *
164
+ * The first occurrence of each unique suggestion is kept. For file
165
+ * suggestions, `includeHidden` is merged across duplicates: if any
166
+ * duplicate has `includeHidden: true`, the kept suggestion is upgraded
167
+ * to `includeHidden: true` because it is a superset of the non-hidden
168
+ * variant.
169
+ *
170
+ * @param suggestions Array of suggestions that may contain duplicates
171
+ * @returns A new array with duplicates removed, preserving order of first occurrences
172
+ *
173
+ * @example
174
+ * ```typescript
175
+ * const suggestions = [
176
+ * { kind: "literal", text: "--verbose" },
177
+ * { kind: "literal", text: "--help" },
178
+ * { kind: "literal", text: "--verbose" }, // duplicate
179
+ * ];
180
+ * deduplicateSuggestions(suggestions);
181
+ * // returns [{ kind: "literal", text: "--verbose" }, { kind: "literal", text: "--help" }]
182
+ * ```
183
+ *
184
+ * @since 0.9.0
185
+ */
186
+ declare function deduplicateSuggestions(suggestions: readonly Suggestion[]): Suggestion[];
187
+ //#endregion
188
+ export { DEFAULT_FIND_SIMILAR_OPTIONS, FindSimilarOptions, createErrorWithSuggestions, createSuggestionMessage, deduplicateSuggestions, expandCommandAliasSuggestions, findSimilar, levenshteinDistance };
@@ -0,0 +1,188 @@
1
+ import { Message } from "./message.js";
2
+ import { Usage } from "./usage.js";
3
+ import { Suggestion } from "./internal/parser.js";
4
+
5
+ //#region src/suggestion.d.ts
6
+
7
+ /**
8
+ * Calculates the Levenshtein distance between two strings.
9
+ *
10
+ * The Levenshtein distance is the minimum number of single-character edits
11
+ * (insertions, deletions, or substitutions) required to transform one string
12
+ * into another.
13
+ *
14
+ * @param source The source string
15
+ * @param target The target string
16
+ * @returns The edit distance (number of insertions, deletions, substitutions)
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * levenshteinDistance("kitten", "sitting"); // returns 3
21
+ * levenshteinDistance("--verbos", "--verbose"); // returns 1
22
+ * levenshteinDistance("hello", "hello"); // returns 0
23
+ * ```
24
+ */
25
+ declare function levenshteinDistance(source: string, target: string): number;
26
+ /**
27
+ * Options for finding similar strings.
28
+ */
29
+ interface FindSimilarOptions {
30
+ /**
31
+ * Maximum edit distance to consider a match.
32
+ * Strings with a distance greater than this value will not be suggested.
33
+ * @default 3
34
+ */
35
+ readonly maxDistance?: number;
36
+ /**
37
+ * Maximum distance ratio (distance / input length).
38
+ * Prevents suggesting long strings for very short inputs.
39
+ * For example, with maxDistanceRatio=0.5, an input of length 2
40
+ * will only suggest strings within distance 1.
41
+ * @default 0.5
42
+ */
43
+ readonly maxDistanceRatio?: number;
44
+ /**
45
+ * Maximum number of suggestions to return.
46
+ * @default 3
47
+ */
48
+ readonly maxSuggestions?: number;
49
+ /**
50
+ * Case-sensitive comparison.
51
+ * If false, strings are compared case-insensitively.
52
+ * @default false
53
+ */
54
+ readonly caseSensitive?: boolean;
55
+ }
56
+ /**
57
+ * Default options for finding similar strings.
58
+ * These values are optimized for command-line option/command name suggestions.
59
+ *
60
+ * @since 0.7.0
61
+ */
62
+ declare const DEFAULT_FIND_SIMILAR_OPTIONS: Required<FindSimilarOptions>;
63
+ /**
64
+ * Finds similar strings from a list of candidates.
65
+ *
66
+ * This function uses Levenshtein distance to find strings that are similar
67
+ * to the input string. Results are sorted by similarity (most similar first).
68
+ *
69
+ * @param input The input string to find matches for
70
+ * @param candidates List of candidate strings to compare against
71
+ * @param options Configuration options
72
+ * @returns Array of similar strings, sorted by similarity (most similar first)
73
+ *
74
+ * @example
75
+ * ```typescript
76
+ * const candidates = ["--verbose", "--version", "--verify", "--help"];
77
+ * findSimilar("--verbos", candidates);
78
+ * // returns ["--verbose"]
79
+ *
80
+ * findSimilar("--ver", candidates, { maxDistance: 5 });
81
+ * // returns ["--verify", "--version", "--verbose"]
82
+ *
83
+ * findSimilar("--xyz", candidates);
84
+ * // returns [] (no similar matches)
85
+ * ```
86
+ */
87
+ declare function findSimilar(input: string, candidates: Iterable<string>, options?: FindSimilarOptions): string[];
88
+ /**
89
+ * Creates a suggestion message for a mismatched option/command.
90
+ *
91
+ * This function formats suggestions in a user-friendly way:
92
+ * - No suggestions: returns empty message
93
+ * - One suggestion: "Did you mean `option`?"
94
+ * - Multiple suggestions: "Did you mean one of these?\n option1\n option2"
95
+ *
96
+ * @param suggestions List of similar valid options/commands
97
+ * @returns A Message array with suggestion text
98
+ *
99
+ * @example
100
+ * ```typescript
101
+ * createSuggestionMessage(["--verbose", "--version"]);
102
+ * // returns message parts for:
103
+ * // "Did you mean one of these?
104
+ * // --verbose
105
+ * // --version"
106
+ *
107
+ * createSuggestionMessage(["--verbose"]);
108
+ * // returns message parts for:
109
+ * // "Did you mean `--verbose`?"
110
+ *
111
+ * createSuggestionMessage([]);
112
+ * // returns []
113
+ * ```
114
+ */
115
+ declare function createSuggestionMessage(suggestions: readonly string[]): Message;
116
+ /**
117
+ * Expands command alias suggestions so an alias typo can point at both the
118
+ * canonical command and the alias that matched.
119
+ *
120
+ * @param usage Usage terms that define command aliases.
121
+ * @param suggestions Candidate suggestions returned by {@link findSimilar}.
122
+ * @returns Suggestions with alias hits expanded to canonical name + alias.
123
+ * @internal
124
+ */
125
+ declare function expandCommandAliasSuggestions(usage: Usage, suggestions: readonly string[]): readonly string[];
126
+ /**
127
+ * Creates an error message with suggestions for similar options or commands.
128
+ *
129
+ * This is a convenience function that combines the functionality of
130
+ * `findSimilar()` and `createSuggestionMessage()` to generate user-friendly
131
+ * error messages with "Did you mean?" suggestions.
132
+ *
133
+ * @param baseError The base error message to display
134
+ * @param invalidInput The invalid option or command name that the user typed
135
+ * @param usage The usage information to extract available options/commands from
136
+ * @param type What type of names to suggest ("option", "command", or "both")
137
+ * @param customFormatter Optional custom function to format suggestions instead
138
+ * of using the default "Did you mean?" formatting
139
+ * @returns A message combining the base error with suggestions, or just the
140
+ * base error if no similar names are found
141
+ *
142
+ * @example
143
+ * ```typescript
144
+ * const baseError = message`No matched option for ${optionName("--verbos")}.`;
145
+ * const error = createErrorWithSuggestions(
146
+ * baseError,
147
+ * "--verbos",
148
+ * context.usage,
149
+ * "option"
150
+ * );
151
+ * // Returns: "No matched option for `--verbos`.\nDid you mean `--verbose`?"
152
+ * ```
153
+ *
154
+ * @since 0.7.0
155
+ */
156
+ declare function createErrorWithSuggestions(baseError: Message, invalidInput: string, usage: Usage, type?: "option" | "command" | "both", customFormatter?: (suggestions: readonly string[]) => Message): Message;
157
+ /**
158
+ * Removes duplicate suggestions from an array while preserving order.
159
+ *
160
+ * Suggestions are considered duplicates if they have the same key:
161
+ * - Literal suggestions: same text
162
+ * - File suggestions: same type, extensions, and pattern
163
+ *
164
+ * The first occurrence of each unique suggestion is kept. For file
165
+ * suggestions, `includeHidden` is merged across duplicates: if any
166
+ * duplicate has `includeHidden: true`, the kept suggestion is upgraded
167
+ * to `includeHidden: true` because it is a superset of the non-hidden
168
+ * variant.
169
+ *
170
+ * @param suggestions Array of suggestions that may contain duplicates
171
+ * @returns A new array with duplicates removed, preserving order of first occurrences
172
+ *
173
+ * @example
174
+ * ```typescript
175
+ * const suggestions = [
176
+ * { kind: "literal", text: "--verbose" },
177
+ * { kind: "literal", text: "--help" },
178
+ * { kind: "literal", text: "--verbose" }, // duplicate
179
+ * ];
180
+ * deduplicateSuggestions(suggestions);
181
+ * // returns [{ kind: "literal", text: "--verbose" }, { kind: "literal", text: "--help" }]
182
+ * ```
183
+ *
184
+ * @since 0.9.0
185
+ */
186
+ declare function deduplicateSuggestions(suggestions: readonly Suggestion[]): Suggestion[];
187
+ //#endregion
188
+ export { DEFAULT_FIND_SIMILAR_OPTIONS, FindSimilarOptions, createErrorWithSuggestions, createSuggestionMessage, deduplicateSuggestions, expandCommandAliasSuggestions, findSimilar, levenshteinDistance };
@@ -1,5 +1,5 @@
1
1
  import { message, optionName, text } from "./message.js";
2
- import { extractCommandNames, extractOptionNames } from "./usage.js";
2
+ import { extractCommandNames, extractOptionNames, isSuggestionHidden } from "./usage.js";
3
3
 
4
4
  //#region src/suggestion.ts
5
5
  /**
@@ -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 (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 extractOptionNames(usage)) candidates.add(name);
176
243
  if (type === "command" || type === "both") for (const name of 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
  text("\n\n"),
@@ -244,4 +312,4 @@ function deduplicateSuggestions(suggestions) {
244
312
  }
245
313
 
246
314
  //#endregion
247
- export { DEFAULT_FIND_SIMILAR_OPTIONS, createErrorWithSuggestions, createSuggestionMessage, deduplicateSuggestions, findSimilar };
315
+ export { DEFAULT_FIND_SIMILAR_OPTIONS, createErrorWithSuggestions, createSuggestionMessage, deduplicateSuggestions, expandCommandAliasSuggestions, findSimilar, levenshteinDistance };
@@ -24,7 +24,11 @@ function collectLeadingCandidates(terms, optionNames, commandNames, includeHidde
24
24
  return false;
25
25
  }
26
26
  if (term.type === "command") {
27
- if (includeHidden || !require_usage.isSuggestionHidden(term.hidden)) commandNames.add(term.name);
27
+ if (includeHidden || !require_usage.isSuggestionHidden(term.hidden)) {
28
+ commandNames.add(term.name);
29
+ for (const alias of term.aliases ?? []) commandNames.add(alias);
30
+ for (const alias of term.hiddenAliases ?? []) commandNames.add(alias);
31
+ }
28
32
  return false;
29
33
  }
30
34
  if (term.type === "argument") return false;
@@ -24,7 +24,11 @@ function collectLeadingCandidates(terms, optionNames, commandNames, includeHidde
24
24
  return false;
25
25
  }
26
26
  if (term.type === "command") {
27
- if (includeHidden || !isSuggestionHidden(term.hidden)) commandNames.add(term.name);
27
+ if (includeHidden || !isSuggestionHidden(term.hidden)) {
28
+ commandNames.add(term.name);
29
+ for (const alias of term.aliases ?? []) commandNames.add(alias);
30
+ for (const alias of term.hiddenAliases ?? []) commandNames.add(alias);
31
+ }
28
32
  return false;
29
33
  }
30
34
  if (term.type === "argument") return false;
package/dist/usage.cjs CHANGED
@@ -98,6 +98,8 @@ function extractCommandNames(usage, includeHidden) {
98
98
  for (const term of terms) if (term.type === "command") {
99
99
  if (!includeHidden && isSuggestionHidden(term.hidden)) continue;
100
100
  names.add(term.name);
101
+ for (const alias of term.aliases ?? []) names.add(alias);
102
+ if (includeHidden) for (const alias of term.hiddenAliases ?? []) names.add(alias);
101
103
  } else if (term.type === "optional" || term.type === "multiple" || term.type === "sequence") traverseUsage(term.terms);
102
104
  else if (term.type === "exclusive") for (const exclusiveUsage of term.terms) traverseUsage(exclusiveUsage);
103
105
  }
@@ -354,9 +356,15 @@ function cloneUsageTerm(term) {
354
356
  names: [...term.names]
355
357
  };
356
358
  case "command": {
357
- if (term.usageLine == null || typeof term.usageLine === "function") return { ...term };
359
+ if (term.usageLine == null || typeof term.usageLine === "function") return {
360
+ ...term,
361
+ ...term.aliases != null ? { aliases: [...term.aliases] } : {},
362
+ ...term.hiddenAliases != null ? { hiddenAliases: [...term.hiddenAliases] } : {}
363
+ };
358
364
  return {
359
365
  ...term,
366
+ ...term.aliases != null ? { aliases: [...term.aliases] } : {},
367
+ ...term.hiddenAliases != null ? { hiddenAliases: [...term.hiddenAliases] } : {},
360
368
  usageLine: term.usageLine.map(cloneUsageTerm)
361
369
  };
362
370
  }
package/dist/usage.d.cts CHANGED
@@ -104,6 +104,20 @@ type UsageTerm =
104
104
  * in the command-line usage.
105
105
  */
106
106
  readonly name: string;
107
+ /**
108
+ * Additional command names that invoke the same parser.
109
+ * These aliases participate in parsing, completion, and typo
110
+ * suggestions, but are not rendered in usage or documentation output.
111
+ * @since 1.1.0
112
+ */
113
+ readonly aliases?: readonly string[];
114
+ /**
115
+ * Additional command names that invoke the same parser but are not
116
+ * rendered or suggested. They are still available to parsers and
117
+ * suggestion matchers so alias typos can resolve to the canonical command.
118
+ * @since 1.1.0
119
+ */
120
+ readonly hiddenAliases?: readonly string[];
107
121
  /**
108
122
  * Optional usage line override for this command's own help page.
109
123
  * This affects help/documentation rendering only.
package/dist/usage.d.ts CHANGED
@@ -104,6 +104,20 @@ type UsageTerm =
104
104
  * in the command-line usage.
105
105
  */
106
106
  readonly name: string;
107
+ /**
108
+ * Additional command names that invoke the same parser.
109
+ * These aliases participate in parsing, completion, and typo
110
+ * suggestions, but are not rendered in usage or documentation output.
111
+ * @since 1.1.0
112
+ */
113
+ readonly aliases?: readonly string[];
114
+ /**
115
+ * Additional command names that invoke the same parser but are not
116
+ * rendered or suggested. They are still available to parsers and
117
+ * suggestion matchers so alias typos can resolve to the canonical command.
118
+ * @since 1.1.0
119
+ */
120
+ readonly hiddenAliases?: readonly string[];
107
121
  /**
108
122
  * Optional usage line override for this command's own help page.
109
123
  * This affects help/documentation rendering only.
package/dist/usage.js CHANGED
@@ -98,6 +98,8 @@ function extractCommandNames(usage, includeHidden) {
98
98
  for (const term of terms) if (term.type === "command") {
99
99
  if (!includeHidden && isSuggestionHidden(term.hidden)) continue;
100
100
  names.add(term.name);
101
+ for (const alias of term.aliases ?? []) names.add(alias);
102
+ if (includeHidden) for (const alias of term.hiddenAliases ?? []) names.add(alias);
101
103
  } else if (term.type === "optional" || term.type === "multiple" || term.type === "sequence") traverseUsage(term.terms);
102
104
  else if (term.type === "exclusive") for (const exclusiveUsage of term.terms) traverseUsage(exclusiveUsage);
103
105
  }
@@ -354,9 +356,15 @@ function cloneUsageTerm(term) {
354
356
  names: [...term.names]
355
357
  };
356
358
  case "command": {
357
- if (term.usageLine == null || typeof term.usageLine === "function") return { ...term };
359
+ if (term.usageLine == null || typeof term.usageLine === "function") return {
360
+ ...term,
361
+ ...term.aliases != null ? { aliases: [...term.aliases] } : {},
362
+ ...term.hiddenAliases != null ? { hiddenAliases: [...term.hiddenAliases] } : {}
363
+ };
358
364
  return {
359
365
  ...term,
366
+ ...term.aliases != null ? { aliases: [...term.aliases] } : {},
367
+ ...term.hiddenAliases != null ? { hiddenAliases: [...term.hiddenAliases] } : {},
360
368
  usageLine: term.usageLine.map(cloneUsageTerm)
361
369
  };
362
370
  }
package/dist/validate.cjs CHANGED
@@ -162,6 +162,7 @@ function validateContextIds(contexts) {
162
162
  }
163
163
 
164
164
  //#endregion
165
+ exports.escapeControlChars = escapeControlChars;
165
166
  exports.validateCommandNames = validateCommandNames;
166
167
  exports.validateContextIds = validateContextIds;
167
168
  exports.validateLabel = validateLabel;