@optique/core 1.2.7 → 1.2.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');
@@ -145,6 +146,7 @@ function constant(value) {
145
146
  return { fragments: [] };
146
147
  }
147
148
  };
149
+ require_passthrough.defineKnownCompletion(result, (state) => require_annotation_state.normalizeInjectedAnnotationState(state));
148
150
  Object.defineProperty(result, "placeholder", {
149
151
  value,
150
152
  configurable: true,
@@ -521,19 +523,17 @@ function option(...args) {
521
523
  const prefixes = optionNames$1.filter((name) => name.startsWith("--") || name.startsWith("/") || name.startsWith("-") && name.length > 2).map((name) => name.startsWith("/") ? `${name}:` : `${name}=`);
522
524
  for (const prefix of prefixes) {
523
525
  if (!context.buffer[0].startsWith(prefix)) continue;
524
- if (hasParsedOptionValue(context.state, valueParser)) {
525
- const optionName$1 = prefix.slice(0, -1);
526
- 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
- }
526
+ const optionName$1 = prefix.slice(0, -1);
527
+ if (hasParsedOptionValue(context.state, valueParser)) return {
528
+ success: false,
529
+ consumed: 1,
530
+ 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.`
531
+ };
532
532
  const rawInput = context.buffer[0].slice(prefix.length);
533
533
  if (valueParser == null) return {
534
534
  success: false,
535
535
  consumed: 1,
536
- 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}.`
536
+ 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}.`
537
537
  };
538
538
  return require_mode_dispatch.dispatchByMode(mode, () => {
539
539
  const parseResult = syncValueParser.parse(rawInput);
@@ -586,10 +586,15 @@ function option(...args) {
586
586
  }
587
587
  const invalidOption = context.buffer[0];
588
588
  if (options.errors?.noMatch) {
589
+ if (typeof options.errors.noMatch !== "function") return {
590
+ success: false,
591
+ consumed: 0,
592
+ error: options.errors.noMatch
593
+ };
589
594
  const candidates = /* @__PURE__ */ new Set();
590
595
  for (const name of require_usage.extractOptionNames(context.usage)) candidates.add(name);
591
596
  const suggestions = require_suggestion.findSimilar(invalidOption, candidates, require_suggestion.DEFAULT_FIND_SIMILAR_OPTIONS);
592
- const errorMessage = typeof options.errors.noMatch === "function" ? options.errors.noMatch(invalidOption, suggestions) : options.errors.noMatch;
597
+ const errorMessage = options.errors.noMatch(invalidOption, suggestions);
593
598
  return {
594
599
  success: false,
595
600
  consumed: 0,
@@ -874,10 +879,15 @@ function flag(...args) {
874
879
  }
875
880
  const invalidOption = context.buffer[0];
876
881
  if (options.errors?.noMatch) {
882
+ if (typeof options.errors.noMatch !== "function") return {
883
+ success: false,
884
+ consumed: 0,
885
+ error: options.errors.noMatch
886
+ };
877
887
  const candidates = /* @__PURE__ */ new Set();
878
888
  for (const name of require_usage.extractOptionNames(context.usage)) candidates.add(name);
879
889
  const suggestions = require_suggestion.findSimilar(invalidOption, candidates, require_suggestion.DEFAULT_FIND_SIMILAR_OPTIONS);
880
- const errorMessage = typeof options.errors.noMatch === "function" ? options.errors.noMatch(invalidOption, suggestions) : options.errors.noMatch;
890
+ const errorMessage = options.errors.noMatch(invalidOption, suggestions);
881
891
  return {
882
892
  success: false,
883
893
  consumed: 0,
@@ -1186,7 +1196,7 @@ function argument(valueParser, options = {}) {
1186
1196
  const syncValueParser = valueParser;
1187
1197
  const dependencyMetadata = require_dependency_metadata.extractDependencyMetadata(valueParser);
1188
1198
  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}`;
1189
- const optionPattern = /^--?[a-z0-9-]+$/i;
1199
+ const optionPattern = /^(?!--=)(--?[a-z0-9-]+)(?:=|$)/i;
1190
1200
  const term = {
1191
1201
  type: "argument",
1192
1202
  metavar: valueParser.metavar,
@@ -1213,14 +1223,15 @@ function argument(valueParser, options = {}) {
1213
1223
  };
1214
1224
  let i = 0;
1215
1225
  let optionsTerminated = context.optionsTerminated;
1216
- if (!optionsTerminated) {
1217
- if (context.buffer[i] === "--") {
1218
- optionsTerminated = true;
1219
- i++;
1220
- } else if (context.buffer[i].match(optionPattern)) return {
1226
+ if (!optionsTerminated) if (context.buffer[i] === "--") {
1227
+ optionsTerminated = true;
1228
+ i++;
1229
+ } else {
1230
+ const optionMatch = context.buffer[i].match(optionPattern);
1231
+ if (optionMatch != null) return {
1221
1232
  success: false,
1222
1233
  consumed: i,
1223
- error: require_message.message`Expected an argument, but got an option: ${require_message.optionName(context.buffer[i])}.`
1234
+ error: require_message.message`Expected an argument, but got an option: ${require_message.optionName(optionMatch[1])}.`
1224
1235
  };
1225
1236
  }
1226
1237
  if (context.buffer.length < i + 1) return {
@@ -1706,6 +1717,22 @@ function command(name, parser, options = {}) {
1706
1717
  configurable: true,
1707
1718
  enumerable: false
1708
1719
  });
1720
+ require_passthrough.definePassThroughPriority(result, [parser], (state, token) => {
1721
+ const active = normalizeCommandState(state);
1722
+ if (active == null) return void 0;
1723
+ const childState = active[0] === "matched" ? parser.initialState : active[1];
1724
+ return require_passthrough.getPassThroughPriority(parser, getCommandParseChildState(state, childState, parser), token);
1725
+ });
1726
+ require_passthrough.defineOptionMatch(result, (state, token) => {
1727
+ const active = normalizeCommandState(state);
1728
+ if (active == null) return void 0;
1729
+ const childState = active[0] === "matched" ? parser.initialState : active[1];
1730
+ const match = require_passthrough.getOptionMatch(parser, getCommandParseChildState(state, childState, parser), token);
1731
+ return match == null ? void 0 : {
1732
+ ...match,
1733
+ continuesCommand: true
1734
+ };
1735
+ });
1709
1736
  return require_modifiers.fluent(result);
1710
1737
  }
1711
1738
  /**
@@ -1779,6 +1806,8 @@ function passThrough(options = {}) {
1779
1806
  consumed: 0,
1780
1807
  error: require_message.message`No input to pass through.`
1781
1808
  };
1809
+ const failure = require_passthrough.getPassThroughFailure(context, this?.priority ?? -10);
1810
+ if (failure != null) return failure.failure;
1782
1811
  const token = context.buffer[0];
1783
1812
  if (format === "greedy") {
1784
1813
  const captured = [...context.buffer];
@@ -1890,6 +1919,11 @@ function passThrough(options = {}) {
1890
1919
  return `passThrough(${format})`;
1891
1920
  }
1892
1921
  };
1922
+ require_passthrough.definePassThroughPriority(result, [result], (_state, token) => {
1923
+ if (token == null || format === "greedy") return result.priority;
1924
+ const accepts = format === "equalsOnly" ? equalsOptionPattern.test(token) : format === "nextToken" && optionPattern.test(token);
1925
+ return accepts ? result.priority : void 0;
1926
+ });
1893
1927
  return require_modifiers.fluent(result);
1894
1928
  }
1895
1929
 
@@ -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";
@@ -145,6 +146,7 @@ function constant(value) {
145
146
  return { fragments: [] };
146
147
  }
147
148
  };
149
+ defineKnownCompletion(result, (state) => normalizeInjectedAnnotationState(state));
148
150
  Object.defineProperty(result, "placeholder", {
149
151
  value,
150
152
  configurable: true,
@@ -521,19 +523,17 @@ function option(...args) {
521
523
  const prefixes = optionNames$1.filter((name) => name.startsWith("--") || name.startsWith("/") || name.startsWith("-") && name.length > 2).map((name) => name.startsWith("/") ? `${name}:` : `${name}=`);
522
524
  for (const prefix of prefixes) {
523
525
  if (!context.buffer[0].startsWith(prefix)) continue;
524
- if (hasParsedOptionValue(context.state, valueParser)) {
525
- const optionName$1 = prefix.slice(0, -1);
526
- 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
- }
526
+ const optionName$1 = prefix.slice(0, -1);
527
+ if (hasParsedOptionValue(context.state, valueParser)) return {
528
+ success: false,
529
+ consumed: 1,
530
+ 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.`
531
+ };
532
532
  const rawInput = context.buffer[0].slice(prefix.length);
533
533
  if (valueParser == null) return {
534
534
  success: false,
535
535
  consumed: 1,
536
- 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}.`
536
+ 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}.`
537
537
  };
538
538
  return dispatchByMode(mode, () => {
539
539
  const parseResult = syncValueParser.parse(rawInput);
@@ -586,10 +586,15 @@ function option(...args) {
586
586
  }
587
587
  const invalidOption = context.buffer[0];
588
588
  if (options.errors?.noMatch) {
589
+ if (typeof options.errors.noMatch !== "function") return {
590
+ success: false,
591
+ consumed: 0,
592
+ error: options.errors.noMatch
593
+ };
589
594
  const candidates = /* @__PURE__ */ new Set();
590
595
  for (const name of extractOptionNames(context.usage)) candidates.add(name);
591
596
  const suggestions = findSimilar(invalidOption, candidates, DEFAULT_FIND_SIMILAR_OPTIONS);
592
- const errorMessage = typeof options.errors.noMatch === "function" ? options.errors.noMatch(invalidOption, suggestions) : options.errors.noMatch;
597
+ const errorMessage = options.errors.noMatch(invalidOption, suggestions);
593
598
  return {
594
599
  success: false,
595
600
  consumed: 0,
@@ -874,10 +879,15 @@ function flag(...args) {
874
879
  }
875
880
  const invalidOption = context.buffer[0];
876
881
  if (options.errors?.noMatch) {
882
+ if (typeof options.errors.noMatch !== "function") return {
883
+ success: false,
884
+ consumed: 0,
885
+ error: options.errors.noMatch
886
+ };
877
887
  const candidates = /* @__PURE__ */ new Set();
878
888
  for (const name of extractOptionNames(context.usage)) candidates.add(name);
879
889
  const suggestions = findSimilar(invalidOption, candidates, DEFAULT_FIND_SIMILAR_OPTIONS);
880
- const errorMessage = typeof options.errors.noMatch === "function" ? options.errors.noMatch(invalidOption, suggestions) : options.errors.noMatch;
890
+ const errorMessage = options.errors.noMatch(invalidOption, suggestions);
881
891
  return {
882
892
  success: false,
883
893
  consumed: 0,
@@ -1186,7 +1196,7 @@ function argument(valueParser, options = {}) {
1186
1196
  const syncValueParser = valueParser;
1187
1197
  const dependencyMetadata = extractDependencyMetadata(valueParser);
1188
1198
  const formatInvalidValueError = (error) => options.errors?.invalidValue ? typeof options.errors.invalidValue === "function" ? options.errors.invalidValue(error) : options.errors.invalidValue : message`${metavar(valueParser.metavar)}: ${error}`;
1189
- const optionPattern = /^--?[a-z0-9-]+$/i;
1199
+ const optionPattern = /^(?!--=)(--?[a-z0-9-]+)(?:=|$)/i;
1190
1200
  const term = {
1191
1201
  type: "argument",
1192
1202
  metavar: valueParser.metavar,
@@ -1213,14 +1223,15 @@ function argument(valueParser, options = {}) {
1213
1223
  };
1214
1224
  let i = 0;
1215
1225
  let optionsTerminated = context.optionsTerminated;
1216
- if (!optionsTerminated) {
1217
- if (context.buffer[i] === "--") {
1218
- optionsTerminated = true;
1219
- i++;
1220
- } else if (context.buffer[i].match(optionPattern)) return {
1226
+ if (!optionsTerminated) if (context.buffer[i] === "--") {
1227
+ optionsTerminated = true;
1228
+ i++;
1229
+ } else {
1230
+ const optionMatch = context.buffer[i].match(optionPattern);
1231
+ if (optionMatch != null) return {
1221
1232
  success: false,
1222
1233
  consumed: i,
1223
- error: message`Expected an argument, but got an option: ${optionName(context.buffer[i])}.`
1234
+ error: message`Expected an argument, but got an option: ${optionName(optionMatch[1])}.`
1224
1235
  };
1225
1236
  }
1226
1237
  if (context.buffer.length < i + 1) return {
@@ -1706,6 +1717,22 @@ function command(name, parser, options = {}) {
1706
1717
  configurable: true,
1707
1718
  enumerable: false
1708
1719
  });
1720
+ definePassThroughPriority(result, [parser], (state, token) => {
1721
+ const active = normalizeCommandState(state);
1722
+ if (active == null) return void 0;
1723
+ const childState = active[0] === "matched" ? parser.initialState : active[1];
1724
+ return getPassThroughPriority(parser, getCommandParseChildState(state, childState, parser), token);
1725
+ });
1726
+ defineOptionMatch(result, (state, token) => {
1727
+ const active = normalizeCommandState(state);
1728
+ if (active == null) return void 0;
1729
+ const childState = active[0] === "matched" ? parser.initialState : active[1];
1730
+ const match = getOptionMatch(parser, getCommandParseChildState(state, childState, parser), token);
1731
+ return match == null ? void 0 : {
1732
+ ...match,
1733
+ continuesCommand: true
1734
+ };
1735
+ });
1709
1736
  return fluent(result);
1710
1737
  }
1711
1738
  /**
@@ -1779,6 +1806,8 @@ function passThrough(options = {}) {
1779
1806
  consumed: 0,
1780
1807
  error: message`No input to pass through.`
1781
1808
  };
1809
+ const failure = getPassThroughFailure(context, this?.priority ?? -10);
1810
+ if (failure != null) return failure.failure;
1782
1811
  const token = context.buffer[0];
1783
1812
  if (format === "greedy") {
1784
1813
  const captured = [...context.buffer];
@@ -1890,6 +1919,11 @@ function passThrough(options = {}) {
1890
1919
  return `passThrough(${format})`;
1891
1920
  }
1892
1921
  };
1922
+ definePassThroughPriority(result, [result], (_state, token) => {
1923
+ if (token == null || format === "greedy") return result.priority;
1924
+ const accepts = format === "equalsOnly" ? equalsOptionPattern.test(token) : format === "nextToken" && optionPattern.test(token);
1925
+ return accepts ? result.priority : void 0;
1926
+ });
1893
1927
  return fluent(result);
1894
1928
  }
1895
1929
 
@@ -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.2.7",
3
+ "version": "1.2.9",
4
4
  "description": "Type-safe combinatorial command-line interface parser",
5
5
  "keywords": [
6
6
  "CLI",
@@ -221,7 +221,7 @@
221
221
  },
222
222
  "sideEffects": false,
223
223
  "devDependencies": {
224
- "@optique/env": "1.2.7",
224
+ "@optique/env": "1.2.9",
225
225
  "@types/node": "^24.0.0",
226
226
  "fast-check": "^4.7.0",
227
227
  "tsdown": "^0.13.0",