@optique/core 0.9.3 → 0.9.5

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.
@@ -4,6 +4,57 @@ const require_suggestion = require('./suggestion.cjs');
4
4
 
5
5
  //#region src/constructs.ts
6
6
  /**
7
+ * Collects option names and command names that are valid at the current
8
+ * parse position by walking the usage tree. Only "leading" candidates
9
+ * (those reachable before a required positional argument) are collected.
10
+ */
11
+ function collectLeadingCandidates(terms, optionNames, commandNames) {
12
+ if (!terms || !Array.isArray(terms)) return true;
13
+ for (const term of terms) {
14
+ if (term.type === "option") {
15
+ for (const name of term.names) optionNames.add(name);
16
+ return false;
17
+ }
18
+ if (term.type === "command") {
19
+ commandNames.add(term.name);
20
+ return false;
21
+ }
22
+ if (term.type === "argument") return false;
23
+ if (term.type === "optional") {
24
+ collectLeadingCandidates(term.terms, optionNames, commandNames);
25
+ continue;
26
+ }
27
+ if (term.type === "multiple") {
28
+ collectLeadingCandidates(term.terms, optionNames, commandNames);
29
+ if (term.min === 0) continue;
30
+ return false;
31
+ }
32
+ if (term.type === "exclusive") {
33
+ let allAlternativesSkippable = true;
34
+ for (const exclusiveUsage of term.terms) {
35
+ const alternativeSkippable = collectLeadingCandidates(exclusiveUsage, optionNames, commandNames);
36
+ allAlternativesSkippable = allAlternativesSkippable && alternativeSkippable;
37
+ }
38
+ if (allAlternativesSkippable) continue;
39
+ return false;
40
+ }
41
+ }
42
+ return true;
43
+ }
44
+ function createUnexpectedInputErrorWithScopedSuggestions(baseError, invalidInput, parsers, customFormatter) {
45
+ const options = /* @__PURE__ */ new Set();
46
+ const commands = /* @__PURE__ */ new Set();
47
+ for (const parser of parsers) collectLeadingCandidates(parser.usage, options, commands);
48
+ const candidates = new Set([...options, ...commands]);
49
+ const suggestions = require_suggestion.findSimilar(invalidInput, candidates, require_suggestion.DEFAULT_FIND_SIMILAR_OPTIONS);
50
+ const suggestionMsg = customFormatter ? customFormatter(suggestions) : require_suggestion.createSuggestionMessage(suggestions);
51
+ return suggestionMsg.length > 0 ? [
52
+ ...baseError,
53
+ require_message.text("\n\n"),
54
+ ...suggestionMsg
55
+ ] : baseError;
56
+ }
57
+ /**
7
58
  * Checks if the given token is an option name that requires a value
8
59
  * (i.e., has a metavar) within the given usage terms.
9
60
  * @param usage The usage terms to search through.
@@ -202,16 +253,6 @@ function getNoMatchError(options, noMatchContext) {
202
253
  return customNoMatch ? typeof customNoMatch === "function" ? customNoMatch(noMatchContext) : customNoMatch : generateNoMatchError(noMatchContext);
203
254
  }
204
255
  /**
205
- * Creates default error for parse() method when buffer is not empty.
206
- * Shared by or() and longestMatch().
207
- * @internal
208
- */
209
- function createUnexpectedInputError(token, usage, options) {
210
- const defaultMsg = require_message.message`Unexpected option or subcommand: ${require_message.optionName(token)}.`;
211
- if (options?.errors?.unexpectedInput != null) return typeof options.errors.unexpectedInput === "function" ? options.errors.unexpectedInput(token) : options.errors.unexpectedInput;
212
- return require_suggestion.createErrorWithSuggestions(defaultMsg, token, usage, "both", options?.errors?.suggestions);
213
- }
214
- /**
215
256
  * @since 0.5.0
216
257
  */
