@optique/core 1.0.0-dev.1758 → 1.0.0-dev.1766

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.
@@ -3795,6 +3795,11 @@ function group(label, parser, options = {}) {
3795
3795
  configurable: true,
3796
3796
  enumerable: false
3797
3797
  });
3798
+ if (typeof parser.validateValue === "function") Object.defineProperty(groupParser, "validateValue", {
3799
+ value: parser.validateValue.bind(parser),
3800
+ configurable: true,
3801
+ enumerable: false
3802
+ });
3798
3803
  return groupParser;
3799
3804
  }
3800
3805
  /**
@@ -3795,6 +3795,11 @@ function group(label, parser, options = {}) {
3795
3795
  configurable: true,
3796
3796
  enumerable: false
3797
3797
  });
3798
+ if (typeof parser.validateValue === "function") Object.defineProperty(groupParser, "validateValue", {
3799
+ value: parser.validateValue.bind(parser),
3800
+ configurable: true,
3801
+ enumerable: false
3802
+ });
3798
3803
  return groupParser;
3799
3804
  }
3800
3805
  /**
@@ -306,6 +306,20 @@ function optional(parser) {
306
306
  enumerable: false
307
307
  });
308
308
  }
309
+ if (typeof parser.validateValue === "function") {
310
+ const innerValidate = parser.validateValue.bind(parser);
311
+ Object.defineProperty(optionalParser, "validateValue", {
312
+ value(v) {
313
+ if (v === void 0) return require_mode_dispatch.wrapForMode(parser.$mode, {
314
+ success: true,
315
+ value: v
316
+ });
317
+ return innerValidate(v);
318
+ },
319
+ configurable: true,
320
+ enumerable: false
321
+ });
322
+ }
309
323
  if (parser.dependencyMetadata != null) {
310
324
  const composed = require_dependency_metadata.composeDependencyMetadata(parser.dependencyMetadata, "optional");
311
325
  if (composed != null) optionalParser.dependencyMetadata = composed;
@@ -504,6 +518,16 @@ function withDefault(parser, defaultValue, options) {
504
518
  enumerable: false
505
519
  });
506
520
  }
521
+ if (typeof parser.validateValue === "function") {
522
+ const innerValidate = parser.validateValue.bind(parser);
523
+ Object.defineProperty(withDefaultParser, "validateValue", {
524
+ value(v) {
525
+ return innerValidate(v);
526
+ },
527
+ configurable: true,
528
+ enumerable: false
529
+ });
530
+ }
507
531
  if (parser.dependencyMetadata != null) {
508
532
  const composed = require_dependency_metadata.composeDependencyMetadata(parser.dependencyMetadata, "withDefault", { defaultValue: () => {
509
533
  let v;
@@ -649,6 +673,7 @@ function map(parser, transform) {
649
673
  }
650
674
  };
651
675
  delete mappedParser.normalizeValue;
676
+ delete mappedParser.validateValue;
652
677
  if ("placeholder" in parser) Object.defineProperty(mappedParser, "placeholder", {
653
678
  get() {
654
679
  try {
@@ -1152,6 +1177,72 @@ function multiple(parser, options = {}) {
1152
1177
  enumerable: false
1153
1178
  });
1154
1179
  }
1180
+ {
1181
+ const innerValidate = typeof parser.validateValue === "function" ? parser.validateValue.bind(parser) : void 0;
1182
+ const validateArity = (values) => {
1183
+ if (values.length < min) {
1184
+ const customMessage = options.errors?.tooFew;
1185
+ return {
1186
+ success: false,
1187
+ error: customMessage ? typeof customMessage === "function" ? customMessage(min, values.length) : customMessage : require_message.message`Expected at least ${require_message.text(min.toLocaleString("en"))} values, but got only ${require_message.text(values.length.toLocaleString("en"))}.`
1188
+ };
1189
+ }
1190
+ if (values.length > max) {
1191
+ const customMessage = options.errors?.tooMany;
1192
+ return {
1193
+ success: false,
1194
+ error: customMessage ? typeof customMessage === "function" ? customMessage(max, values.length) : customMessage : require_message.message`Expected at most ${require_message.text(max.toLocaleString("en"))} values, but got ${require_message.text(values.length.toLocaleString("en"))}.`
1195
+ };
1196
+ }
1197
+ return {
1198
+ success: true,
1199
+ value: values
1200
+ };
1201
+ };
1202
+ Object.defineProperty(resultParser, "validateValue", {
1203
+ value(values) {
1204
+ if (!Array.isArray(values)) {
1205
+ const actualType = values === null ? "null" : typeof values;
1206
+ return require_mode_dispatch.wrapForMode(parser.$mode, {
1207
+ success: false,
1208
+ error: require_message.message`Expected an array of values, but received ${actualType}.`
1209
+ });
1210
+ }
1211
+ const arity = validateArity(values);
1212
+ if (!arity.success) return require_mode_dispatch.wrapForMode(parser.$mode, arity);
1213
+ if (innerValidate == null) return require_mode_dispatch.wrapForMode(parser.$mode, arity);
1214
+ return require_mode_dispatch.dispatchByMode(parser.$mode, () => {
1215
+ let changed = false;
1216
+ const normalized = [];
1217
+ for (const v of values) {
1218
+ const r = innerValidate(v);
1219
+ if (!r.success) return r;
1220
+ normalized.push(r.value);
1221
+ if (r.value !== v) changed = true;
1222
+ }
1223
+ return {
1224
+ success: true,
1225
+ value: changed ? normalized : values
1226
+ };
1227
+ }, async () => {
1228
+ let changed = false;
1229
+ const normalized = [];
1230
+ for (const v of values) {
1231
+ const r = await innerValidate(v);
1232
+ if (!r.success) return r;
1233
+ normalized.push(r.value);
1234
+ if (r.value !== v) changed = true;
1235
+ }
1236
+ return {
1237
+ success: true,
1238
+ value: changed ? normalized : values
1239
+ };
1240
+ });
1241
+ },
1242
+ configurable: true,
1243
+ enumerable: false
1244
+ });
1245
+ }
1155
1246
  if (parser.dependencyMetadata?.source != null) {
1156
1247
  const innerSource = parser.dependencyMetadata.source;
1157
1248
  Object.defineProperty(resultParser, "dependencyMetadata", {
@@ -1279,6 +1370,11 @@ function nonEmpty(parser) {
1279
1370
  configurable: true,
1280
1371
  enumerable: false
1281
1372
  });
1373
+ if (typeof parser.validateValue === "function") Object.defineProperty(nonEmptyParser, "validateValue", {
1374
+ value: parser.validateValue.bind(parser),
1375
+ configurable: true,
1376
+ enumerable: false
1377
+ });
1282
1378
  return nonEmptyParser;
1283
1379
  }
1284
1380
 
package/dist/modifiers.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { annotateFreshArray, annotationKey, getAnnotations, inheritAnnotations, isInjectedAnnotationWrapper, unwrapInjectedAnnotationWrapper } from "./annotations.js";
2
2
  import { formatMessage, message, text } from "./message.js";
3
- import { dispatchByMode, dispatchIterableByMode, mapModeValue } from "./mode-dispatch.js";
3
+ import { dispatchByMode, dispatchIterableByMode, mapModeValue, wrapForMode } from "./mode-dispatch.js";
4
4
  import { composeDependencyMetadata } from "./dependency-metadata.js";
5
5
  import { defineInheritedAnnotationParser, defineSourceBindingOnlyAnnotationCompletionParser, unmatchedNonCliDependencySourceStateMarker } from "./parser.js";
6
6
 
@@ -306,6 +306,20 @@ function optional(parser) {
306
306
  enumerable: false
307
307
  });
308
308
  }
309
+ if (typeof parser.validateValue === "function") {
310
+ const innerValidate = parser.validateValue.bind(parser);
311
+ Object.defineProperty(optionalParser, "validateValue", {
312
+ value(v) {
313
+ if (v === void 0) return wrapForMode(parser.$mode, {
314
+ success: true,
315
+ value: v
316
+ });
317
+ return innerValidate(v);
318
+ },
319
+ configurable: true,
320
+ enumerable: false
321
+ });
322
+ }
309
323
  if (parser.dependencyMetadata != null) {
310
324
  const composed = composeDependencyMetadata(parser.dependencyMetadata, "optional");
311
325
  if (composed != null) optionalParser.dependencyMetadata = composed;
@@ -504,6 +518,16 @@ function withDefault(parser, defaultValue, options) {
504
518
  enumerable: false
505
519
  });
506
520
  }
521
+ if (typeof parser.validateValue === "function") {
522
+ const innerValidate = parser.validateValue.bind(parser);
523
+ Object.defineProperty(withDefaultParser, "validateValue", {
524
+ value(v) {
525
+ return innerValidate(v);
526
+ },
527
+ configurable: true,
528
+ enumerable: false
529
+ });
530
+ }
507
531
  if (parser.dependencyMetadata != null) {
508
532
  const composed = composeDependencyMetadata(parser.dependencyMetadata, "withDefault", { defaultValue: () => {
509
533
  let v;
@@ -649,6 +673,7 @@ function map(parser, transform) {
649
673
  }
650
674
  };
651
675
  delete mappedParser.normalizeValue;
676
+ delete mappedParser.validateValue;
652
677
  if ("placeholder" in parser) Object.defineProperty(mappedParser, "placeholder", {
653
678
  get() {
654
679
  try {
@@ -1152,6 +1177,72 @@ function multiple(parser, options = {}) {
1152
1177
  enumerable: false
1153
1178
  });
1154
1179
  }
1180
+ {
1181
+ const innerValidate = typeof parser.validateValue === "function" ? parser.validateValue.bind(parser) : void 0;
1182
+ const validateArity = (values) => {
1183
+ if (values.length < min) {
1184
+ const customMessage = options.errors?.tooFew;
1185
+ return {
1186
+ success: false,
1187
+ error: customMessage ? typeof customMessage === "function" ? customMessage(min, values.length) : customMessage : message`Expected at least ${text(min.toLocaleString("en"))} values, but got only ${text(values.length.toLocaleString("en"))}.`
1188
+ };
1189
+ }
1190
+ if (values.length > max) {
1191
+ const customMessage = options.errors?.tooMany;
1192
+ return {
1193
+ success: false,
1194
+ error: customMessage ? typeof customMessage === "function" ? customMessage(max, values.length) : customMessage : message`Expected at most ${text(max.toLocaleString("en"))} values, but got ${text(values.length.toLocaleString("en"))}.`
1195
+ };
1196
+ }
1197
+ return {
1198
+ success: true,
1199
+ value: values
1200
+ };
1201
+ };
1202
+ Object.defineProperty(resultParser, "validateValue", {
1203
+ value(values) {
1204
+ if (!Array.isArray(values)) {
1205
+ const actualType = values === null ? "null" : typeof values;
1206
+ return wrapForMode(parser.$mode, {
1207
+ success: false,
1208
+ error: message`Expected an array of values, but received ${actualType}.`
1209
+ });
1210
+ }
1211
+ const arity = validateArity(values);
1212
+ if (!arity.success) return wrapForMode(parser.$mode, arity);
1213
+ if (innerValidate == null) return wrapForMode(parser.$mode, arity);
1214
+ return dispatchByMode(parser.$mode, () => {
1215
+ let changed = false;
1216
+ const normalized = [];
1217
+ for (const v of values) {
1218
+ const r = innerValidate(v);
1219
+ if (!r.success) return r;
1220
+ normalized.push(r.value);
1221
+ if (r.value !== v) changed = true;
1222
+ }
1223
+ return {
1224
+ success: true,
1225
+ value: changed ? normalized : values
1226
+ };
1227
+ }, async () => {
1228
+ let changed = false;
1229
+ const normalized = [];
1230
+ for (const v of values) {
1231
+ const r = await innerValidate(v);
1232
+ if (!r.success) return r;
1233
+ normalized.push(r.value);
1234
+ if (r.value !== v) changed = true;
1235
+ }
1236
+ return {
1237
+ success: true,
1238
+ value: changed ? normalized : values
1239
+ };
1240
+ });
1241
+ },
1242
+ configurable: true,
1243
+ enumerable: false
1244
+ });
1245
+ }
1155
1246
  if (parser.dependencyMetadata?.source != null) {
1156
1247
  const innerSource = parser.dependencyMetadata.source;
1157
1248
  Object.defineProperty(resultParser, "dependencyMetadata", {
@@ -1279,6 +1370,11 @@ function nonEmpty(parser) {
1279
1370
  configurable: true,
1280
1371
  enumerable: false
1281
1372
  });
1373
+ if (typeof parser.validateValue === "function") Object.defineProperty(nonEmptyParser, "validateValue", {
1374
+ value: parser.validateValue.bind(parser),
1375
+ configurable: true,
1376
+ enumerable: false
1377
+ });
1282
1378
  return nonEmptyParser;
1283
1379
  }
1284
1380
 
package/dist/parser.d.cts CHANGED
@@ -262,6 +262,50 @@ interface Parser<M extends Mode = "sync", TValue = unknown, TState = unknown> {
262
262
  * @since 1.0.0
263
263
  */
