@optique/core 1.1.0-dev.1998 → 1.1.0-dev.2053

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.
@@ -260,7 +260,7 @@ function* suggestOptionSync(optionNames$1, valueParser, hidden, context, prefix)
260
260
  }
261
261
  } else {
262
262
  const expectingValue = context.buffer.length > 0 && optionNames$1.includes(context.buffer[context.buffer.length - 1]);
263
- if (!expectingValue && (prefix.startsWith("--") || prefix.startsWith("-") || prefix.startsWith("/"))) {
263
+ if (!expectingValue && (prefix.startsWith("--") || prefix.startsWith("-") || prefix.startsWith("/") || prefix.startsWith("+"))) {
264
264
  for (const optionName$1 of optionNames$1) if (optionName$1.startsWith(prefix)) {
265
265
  if (prefix === "-" && optionName$1.length !== 2) continue;
266
266
  yield {
@@ -274,7 +274,7 @@ function* suggestOptionSync(optionNames$1, valueParser, hidden, context, prefix)
274
274
  if (context.buffer.length > 0) {
275
275
  const lastToken = context.buffer[context.buffer.length - 1];
276
276
  if (optionNames$1.includes(lastToken)) shouldSuggestValues = true;
277
- } else if (isAnnotationWrappedInitialState(context.state) && context.buffer.length === 0 && (context.exec?.path?.length ?? 0) === 0 && !(prefix.startsWith("--") || prefix.startsWith("-") || prefix.startsWith("/"))) shouldSuggestValues = true;
277
+ } else if (isAnnotationWrappedInitialState(context.state) && context.buffer.length === 0 && (context.exec?.path?.length ?? 0) === 0 && !(prefix.startsWith("--") || prefix.startsWith("-") || prefix.startsWith("/") || prefix.startsWith("+"))) shouldSuggestValues = true;
278
278
  if (shouldSuggestValues) yield* getSuggestionsWithDependency(valueParser, prefix, context.dependencyRegistry, context.exec);
279
279
  }
280
280
  }
@@ -344,7 +344,7 @@ async function* suggestOptionAsync(optionNames$1, valueParser, hidden, context,
344
344
  }
345
345
  } else {
346
346
  const expectingValue = context.buffer.length > 0 && optionNames$1.includes(context.buffer[context.buffer.length - 1]);
347
- if (!expectingValue && (prefix.startsWith("--") || prefix.startsWith("-") || prefix.startsWith("/"))) {
347
+ if (!expectingValue && (prefix.startsWith("--") || prefix.startsWith("-") || prefix.startsWith("/") || prefix.startsWith("+"))) {
348
348
  for (const optionName$1 of optionNames$1) if (optionName$1.startsWith(prefix)) {
349
349
  if (prefix === "-" && optionName$1.length !== 2) continue;
350
350
  yield {
@@ -358,7 +358,7 @@ async function* suggestOptionAsync(optionNames$1, valueParser, hidden, context,
358
358
  if (context.buffer.length > 0) {
359
359
  const lastToken = context.buffer[context.buffer.length - 1];
360
360
  if (optionNames$1.includes(lastToken)) shouldSuggestValues = true;
361
- } else if (isAnnotationWrappedInitialState(context.state) && context.buffer.length === 0 && (context.exec?.path?.length ?? 0) === 0 && !(prefix.startsWith("--") || prefix.startsWith("-") || prefix.startsWith("/"))) shouldSuggestValues = true;
361
+ } else if (isAnnotationWrappedInitialState(context.state) && context.buffer.length === 0 && (context.exec?.path?.length ?? 0) === 0 && !(prefix.startsWith("--") || prefix.startsWith("-") || prefix.startsWith("/") || prefix.startsWith("+"))) shouldSuggestValues = true;
362
362
  if (shouldSuggestValues) for await (const suggestion of getSuggestionsWithDependencyAsync(valueParser, prefix, context.dependencyRegistry, context.exec)) yield suggestion;
363
363
  }
364
364
  }
@@ -888,7 +888,7 @@ function flag(...args) {
888
888
  suggest(_context, prefix) {
889
889
  if (isSuggestionHidden(options.hidden)) return [];
890
890
  const suggestions = [];
891
- if (prefix.startsWith("--") || prefix.startsWith("-") || prefix.startsWith("/")) {
891
+ if (prefix.startsWith("--") || prefix.startsWith("-") || prefix.startsWith("/") || prefix.startsWith("+")) {
892
892
  for (const optionName$1 of optionNames$1) if (optionName$1.startsWith(prefix)) {
893
893
  if (prefix === "-" && optionName$1.length !== 2) continue;
894
894
  suggestions.push({
@@ -929,6 +929,221 @@ function flag(...args) {
929
929
  });
930
930
  return result;
931
931
  }
932
+ function normalizeNegatableFlagNameList(names) {
933
+ return typeof names === "string" ? [names] : names;
934
+ }
935
+ function validateNoDuplicateOptionNames(names, label) {
936
+ const seen = /* @__PURE__ */ new Set();
937
+ for (const name of names) {
938
+ if (seen.has(name)) throw new TypeError(`${label} has a duplicate name: "${name}".`);
939
+ seen.add(name);
940
+ }
941
+ }
942
+ function validateNoNegatableFlagNameCollision(positiveNames, negativeNames) {
943
+ const positiveSet = new Set(positiveNames);
944
+ for (const name of negativeNames) if (positiveSet.has(name)) throw new TypeError(`Negatable flag name is both positive and negative: "${name}".`);
945
+ }
946
+ function formatNegatableFlagDuplicateError(options, token) {
947
+ return options.errors?.duplicate ? typeof options.errors.duplicate === "function" ? options.errors.duplicate(token) : options.errors.duplicate : message`${optionName(token)} cannot be used multiple times.`;
948
+ }
949
+ function formatNegatableFlagConflictError(options, previousToken, token) {
950
+ return options.errors?.conflict ? typeof options.errors.conflict === "function" ? options.errors.conflict(previousToken, token) : options.errors.conflict : message`${optionName(previousToken)} and ${optionName(token)} cannot be used together.`;
951
+ }
952
+ function formatNegatableFlagUnexpectedValueError(options, optionName$1, value) {
953
+ return options.errors?.unexpectedValue ? typeof options.errors.unexpectedValue === "function" ? options.errors.unexpectedValue(optionName$1, value) : options.errors.unexpectedValue : message`Flag ${optionName(optionName$1)} does not accept a value, but got: ${value}.`;
954
+ }
955
+ function parseMatchedNegatableFlag(context, token, value, consumed, consumedOnFailure, buffer, options) {
956
+ if (context.state != null) return {
957
+ success: false,
958
+ consumed: consumedOnFailure,
959
+ error: context.state.value === value ? formatNegatableFlagDuplicateError(options, token) : formatNegatableFlagConflictError(options, context.state.token, token)
960
+ };
961
+ return {
962
+ success: true,
963
+ next: {
964
+ ...context,
965
+ state: {
966
+ value,
967
+ token
968
+ },
969
+ buffer
970
+ },
971
+ consumed
972
+ };
973
+ }
974
+ /**
975
+ * Creates a parser for a pair of command-line flags that explicitly enable or
976
+ * disable a Boolean value.
977
+ *
978
+ * The positive names produce `true`; the negative names produce `false`.
979
+ * Unlike {@link option}, this parser fails when neither side is present,
980
+ * matching {@link flag} semantics. Wrap it in {@link optional} for a
981
+ * tri-state override or {@link withDefault} for a concrete fallback.
982
+ *
983
+ * @param names The positive and negative option names to parse.
984
+ * @param options Optional metadata and error customization.
985
+ * @returns A {@link Parser} that produces `true` for positive names and
986
+ * `false` for negative names.
987
+ * @throws {TypeError} If any option name is invalid, duplicated within one
988
+ * side, or shared by the positive and negative sides.
989
+ * @since 1.1.0
990
+ */
991
+ function negatableFlag(names, options = {}) {
992
+ const positiveNames = normalizeNegatableFlagNameList(names.positive);
993
+ const negativeNames = normalizeNegatableFlagNameList(names.negative);
994
+ validateOptionNames(positiveNames, "Positive flag");
995
+ validateOptionNames(negativeNames, "Negative flag");
996
+ validateNoDuplicateOptionNames(positiveNames, "Positive flag");
997
+ validateNoDuplicateOptionNames(negativeNames, "Negative flag");
998
+ validateNoNegatableFlagNameCollision(positiveNames, negativeNames);
999
+ const optionNames$1 = [...positiveNames, ...negativeNames];
1000
+ const valueByName = /* @__PURE__ */ new Map();
1001
+ for (const name of positiveNames) valueByName.set(name, true);
1002
+ for (const name of negativeNames) valueByName.set(name, false);
1003
+ const result = {
1004
+ $valueType: [],
1005
+ $stateType: [],
1006
+ mode: "sync",
1007
+ priority: 10,
1008
+ usage: [{
1009
+ type: "option",
1010
+ names: optionNames$1,
1011
+ ...options.hidden != null && { hidden: options.hidden }
1012
+ }],
1013
+ leadingNames: new Set(optionNames$1),
1014
+ acceptingAnyToken: false,
1015
+ initialState: void 0,
1016
+ parse(context) {
1017
+ if (context.optionsTerminated) return {
1018
+ success: false,
1019
+ consumed: 0,
1020
+ error: options.errors?.optionsTerminated ?? message`No more options can be parsed.`
1021
+ };
1022
+ else if (context.buffer.length < 1) return {
1023
+ success: false,
1024
+ consumed: 0,
1025
+ error: options.errors?.endOfInput ?? message`Expected an option, but got end of input.`
1026
+ };
1027
+ if (context.buffer[0] === "--") return {
1028
+ success: true,
1029
+ next: {
1030
+ ...context,
1031
+ buffer: context.buffer.slice(1),
1032
+ state: context.state,
1033
+ optionsTerminated: true
1034
+ },
1035
+ consumed: context.buffer.slice(0, 1)
1036
+ };
1037
+ const directValue = valueByName.get(context.buffer[0]);
1038
+ if (directValue != null) return parseMatchedNegatableFlag(context, context.buffer[0], directValue, context.buffer.slice(0, 1), 1, context.buffer.slice(1), options);
1039
+ for (const name of optionNames$1) {
1040
+ if (!name.startsWith("--") && !name.startsWith("/") && !name.startsWith("+") && !(name.startsWith("-") && name.length > 2)) continue;
1041
+ const prefix = name.startsWith("/") ? `${name}:` : `${name}=`;
1042
+ if (context.buffer[0].startsWith(prefix)) {
1043
+ const value = context.buffer[0].slice(prefix.length);
1044
+ return {
1045
+ success: false,
1046
+ consumed: 1,
1047
+ error: formatNegatableFlagUnexpectedValueError(options, prefix.slice(0, -1), value)
1048
+ };
1049
+ }
1050
+ }
1051
+ for (const shortOption of optionNames$1) {
1052
+ if (!shortOption.match(/^-[^-]$/)) continue;
1053
+ if (!context.buffer[0].startsWith(shortOption)) continue;
1054
+ return parseMatchedNegatableFlag(context, shortOption, valueByName.get(shortOption), [context.buffer[0].slice(0, 2)], 1, [`-${context.buffer[0].slice(2)}`, ...context.buffer.slice(1)], options);
1055
+ }
1056
+ const invalidOption = context.buffer[0];
1057
+ if (options.errors?.noMatch) {
1058
+ const candidates = /* @__PURE__ */ new Set();
1059
+ for (const name of extractOptionNames(context.usage)) candidates.add(name);
1060
+ const suggestions = findSimilar(invalidOption, candidates, DEFAULT_FIND_SIMILAR_OPTIONS);
1061
+ return {
1062
+ success: false,
1063
+ consumed: 0,
1064
+ error: typeof options.errors.noMatch === "function" ? options.errors.noMatch(invalidOption, suggestions) : options.errors.noMatch
1065
+ };
1066
+ }
1067
+ const baseError = message`No matched option for ${optionName(invalidOption)}.`;
1068
+ return {
1069
+ success: false,
1070
+ consumed: 0,
1071
+ error: createErrorWithSuggestions(baseError, invalidOption, context.usage, "option")
1072
+ };
1073
+ },
1074
+ complete(state, _exec) {
1075
+ if (state == null) return {
1076
+ success: false,
1077
+ error: options.errors?.missing ? typeof options.errors.missing === "function" ? options.errors.missing(positiveNames, negativeNames) : options.errors.missing : message`Required flag ${optionNames(optionNames$1)} is missing.`
1078
+ };
1079
+ return {
1080
+ success: true,
1081
+ value: state.value
1082
+ };
1083
+ },
1084
+ suggest(_context, prefix) {
1085
+ if (isSuggestionHidden(options.hidden)) return [];
1086
+ const suggestions = [];
1087
+ if (prefix.startsWith("--") || prefix.startsWith("-") || prefix.startsWith("/") || prefix.startsWith("+")) {
1088
+ for (const optionName$1 of optionNames$1) if (optionName$1.startsWith(prefix)) {
1089
+ if (prefix === "-" && optionName$1.length !== 2) continue;
1090
+ suggestions.push({
1091
+ kind: "literal",
1092
+ text: optionName$1
1093
+ });
1094
+ }
1095
+ }
1096
+ return suggestions;
1097
+ },
1098
+ getDocFragments(_state, _defaultValue) {
1099
+ if (isDocHidden(options.hidden)) return {
1100
+ fragments: [],
1101
+ description: options.description
1102
+ };
1103
+ const fragments = [{
1104
+ type: "entry",
1105
+ term: {
1106
+ type: "option",
1107
+ names: optionNames$1
1108
+ },
1109
+ description: options.description
1110
+ }];
1111
+ return {
1112
+ fragments,
1113
+ description: options.description
1114
+ };
1115
+ },
1116
+ [Symbol.for("Deno.customInspect")]() {
1117
+ const args = [`positive: ${JSON.stringify(positiveNames)}`, `negative: ${JSON.stringify(negativeNames)}`];
1118
+ return `negatableFlag({ ${args.join(", ")} })`;
1119
+ }
1120
+ };
1121
+ Object.defineProperty(result, "validateValue", {
1122
+ value(v) {
1123
+ if (typeof v !== "boolean") {
1124
+ const actualType = v === null ? "null" : typeof v;
1125
+ return {
1126
+ success: false,
1127
+ error: message`${optionNames(optionNames$1)}: Expected a boolean value, but received ${actualType}.`
1128
+ };
1129
+ }
1130
+ return {
1131
+ success: true,
1132
+ value: v
1133
+ };
1134
+ },
1135
+ configurable: true,
1136
+ enumerable: false,
1137
+ writable: false
1138
+ });
1139
+ Object.defineProperty(result, "placeholder", {
1140
+ value: false,
1141
+ configurable: true,
1142
+ enumerable: false,
1143
+ writable: false
1144
+ });
1145
+ return result;
1146
+ }
932
1147
  /**
933
1148
  * Creates a parser that expects a single argument value.
934
1149
  * This parser is typically used for positional arguments
@@ -1601,4 +1816,4 @@ function passThrough(options = {}) {
1601
1816
  }
1602
1817
 
1603
1818
  //#endregion
1604
- export { argument, command, constant, fail, flag, option, passThrough };
1819
+ export { argument, command, constant, fail, flag, negatableFlag, option, passThrough };
@@ -2151,34 +2151,18 @@ function macAddress(options) {
2151
2151
  if (sep === "none") return formatted.join("");
2152
2152
  return formatted.join(sep);
2153
2153
  }
2154
- function normalizeMac(value) {
2155
- if (typeof value !== "string") return metavar;
2156
- let octets;
2157
- let detectedSep;
2158
- if (value.includes(":")) {
2159
- octets = value.split(":");
2160
- detectedSep = ":";
2161
- } else if (value.includes("-")) {
2162
- octets = value.split("-");
2163
- detectedSep = "-";
2164
- } else if (value.includes(".")) {
2165
- const groups = value.split(".");
2166
- if (groups.length !== 3 || !groups.every((g) => /^[0-9a-fA-F]{4}$/.test(g))) return value;
2167
- octets = groups.flatMap((g) => [g.slice(0, 2), g.slice(2)]);
2168
- detectedSep = ".";
2169
- } else {
2170
- if (value.length !== 12) return value;
2171
- octets = [];
2172
- for (let i = 0; i < value.length; i += 2) octets.push(value.slice(i, i + 2));
2173
- detectedSep = "none";
2154
+ let macParsing = false;
2155
+ function normalizeMacValue(value, fallback) {
2156
+ if (macParsing) return value;
2157
+ macParsing = true;
2158
+ try {
2159
+ const result = parserObj.parse(value);
2160
+ return result.success ? result.value : fallback;
2161
+ } catch {
2162
+ return fallback;
2163
+ } finally {
2164
+ macParsing = false;
2174
2165
  }
2175
- if (octets.length !== 6 || !octets.every((o) => /^[0-9a-fA-F]{1,2}$/.test(o))) return value;
2176
- octets = octets.map((o) => o.padStart(2, "0"));
2177
- let sep;
2178
- if (outputSeparator != null) sep = outputSeparator;
2179
- else if (separator !== "any") sep = separator;
2180
- else sep = detectedSep;
2181
- return joinOctets(octets, sep);
2182
2166
  }
2183
2167
  const parserObj = {
2184
2168
  mode: "sync",
@@ -2263,44 +2247,15 @@ function macAddress(options) {
2263
2247
  value: joinOctets(octets, finalSeparator)
2264
2248
  };
2265
2249
  },
2266
- format: normalizeMac
2267
- };
2268
- const macParser = parserObj;
2269
- let macParsing = false;
2270
- Object.defineProperty(parserObj, "format", {
2271
- value(v) {
2272
- if (typeof v !== "string") return metavar;
2273
- if (macParsing) return v;
2274
- macParsing = true;
2275
- try {
2276
- const result = macParser.parse(v);
2277
- return result.success ? result.value : v;
2278
- } catch {
2279
- return v;
2280
- } finally {
2281
- macParsing = false;
2282
- }
2283
- },
2284
- configurable: true,
2285
- enumerable: true
2286
- });
2287
- Object.defineProperty(parserObj, "normalize", {
2288
- value(v) {
2289
- if (typeof v !== "string") return v;
2290
- if (macParsing) return v;
2291
- macParsing = true;
2292
- try {
2293
- const result = macParser.parse(v);
2294
- return result.success ? result.value : v;
2295
- } catch {
2296
- return v;
2297
- } finally {
2298
- macParsing = false;
2299
- }
2250
+ format(value) {
2251
+ if (typeof value !== "string") return metavar;
2252
+ return normalizeMacValue(value, value);
2300
2253
  },
2301
- configurable: true,
2302
- enumerable: true
2303
- });
2254
+ normalize(value) {
2255
+ if (typeof value !== "string") return value;
2256
+ return normalizeMacValue(value, value);
2257
+ }
2258
+ };
2304
2259
  return parserObj;
2305
2260
  }
2306
2261
  /**
@@ -2151,34 +2151,18 @@ function macAddress(options) {
2151
2151
  if (sep === "none") return formatted.join("");
2152
2152
  return formatted.join(sep);
2153
2153
  }
2154
- function normalizeMac(value) {
2155
- if (typeof value !== "string") return metavar;
2156
- let octets;
2157
- let detectedSep;
2158
- if (value.includes(":")) {
2159
- octets = value.split(":");
2160
- detectedSep = ":";
2161
- } else if (value.includes("-")) {
2162
- octets = value.split("-");
2163
- detectedSep = "-";
2164
- } else if (value.includes(".")) {
2165
- const groups = value.split(".");
2166
- if (groups.length !== 3 || !groups.every((g) => /^[0-9a-fA-F]{4}$/.test(g))) return value;
2167
- octets = groups.flatMap((g) => [g.slice(0, 2), g.slice(2)]);
2168
- detectedSep = ".";
2169
- } else {
2170
- if (value.length !== 12) return value;
2171
- octets = [];
2172
- for (let i = 0; i < value.length; i += 2) octets.push(value.slice(i, i + 2));
2173
- detectedSep = "none";
2154
+ let macParsing = false;
2155
+ function normalizeMacValue(value, fallback) {
2156
+ if (macParsing) return value;
2157
+ macParsing = true;
2158
+ try {
2159
+ const result = parserObj.parse(value);
2160
+ return result.success ? result.value : fallback;
2161
+ } catch {
2162
+ return fallback;
2163
+ } finally {
2164
+ macParsing = false;
2174
2165
  }
2175
- if (octets.length !== 6 || !octets.every((o) => /^[0-9a-fA-F]{1,2}$/.test(o))) return value;
2176
- octets = octets.map((o) => o.padStart(2, "0"));
2177
- let sep;
2178
- if (outputSeparator != null) sep = outputSeparator;
2179
- else if (separator !== "any") sep = separator;
2180
- else sep = detectedSep;
2181
- return joinOctets(octets, sep);
2182
2166
  }
2183
2167
  const parserObj = {
2184
2168
  mode: "sync",
@@ -2263,44 +2247,15 @@ function macAddress(options) {
2263
2247
  value: joinOctets(octets, finalSeparator)
2264
2248
  };
2265
2249
  },
2266
- format: normalizeMac
2267
- };
2268
- const macParser = parserObj;
2269
- let macParsing = false;
2270
- Object.defineProperty(parserObj, "format", {
2271
- value(v) {
2272
- if (typeof v !== "string") return metavar;
2273
- if (macParsing) return v;
2274
- macParsing = true;
2275
- try {
2276
- const result = macParser.parse(v);
2277
- return result.success ? result.value : v;
2278
- } catch {
2279
- return v;
2280
- } finally {
2281
- macParsing = false;
2282
- }
2283
- },
2284
- configurable: true,
2285
- enumerable: true
2286
- });
2287
- Object.defineProperty(parserObj, "normalize", {
2288
- value(v) {
2289
- if (typeof v !== "string") return v;
2290
- if (macParsing) return v;
2291
- macParsing = true;
2292
- try {
2293
- const result = macParser.parse(v);
2294
- return result.success ? result.value : v;
2295
- } catch {
2296
- return v;
2297
- } finally {
2298
- macParsing = false;
2299
- }
2250
+ format(value) {
2251
+ if (typeof value !== "string") return metavar;
2252
+ return normalizeMacValue(value, value);
2300
2253
  },
2301
- configurable: true,
2302
- enumerable: true
2303
- });
2254
+ normalize(value) {
2255
+ if (typeof value !== "string") return value;
2256
+ return normalizeMacValue(value, value);
2257
+ }
2258
+ };
2304
2259
  return parserObj;
2305
2260
  }
2306
2261
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optique/core",
3
- "version": "1.1.0-dev.1998+3e697633",
3
+ "version": "1.1.0-dev.2053",
4
4
  "description": "Type-safe combinatorial command-line interface parser",
5
5
  "keywords": [
6
6
  "CLI",