217
258
  function or(...args) {
@@ -230,7 +271,12 @@ function or(...args) {
230
271
  const syncParsers = parsers;
231
272
  const getInitialError = (context) => ({
232
273
  consumed: 0,
233
- error: context.buffer.length < 1 ? getNoMatchError(options, noMatchContext) : createUnexpectedInputError(context.buffer[0], context.usage, options)
274
+ error: context.buffer.length < 1 ? getNoMatchError(options, noMatchContext) : (() => {
275
+ const token = context.buffer[0];
276
+ const defaultMsg = require_message.message`Unexpected option or subcommand: ${require_message.optionName(token)}.`;
277
+ if (options?.errors?.unexpectedInput != null) return typeof options.errors.unexpectedInput === "function" ? options.errors.unexpectedInput(token) : options.errors.unexpectedInput;
278
+ return createUnexpectedInputErrorWithScopedSuggestions(defaultMsg, token, parsers, options?.errors?.suggestions);
279
+ })()
234
280
  });
235
281
  const parseSync = (context) => {
236
282
  let error = getInitialError(context);
@@ -314,7 +360,9 @@ function or(...args) {
314
360
  },
315
361
  suggest: createExclusiveSuggest(parsers, isAsync),
316
362
  getDocFragments(state, _defaultValue) {
363
+ let brief;
317
364
  let description;
365
+ let footer;
318
366
  let fragments;
319
367
  if (state.kind === "unavailable" || state.state == null) fragments = parsers.flatMap((p) => p.getDocFragments({ kind: "unavailable" }, void 0).fragments);
320
368
  else {
@@ -324,7 +372,9 @@ function or(...args) {
324
372
  state: parserResult.next.state
325
373
  } : { kind: "unavailable" };
326
374
  const docFragments = parsers[index].getDocFragments(innerState, void 0);
375
+ brief = docFragments.brief;
327
376
  description = docFragments.description;
377
+ footer = docFragments.footer;
328
378
  fragments = docFragments.fragments;
329
379
  }
330
380
  const entries = fragments.filter((f) => f.type === "entry");
@@ -335,7 +385,9 @@ function or(...args) {
335
385
  else sections.push(fragment);
336
386
  }
337
387
  return {
388
+ brief,
338
389
  description,
390
+ footer,
339
391
  fragments: [...sections.map((s) => ({
340
392
  ...s,
341
393
  type: "section"
@@ -366,7 +418,12 @@ function longestMatch(...args) {
366
418
  const syncParsers = parsers;
367
419
  const getInitialError = (context) => ({
368
420
  consumed: 0,
369
- error: context.buffer.length < 1 ? getNoMatchError(options, noMatchContext) : createUnexpectedInputError(context.buffer[0], context.usage, options)
421
+ error: context.buffer.length < 1 ? getNoMatchError(options, noMatchContext) : (() => {
422
+ const token = context.buffer[0];
423
+ const defaultMsg = require_message.message`Unexpected option or subcommand: ${require_message.optionName(token)}.`;
424
+ if (options?.errors?.unexpectedInput != null) return typeof options.errors.unexpectedInput === "function" ? options.errors.unexpectedInput(token) : options.errors.unexpectedInput;
425
+ return createUnexpectedInputErrorWithScopedSuggestions(defaultMsg, token, parsers, options?.errors?.suggestions);
426
+ })()
370
427
  });
371
428
  const parseSync = (context) => {
372
429
  let bestMatch = null;
@@ -452,6 +509,7 @@ function longestMatch(...args) {
452
509
  },
453
510
  suggest: createExclusiveSuggest(parsers, isAsync),
454
511
  getDocFragments(state, _defaultValue) {
512
+ let brief;
455
513
  let description;
456
514
  let footer;
457
515
  let fragments;
@@ -463,12 +521,14 @@ function longestMatch(...args) {
463
521
  kind: "available",
464
522
  state: result.next.state
465
523
  });
524
+ brief = docResult.brief;
466
525
  description = docResult.description;
467
526
  footer = docResult.footer;
468
527
  fragments = docResult.fragments;
469
528
  } else fragments = parsers.flatMap((p) => p.getDocFragments({ kind: "unavailable" }).fragments);
470
529
  }
471
530
  return {
531
+ brief,
472
532
  description,
473
533
  fragments,
474
534
  footer
@@ -1,9 +1,60 @@
1
- import { message, optionName, values } from "./message.js";
1
+ import { message, optionName, text, values } from "./message.js";
2
2
  import { extractArgumentMetavars, extractCommandNames, extractOptionNames } from "./usage.js";
3
- import { createErrorWithSuggestions, deduplicateSuggestions } from "./suggestion.js";
3
+ import { DEFAULT_FIND_SIMILAR_OPTIONS, createErrorWithSuggestions, createSuggestionMessage, deduplicateSuggestions, findSimilar } from "./suggestion.js";
4
4
 
5
5
  //#region src/constructs.ts
6
6
  /**
7
+ * Collects option names and command names that are valid at the current
8
+ * parse position by walking the usage tree. Only "leading" candidates
9
+ * (those reachable before a required positional argument) are collected.
10
+ */
11
+ function collectLeadingCandidates(terms, optionNames, commandNames) {
12
+ if (!terms || !Array.isArray(terms)) return true;
13
+ for (const term of terms) {
14
+ if (term.type === "option") {
15
+ for (const name of term.names) optionNames.add(name);
16
+ return false;
17
+ }
18
+ if (term.type === "command") {
19
+ commandNames.add(term.name);
20
+ return false;
21
+ }
22
+ if (term.type === "argument") return false;
23
+ if (term.type === "optional") {
24
+ collectLeadingCandidates(term.terms, optionNames, commandNames);
25
+ continue;
26
+ }
27
+ if (term.type === "multiple") {
28
+ collectLeadingCandidates(term.terms, optionNames, commandNames);
29
+ if (term.min === 0) continue;
30
+ return false;
31
+ }
32
+ if (term.type === "exclusive") {
33
+ let allAlternativesSkippable = true;
34
+ for (const exclusiveUsage of term.terms) {
35
+ const alternativeSkippable = collectLeadingCandidates(exclusiveUsage, optionNames, commandNames);
36
+ allAlternativesSkippable = allAlternativesSkippable && alternativeSkippable;
37
+ }
38
+ if (allAlternativesSkippable) continue;
39
+ return false;
40
+ }
41
+ }
42
+ return true;
43
+ }
44
+ function createUnexpectedInputErrorWithScopedSuggestions(baseError, invalidInput, parsers, customFormatter) {
45
+ const options = /* @__PURE__ */ new Set();
46
+ const commands = /* @__PURE__ */ new Set();
47
+ for (const parser of parsers) collectLeadingCandidates(parser.usage, options, commands);
48
+ const candidates = new Set([...options, ...commands]);
49
+ const suggestions = findSimilar(invalidInput, candidates, DEFAULT_FIND_SIMILAR_OPTIONS);
50
+ const suggestionMsg = customFormatter ? customFormatter(suggestions) : createSuggestionMessage(suggestions);
51
+ return suggestionMsg.length > 0 ? [
52
+ ...baseError,
53
+ text("\n\n"),
54
+ ...suggestionMsg
55
+ ] : baseError;
56
+ }
57
+ /**
7
58
  * Checks if the given token is an option name that requires a value
8
59
  * (i.e., has a metavar) within the given usage terms.
9
60
  * @param usage The usage terms to search through.
@@ -202,16 +253,6 @@ function getNoMatchError(options, noMatchContext) {
202
253
  return customNoMatch ? typeof customNoMatch === "function" ? customNoMatch(noMatchContext) : customNoMatch : generateNoMatchError(noMatchContext);
203
254
  }
204
255
  /**
205
- * Creates default error for parse() method when buffer is not empty.
206
- * Shared by or() and longestMatch().
207
- * @internal
208
- */
209
- function createUnexpectedInputError(token, usage, options) {
210
- const defaultMsg = message`Unexpected option or subcommand: ${optionName(token)}.`;
211
- if (options?.errors?.unexpectedInput != null) return typeof options.errors.unexpectedInput === "function" ? options.errors.unexpectedInput(token) : options.errors.unexpectedInput;
212
- return createErrorWithSuggestions(defaultMsg, token, usage, "both", options?.errors?.suggestions);
213
- }
214
- /**
215
256
  * @since 0.5.0
216
257
  */
217
258
  function or(...args) {
@@ -230,7 +271,12 @@ function or(...args) {
230
271
  const syncParsers = parsers;
231
272
  const getInitialError = (context) => ({
232
273
  consumed: 0,
233
- error: context.buffer.length < 1 ? getNoMatchError(options, noMatchContext) : createUnexpectedInputError(context.buffer[0], context.usage, options)
274
+ error: context.buffer.length < 1 ? getNoMatchError(options, noMatchContext) : (() => {
275
+ const token = context.buffer[0];
276
+ const defaultMsg = message`Unexpected option or subcommand: ${optionName(token)}.`;
277
+ if (options?.errors?.unexpectedInput != null) return typeof options.errors.unexpectedInput === "function" ? options.errors.unexpectedInput(token) : options.errors.unexpectedInput;
278
+ return createUnexpectedInputErrorWithScopedSuggestions(defaultMsg, token, parsers, options?.errors?.suggestions);
279
+ })()
234
280
  });
235
281
  const parseSync = (context) => {
236
282
  let error = getInitialError(context);
@@ -314,7 +360,9 @@ function or(...args) {
314
360
  },
315
361
  suggest: createExclusiveSuggest(parsers, isAsync),
316
362
  getDocFragments(state, _defaultValue) {
363
+ let brief;
317
364
  let description;
365
+ let footer;
318
366
  let fragments;
319
367
  if (state.kind === "unavailable" || state.state == null) fragments = parsers.flatMap((p) => p.getDocFragments({ kind: "unavailable" }, void 0).fragments);
320
368
  else {
@@ -324,7 +372,9 @@ function or(...args) {
324
372
  state: parserResult.next.state
325
373
  } : { kind: "unavailable" };
326
374
  const docFragments = parsers[index].getDocFragments(innerState, void 0);
375
+ brief = docFragments.brief;
327
376
  description = docFragments.description;
377
+ footer = docFragments.footer;
328
378
  fragments = docFragments.fragments;
329
379
  }
330
380
  const entries = fragments.filter((f) => f.type === "entry");
@@ -335,7 +385,9 @@ function or(...args) {
335
385
  else sections.push(fragment);
336
386
  }
337
387
  return {
388
+ brief,
338
389
  description,
390
+ footer,
339
391
  fragments: [...sections.map((s) => ({
340
392
  ...s,
341
393
  type: "section"
@@ -366,7 +418,12 @@ function longestMatch(...args) {
366
418
  const syncParsers = parsers;
367
419
  const getInitialError = (context) => ({
368
420
  consumed: 0,
369
- error: context.buffer.length < 1 ? getNoMatchError(options, noMatchContext) : createUnexpectedInputError(context.buffer[0], context.usage, options)
421
+ error: context.buffer.length < 1 ? getNoMatchError(options, noMatchContext) : (() => {
422
+ const token = context.buffer[0];
423
+ const defaultMsg = message`Unexpected option or subcommand: ${optionName(token)}.`;
424
+ if (options?.errors?.unexpectedInput != null) return typeof options.errors.unexpectedInput === "function" ? options.errors.unexpectedInput(token) : options.errors.unexpectedInput;
425
+ return createUnexpectedInputErrorWithScopedSuggestions(defaultMsg, token, parsers, options?.errors?.suggestions);
426
+ })()
370
427
  });
371
428
  const parseSync = (context) => {
372
429
  let bestMatch = null;
@@ -452,6 +509,7 @@ function longestMatch(...args) {
452
509
  },
453
510
  suggest: createExclusiveSuggest(parsers, isAsync),
454
511
  getDocFragments(state, _defaultValue) {
512
+ let brief;
455
513
  let description;
456
514
  let footer;
457
515
  let fragments;
@@ -463,12 +521,14 @@ function longestMatch(...args) {
463
521
  kind: "available",
464
522
  state: result.next.state
465
523
  });
524
+ brief = docResult.brief;
466
525
  description = docResult.description;
467
526
  footer = docResult.footer;
468
527
  fragments = docResult.fragments;
469
528
  } else fragments = parsers.flatMap((p) => p.getDocFragments({ kind: "unavailable" }).fragments);
470
529
  }
471
530
  return {
531
+ brief,
472
532
  description,
473
533
  fragments,
474
534
  footer
package/dist/doc.d.cts CHANGED
@@ -60,6 +60,12 @@ type DocFragment = {
60
60
  * a final document page.
61
61
  */
62
62
  interface DocFragments {
63
+ /**
64
+ * An optional brief that provides a short summary for the collection
65
+ * of fragments.
66
+ * @since 0.7.12
67
+ */
68
+ readonly brief?: Message;
63
69
  /**
64
70
  * An optional description that applies to the entire collection of fragments.
65
71
  */
package/dist/doc.d.ts CHANGED
@@ -60,6 +60,12 @@ type DocFragment = {
60
60
  * a final document page.
61
61
  */
62
62
  interface DocFragments {
63
+ /**
64
+ * An optional brief that provides a short summary for the collection
65
+ * of fragments.
66
+ * @since 0.7.12
67
+ */
68
+ readonly brief?: Message;
63
69
  /**
64
70
  * An optional description that applies to the entire collection of fragments.
65
71
  */
package/dist/facade.cjs CHANGED
@@ -455,9 +455,9 @@ function runParser(parser, programName, args, options = {}) {
455
455
  } else {
456
456
  const singularMatchExact = completionName === "singular" || completionName === "both" ? arg === "--completion" : false;
457
457
  const pluralMatchExact = completionName === "plural" || completionName === "both" ? arg === "--completions" : false;
458
- if ((singularMatchExact || pluralMatchExact) && i + 1 < args.length) {
459
- const shell = args[i + 1];
460
- const completionArgs = args.slice(i + 2);
458
+ if (singularMatchExact || pluralMatchExact) {
459
+ const shell = i + 1 < args.length ? args[i + 1] : "";
460
+ const completionArgs = i + 1 < args.length ? args.slice(i + 2) : [];
461
461
  return handleCompletion([shell, ...completionArgs], programName, parser, completionParsers.completionCommand, stdout, stderr, onCompletion, onError, availableShells, colors, maxWidth, completionMode, completionName);
462
462
  }
463
463
  }
@@ -556,11 +556,13 @@ function runParser(parser, programName, args, options = {}) {
556
556
  const displayHelp = (doc) => {
557
557
  if (doc != null) {
558
558
  const isMetaCommandHelp = (completionName === "singular" || completionName === "both" ? requestedCommand === "completion" : false) || (completionName === "plural" || completionName === "both" ? requestedCommand === "completions" : false) || requestedCommand === "help" || requestedCommand === "version";
559
+ const isSubcommandHelp = classified.commands.length > 0;
560
+ const shouldOverride = !isMetaCommandHelp && !isSubcommandHelp;
559
561
  const augmentedDoc = {
560
562
  ...doc,
561
- brief: !isMetaCommandHelp ? brief ?? doc.brief : doc.brief,
562
- description: !isMetaCommandHelp ? description ?? doc.description : doc.description,
563
- footer: !isMetaCommandHelp ? footer ?? doc.footer : doc.footer
563
+ brief: shouldOverride ? brief ?? doc.brief : doc.brief ?? brief,
564
+ description: shouldOverride ? description ?? doc.description : doc.description ?? description,
565
+ footer: shouldOverride ? footer ?? doc.footer : doc.footer ?? footer
564
566
  };
565
567
  stdout(require_doc.formatDocPage(programName, augmentedDoc, {
566
568
  colors,
package/dist/facade.js CHANGED
@@ -455,9 +455,9 @@ function runParser(parser, programName, args, options = {}) {
455
455
  } else {
456
456
  const singularMatchExact = completionName === "singular" || completionName === "both" ? arg === "--completion" : false;
457
457
  const pluralMatchExact = completionName === "plural" || completionName === "both" ? arg === "--completions" : false;
458
- if ((singularMatchExact || pluralMatchExact) && i + 1 < args.length) {
459
- const shell = args[i + 1];
460
- const completionArgs = args.slice(i + 2);
458
+ if (singularMatchExact || pluralMatchExact) {
459
+ const shell = i + 1 < args.length ? args[i + 1] : "";
460
+ const completionArgs = i + 1 < args.length ? args.slice(i + 2) : [];
461
461
  return handleCompletion([shell, ...completionArgs], programName, parser, completionParsers.completionCommand, stdout, stderr, onCompletion, onError, availableShells, colors, maxWidth, completionMode, completionName);
462
462
  }
463
463
  }
@@ -556,11 +556,13 @@ function runParser(parser, programName, args, options = {}) {
556
556
  const displayHelp = (doc) => {
557
557
  if (doc != null) {
558
558
  const isMetaCommandHelp = (completionName === "singular" || completionName === "both" ? requestedCommand === "completion" : false) || (completionName === "plural" || completionName === "both" ? requestedCommand === "completions" : false) || requestedCommand === "help" || requestedCommand === "version";
559
+ const isSubcommandHelp = classified.commands.length > 0;
560
+ const shouldOverride = !isMetaCommandHelp && !isSubcommandHelp;
559
561
  const augmentedDoc = {
560
562
  ...doc,
561
- brief: !isMetaCommandHelp ? brief ?? doc.brief : doc.brief,
562
- description: !isMetaCommandHelp ? description ?? doc.description : doc.description,
563
- footer: !isMetaCommandHelp ? footer ?? doc.footer : doc.footer
563
+ brief: shouldOverride ? brief ?? doc.brief : doc.brief ?? brief,
564
+ description: shouldOverride ? description ?? doc.description : doc.description ?? description,
565
+ footer: shouldOverride ? footer ?? doc.footer : doc.footer ?? footer
564
566
  };
565
567
  stdout(formatDocPage(programName, augmentedDoc, {
566
568
  colors,
package/dist/parser.cjs CHANGED
@@ -337,7 +337,7 @@ async function getDocPageAsyncImpl(parser, args) {
337
337
  * Shared by both sync and async implementations.
338
338
  */
339
339
  function buildDocPage(parser, context, args) {
340
- const { description, fragments, footer } = parser.getDocFragments({
340
+ const { brief, description, fragments, footer } = parser.getDocFragments({
341
341
  kind: "available",
342
342
  state: context.state
343
343
  }, void 0);
@@ -363,6 +363,7 @@ function buildDocPage(parser, context, args) {
363
363
  return {
364
364
  usage,
365
365
  sections,
366
+ ...brief != null && { brief },
366
367
  ...description != null && { description },
367
368
  ...footer != null && { footer }
368
369
  };
package/dist/parser.js CHANGED
@@ -337,7 +337,7 @@ async function getDocPageAsyncImpl(parser, args) {
337
337
  * Shared by both sync and async implementations.
338
338
  */
339
339
  function buildDocPage(parser, context, args) {
340
- const { description, fragments, footer } = parser.getDocFragments({
340
+ const { brief, description, fragments, footer } = parser.getDocFragments({
341
341
  kind: "available",
342
342
  state: context.state
343
343
  }, void 0);
@@ -363,6 +363,7 @@ function buildDocPage(parser, context, args) {
363
363
  return {
364
364
  usage,
365
365
  sections,
366
+ ...brief != null && { brief },
366
367
  ...description != null && { description },
367
368
  ...footer != null && { footer }
368
369
  };
@@ -867,6 +867,7 @@ function command(name, parser, options = {}) {
867
867
  const innerFragments = parser.getDocFragments(innerState, defaultValue);
868
868
  return {
869
869
  ...innerFragments,
870
+ brief: innerFragments.brief ?? options.brief,
870
871
  description: innerFragments.description ?? options.description,
871
872
  footer: innerFragments.footer ?? options.footer
872
873
  };
@@ -867,6 +867,7 @@ function command(name, parser, options = {}) {
867
867
  const innerFragments = parser.getDocFragments(innerState, defaultValue);
868
868
  return {
869
869
  ...innerFragments,
870
+ brief: innerFragments.brief ?? options.brief,
870
871
  description: innerFragments.description ?? options.description,
871
872
  footer: innerFragments.footer ?? options.footer
872
873
  };
@@ -232,5 +232,6 @@ function deduplicateSuggestions(suggestions) {
232
232
  //#endregion
233
233
  exports.DEFAULT_FIND_SIMILAR_OPTIONS = DEFAULT_FIND_SIMILAR_OPTIONS;
234
234
  exports.createErrorWithSuggestions = createErrorWithSuggestions;
235
+ exports.createSuggestionMessage = createSuggestionMessage;
235
236
  exports.deduplicateSuggestions = deduplicateSuggestions;
236
237
  exports.findSimilar = findSimilar;
@@ -230,4 +230,4 @@ function deduplicateSuggestions(suggestions) {
230
230
  }
231
231
 
232
232
  //#endregion
233
- export { DEFAULT_FIND_SIMILAR_OPTIONS, createErrorWithSuggestions, deduplicateSuggestions, findSimilar };
233
+ export { DEFAULT_FIND_SIMILAR_OPTIONS, createErrorWithSuggestions, createSuggestionMessage, deduplicateSuggestions, findSimilar };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optique/core",
3
- "version": "0.9.3",
3
+ "version": "0.9.5",
4
4
  "description": "Type-safe combinatorial command-line interface parser",
5
5
  "keywords": [
6
6
  "CLI",