@optique/core 1.3.0 → 1.3.2

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');
@@ -151,6 +152,7 @@ function constant(value) {
151
152
  return { fragments: [] };
152
153
  }
153
154
  };
155
+ require_passthrough.defineKnownCompletion(result, (state) => require_annotation_state.normalizeInjectedAnnotationState(state));
154
156
  Object.defineProperty(result, "placeholder", {
155
157
  value,
156
158
  configurable: true,
@@ -527,19 +529,17 @@ function option(...args) {
527
529
  const prefixes = optionNames$1.filter((name) => name.startsWith("--") || name.startsWith("/") || name.startsWith("-") && name.length > 2).map((name) => name.startsWith("/") ? `${name}:` : `${name}=`);
528
530
  for (const prefix of prefixes) {
529
531
  if (!context.buffer[0].startsWith(prefix)) continue;
530
- if (hasParsedOptionValue(context.state, valueParser)) {
531
- const optionName$1 = prefix.slice(0, -1);
532
- return {
533
- success: false,
534
- consumed: 1,
535
- 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.`
536
- };
537
- }
532
+ const optionName$1 = prefix.slice(0, -1);
533
+ if (hasParsedOptionValue(context.state, valueParser)) return {
534
+ success: false,
535
+ consumed: 1,
536
+ 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.`
537
+ };
538
538
  const rawInput = context.buffer[0].slice(prefix.length);
539
539
  if (valueParser == null) return {
540
540
  success: false,
541
541
  consumed: 1,
542
- 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}.`
542
+ 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}.`
543
543
  };
544
544
  return require_mode_dispatch.dispatchByMode(mode, () => {
545
545
  const parseResult = syncValueParser.parse(rawInput);
@@ -592,10 +592,15 @@ function option(...args) {
592
592
  }
593
593
  const invalidOption = context.buffer[0];
594
594
  if (options.errors?.noMatch) {
595
+ if (typeof options.errors.noMatch !== "function") return {
596
+ success: false,
597
+ consumed: 0,
598
+ error: options.errors.noMatch
599
+ };
595
600
  const candidates = /* @__PURE__ */ new Set();
596
601
  for (const name of require_usage.extractOptionNames(context.usage)) candidates.add(name);
597
602
  const suggestions = require_suggestion.findSimilar(invalidOption, candidates, require_suggestion.DEFAULT_FIND_SIMILAR_OPTIONS);
598
- const errorMessage = typeof options.errors.noMatch === "function" ? options.errors.noMatch(invalidOption, suggestions) : options.errors.noMatch;
603
+ const errorMessage = options.errors.noMatch(invalidOption, suggestions);
599
604
  return {
600
605
  success: false,
601
606
  consumed: 0,
@@ -880,10 +885,15 @@ function flag(...args) {
880
885
  }
881
886
  const invalidOption = context.buffer[0];
882
887
  if (options.errors?.noMatch) {
888
+ if (typeof options.errors.noMatch !== "function") return {
889
+ success: false,
890
+ consumed: 0,
891
+ error: options.errors.noMatch
892
+ };
883
893
  const candidates = /* @__PURE__ */ new Set();
884
894
  for (const name of require_usage.extractOptionNames(context.usage)) candidates.add(name);
885
895
  const suggestions = require_suggestion.findSimilar(invalidOption, candidates, require_suggestion.DEFAULT_FIND_SIMILAR_OPTIONS);
886
- const errorMessage = typeof options.errors.noMatch === "function" ? options.errors.noMatch(invalidOption, suggestions) : options.errors.noMatch;
896
+ const errorMessage = options.errors.noMatch(invalidOption, suggestions);
887
897
  return {
888
898
  success: false,
889
899
  consumed: 0,
@@ -1192,7 +1202,7 @@ function argument(valueParser, options = {}) {
1192
1202
  const syncValueParser = valueParser;
1193
1203
  const dependencyMetadata = require_dependency_metadata.extractDependencyMetadata(valueParser);
1194
1204
  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}`;
1195
- const optionPattern = /^--?[a-z0-9-]+$/i;
1205
+ const optionPattern = /^(?!--=)(--?[a-z0-9-]+)(?:=|$)/i;
1196
1206
  const term = {
1197
1207
  type: "argument",
1198
1208
  metavar: valueParser.metavar,
@@ -1219,14 +1229,15 @@ function argument(valueParser, options = {}) {
1219
1229
  };
1220
1230
  let i = 0;
1221
1231
  let optionsTerminated = context.optionsTerminated;
1222
- if (!optionsTerminated) {
1223
- if (context.buffer[i] === "--") {
1224
- optionsTerminated = true;
1225
- i++;
1226
- } else if (context.buffer[i].match(optionPattern)) return {
1232
+ if (!optionsTerminated) if (context.buffer[i] === "--") {
1233
+ optionsTerminated = true;
1234
+ i++;
1235
+ } else {
1236
+ const optionMatch = context.buffer[i].match(optionPattern);
1237
+ if (optionMatch != null) return {
1227
1238
  success: false,
1228
1239
  consumed: i,
1229
- error: require_message.message`Expected an argument, but got an option: ${require_message.optionName(context.buffer[i])}.`
1240
+ error: require_message.message`Expected an argument, but got an option: ${require_message.optionName(optionMatch[1])}.`
1230
1241
  };
1231
1242
  }
1232
1243
  if (context.buffer.length < i + 1) return {
@@ -1736,6 +1747,22 @@ function command(name, parser, options = {}) {
1736
1747
  configurable: true,
1737
1748
  enumerable: false
1738
1749
  });
1750
+ require_passthrough.definePassThroughPriority(result, [parser], (state, token) => {
1751
+ const active = normalizeCommandState(state);
1752
+ if (active == null) return void 0;
1753
+ const childState = active[0] === "matched" ? parser.initialState : active[1];
1754
+ return require_passthrough.getPassThroughPriority(parser, getCommandParseChildState(state, childState, parser), token);
1755
+ });
1756
+ require_passthrough.defineOptionMatch(result, (state, token) => {
1757
+ const active = normalizeCommandState(state);
1758
+ if (active == null) return void 0;
1759
+ const childState = active[0] === "matched" ? parser.initialState : active[1];
1760
+ const match = require_passthrough.getOptionMatch(parser, getCommandParseChildState(state, childState, parser), token);
1761
+ return match == null ? void 0 : {
1762
+ ...match,
1763
+ continuesCommand: true
1764
+ };
1765
+ });
1739
1766
  return require_modifiers.fluent(result);
1740
1767
  }
1741
1768
  /**
@@ -1809,6 +1836,8 @@ function passThrough(options = {}) {
1809
1836
  consumed: 0,
1810
1837
  error: require_message.message`No input to pass through.`
1811
1838
  };
1839
+ const failure = require_passthrough.getPassThroughFailure(context, this?.priority ?? -10);
1840
+ if (failure != null) return failure.failure;
1812
1841
  const token = context.buffer[0];
1813
1842
  if (format === "greedy") {
1814
1843
  const captured = [...context.buffer];
@@ -1920,6 +1949,11 @@ function passThrough(options = {}) {
1920
1949
  return `passThrough(${format})`;
1921
1950
  }
1922
1951
  };
1952
+ require_passthrough.definePassThroughPriority(result, [result], (_state, token) => {
1953
+ if (token == null || format === "greedy") return result.priority;
1954
+ const accepts = format === "equalsOnly" ? equalsOptionPattern.test(token) : format === "nextToken" && optionPattern.test(token);
1955
+ return accepts ? result.priority : void 0;
1956
+ });
1923
1957
  return require_modifiers.fluent(result);
1924
1958
  }
1925
1959
 
@@ -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 { effectfulSchedulingNodesKey, exclusiveSourceScopeKey, includeSourceFailureChain, recordDerivedRawInput, replayDerivedParser, replayDerivedParserAsync, sourceCollectionExpansionKey } 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";
@@ -151,6 +152,7 @@ function constant(value) {
151
152
  return { fragments: [] };
152
153
  }
153
154
  };
155
+ defineKnownCompletion(result, (state) => normalizeInjectedAnnotationState(state));
154
156
  Object.defineProperty(result, "placeholder", {
155
157
  value,
156
158
  configurable: true,
@@ -527,19 +529,17 @@ function option(...args) {
527
529
  const prefixes = optionNames$1.filter((name) => name.startsWith("--") || name.startsWith("/") || name.startsWith("-") && name.length > 2).map((name) => name.startsWith("/") ? `${name}:` : `${name}=`);
528
530
  for (const prefix of prefixes) {
529
531
  if (!context.buffer[0].startsWith(prefix)) continue;
530
- if (hasParsedOptionValue(context.state, valueParser)) {
531
- const optionName$1 = prefix.slice(0, -1);
532
- return {
533
- success: false,
534
- consumed: 1,
535
- 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.`
536
- };
537
- }
532
+ const optionName$1 = prefix.slice(0, -1);
533
+ if (hasParsedOptionValue(context.state, valueParser)) return {
534
+ success: false,
535
+ consumed: 1,
536
+ 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.`
537
+ };
538
538
  const rawInput = context.buffer[0].slice(prefix.length);
539
539
  if (valueParser == null) return {
540
540
  success: false,
541
541
  consumed: 1,
542
- 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}.`
542
+ 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}.`
543
543
  };
544
544
  return dispatchByMode(mode, () => {
545
545
  const parseResult = syncValueParser.parse(rawInput);
@@ -592,10 +592,15 @@ function option(...args) {
592
592
  }
593
593
  const invalidOption = context.buffer[0];
594
594
  if (options.errors?.noMatch) {
595
+ if (typeof options.errors.noMatch !== "function") return {
596
+ success: false,
597
+ consumed: 0,
598
+ error: options.errors.noMatch
599
+ };
595
600
  const candidates = /* @__PURE__ */ new Set();
596
601
  for (const name of extractOptionNames(context.usage)) candidates.add(name);
597
602
  const suggestions = findSimilar(invalidOption, candidates, DEFAULT_FIND_SIMILAR_OPTIONS);
598
- const errorMessage = typeof options.errors.noMatch === "function" ? options.errors.noMatch(invalidOption, suggestions) : options.errors.noMatch;
603
+ const errorMessage = options.errors.noMatch(invalidOption, suggestions);
599
604
  return {
600
605
  success: false,
601
606
  consumed: 0,
@@ -880,10 +885,15 @@ function flag(...args) {
880
885
  }
881
886
  const invalidOption = context.buffer[0];
882
887
  if (options.errors?.noMatch) {
888
+ if (typeof options.errors.noMatch !== "function") return {
889
+ success: false,
890
+ consumed: 0,
891
+ error: options.errors.noMatch
892
+ };
883
893
  const candidates = /* @__PURE__ */ new Set();
884
894
  for (const name of extractOptionNames(context.usage)) candidates.add(name);
885
895
  const suggestions = findSimilar(invalidOption, candidates, DEFAULT_FIND_SIMILAR_OPTIONS);
886
- const errorMessage = typeof options.errors.noMatch === "function" ? options.errors.noMatch(invalidOption, suggestions) : options.errors.noMatch;
896
+ const errorMessage = options.errors.noMatch(invalidOption, suggestions);
887
897
  return {
888
898
  success: false,
889
899
  consumed: 0,
@@ -1192,7 +1202,7 @@ function argument(valueParser, options = {}) {
1192
1202
  const syncValueParser = valueParser;
1193
1203
  const dependencyMetadata = extractDependencyMetadata(valueParser);
1194
1204
  const formatInvalidValueError = (error) => options.errors?.invalidValue ? typeof options.errors.invalidValue === "function" ? options.errors.invalidValue(error) : options.errors.invalidValue : message`${metavar(valueParser.metavar)}: ${error}`;
1195
- const optionPattern = /^--?[a-z0-9-]+$/i;
1205
+ const optionPattern = /^(?!--=)(--?[a-z0-9-]+)(?:=|$)/i;
1196
1206
  const term = {
1197
1207
  type: "argument",
1198
1208
  metavar: valueParser.metavar,
@@ -1219,14 +1229,15 @@ function argument(valueParser, options = {}) {
1219
1229
  };
1220
1230
  let i = 0;
1221
1231
  let optionsTerminated = context.optionsTerminated;
1222
- if (!optionsTerminated) {
1223
- if (context.buffer[i] === "--") {
1224
- optionsTerminated = true;
1225
- i++;
1226
- } else if (context.buffer[i].match(optionPattern)) return {
1232
+ if (!optionsTerminated) if (context.buffer[i] === "--") {
1233
+ optionsTerminated = true;
1234
+ i++;
1235
+ } else {
1236
+ const optionMatch = context.buffer[i].match(optionPattern);
1237
+ if (optionMatch != null) return {
1227
1238
  success: false,
1228
1239
  consumed: i,
1229
- error: message`Expected an argument, but got an option: ${optionName(context.buffer[i])}.`
1240
+ error: message`Expected an argument, but got an option: ${optionName(optionMatch[1])}.`
1230
1241
  };
1231
1242
  }
1232
1243
  if (context.buffer.length < i + 1) return {
@@ -1736,6 +1747,22 @@ function command(name, parser, options = {}) {
1736
1747
  configurable: true,
1737
1748
  enumerable: false
1738
1749
  });
1750
+ definePassThroughPriority(result, [parser], (state, token) => {
1751
+ const active = normalizeCommandState(state);
1752
+ if (active == null) return void 0;
1753
+ const childState = active[0] === "matched" ? parser.initialState : active[1];
1754
+ return getPassThroughPriority(parser, getCommandParseChildState(state, childState, parser), token);
1755
+ });
1756
+ defineOptionMatch(result, (state, token) => {
1757
+ const active = normalizeCommandState(state);
1758
+ if (active == null) return void 0;
1759
+ const childState = active[0] === "matched" ? parser.initialState : active[1];
1760
+ const match = getOptionMatch(parser, getCommandParseChildState(state, childState, parser), token);
1761
+ return match == null ? void 0 : {
1762
+ ...match,
1763
+ continuesCommand: true
1764
+ };
1765
+ });
1739
1766
  return fluent(result);
1740
1767
  }
1741
1768
  /**
@@ -1809,6 +1836,8 @@ function passThrough(options = {}) {
1809
1836
  consumed: 0,
1810
1837
  error: message`No input to pass through.`
1811
1838
  };
1839
+ const failure = getPassThroughFailure(context, this?.priority ?? -10);
1840
+ if (failure != null) return failure.failure;
1812
1841
  const token = context.buffer[0];
1813
1842
  if (format === "greedy") {
1814
1843
  const captured = [...context.buffer];
@@ -1920,6 +1949,11 @@ function passThrough(options = {}) {
1920
1949
  return `passThrough(${format})`;
1921
1950
  }
1922
1951
  };
1952
+ definePassThroughPriority(result, [result], (_state, token) => {
1953
+ if (token == null || format === "greedy") return result.priority;
1954
+ const accepts = format === "equalsOnly" ? equalsOptionPattern.test(token) : format === "nextToken" && optionPattern.test(token);
1955
+ return accepts ? result.priority : void 0;
1956
+ });
1923
1957
  return fluent(result);
1924
1958
  }
1925
1959
 
@@ -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.3.0",
3
+ "version": "1.3.2",
4
4
  "description": "Type-safe combinatorial command-line interface parser",
5
5
  "keywords": [
6
6
  "CLI",
@@ -237,7 +237,7 @@
237
237
  },
238
238
  "sideEffects": false,
239
239
  "devDependencies": {
240
- "@optique/env": "1.3.0",
240
+ "@optique/env": "1.3.2",
241
241
  "@types/node": "^24.0.0",
242
242
  "fast-check": "^4.7.0",
243
243
  "tsdown": "^0.13.0",