264
264
  normalizeValue?(value: TValue): TValue;
265
+ /**
266
+ * Optionally re-validates a value as if it had been parsed from CLI
267
+ * input, surfacing any constraint violations from the underlying value
268
+ * parser (e.g., regex patterns, numeric bounds, `choice()` values).
269
+ *
270
+ * Wrappers like `bindEnv()` and `bindConfig()` call this on fallback
271
+ * values — environment variables parsed by a looser env parser,
272
+ * configured defaults, and values loaded from config files — so that
273
+ * those values obey the same validation semantics as CLI input.
274
+ * Without it, parser constraints can be silently bypassed through
275
+ * fallback paths.
276
+ *
277
+ * Built-in primitive parsers ({@link option}, {@link argument})
278
+ * implement this method by round-tripping the value through the inner
279
+ * {@link ValueParser.format} and {@link ValueParser.parse} calls: the
280
+ * value is serialized back to a string and re-parsed, which re-runs
281
+ * every constraint check. Combinator wrappers ({@link optional},
282
+ * {@link withDefault}) forward this method from their inner parser.
283
+ * {@link map} intentionally does *not* forward it because the mapping
284
+ * function is one-way: the mapped output type no longer corresponds
285
+ * to the inner parser's constraints. Exclusive combinators
286
+ * ({@link or}, `longestMatch()`) and multi-source combinators
287
+ * (`merge()`, `concat()`) intentionally do not implement this method
288
+ * because the active branch or key ownership is unknown at validation
289
+ * time.
290
+ *
291
+ * Implementations must wrap any *exception* thrown by `format()` in
292
+ * `try`/`catch` and return the original value as a successful
293
+ * {@link ValueParserResult}. This specifically protects
294
+ * dependency-derived parsers whose factory cannot run without the
295
+ * current dependency value, and custom value parsers whose `format()`
296
+ * intentionally throws for unsupported inputs. Values that
297
+ * `format()` successfully serializes to a string are always re-parsed,
298
+ * and any resulting parse failure is propagated — they represent the
299
+ * bug class this method exists to surface.
300
+ *
301
+ * @param value The candidate value to validate.
302
+ * @returns A {@link ValueParserResult} indicating success (with the
303
+ * possibly-canonicalized value) or failure (with an error
304
+ * message). In async mode, returns a `Promise` resolving to
305
+ * the result.
306
+ * @since 1.0.0
307
+ */
308
+ validateValue?(value: TValue): ModeValue<M, ValueParserResult<TValue>>;
265
309
  /**
266
310
  * Internal dependency metadata describing this parser's dependency
267
311
  * capabilities. Used by the dependency runtime to resolve dependencies
package/dist/parser.d.ts CHANGED
@@ -262,6 +262,50 @@ interface Parser<M extends Mode = "sync", TValue = unknown, TState = unknown> {
262
262
  * @since 1.0.0
263
263
  */
