@optique/core 1.1.7 → 1.1.9

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.
@@ -6,6 +6,7 @@ const require_mode_dispatch = require('./internal/mode-dispatch.cjs');
6
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_passthrough = require('./internal/passthrough.cjs');
9
10
  const require_command_alias = require('./internal/command-alias.cjs');
10
11
  const require_execution_context = require('./execution-context.cjs');
11
12
  const require_phase2_seed = require('./phase2-seed.cjs');
@@ -144,6 +145,7 @@ function constant(value) {
144
145
  return { fragments: [] };
145
146
  }
146
147
  };
148
+ require_passthrough.defineKnownCompletion(result, (state) => require_annotation_state.normalizeInjectedAnnotationState(state));
147
149
  Object.defineProperty(result, "placeholder", {
148
150
  value,
149
151
  configurable: true,
@@ -520,19 +522,17 @@ function option(...args) {
520
522
  const prefixes = optionNames$1.filter((name) => name.startsWith("--") || name.startsWith("/") || name.startsWith("-") && name.length > 2).map((name) => name.startsWith("/") ? `${name}:` : `${name}=`);
521
523
  for (const prefix of prefixes) {
522
524
  if (!context.buffer[0].startsWith(prefix)) continue;
523
- if (hasParsedOptionValue(context.state, valueParser)) {
524
- const optionName$1 = prefix.slice(0, -1);
525
- return {
526
- success: false,
527
- consumed: 1,
528
- error: options.errors?.duplicate ? typeof options.errors.duplicate === "function" ? options.errors.duplicate(prefix) : options.errors.duplicate : require_message.message`${require_message.optionName(optionName$1)} cannot be used multiple times.`
529
- };
530
- }
525
+ const optionName$1 = prefix.slice(0, -1);
526
+ if (hasParsedOptionValue(context.state, valueParser)) return {
527
+ success: false,
528
+ consumed: 1,
529
+ error: options.errors?.duplicate ? typeof options.errors.duplicate === "function" ? options.errors.duplicate(prefix) : options.errors.duplicate : require_message.message`${require_message.optionName(optionName$1)} cannot be used multiple times.`
530
+ };
531
531
  const rawInput = context.buffer[0].slice(prefix.length);
532
532
  if (valueParser == null) return {
533
533
  success: false,
534
534
  consumed: 1,
535
- error: options.errors?.unexpectedValue ? typeof options.errors.unexpectedValue === "function" ? options.errors.unexpectedValue(rawInput) : options.errors.unexpectedValue : require_message.message`Option ${require_message.optionName(prefix)} is a Boolean flag, but got a value: ${rawInput}.`
535
+ error: options.errors?.unexpectedValue ? typeof options.errors.unexpectedValue === "function" ? options.errors.unexpectedValue(rawInput) : options.errors.unexpectedValue : require_message.message`Option ${require_message.optionName(optionName$1)} is a Boolean flag, but got a value: ${rawInput}.`
536
536
  };
537
537
  return require_mode_dispatch.dispatchByMode(mode, () => {
538
538
  const parseResult = syncValueParser.parse(rawInput);
@@ -585,10 +585,15 @@ function option(...args) {
585
585
  }
586
586
  const invalidOption = context.buffer[0];
587
587
  if (options.errors?.noMatch) {
588
+ if (typeof options.errors.noMatch !== "function") return {
589
+ success: false,
590
+ consumed: 0,
591
+ error: options.errors.noMatch
592
+ };
588
593
  const candidates = /* @__PURE__ */ new Set();
589
594
  for (const name of require_usage.extractOptionNames(context.usage)) candidates.add(name);
590
595
  const suggestions = require_suggestion.findSimilar(invalidOption, candidates, require_suggestion.DEFAULT_FIND_SIMILAR_OPTIONS);
591
- const errorMessage = typeof options.errors.noMatch === "function" ? options.errors.noMatch(invalidOption, suggestions) : options.errors.noMatch;
596
+ const errorMessage = options.errors.noMatch(invalidOption, suggestions);
592
597
  return {
593
598
  success: false,
594
599
  consumed: 0,
@@ -873,10 +878,15 @@ function flag(...args) {
873
878
  }
874
879
  const invalidOption = context.buffer[0];
875
880
  if (options.errors?.noMatch) {
881
+ if (typeof options.errors.noMatch !== "function") return {
882
+ success: false,
883
+ consumed: 0,
884
+ error: options.errors.noMatch
885
+ };
876
886
  const candidates = /* @__PURE__ */ new Set();
877
887
  for (const name of require_usage.extractOptionNames(context.usage)) candidates.add(name);
878
888
  const suggestions = require_suggestion.findSimilar(invalidOption, candidates, require_suggestion.DEFAULT_FIND_SIMILAR_OPTIONS);
879
- const errorMessage = typeof options.errors.noMatch === "function" ? options.errors.noMatch(invalidOption, suggestions) : options.errors.noMatch;
889
+ const errorMessage = options.errors.noMatch(invalidOption, suggestions);
880
890
  return {
881
891
  success: false,
882
892
  consumed: 0,
@@ -1185,7 +1195,7 @@ function argument(valueParser, options = {}) {
1185
1195
  const syncValueParser = valueParser;
1186
1196
  const dependencyMetadata = require_dependency_metadata.extractDependencyMetadata(valueParser);
1187
1197
  const formatInvalidValueError = (error) => options.errors?.invalidValue ? typeof options.errors.invalidValue === "function" ? options.errors.invalidValue(error) : options.errors.invalidValue : require_message.message`${require_message.metavar(valueParser.metavar)}: ${error}`;
1188
- const optionPattern = /^--?[a-z0-9-]+$/i;
1198
+ const optionPattern = /^(?!--=)(--?[a-z0-9-]+)(?:=|$)/i;
1189
1199
  const term = {
1190
1200
  type: "argument",
1191
1201
  metavar: valueParser.metavar,
@@ -1212,14 +1222,15 @@ function argument(valueParser, options = {}) {
1212
1222
  };
1213
1223
  let i = 0;
1214
1224
  let optionsTerminated = context.optionsTerminated;
1215
- if (!optionsTerminated) {
1216
- if (context.buffer[i] === "--") {
1217
- optionsTerminated = true;
1218
- i++;
1219
- } else if (context.buffer[i].match(optionPattern)) return {
1225
+ if (!optionsTerminated) if (context.buffer[i] === "--") {
1226
+ optionsTerminated = true;
1227
+ i++;
1228
+ } else {
1229
+ const optionMatch = context.buffer[i].match(optionPattern);
1230
+ if (optionMatch != null) return {
1220
1231
  success: false,
1221
1232
  consumed: i,
1222
- error: require_message.message`Expected an argument, but got an option: ${require_message.optionName(context.buffer[i])}.`
1233
+ error: require_message.message`Expected an argument, but got an option: ${require_message.optionName(optionMatch[1])}.`
1223
1234
  };
1224
1235
  }
1225
1236
  if (context.buffer.length < i + 1) return {
@@ -1705,6 +1716,22 @@ function command(name, parser, options = {}) {
1705
1716
  configurable: true,
1706
1717
  enumerable: false
1707
1718
  });
1719
+ require_passthrough.definePassThroughPriority(result, [parser], (state, token) => {
1720
+ const active = normalizeCommandState(state);
1721
+ if (active == null) return void 0;
1722
+ const childState = active[0] === "matched" ? parser.initialState : active[1];
1723
+ return require_passthrough.getPassThroughPriority(parser, getCommandParseChildState(state, childState, parser), token);
1724
+ });
1725
+ require_passthrough.defineOptionMatch(result, (state, token) => {
1726
+ const active = normalizeCommandState(state);
1727
+ if (active == null) return void 0;
1728
+ const childState = active[0] === "matched" ? parser.initialState : active[1];
1729
+ const match = require_passthrough.getOptionMatch(parser, getCommandParseChildState(state, childState, parser), token);
1730
+ return match == null ? void 0 : {
1731
+ ...match,
1732
+ continuesCommand: true
1733
+ };
1734
+ });
1708
1735
  return result;
1709
1736
  }
1710
1737
  /**
@@ -1757,7 +1784,7 @@ function passThrough(options = {}) {
1757
1784
  const format = options.format ?? "equalsOnly";
1758
1785
  const optionPattern = /^-[a-z0-9-]|^--[a-z0-9-]+/i;
1759
1786
  const equalsOptionPattern = /^--[a-z0-9-]+=/i;
1760
- return {
1787
+ const result = {
1761
1788
  $valueType: [],
1762
1789
  $stateType: [],
1763
1790
  mode: "sync",
@@ -1778,6 +1805,8 @@ function passThrough(options = {}) {
1778
1805
  consumed: 0,
1779
1806
  error: require_message.message`No input to pass through.`
1780
1807
  };
1808
+ const failure = require_passthrough.getPassThroughFailure(context, this?.priority ?? -10);
1809
+ if (failure != null) return failure.failure;
1781
1810
  const token = context.buffer[0];
1782
1811
  if (format === "greedy") {
1783
1812
  const captured = [...context.buffer];
@@ -1889,6 +1918,12 @@ function passThrough(options = {}) {
1889
1918
  return `passThrough(${format})`;
1890
1919
  }
1891
1920
  };
1921
+ require_passthrough.definePassThroughPriority(result, [result], (_state, token) => {
1922
+ if (token == null || format === "greedy") return result.priority;
1923
+ const accepts = format === "equalsOnly" ? equalsOptionPattern.test(token) : format === "nextToken" && optionPattern.test(token);
1924
+ return accepts ? result.priority : void 0;
1925
+ });
1926
+ return result;
1892
1927
  }
1893
1928
 
1894
1929
  //#endregion
@@ -6,6 +6,7 @@ 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 { defineKnownCompletion, defineOptionMatch, definePassThroughPriority, getOptionMatch, getPassThroughFailure, getPassThroughPriority } from "./internal/passthrough.js";
9
10
  import { hiddenCommandAliasesKey } from "./internal/command-alias.js";
10
11
  import { mergeChildExec, withChildContext, withChildExecPath } from "./execution-context.js";
11
12
  import { completeOrExtractPhase2Seed, extractPhase2SeedKey } from "./phase2-seed.js";
@@ -144,6 +145,7 @@ function constant(value) {
144
145
  return { fragments: [] };
145
146
  }
146
147
  };
148
+ defineKnownCompletion(result, (state) => normalizeInjectedAnnotationState(state));
147
149
  Object.defineProperty(result, "placeholder", {
148
150
  value,
149
151
  configurable: true,
@@ -520,19 +522,17 @@ function option(...args) {
520
522
  const prefixes = optionNames$1.filter((name) => name.startsWith("--") || name.startsWith("/") || name.startsWith("-") && name.length > 2).map((name) => name.startsWith("/") ? `${name}:` : `${name}=`);
521
523
  for (const prefix of prefixes) {
522
524
  if (!context.buffer[0].startsWith(prefix)) continue;
523
- if (hasParsedOptionValue(context.state, valueParser)) {
524
- const optionName$1 = prefix.slice(0, -1);
525
- return {
526
- success: false,
527
- consumed: 1,
528
- error: options.errors?.duplicate ? typeof options.errors.duplicate === "function" ? options.errors.duplicate(prefix) : options.errors.duplicate : message`${optionName(optionName$1)} cannot be used multiple times.`
529
- };
530
- }
525
+ const optionName$1 = prefix.slice(0, -1);
526
+ if (hasParsedOptionValue(context.state, valueParser)) return {
527
+ success: false,
528
+ consumed: 1,
529
+ error: options.errors?.duplicate ? typeof options.errors.duplicate === "function" ? options.errors.duplicate(prefix) : options.errors.duplicate : message`${optionName(optionName$1)} cannot be used multiple times.`
530
+ };
531
531
  const rawInput = context.buffer[0].slice(prefix.length);
532
532
  if (valueParser == null) return {
533
533
  success: false,
534
534
  consumed: 1,
535
- error: options.errors?.unexpectedValue ? typeof options.errors.unexpectedValue === "function" ? options.errors.unexpectedValue(rawInput) : options.errors.unexpectedValue : message`Option ${optionName(prefix)} is a Boolean flag, but got a value: ${rawInput}.`
535
+ error: options.errors?.unexpectedValue ? typeof options.errors.unexpectedValue === "function" ? options.errors.unexpectedValue(rawInput) : options.errors.unexpectedValue : message`Option ${optionName(optionName$1)} is a Boolean flag, but got a value: ${rawInput}.`
536
536
  };
537
537
  return dispatchByMode(mode, () => {
538
538
  const parseResult = syncValueParser.parse(rawInput);
@@ -585,10 +585,15 @@ function option(...args) {
585
585
  }
586
586
  const invalidOption = context.buffer[0];
587
587
  if (options.errors?.noMatch) {
588
+ if (typeof options.errors.noMatch !== "function") return {
589
+ success: false,
590
+ consumed: 0,
591
+ error: options.errors.noMatch
592
+ };
588
593
  const candidates = /* @__PURE__ */ new Set();
589
594
  for (const name of extractOptionNames(context.usage)) candidates.add(name);
590
595
  const suggestions = findSimilar(invalidOption, candidates, DEFAULT_FIND_SIMILAR_OPTIONS);
591
- const errorMessage = typeof options.errors.noMatch === "function" ? options.errors.noMatch(invalidOption, suggestions) : options.errors.noMatch;
596
+ const errorMessage = options.errors.noMatch(invalidOption, suggestions);
592
597
  return {
593
598
  success: false,
594
599
  consumed: 0,
@@ -873,10 +878,15 @@ function flag(...args) {
873
878
  }
874
879
  const invalidOption = context.buffer[0];
875
880
  if (options.errors?.noMatch) {
881
+ if (typeof options.errors.noMatch !== "function") return {
882
+ success: false,
883
+ consumed: 0,
884
+ error: options.errors.noMatch
885
+ };
876
886
  const candidates = /* @__PURE__ */ new Set();
877
887
  for (const name of extractOptionNames(context.usage)) candidates.add(name);
878
888
  const suggestions = findSimilar(invalidOption, candidates, DEFAULT_FIND_SIMILAR_OPTIONS);
879
- const errorMessage = typeof options.errors.noMatch === "function" ? options.errors.noMatch(invalidOption, suggestions) : options.errors.noMatch;
889
+ const errorMessage = options.errors.noMatch(invalidOption, suggestions);
880
890
  return {
881
891
  success: false,
882
892
  consumed: 0,
@@ -1185,7 +1195,7 @@ function argument(valueParser, options = {}) {
1185
1195
  const syncValueParser = valueParser;
1186
1196
  const dependencyMetadata = extractDependencyMetadata(valueParser);
1187
1197
  const formatInvalidValueError = (error) => options.errors?.invalidValue ? typeof options.errors.invalidValue === "function" ? options.errors.invalidValue(error) : options.errors.invalidValue : message`${metavar(valueParser.metavar)}: ${error}`;
1188
- const optionPattern = /^--?[a-z0-9-]+$/i;
1198
+ const optionPattern = /^(?!--=)(--?[a-z0-9-]+)(?:=|$)/i;
1189
1199
  const term = {
1190
1200
  type: "argument",
1191
1201
  metavar: valueParser.metavar,
@@ -1212,14 +1222,15 @@ function argument(valueParser, options = {}) {
1212
1222
  };
1213
1223
  let i = 0;
1214
1224
  let optionsTerminated = context.optionsTerminated;
1215
- if (!optionsTerminated) {
1216
- if (context.buffer[i] === "--") {
1217
- optionsTerminated = true;
1218
- i++;
1219
- } else if (context.buffer[i].match(optionPattern)) return {
1225
+ if (!optionsTerminated) if (context.buffer[i] === "--") {
1226
+ optionsTerminated = true;
1227
+ i++;
1228
+ } else {
1229
+ const optionMatch = context.buffer[i].match(optionPattern);
1230
+ if (optionMatch != null) return {
1220
1231
  success: false,
1221
1232
  consumed: i,
1222
- error: message`Expected an argument, but got an option: ${optionName(context.buffer[i])}.`
1233
+ error: message`Expected an argument, but got an option: ${optionName(optionMatch[1])}.`
1223
1234
  };
1224
1235
  }
1225
1236
  if (context.buffer.length < i + 1) return {
@@ -1705,6 +1716,22 @@ function command(name, parser, options = {}) {
1705
1716
  configurable: true,
1706
1717
  enumerable: false
1707
1718
  });
1719
+ definePassThroughPriority(result, [parser], (state, token) => {
1720
+ const active = normalizeCommandState(state);
1721
+ if (active == null) return void 0;
1722
+ const childState = active[0] === "matched" ? parser.initialState : active[1];
1723
+ return getPassThroughPriority(parser, getCommandParseChildState(state, childState, parser), token);
1724
+ });
1725
+ defineOptionMatch(result, (state, token) => {
1726
+ const active = normalizeCommandState(state);
1727
+ if (active == null) return void 0;
1728
+ const childState = active[0] === "matched" ? parser.initialState : active[1];
1729
+ const match = getOptionMatch(parser, getCommandParseChildState(state, childState, parser), token);
1730
+ return match == null ? void 0 : {
1731
+ ...match,
1732
+ continuesCommand: true
1733
+ };
1734
+ });
1708
1735
  return result;
1709
1736
  }
1710
1737
  /**
@@ -1757,7 +1784,7 @@ function passThrough(options = {}) {
1757
1784
  const format = options.format ?? "equalsOnly";
1758
1785
  const optionPattern = /^-[a-z0-9-]|^--[a-z0-9-]+/i;
1759
1786
  const equalsOptionPattern = /^--[a-z0-9-]+=/i;
1760
- return {
1787
+ const result = {
1761
1788
  $valueType: [],
1762
1789
  $stateType: [],
1763
1790
  mode: "sync",
@@ -1778,6 +1805,8 @@ function passThrough(options = {}) {
1778
1805
  consumed: 0,
1779
1806
  error: message`No input to pass through.`
1780
1807
  };
1808
+ const failure = getPassThroughFailure(context, this?.priority ?? -10);
1809
+ if (failure != null) return failure.failure;
1781
1810
  const token = context.buffer[0];
1782
1811
  if (format === "greedy") {
1783
1812
  const captured = [...context.buffer];
@@ -1889,6 +1918,12 @@ function passThrough(options = {}) {
1889
1918
  return `passThrough(${format})`;
1890
1919
  }
1891
1920
  };
1921
+ definePassThroughPriority(result, [result], (_state, token) => {
1922
+ if (token == null || format === "greedy") return result.priority;
1923
+ const accepts = format === "equalsOnly" ? equalsOptionPattern.test(token) : format === "nextToken" && optionPattern.test(token);
1924
+ return accepts ? result.priority : void 0;
1925
+ });
1926
+ return result;
1892
1927
  }
1893
1928
 
1894
1929
  //#endregion
@@ -86,8 +86,10 @@ function findSimilar(input, candidates, options = {}) {
86
86
  const normalizedCandidate = caseSensitive ? candidate : candidate.toLowerCase();
87
87
  if (seen.has(candidate)) continue;
88
88
  seen.add(candidate);
89
+ if (normalizedInput === normalizedCandidate) return [candidate];
90
+ const minimumDistance = Math.abs(normalizedInput.length - normalizedCandidate.length);
91
+ if (minimumDistance > maxDistance || minimumDistance / input.length > maxDistanceRatio) continue;
89
92
  const distance = levenshteinDistance(normalizedInput, normalizedCandidate);
90
- if (distance === 0) return [candidate];
91
93
  const distanceRatio = distance / input.length;
92
94
  if (distance <= maxDistance && distanceRatio <= maxDistanceRatio) matches.push({
93
95
  candidate,
@@ -86,8 +86,10 @@ function findSimilar(input, candidates, options = {}) {
86
86
  const normalizedCandidate = caseSensitive ? candidate : candidate.toLowerCase();
87
87
  if (seen.has(candidate)) continue;
88
88
  seen.add(candidate);
89
+ if (normalizedInput === normalizedCandidate) return [candidate];
90
+ const minimumDistance = Math.abs(normalizedInput.length - normalizedCandidate.length);
91
+ if (minimumDistance > maxDistance || minimumDistance / input.length > maxDistanceRatio) continue;
89
92
  const distance = levenshteinDistance(normalizedInput, normalizedCandidate);
90
- if (distance === 0) return [candidate];
91
93
  const distanceRatio = distance / input.length;
92
94
  if (distance <= maxDistance && distanceRatio <= maxDistanceRatio) matches.push({
93
95
  candidate,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optique/core",
3
- "version": "1.1.7",
3
+ "version": "1.1.9",
4
4
  "description": "Type-safe combinatorial command-line interface parser",
5
5
  "keywords": [
6
6
  "CLI",
@@ -196,7 +196,7 @@
196
196
  },
197
197
  "sideEffects": false,
198
198
  "devDependencies": {
199
- "@optique/env": "1.1.7",
199
+ "@optique/env": "1.1.9",
200
200
  "@types/node": "^24.0.0",
201
201
  "fast-check": "^4.7.0",
202
202
  "tsdown": "^0.13.0",