@optique/core 1.0.0-dev.1533 → 1.0.0-dev.1547

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.
package/dist/parser.js CHANGED
@@ -52,7 +52,7 @@ function parseSync(parser, args, options) {
52
52
  };
53
53
  const previousBuffer = context.buffer;
54
54
  context = result.next;
55
- if (context.buffer.length > 0 && context.buffer.length === previousBuffer.length && context.buffer.every((item, i) => item === previousBuffer[i])) return {
55
+ if (isBufferUnchanged(previousBuffer, context.buffer)) return {
56
56
  success: false,
57
57
  error: message`Unexpected option or argument: ${context.buffer[0]}.`
58
58
  };
@@ -69,6 +69,13 @@ function parseSync(parser, args, options) {
69
69
  };
70
70
  }
71
71
  /**
72
+ * Returns `true` when the buffer has not changed between iterations,
73
+ * indicating a parser is stalling without consuming input.
74
+ */
75
+ function isBufferUnchanged(previous, current) {
76
+ return current.length > 0 && current.length === previous.length && current.every((item, i) => item === previous[i]);
77
+ }
78
+ /**
72
79
  * Parses an array of command-line arguments using the provided combined parser.
73
80
  * This function processes the input arguments, applying the parser to each
74
81
  * argument until all arguments are consumed or an error occurs.
@@ -104,7 +111,7 @@ async function parseAsync(parser, args, options) {
104
111
  };
105
112
  const previousBuffer = context.buffer;
106
113
  context = result.next;
107
- if (context.buffer.length > 0 && context.buffer.length === previousBuffer.length && context.buffer.every((item, i) => item === previousBuffer[i])) return {
114
+ if (isBufferUnchanged(previousBuffer, context.buffer)) return {
108
115
  success: false,
109
116
  error: message`Unexpected option or argument: ${context.buffer[0]}.`
110
117
  };
@@ -196,7 +203,7 @@ function suggestSync(parser, args, options) {
196
203
  if (!result.success) return Array.from(parser.suggest(context, prefix));
197
204
  const previousBuffer = context.buffer;
198
205
  context = result.next;
199
- if (context.buffer.length > 0 && context.buffer.length === previousBuffer.length && context.buffer.every((item, i) => item === previousBuffer[i])) return [];
206
+ if (isBufferUnchanged(previousBuffer, context.buffer)) return [];
200
207
  }
201
208
  return Array.from(parser.suggest(context, prefix));
202
209
  }
@@ -239,7 +246,7 @@ async function suggestAsync(parser, args, options) {
239
246
  }
240
247
  const previousBuffer = context.buffer;
241
248
  context = result.next;
242
- if (context.buffer.length > 0 && context.buffer.length === previousBuffer.length && context.buffer.every((item, i) => item === previousBuffer[i])) return [];
249
+ if (isBufferUnchanged(previousBuffer, context.buffer)) return [];
243
250
  }
244
251
  const suggestions = [];
245
252
  for await (const suggestion of parser.suggest(context, prefix)) suggestions.push(suggestion);
@@ -359,7 +366,9 @@ function getDocPageSyncImpl(parser, args, options) {
359
366
  while (context.buffer.length > 0) {
360
367
  const result = parser.parse(context);
361
368
  if (!result.success) break;
369
+ const previousBuffer = context.buffer;
362
370
  context = result.next;
371
+ if (isBufferUnchanged(previousBuffer, context.buffer)) break;
363
372
  }
364
373
  return buildDocPage(parser, context, args);
365
374
  }
@@ -377,7 +386,9 @@ async function getDocPageAsyncImpl(parser, args, options) {
377
386
  while (context.buffer.length > 0) {
378
387
  const result = await parser.parse(context);
379
388
  if (!result.success) break;
389
+ const previousBuffer = context.buffer;
380
390
  context = result.next;
391
+ if (isBufferUnchanged(previousBuffer, context.buffer)) break;
381
392
  }
382
393
  return buildDocPage(parser, context, args);
383
394
  }
@@ -9,6 +9,11 @@ const require_usage_internals = require('./usage-internals.cjs');
9
9
  const require_valueparser = require('./valueparser.cjs');
10
10
 
11
11
  //#region src/primitives.ts
12
+ /**
13
+ * A shared empty set used as the `leadingNames` value for parsers that
14
+ * do not match any specific name at the first buffer position.
15
+ */
16
+ const EMPTY_LEADING_NAMES = /* @__PURE__ */ new Set();
12
17
  function hasParsedOptionValue(state, valueParser) {
13
18
  if (valueParser != null) return require_dependency.isDeferredParseState(state) || require_dependency.isDependencySourceState(state) || state != null && "success" in state && state.success;
14
19
  return state != null && "success" in state && state.success && state.value === true;
@@ -38,6 +43,8 @@ function constant(value) {
38
43
  $mode: "sync",
39
44
  priority: 0,
40
45
  usage: [],
46
+ leadingNames: EMPTY_LEADING_NAMES,
47
+ acceptingAnyToken: false,
41
48
  initialState: value,
42
49
  parse(context) {
43
50
  return {
@@ -93,6 +100,8 @@ function fail() {
93
100
  $mode: "sync",
94
101
  priority: 0,
95
102
  usage: [],
103
+ leadingNames: EMPTY_LEADING_NAMES,
104
+ acceptingAnyToken: false,
96
105
  initialState: void 0,
97
106
  parse(_context) {
98
107
  return {
@@ -338,6 +347,8 @@ function option(...args) {
338
347
  metavar: valueParser.metavar,
339
348
  ...options.hidden != null && { hidden: options.hidden }
340
349
  }],
350
+ leadingNames: new Set(optionNames$1),
351
+ acceptingAnyToken: false,
341
352
  initialState: valueParser == null ? {
342
353
  success: true,
343
354
  value: false
@@ -622,6 +633,8 @@ function flag(...args) {
622
633
  names: optionNames$1,
623
634
  ...options.hidden != null && { hidden: options.hidden }
624
635
  }],
636
+ leadingNames: new Set(optionNames$1),
637
+ acceptingAnyToken: false,
625
638
  initialState: void 0,
626
639
  parse(context) {
627
640
  if (context.optionsTerminated) return {
@@ -797,6 +810,8 @@ function argument(valueParser, options = {}) {
797
810
  $stateType: [],
798
811
  priority: 5,
799
812
  usage: [term],
813
+ leadingNames: EMPTY_LEADING_NAMES,
814
+ acceptingAnyToken: true,
800
815
  initialState: void 0,
801
816
  parse(context) {
802
817
  if (context.buffer.length < 1) return {
@@ -986,6 +1001,8 @@ function command(name, parser, options = {}) {
986
1001
  ...options.usageLine != null && { usageLine: options.usageLine },
987
1002
  ...options.hidden != null && { hidden: options.hidden }
988
1003
  }, ...parser.usage],
1004
+ leadingNames: new Set([name]),
1005
+ acceptingAnyToken: false,
989
1006
  initialState: void 0,
990
1007
  parse(context) {
991
1008
  if (context.state === void 0) {
@@ -1180,6 +1197,8 @@ function passThrough(options = {}) {
1180
1197
  type: "passthrough",
1181
1198
  ...options.hidden != null && { hidden: options.hidden }
1182
1199
  }],
1200
+ leadingNames: EMPTY_LEADING_NAMES,
1201
+ acceptingAnyToken: false,
1183
1202
  initialState: [],
1184
1203
  parse(context) {
1185
1204
  if (context.buffer.length < 1) return {
@@ -9,6 +9,11 @@ import { extractLeadingCommandNames } from "./usage-internals.js";
9
9
  import { isValueParser } from "./valueparser.js";
10
10
 
11
11
  //#region src/primitives.ts
12
+ /**
13
+ * A shared empty set used as the `leadingNames` value for parsers that
14
+ * do not match any specific name at the first buffer position.
15
+ */
16
+ const EMPTY_LEADING_NAMES = /* @__PURE__ */ new Set();
12
17
  function hasParsedOptionValue(state, valueParser) {
13
18
  if (valueParser != null) return isDeferredParseState(state) || isDependencySourceState(state) || state != null && "success" in state && state.success;
14
19
  return state != null && "success" in state && state.success && state.value === true;
@@ -38,6 +43,8 @@ function constant(value) {
38
43
  $mode: "sync",
39
44
  priority: 0,
40
45
  usage: [],
46
+ leadingNames: EMPTY_LEADING_NAMES,
47
+ acceptingAnyToken: false,
41
48
  initialState: value,
42
49
  parse(context) {
43
50
  return {
@@ -93,6 +100,8 @@ function fail() {
93
100
  $mode: "sync",
94
101
  priority: 0,
95
102
  usage: [],
103
+ leadingNames: EMPTY_LEADING_NAMES,
104
+ acceptingAnyToken: false,
96
105
  initialState: void 0,
97
106
  parse(_context) {
98
107
  return {
@@ -338,6 +347,8 @@ function option(...args) {
338
347
  metavar: valueParser.metavar,
339
348
  ...options.hidden != null && { hidden: options.hidden }
340
349
  }],
350
+ leadingNames: new Set(optionNames$1),
351
+ acceptingAnyToken: false,
341
352
  initialState: valueParser == null ? {
342
353
  success: true,
343
354
  value: false
@@ -622,6 +633,8 @@ function flag(...args) {
622
633
  names: optionNames$1,
623
634
  ...options.hidden != null && { hidden: options.hidden }
624
635
  }],
636
+ leadingNames: new Set(optionNames$1),
637
+ acceptingAnyToken: false,
625
638
  initialState: void 0,
626
639
  parse(context) {
627
640
  if (context.optionsTerminated) return {
@@ -797,6 +810,8 @@ function argument(valueParser, options = {}) {
797
810
  $stateType: [],
798
811
  priority: 5,
799
812
  usage: [term],
813
+ leadingNames: EMPTY_LEADING_NAMES,
814
+ acceptingAnyToken: true,
800
815
  initialState: void 0,
801
816
  parse(context) {
802
817
  if (context.buffer.length < 1) return {
@@ -986,6 +1001,8 @@ function command(name, parser, options = {}) {
986
1001
  ...options.usageLine != null && { usageLine: options.usageLine },
987
1002
  ...options.hidden != null && { hidden: options.hidden }
988
1003
  }, ...parser.usage],
1004
+ leadingNames: new Set([name]),
1005
+ acceptingAnyToken: false,
989
1006
  initialState: void 0,
990
1007
  parse(context) {
991
1008
  if (context.state === void 0) {
@@ -1180,6 +1197,8 @@ function passThrough(options = {}) {
1180
1197
  type: "passthrough",
1181
1198
  ...options.hidden != null && { hidden: options.hidden }
1182
1199
  }],
1200
+ leadingNames: EMPTY_LEADING_NAMES,
1201
+ acceptingAnyToken: false,
1183
1202
  initialState: [],
1184
1203
  parse(context) {
1185
1204
  if (context.buffer.length < 1) return {
package/dist/usage.cjs CHANGED
@@ -104,197 +104,6 @@ function extractCommandNames(usage, includeHidden) {
104
104
  return names;
105
105
  }
106
106
  /**
107
- * Extracts option names that are reachable at the leading token position
108
- * (before any command or argument gate).
109
- *
110
- * Unlike {@link extractOptionNames}, which traverses the entire usage tree,
111
- * this function stops scanning a terms array after encountering a `command`,
112
- * `argument`, or `literal` term, because subsequent terms are scoped under
113
- * that positional token. It still recurses into `optional`, `multiple`,
114
- * and `exclusive` containers, since they represent alternatives or wrappers
115
- * at the same position.
116
- *
117
- * Known limitation: this function infers token positions from the `usage`
118
- * tree, which is a display-oriented structure. Combinators like `tuple()`
119
- * and `object()` sort or flatten usage by priority rather than token order,
120
- * so the results can be inaccurate—both false positives (e.g., options
121
- * appearing before commands due to priority sorting) and false negatives
122
- * (e.g., options after commands that are actually parallel peers).
123
- * The proper fix is to use `Parser.leadingNames` instead of usage-tree
124
- * analysis.
125
- * See https://github.com/dahlia/optique/issues/735
126
- *
127
- * @param usage The usage description to extract leading option names from.
128
- * @param includeHidden Whether to include fully hidden options
129
- * (`hidden: true`) in the result. Defaults to `false`.
130
- * @returns A set of option names reachable at the leading token position.
131
- * @since 1.0.0
132
- */
133
- function extractLeadingOptionNames(usage, includeHidden) {
134
- const names = /* @__PURE__ */ new Set();
135
- function collectLeading(terms) {
136
- if (!terms || !Array.isArray(terms)) return;
137
- for (const term of terms) switch (term.type) {
138
- case "option":
139
- if (!includeHidden && isSuggestionHidden(term.hidden)) break;
140
- for (const name of term.names) names.add(name);
141
- break;
142
- case "command": return;
143
- case "argument":
144
- case "literal": return;
145
- case "optional":
146
- collectLeading(term.terms);
147
- break;
148
- case "multiple":
149
- collectLeading(term.terms);
150
- if (term.min > 0 && branchConsumesToken(term.terms)) return;
151
- break;
152
- case "exclusive":
153
- for (const branch of term.terms) collectLeading(branch);
154
- if (exclusiveConsumesToken(term.terms)) return;
155
- break;
156
- default: break;
157
- }
158
- }
159
- collectLeading(usage);
160
- return names;
161
- }
162
- /**
163
- * Extracts command names that could match as the first positional token.
164
- *
165
- * Unlike {@link extractCommandNames}, which traverses the entire usage tree,
166
- * this function stops scanning a terms array after encountering a `command`,
167
- * `argument`, or `literal` term, because subsequent terms in that array are
168
- * scoped under that positional token (reachable only after the leading term
169
- * is consumed). It still recurses into `optional`, `multiple`, and
170
- * `exclusive` containers, since they represent alternatives or optional
171
- * wrappers at the same token position.
172
- *
173
- * Known limitation: this function has the same usage-tree ordering caveat
174
- * as {@link extractLeadingOptionNames}.
175
- * See https://github.com/dahlia/optique/issues/735
176
- *
177
- * @param usage The usage description to extract leading command names from.
178
- * @param includeHidden Whether to include fully hidden commands
179
- * (`hidden: true`) in the result. Defaults to `false`.
180
- * @returns A set of command names that could match at the first token position.
181
- * @since 1.0.0
182
- */
183
- function extractLeadingCommandNames(usage, includeHidden) {
184
- const names = /* @__PURE__ */ new Set();
185
- function collectLeading(terms) {
186
- if (!terms || !Array.isArray(terms)) return;
187
- for (const term of terms) switch (term.type) {
188
- case "command":
189
- if (!includeHidden && isSuggestionHidden(term.hidden)) return;
190
- names.add(term.name);
191
- return;
192
- case "argument":
193
- case "literal": return;
194
- case "optional":
195
- collectLeading(term.terms);
196
- break;
197
- case "multiple":
198
- collectLeading(term.terms);
199
- if (term.min > 0 && branchConsumesToken(term.terms)) return;
200
- break;
201
- case "exclusive":
202
- for (const branch of term.terms) collectLeading(branch);
203
- if (exclusiveConsumesToken(term.terms)) return;
204
- break;
205
- default: break;
206
- }
207
- }
208
- collectLeading(usage);
209
- return names;
210
- }
211
- /**
212
- * Extracts literal values that could match as the first positional token.
213
- *
214
- * Unlike {@link extractLiteralValues}, which traverses the entire usage tree,
215
- * this function stops scanning a terms array after encountering a `command`,
216
- * `argument`, or `literal` term, because subsequent terms in that array are
217
- * scoped under that positional token. It still recurses into `optional`,
218
- * `multiple`, and `exclusive` containers.
219
- *
220
- * Literals tagged with `optionValue: true` (produced by
221
- * `appendLiteralToUsage()` when rewriting option metavars for
222
- * `conditional()` discriminators) are skipped, because they represent
223
- * option values rather than standalone positional tokens.
224
- *
225
- * Known limitation: this function has the same usage-tree ordering caveat
226
- * as {@link extractLeadingOptionNames}.
227
- * See https://github.com/dahlia/optique/issues/735
228
- *
229
- * @param usage The usage description to extract leading literal values from.
230
- * @returns A set of literal values that could match at the first token
231
- * position.
232
- * @since 1.0.0
233
- */
234
- function extractLeadingLiteralValues(usage) {
235
- const values = /* @__PURE__ */ new Set();
236
- function collectLeading(terms) {
237
- if (!terms || !Array.isArray(terms)) return;
238
- for (const term of terms) switch (term.type) {
239
- case "literal":
240
- if (term.optionValue) break;
241
- values.add(term.value);
242
- return;
243
- case "command":
244
- case "argument": return;
245
- case "optional":
246
- collectLeading(term.terms);
247
- break;
248
- case "multiple":
249
- collectLeading(term.terms);
250
- if (term.min > 0 && branchConsumesToken(term.terms, true)) return;
251
- break;
252
- case "exclusive":
253
- for (const branch of term.terms) collectLeading(branch);
254
- if (exclusiveConsumesToken(term.terms, true)) return;
255
- break;
256
- default: break;
257
- }
258
- }
259
- collectLeading(usage);
260
- return values;
261
- }
262
- /**
263
- * Checks whether every branch of an exclusive term must consume a positional
264
- * token. When true, terms after the exclusive are at position N+1 and should
265
- * not be considered "leading".
266
- *
267
- * @param skipOptionValueLiterals When `true`, literals tagged with
268
- * `optionValue` are treated as non-positional. Only
269
- * `extractLeadingLiteralValues()` passes `true`; the option/command
270
- * extractors use the default (`false`) so that option+value pairs
271
- * still act as positional gates.
272
- */
273
- function exclusiveConsumesToken(branches, skipOptionValueLiterals = false) {
274
- if (branches.length === 0) return false;
275
- return branches.every((branch) => branchConsumesToken(branch, skipOptionValueLiterals));
276
- }
277
- function branchConsumesToken(terms, skipOptionValueLiterals = false) {
278
- if (!terms || !Array.isArray(terms)) return false;
279
- for (const term of terms) switch (term.type) {
280
- case "command":
281
- case "argument": return true;
282
- case "literal":
283
- if (skipOptionValueLiterals && term.optionValue) break;
284
- return true;
285
- case "option": break;
286
- case "optional": break;
287
- case "multiple":
288
- if (term.min > 0 && branchConsumesToken(term.terms, skipOptionValueLiterals)) return true;
289
- break;
290
- case "exclusive":
291
- if (exclusiveConsumesToken(term.terms, skipOptionValueLiterals)) return true;
292
- break;
293
- default: break;
294
- }
295
- return false;
296
- }
297
- /**
298
107
  * Extracts all literal values from a usage description.
299
108
  *
300
109
  * This function recursively traverses the usage tree and collects all
@@ -769,9 +578,6 @@ exports.cloneUsage = cloneUsage;
769
578
  exports.cloneUsageTerm = cloneUsageTerm;
770
579
  exports.extractArgumentMetavars = extractArgumentMetavars;
771
580
  exports.extractCommandNames = extractCommandNames;
772
- exports.extractLeadingCommandNames = extractLeadingCommandNames;
773
- exports.extractLeadingLiteralValues = extractLeadingLiteralValues;
774
- exports.extractLeadingOptionNames = extractLeadingOptionNames;
775
581
  exports.extractLiteralValues = extractLiteralValues;
776
582
  exports.extractOptionNames = extractOptionNames;
777
583
  exports.formatUsage = formatUsage;
package/dist/usage.d.cts CHANGED
@@ -270,80 +270,6 @@ declare function extractOptionNames(usage: Usage, includeHidden?: boolean): Set<
270
270
  * @since 0.7.0
271
271
  */
272
272
  declare function extractCommandNames(usage: Usage, includeHidden?: boolean): Set<string>;
273
- /**
274
- * Extracts option names that are reachable at the leading token position
275
- * (before any command or argument gate).
276
- *
277
- * Unlike {@link extractOptionNames}, which traverses the entire usage tree,
278
- * this function stops scanning a terms array after encountering a `command`,
279
- * `argument`, or `literal` term, because subsequent terms are scoped under
280
- * that positional token. It still recurses into `optional`, `multiple`,
281
- * and `exclusive` containers, since they represent alternatives or wrappers
282
- * at the same position.
283
- *
284
- * Known limitation: this function infers token positions from the `usage`
285
- * tree, which is a display-oriented structure. Combinators like `tuple()`
286
- * and `object()` sort or flatten usage by priority rather than token order,
287
- * so the results can be inaccurate—both false positives (e.g., options
288
- * appearing before commands due to priority sorting) and false negatives
289
- * (e.g., options after commands that are actually parallel peers).
290
- * The proper fix is to use `Parser.leadingNames` instead of usage-tree
291
- * analysis.
292
- * See https://github.com/dahlia/optique/issues/735
293
- *
294
- * @param usage The usage description to extract leading option names from.
295
- * @param includeHidden Whether to include fully hidden options
296
- * (`hidden: true`) in the result. Defaults to `false`.
297
- * @returns A set of option names reachable at the leading token position.
298
- * @since 1.0.0
299
- */
300
- declare function extractLeadingOptionNames(usage: Usage, includeHidden?: boolean): Set<string>;
301
- /**
302
- * Extracts command names that could match as the first positional token.
303
- *
304
- * Unlike {@link extractCommandNames}, which traverses the entire usage tree,
305
- * this function stops scanning a terms array after encountering a `command`,
306
- * `argument`, or `literal` term, because subsequent terms in that array are
307
- * scoped under that positional token (reachable only after the leading term
308
- * is consumed). It still recurses into `optional`, `multiple`, and
309
- * `exclusive` containers, since they represent alternatives or optional
310
- * wrappers at the same token position.
311
- *
312
- * Known limitation: this function has the same usage-tree ordering caveat
313
- * as {@link extractLeadingOptionNames}.
314
- * See https://github.com/dahlia/optique/issues/735
315
- *
316
- * @param usage The usage description to extract leading command names from.
317
- * @param includeHidden Whether to include fully hidden commands
318
- * (`hidden: true`) in the result. Defaults to `false`.
319
- * @returns A set of command names that could match at the first token position.
320
- * @since 1.0.0
321
- */
322
- declare function extractLeadingCommandNames(usage: Usage, includeHidden?: boolean): Set<string>;
323
- /**
324
- * Extracts literal values that could match as the first positional token.
325
- *
326
- * Unlike {@link extractLiteralValues}, which traverses the entire usage tree,
327
- * this function stops scanning a terms array after encountering a `command`,
328
- * `argument`, or `literal` term, because subsequent terms in that array are
329
- * scoped under that positional token. It still recurses into `optional`,
330
- * `multiple`, and `exclusive` containers.
331
- *
332
- * Literals tagged with `optionValue: true` (produced by
333
- * `appendLiteralToUsage()` when rewriting option metavars for
334
- * `conditional()` discriminators) are skipped, because they represent
335
- * option values rather than standalone positional tokens.
336
- *
337
- * Known limitation: this function has the same usage-tree ordering caveat
338
- * as {@link extractLeadingOptionNames}.
339
- * See https://github.com/dahlia/optique/issues/735
340
- *
341
- * @param usage The usage description to extract leading literal values from.
342
- * @returns A set of literal values that could match at the first token
343
- * position.
344
- * @since 1.0.0
345
- */
346
- declare function extractLeadingLiteralValues(usage: Usage): Set<string>;
347
273
  /**
348
274
  * Extracts all literal values from a usage description.
349
275
  *
@@ -520,4 +446,4 @@ interface UsageTermFormatOptions extends UsageFormatOptions {
520
446
  */
521
447
  declare function formatUsageTerm(term: UsageTerm, options?: UsageTermFormatOptions): string;
522
448
  //#endregion
523
- export { HiddenVisibility, OptionName, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, cloneUsage, cloneUsageTerm, extractArgumentMetavars, extractCommandNames, extractLeadingCommandNames, extractLeadingLiteralValues, extractLeadingOptionNames, extractLiteralValues, extractOptionNames, formatUsage, formatUsageTerm, isDocHidden, isSuggestionHidden, isUsageHidden, mergeHidden, normalizeUsage };
449
+ export { HiddenVisibility, OptionName, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, cloneUsage, cloneUsageTerm, extractArgumentMetavars, extractCommandNames, extractLiteralValues, extractOptionNames, formatUsage, formatUsageTerm, isDocHidden, isSuggestionHidden, isUsageHidden, mergeHidden, normalizeUsage };
package/dist/usage.d.ts CHANGED
@@ -270,80 +270,6 @@ declare function extractOptionNames(usage: Usage, includeHidden?: boolean): Set<
270
270
  * @since 0.7.0
271
271
  */
272
272
  declare function extractCommandNames(usage: Usage, includeHidden?: boolean): Set<string>;
273
- /**
274
- * Extracts option names that are reachable at the leading token position
275
- * (before any command or argument gate).
276
- *
277
- * Unlike {@link extractOptionNames}, which traverses the entire usage tree,
278
- * this function stops scanning a terms array after encountering a `command`,
279
- * `argument`, or `literal` term, because subsequent terms are scoped under
280
- * that positional token. It still recurses into `optional`, `multiple`,
281
- * and `exclusive` containers, since they represent alternatives or wrappers
282
- * at the same position.
283
- *
284
- * Known limitation: this function infers token positions from the `usage`
285
- * tree, which is a display-oriented structure. Combinators like `tuple()`
286
- * and `object()` sort or flatten usage by priority rather than token order,
287
- * so the results can be inaccurate—both false positives (e.g., options
288
- * appearing before commands due to priority sorting) and false negatives
289
- * (e.g., options after commands that are actually parallel peers).
290
- * The proper fix is to use `Parser.leadingNames` instead of usage-tree
291
- * analysis.
292
- * See https://github.com/dahlia/optique/issues/735
293
- *
294
- * @param usage The usage description to extract leading option names from.
295
- * @param includeHidden Whether to include fully hidden options
296
- * (`hidden: true`) in the result. Defaults to `false`.
297
- * @returns A set of option names reachable at the leading token position.
298
- * @since 1.0.0
299
- */
300
- declare function extractLeadingOptionNames(usage: Usage, includeHidden?: boolean): Set<string>;
301
- /**
302
- * Extracts command names that could match as the first positional token.
303
- *
304
- * Unlike {@link extractCommandNames}, which traverses the entire usage tree,
305
- * this function stops scanning a terms array after encountering a `command`,
306
- * `argument`, or `literal` term, because subsequent terms in that array are
307
- * scoped under that positional token (reachable only after the leading term
308
- * is consumed). It still recurses into `optional`, `multiple`, and
309
- * `exclusive` containers, since they represent alternatives or optional
310
- * wrappers at the same token position.
311
- *
312
- * Known limitation: this function has the same usage-tree ordering caveat
313
- * as {@link extractLeadingOptionNames}.
314
- * See https://github.com/dahlia/optique/issues/735
315
- *
316
- * @param usage The usage description to extract leading command names from.
317
- * @param includeHidden Whether to include fully hidden commands
318
- * (`hidden: true`) in the result. Defaults to `false`.
319
- * @returns A set of command names that could match at the first token position.
320
- * @since 1.0.0
321
- */
322
- declare function extractLeadingCommandNames(usage: Usage, includeHidden?: boolean): Set<string>;
323
- /**
324
- * Extracts literal values that could match as the first positional token.
325
- *
326
- * Unlike {@link extractLiteralValues}, which traverses the entire usage tree,
327
- * this function stops scanning a terms array after encountering a `command`,
328
- * `argument`, or `literal` term, because subsequent terms in that array are
329
- * scoped under that positional token. It still recurses into `optional`,
330
- * `multiple`, and `exclusive` containers.
331
- *
332
- * Literals tagged with `optionValue: true` (produced by
333
- * `appendLiteralToUsage()` when rewriting option metavars for
334
- * `conditional()` discriminators) are skipped, because they represent
335
- * option values rather than standalone positional tokens.
336
- *
337
- * Known limitation: this function has the same usage-tree ordering caveat
338
- * as {@link extractLeadingOptionNames}.
339
- * See https://github.com/dahlia/optique/issues/735
340
- *
341
- * @param usage The usage description to extract leading literal values from.
342
- * @returns A set of literal values that could match at the first token
343
- * position.
344
- * @since 1.0.0
345
- */
346
- declare function extractLeadingLiteralValues(usage: Usage): Set<string>;
347
273
  /**
348
274
  * Extracts all literal values from a usage description.
349
275
  *
@@ -520,4 +446,4 @@ interface UsageTermFormatOptions extends UsageFormatOptions {
520
446
  */
521
447
  declare function formatUsageTerm(term: UsageTerm, options?: UsageTermFormatOptions): string;
522
448
  //#endregion
523
- export { HiddenVisibility, OptionName, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, cloneUsage, cloneUsageTerm, extractArgumentMetavars, extractCommandNames, extractLeadingCommandNames, extractLeadingLiteralValues, extractLeadingOptionNames, extractLiteralValues, extractOptionNames, formatUsage, formatUsageTerm, isDocHidden, isSuggestionHidden, isUsageHidden, mergeHidden, normalizeUsage };
449
+ export { HiddenVisibility, OptionName, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, cloneUsage, cloneUsageTerm, extractArgumentMetavars, extractCommandNames, extractLiteralValues, extractOptionNames, formatUsage, formatUsageTerm, isDocHidden, isSuggestionHidden, isUsageHidden, mergeHidden, normalizeUsage };