264
264
  normalizeValue?(value: TValue): TValue;
265
+ /**
266
+ * Optionally re-validates a value as if it had been parsed from CLI
267
+ * input, surfacing any constraint violations from the underlying value
268
+ * parser (e.g., regex patterns, numeric bounds, `choice()` values).
269
+ *
270
+ * Wrappers like `bindEnv()` and `bindConfig()` call this on fallback
271
+ * values — environment variables parsed by a looser env parser,
272
+ * configured defaults, and values loaded from config files — so that
273
+ * those values obey the same validation semantics as CLI input.
274
+ * Without it, parser constraints can be silently bypassed through
275
+ * fallback paths.
276
+ *
277
+ * Built-in primitive parsers ({@link option}, {@link argument})
278
+ * implement this method by round-tripping the value through the inner
279
+ * {@link ValueParser.format} and {@link ValueParser.parse} calls: the
280
+ * value is serialized back to a string and re-parsed, which re-runs
281
+ * every constraint check. Combinator wrappers ({@link optional},
282
+ * {@link withDefault}) forward this method from their inner parser.
283
+ * {@link map} intentionally does *not* forward it because the mapping
284
+ * function is one-way: the mapped output type no longer corresponds
285
+ * to the inner parser's constraints. Exclusive combinators
286
+ * ({@link or}, `longestMatch()`) and multi-source combinators
287
+ * (`merge()`, `concat()`) intentionally do not implement this method
288
+ * because the active branch or key ownership is unknown at validation
289
+ * time.
290
+ *
291
+ * Implementations must wrap any *exception* thrown by `format()` in
292
+ * `try`/`catch` and return the original value as a successful
293
+ * {@link ValueParserResult}. This specifically protects
294
+ * dependency-derived parsers whose factory cannot run without the
295
+ * current dependency value, and custom value parsers whose `format()`
296
+ * intentionally throws for unsupported inputs. Values that
297
+ * `format()` successfully serializes to a string are always re-parsed,
298
+ * and any resulting parse failure is propagated — they represent the
299
+ * bug class this method exists to surface.
300
+ *
301
+ * @param value The candidate value to validate.
302
+ * @returns A {@link ValueParserResult} indicating success (with the
303
+ * possibly-canonicalized value) or failure (with an error
304
+ * message). In async mode, returns a `Promise` resolving to
305
+ * the result.
306
+ * @since 1.0.0
307
+ */
308
+ validateValue?(value: TValue): ModeValue<M, ValueParserResult<TValue>>;
265
309
  /**
266
310
  * Internal dependency metadata describing this parser's dependency
267
311
  * capabilities. Used by the dependency runtime to resolve dependencies
@@ -438,6 +438,7 @@ function option(...args) {
438
438
  const isAsync = mode === "async";
439
439
  const syncValueParser = valueParser;
440
440
  const dependencyMetadata = valueParser != null ? require_dependency_metadata.extractDependencyMetadata(valueParser) : void 0;
441
+ const formatInvalidValueError = (error) => options.errors?.invalidValue ? typeof options.errors.invalidValue === "function" ? options.errors.invalidValue(error) : options.errors.invalidValue : require_message.message`${require_message.optionNames(optionNames$1)}: ${error}`;
441
442
  const result = {
442
443
  $mode: mode,
443
444
  $valueType: [],
@@ -619,7 +620,6 @@ function option(...args) {
619
620
  };
620
621
  },
621
622
  complete(state, exec) {
622
- const formatInvalidValueError = (error) => options.errors?.invalidValue ? typeof options.errors.invalidValue === "function" ? options.errors.invalidValue(error) : options.errors.invalidValue : require_message.message`${require_message.optionNames(optionNames$1)}: ${error}`;
623
623
  const missing = valueParser == null ? {
624
624
  success: true,
625
625
  value: false
@@ -693,6 +693,50 @@ function option(...args) {
693
693
  enumerable: false
694
694
  });
695
695
  }
696
+ if (valueParser == null) Object.defineProperty(result, "validateValue", {
697
+ value(v) {
698
+ if (typeof v !== "boolean") {
699
+ const actualType = v === null ? "null" : typeof v;
700
+ return require_mode_dispatch.wrapForMode(mode, {
701
+ success: false,
702
+ error: formatInvalidValueError(require_message.message`Expected a boolean value, but received ${actualType}.`)
703
+ });
704
+ }
705
+ return require_mode_dispatch.wrapForMode(mode, {
706
+ success: true,
707
+ value: v
708
+ });
709
+ },
710
+ configurable: true,
711
+ enumerable: false
712
+ });
713
+ else if (!require_dependency.isDerivedValueParser(valueParser)) {
714
+ const vp = valueParser;
715
+ const wrapParseResult = (parsed) => parsed.success ? parsed : {
716
+ success: false,
717
+ error: formatInvalidValueError(parsed.error)
718
+ };
719
+ Object.defineProperty(result, "validateValue", {
720
+ value(v) {
721
+ let stringified;
722
+ try {
723
+ stringified = vp.format(v);
724
+ } catch {
725
+ return require_mode_dispatch.wrapForMode(mode, {
726
+ success: true,
727
+ value: v
728
+ });
729
+ }
730
+ if (typeof stringified !== "string") return require_mode_dispatch.wrapForMode(mode, {
731
+ success: true,
732
+ value: v
733
+ });
734
+ return require_mode_dispatch.dispatchByMode(mode, () => wrapParseResult(vp.parse(stringified)), async () => wrapParseResult(await vp.parse(stringified)));
735
+ },
736
+ configurable: true,
737
+ enumerable: false
738
+ });
739
+ }
696
740
  if (valueParser != null) Object.defineProperty(result, "placeholder", {
697
741
  get() {
698
742
  try {
@@ -934,6 +978,7 @@ function argument(valueParser, options = {}) {
934
978
  const isAsync = valueParser.$mode === "async";
935
979
  const syncValueParser = valueParser;
936
980
  const dependencyMetadata = require_dependency_metadata.extractDependencyMetadata(valueParser);
981
+ 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}`;
937
982
  const optionPattern = /^--?[a-z0-9-]+$/i;
938
983
  const term = {
939
984
  type: "argument",
@@ -1007,7 +1052,6 @@ function argument(valueParser, options = {}) {
1007
1052
  });
1008
1053
  },
1009
1054
  complete(state, exec) {
1010
- 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}`;
1011
1055
  const missing = {
1012
1056
  success: false,
1013
1057
  error: options.errors?.endOfInput ?? require_message.message`Expected a ${require_message.metavar(valueParser.metavar)}, but too few arguments.`
@@ -1075,6 +1119,34 @@ function argument(valueParser, options = {}) {
1075
1119
  enumerable: false
1076
1120
  });
1077
1121
  }
1122
+ if (!require_dependency.isDerivedValueParser(valueParser)) {
1123
+ const vp = valueParser;
1124
+ const vpMode = valueParser.$mode;
1125
+ const wrapParseResult = (parsed) => parsed.success ? parsed : {
1126
+ success: false,
1127
+ error: formatInvalidValueError(parsed.error)
1128
+ };
1129
+ Object.defineProperty(result, "validateValue", {
1130
+ value(v) {
1131
+ let stringified;
1132
+ try {
1133
+ stringified = vp.format(v);
1134
+ } catch {
1135
+ return require_mode_dispatch.wrapForMode(vpMode, {
1136
+ success: true,
1137
+ value: v
1138
+ });
1139
+ }
1140
+ if (typeof stringified !== "string") return require_mode_dispatch.wrapForMode(vpMode, {
1141
+ success: true,
1142
+ value: v
1143
+ });
1144
+ return require_mode_dispatch.dispatchByMode(vpMode, () => wrapParseResult(syncValueParser.parse(stringified)), async () => wrapParseResult(await vp.parse(stringified)));
1145
+ },
1146
+ configurable: true,
1147
+ enumerable: false
1148
+ });
1149
+ }
1078
1150
  Object.defineProperty(result, "placeholder", {
1079
1151
  get() {
1080
1152
  try {
@@ -1316,6 +1388,11 @@ function command(name, parser, options = {}) {
1316
1388
  configurable: true,
1317
1389
  enumerable: false
1318
1390
  });
1391
+ if (typeof parser.validateValue === "function") Object.defineProperty(result, "validateValue", {
1392
+ value: parser.validateValue.bind(parser),
1393
+ configurable: true,
1394
+ enumerable: false
1395
+ });
1319
1396
  return result;
1320
1397
  }
1321
1398
  /**
@@ -3,7 +3,7 @@ import { message, metavar, optionName, optionNames, text, valueSet } from "./mes
3
3
  import { getDefaultValuesFunction, getDependencyIds, getSnapshottedDefaultDependencyValues, isDerivedValueParser, suggestWithDependency } from "./dependency.js";
4
4
  import { validateCommandNames, validateOptionNames } from "./validate.js";
5
5
  import { extractOptionNames, isDocHidden, isSuggestionHidden } from "./usage.js";
6
- import { dispatchByMode, dispatchIterableByMode } from "./mode-dispatch.js";
6
+ import { dispatchByMode, dispatchIterableByMode, wrapForMode } from "./mode-dispatch.js";
7
7
  import { extractDependencyMetadata } from "./dependency-metadata.js";
8
8
  import { DEFAULT_FIND_SIMILAR_OPTIONS, createErrorWithSuggestions, createSuggestionMessage, findSimilar } from "./suggestion.js";
9
9
  import { extractLeadingCommandNames } from "./usage-internals.js";
@@ -438,6 +438,7 @@ function option(...args) {
438
438
  const isAsync = mode === "async";
439
439
  const syncValueParser = valueParser;
440
440
  const dependencyMetadata = valueParser != null ? extractDependencyMetadata(valueParser) : void 0;
441
+ const formatInvalidValueError = (error) => options.errors?.invalidValue ? typeof options.errors.invalidValue === "function" ? options.errors.invalidValue(error) : options.errors.invalidValue : message`${optionNames(optionNames$1)}: ${error}`;
441
442
  const result = {
442
443
  $mode: mode,
443
444
  $valueType: [],
@@ -619,7 +620,6 @@ function option(...args) {
619
620
  };
620
621
  },
621
622
  complete(state, exec) {
622
- const formatInvalidValueError = (error) => options.errors?.invalidValue ? typeof options.errors.invalidValue === "function" ? options.errors.invalidValue(error) : options.errors.invalidValue : message`${optionNames(optionNames$1)}: ${error}`;
623
623
  const missing = valueParser == null ? {
624
624
  success: true,
625
625
  value: false
@@ -693,6 +693,50 @@ function option(...args) {
693
693
  enumerable: false
694
694
  });
695
695
  }
696
+ if (valueParser == null) Object.defineProperty(result, "validateValue", {
697
+ value(v) {
698
+ if (typeof v !== "boolean") {
699
+ const actualType = v === null ? "null" : typeof v;
700
+ return wrapForMode(mode, {
701
+ success: false,
702
+ error: formatInvalidValueError(message`Expected a boolean value, but received ${actualType}.`)
703
+ });
704
+ }
705
+ return wrapForMode(mode, {
706
+ success: true,
707
+ value: v
708
+ });
709
+ },
710
+ configurable: true,
711
+ enumerable: false
712
+ });
713
+ else if (!isDerivedValueParser(valueParser)) {
714
+ const vp = valueParser;
715
+ const wrapParseResult = (parsed) => parsed.success ? parsed : {
716
+ success: false,
717
+ error: formatInvalidValueError(parsed.error)
718
+ };
719
+ Object.defineProperty(result, "validateValue", {
720
+ value(v) {
721
+ let stringified;
722
+ try {
723
+ stringified = vp.format(v);
724
+ } catch {
725
+ return wrapForMode(mode, {
726
+ success: true,
727
+ value: v
728
+ });
729
+ }
730
+ if (typeof stringified !== "string") return wrapForMode(mode, {
731
+ success: true,
732
+ value: v
733
+ });
734
+ return dispatchByMode(mode, () => wrapParseResult(vp.parse(stringified)), async () => wrapParseResult(await vp.parse(stringified)));
735
+ },
736
+ configurable: true,
737
+ enumerable: false
738
+ });
739
+ }
696
740
  if (valueParser != null) Object.defineProperty(result, "placeholder", {
697
741
  get() {
698
742
  try {
@@ -934,6 +978,7 @@ function argument(valueParser, options = {}) {
934
978
  const isAsync = valueParser.$mode === "async";
935
979
  const syncValueParser = valueParser;
936
980
  const dependencyMetadata = extractDependencyMetadata(valueParser);
981
+ const formatInvalidValueError = (error) => options.errors?.invalidValue ? typeof options.errors.invalidValue === "function" ? options.errors.invalidValue(error) : options.errors.invalidValue : message`${metavar(valueParser.metavar)}: ${error}`;
937
982
  const optionPattern = /^--?[a-z0-9-]+$/i;
938
983
  const term = {
939
984
  type: "argument",
@@ -1007,7 +1052,6 @@ function argument(valueParser, options = {}) {
1007
1052
  });
1008
1053
  },
1009
1054
  complete(state, exec) {
1010
- const formatInvalidValueError = (error) => options.errors?.invalidValue ? typeof options.errors.invalidValue === "function" ? options.errors.invalidValue(error) : options.errors.invalidValue : message`${metavar(valueParser.metavar)}: ${error}`;
1011
1055
  const missing = {
1012
1056
  success: false,
1013
1057
  error: options.errors?.endOfInput ?? message`Expected a ${metavar(valueParser.metavar)}, but too few arguments.`
@@ -1075,6 +1119,34 @@ function argument(valueParser, options = {}) {
1075
1119
  enumerable: false
1076
1120
  });
1077
1121
  }
1122
+ if (!isDerivedValueParser(valueParser)) {
1123
+ const vp = valueParser;
1124
+ const vpMode = valueParser.$mode;
1125
+ const wrapParseResult = (parsed) => parsed.success ? parsed : {
1126
+ success: false,
1127
+ error: formatInvalidValueError(parsed.error)
1128
+ };
1129
+ Object.defineProperty(result, "validateValue", {
1130
+ value(v) {
1131
+ let stringified;
1132
+ try {
1133
+ stringified = vp.format(v);
1134
+ } catch {
1135
+ return wrapForMode(vpMode, {
1136
+ success: true,
1137
+ value: v
1138
+ });
1139
+ }
1140
+ if (typeof stringified !== "string") return wrapForMode(vpMode, {
1141
+ success: true,
1142
+ value: v
1143
+ });
1144
+ return dispatchByMode(vpMode, () => wrapParseResult(syncValueParser.parse(stringified)), async () => wrapParseResult(await vp.parse(stringified)));
1145
+ },
1146
+ configurable: true,
1147
+ enumerable: false
1148
+ });
1149
+ }
1078
1150
  Object.defineProperty(result, "placeholder", {
1079
1151
  get() {
1080
1152
  try {
@@ -1316,6 +1388,11 @@ function command(name, parser, options = {}) {
1316
1388
  configurable: true,
1317
1389
  enumerable: false
1318
1390
  });
1391
+ if (typeof parser.validateValue === "function") Object.defineProperty(result, "validateValue", {
1392
+ value: parser.validateValue.bind(parser),
1393
+ configurable: true,
1394
+ enumerable: false
1395
+ });
1319
1396
  return result;
1320
1397
  }
1321
1398
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optique/core",
3
- "version": "1.0.0-dev.1758+0d05b449",
3
+ "version": "1.0.0-dev.1766+87276239",
4
4
  "description": "Type-safe combinatorial command-line interface parser",
5
5
  "keywords": [
6
6
  "CLI",