@optique/core 1.0.0-dev.1495 → 1.0.0-dev.1500

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.
package/dist/parser.d.cts CHANGED
@@ -3,7 +3,7 @@ import { Message } from "./message.cjs";
3
3
  import { Usage } from "./usage.cjs";
4
4
  import { DocFragments, DocPage } from "./doc.cjs";
5
5
  import { DependencyRegistryLike } from "./registry-types.cjs";
6
- import { ValueParserResult } from "./valueparser.cjs";
6
+ import { DeferredMap, ValueParserResult } from "./valueparser.cjs";
7
7
  import { ConditionalErrorOptions, ConditionalOptions, DuplicateOptionError, GroupOptions, LongestMatchErrorOptions, LongestMatchOptions, MergeOptions, NoMatchContext, ObjectErrorOptions, ObjectOptions, OrErrorOptions, OrOptions, TupleOptions, concat, conditional, group, longestMatch, merge, object, or, tuple } from "./constructs.cjs";
8
8
  import { MultipleErrorOptions, MultipleOptions, WithDefaultError, WithDefaultOptions, map, multiple, nonEmpty, optional, withDefault } from "./modifiers.cjs";
9
9
  import { ArgumentErrorOptions, ArgumentOptions, CommandErrorOptions, CommandOptions, FlagErrorOptions, FlagOptions, OptionErrorOptions, OptionOptions, OptionState, PassThroughFormat, PassThroughOptions, argument, command, constant, fail, flag, option, passThrough } from "./primitives.cjs";
@@ -158,6 +158,20 @@ interface Parser<M extends Mode = "sync", TValue = unknown, TState = unknown> {
158
158
  * fragments for this parser.
159
159
  */
160
160
  getDocFragments(state: DocState<TState>, defaultValue?: TValue): DocFragments;
161
+ /**
162
+ * A type-appropriate default value used as a stand-in during deferred
163
+ * prompt resolution. When present, combinators like `prompt()` use this
164
+ * value instead of an internal sentinel during two-phase parsing, so that
165
+ * `map()` transforms and dynamic contexts always receive a valid value
166
+ * of type {@link TValue}.
167
+ *
168
+ * This property is set automatically by `option()` and `argument()` from
169
+ * the underlying {@link ValueParser}'s `placeholder`, and propagated by
170
+ * combinators like `map()`, `optional()`, and `withDefault()`.
171
+ *
172
+ * @since 1.0.0
173
+ */
174
+ readonly placeholder?: TValue;
161
175
  /**
162
176
  * Optional predicate that determines whether completion should be
163
177
  * deferred for the given parser state.
@@ -318,6 +332,19 @@ type Result<T> = {
318
332
  * have been applied and completed.
319
333
  */
320
334
  value: T;
335
+ /**
336
+ * When `true`, indicates that the value contains deferred prompt
337
+ * placeholders. Propagated from {@link ValueParserResult.deferred}.
338
+ * @since 1.0.0
339
+ */
340
+ deferred?: true;
341
+ /**
342
+ * Property keys (object field names or array indices) whose values are
343
+ * deferred placeholders.
344
+ * Propagated from {@link ValueParserResult.deferredKeys}.
345
+ * @since 1.0.0
346
+ */
347
+ deferredKeys?: DeferredMap;
321
348
  } | {
322
349
  /**
323
350
  * Indicates that the parsing operation failed.
package/dist/parser.d.ts CHANGED
@@ -3,7 +3,7 @@ import { Message } from "./message.js";
3
3
  import { Usage } from "./usage.js";
4
4
  import { DocFragments, DocPage } from "./doc.js";
5
5
  import { DependencyRegistryLike } from "./registry-types.js";
6
- import { ValueParserResult } from "./valueparser.js";
6
+ import { DeferredMap, ValueParserResult } from "./valueparser.js";
7
7
  import { ConditionalErrorOptions, ConditionalOptions, DuplicateOptionError, GroupOptions, LongestMatchErrorOptions, LongestMatchOptions, MergeOptions, NoMatchContext, ObjectErrorOptions, ObjectOptions, OrErrorOptions, OrOptions, TupleOptions, concat, conditional, group, longestMatch, merge, object, or, tuple } from "./constructs.js";
8
8
  import { MultipleErrorOptions, MultipleOptions, WithDefaultError, WithDefaultOptions, map, multiple, nonEmpty, optional, withDefault } from "./modifiers.js";
9
9
  import { ArgumentErrorOptions, ArgumentOptions, CommandErrorOptions, CommandOptions, FlagErrorOptions, FlagOptions, OptionErrorOptions, OptionOptions, OptionState, PassThroughFormat, PassThroughOptions, argument, command, constant, fail, flag, option, passThrough } from "./primitives.js";
@@ -158,6 +158,20 @@ interface Parser<M extends Mode = "sync", TValue = unknown, TState = unknown> {
158
158
  * fragments for this parser.
159
159
  */
160
160
  getDocFragments(state: DocState<TState>, defaultValue?: TValue): DocFragments;
161
+ /**
162
+ * A type-appropriate default value used as a stand-in during deferred
163
+ * prompt resolution. When present, combinators like `prompt()` use this
164
+ * value instead of an internal sentinel during two-phase parsing, so that
165
+ * `map()` transforms and dynamic contexts always receive a valid value
166
+ * of type {@link TValue}.
167
+ *
168
+ * This property is set automatically by `option()` and `argument()` from
169
+ * the underlying {@link ValueParser}'s `placeholder`, and propagated by
170
+ * combinators like `map()`, `optional()`, and `withDefault()`.
171
+ *
172
+ * @since 1.0.0
173
+ */
174
+ readonly placeholder?: TValue;
161
175
  /**
162
176
  * Optional predicate that determines whether completion should be
163
177
  * deferred for the given parser state.
@@ -318,6 +332,19 @@ type Result<T> = {
318
332
  * have been applied and completed.
319
333
  */
320
334
  value: T;
335
+ /**
336
+ * When `true`, indicates that the value contains deferred prompt
337
+ * placeholders. Propagated from {@link ValueParserResult.deferred}.
338
+ * @since 1.0.0
339
+ */
340
+ deferred?: true;
341
+ /**
342
+ * Property keys (object field names or array indices) whose values are
343
+ * deferred placeholders.
344
+ * Propagated from {@link ValueParserResult.deferredKeys}.
345
+ * @since 1.0.0
346
+ */
347
+ deferredKeys?: DeferredMap;
321
348
  } | {
322
349
  /**
323
350
  * Indicates that the parsing operation failed.
package/dist/parser.js CHANGED
@@ -60,7 +60,9 @@ function parseSync(parser, args, options) {
60
60
  const endResult = parser.complete(context.state);
61
61
  return endResult.success ? {
62
62
  success: true,
63
- value: shouldUnwrapAnnotatedValue ? unwrapInjectedAnnotationWrapper(endResult.value) : endResult.value
63
+ value: shouldUnwrapAnnotatedValue ? unwrapInjectedAnnotationWrapper(endResult.value) : endResult.value,
64
+ ...endResult.deferred ? { deferred: true } : {},
65
+ ...endResult.deferredKeys ? { deferredKeys: endResult.deferredKeys } : {}
64
66
  } : {
65
67
  success: false,
66
68
  error: endResult.error
@@ -110,7 +112,9 @@ async function parseAsync(parser, args, options) {
110
112
  const endResult = await parser.complete(context.state);
111
113
  return endResult.success ? {
112
114
  success: true,
113
- value: shouldUnwrapAnnotatedValue ? unwrapInjectedAnnotationWrapper(endResult.value) : endResult.value
115
+ value: shouldUnwrapAnnotatedValue ? unwrapInjectedAnnotationWrapper(endResult.value) : endResult.value,
116
+ ...endResult.deferred ? { deferred: true } : {},
117
+ ...endResult.deferredKeys ? { deferredKeys: endResult.deferredKeys } : {}
114
118
  } : {
115
119
  success: false,
116
120
  error: endResult.error
@@ -32,7 +32,7 @@ function createOptionParseState(rawInput, valueParser, parseResult) {
32
32
  * @template T The type of the constant value produced by the parser.
33
33
  */
34
34
  function constant(value) {
35
- return {
35
+ const result = {
36
36
  $valueType: [],
37
37
  $stateType: [],
38
38
  $mode: "sync",
@@ -59,6 +59,13 @@ function constant(value) {
59
59
  return { fragments: [] };
60
60
  }
61
61
  };
62
+ Object.defineProperty(result, "placeholder", {
63
+ value,
64
+ configurable: true,
65
+ enumerable: false,
66
+ writable: false
67
+ });
68
+ return result;
62
69
  }
63
70
  /**
64
71
  * Creates a parser that always fails without consuming any input.
@@ -546,6 +553,23 @@ function option(...args) {
546
553
  return `option(${optionNames$1.map((o) => JSON.stringify(o)).join(", ")})`;
547
554
  }
548
555
  };
556
+ if (valueParser != null) Object.defineProperty(result, "placeholder", {
557
+ get() {
558
+ try {
559
+ return valueParser.placeholder;
560
+ } catch {
561
+ return void 0;
562
+ }
563
+ },
564
+ configurable: true,
565
+ enumerable: false
566
+ });
567
+ else Object.defineProperty(result, "placeholder", {
568
+ value: false,
569
+ configurable: true,
570
+ enumerable: false,
571
+ writable: false
572
+ });
549
573
  return result;
550
574
  }
551
575
  /**
@@ -588,7 +612,7 @@ function flag(...args) {
588
612
  optionNames$1 = args.slice(0, -1);
589
613
  } else optionNames$1 = args;
590
614
  require_validate.validateOptionNames(optionNames$1, "Flag");
591
- return {
615
+ const result = {
592
616
  $valueType: [],
593
617
  $stateType: [],
594
618
  $mode: "sync",
@@ -738,6 +762,13 @@ function flag(...args) {
738
762
  return `flag(${optionNames$1.map((o) => JSON.stringify(o)).join(", ")})`;
739
763
  }
740
764
  };
765
+ Object.defineProperty(result, "placeholder", {
766
+ value: true,
767
+ configurable: true,
768
+ enumerable: false,
769
+ writable: false
770
+ });
771
+ return result;
741
772
  }
742
773
  /**
743
774
  * Creates a parser that expects a single argument value.
@@ -872,6 +903,17 @@ function argument(valueParser, options = {}) {
872
903
  return `argument()`;
873
904
  }
874
905
  };
906
+ Object.defineProperty(result, "placeholder", {
907
+ get() {
908
+ try {
909
+ return valueParser.placeholder;
910
+ } catch {
911
+ return void 0;
912
+ }
913
+ },
914
+ configurable: true,
915
+ enumerable: false
916
+ });
875
917
  return result;
876
918
  }
877
919
  function* suggestCommandSync(context, prefix, name, parser, options) {
@@ -32,7 +32,7 @@ function createOptionParseState(rawInput, valueParser, parseResult) {
32
32
  * @template T The type of the constant value produced by the parser.
33
33
  */
34
34
  function constant(value) {
35
- return {
35
+ const result = {
36
36
  $valueType: [],
37
37
  $stateType: [],
38
38
  $mode: "sync",
@@ -59,6 +59,13 @@ function constant(value) {
59
59
  return { fragments: [] };
60
60
  }
61
61
  };
62
+ Object.defineProperty(result, "placeholder", {
63
+ value,
64
+ configurable: true,
65
+ enumerable: false,
66
+ writable: false
67
+ });
68
+ return result;
62
69
  }
63
70
  /**
64
71
  * Creates a parser that always fails without consuming any input.
@@ -546,6 +553,23 @@ function option(...args) {
546
553
  return `option(${optionNames$1.map((o) => JSON.stringify(o)).join(", ")})`;
547
554
  }
548
555
  };
556
+ if (valueParser != null) Object.defineProperty(result, "placeholder", {
557
+ get() {
558
+ try {
559
+ return valueParser.placeholder;
560
+ } catch {
561
+ return void 0;
562
+ }
563
+ },
564
+ configurable: true,
565
+ enumerable: false
566
+ });
567
+ else Object.defineProperty(result, "placeholder", {
568
+ value: false,
569
+ configurable: true,
570
+ enumerable: false,
571
+ writable: false
572
+ });
549
573
  return result;
550
574
  }
551
575
  /**
@@ -588,7 +612,7 @@ function flag(...args) {
588
612
  optionNames$1 = args.slice(0, -1);
589
613
  } else optionNames$1 = args;
590
614
  validateOptionNames(optionNames$1, "Flag");
591
- return {
615
+ const result = {
592
616
  $valueType: [],
593
617
  $stateType: [],
594
618
  $mode: "sync",
@@ -738,6 +762,13 @@ function flag(...args) {
738
762
  return `flag(${optionNames$1.map((o) => JSON.stringify(o)).join(", ")})`;
739
763
  }
740
764
  };
765
+ Object.defineProperty(result, "placeholder", {
766
+ value: true,
767
+ configurable: true,
768
+ enumerable: false,
769
+ writable: false
770
+ });
771
+ return result;
741
772
  }
742
773
  /**
743
774
  * Creates a parser that expects a single argument value.
@@ -872,6 +903,17 @@ function argument(valueParser, options = {}) {
872
903
  return `argument()`;
873
904
  }
874
905
  };
906
+ Object.defineProperty(result, "placeholder", {
907
+ get() {
908
+ try {
909
+ return valueParser.placeholder;
910
+ } catch {
911
+ return void 0;
912
+ }
913
+ },
914
+ configurable: true,
915
+ enumerable: false
916
+ });
875
917
  return result;
876
918
  }
877
919
  function* suggestCommandSync(context, prefix, name, parser, options) {
@@ -81,8 +81,11 @@ function findSimilar(input, candidates, options = {}) {
81
81
  if (input.length === 0) return [];
82
82
  const normalizedInput = caseSensitive ? input : input.toLowerCase();
83
83
  const matches = [];
84
+ const seen = /* @__PURE__ */ new Set();
84
85
  for (const candidate of candidates) {
85
86
  const normalizedCandidate = caseSensitive ? candidate : candidate.toLowerCase();
87
+ if (seen.has(candidate)) continue;
88
+ seen.add(candidate);
86
89
  const distance = levenshteinDistance(normalizedInput, normalizedCandidate);
87
90
  if (distance === 0) return [candidate];
88
91
  const distanceRatio = distance / input.length;
@@ -81,8 +81,11 @@ function findSimilar(input, candidates, options = {}) {
81
81
  if (input.length === 0) return [];
82
82
  const normalizedInput = caseSensitive ? input : input.toLowerCase();
83
83
  const matches = [];
84
+ const seen = /* @__PURE__ */ new Set();
84
85
  for (const candidate of candidates) {
85
86
  const normalizedCandidate = caseSensitive ? candidate : candidate.toLowerCase();
87
+ if (seen.has(candidate)) continue;
88
+ seen.add(candidate);
86
89
  const distance = levenshteinDistance(normalizedInput, normalizedCandidate);
87
90
  if (distance === 0) return [candidate];
88
91
  const distanceRatio = distance / input.length;
@@ -6,9 +6,18 @@ const require_nonempty = require('./nonempty.cjs');
6
6
  * A predicate function that checks if an object is a {@link ValueParser}.
7
7
  * @param object The object to check.
8
8
  * @return `true` if the object is a {@link ValueParser}, `false` otherwise.
9
+ * @throws {TypeError} If the object looks like a value parser (has `$mode`,
10
+ * `metavar`, `parse`, and `format`) but is missing the required
11
+ * `placeholder` property.
9
12
  */
10
13
  function isValueParser(object) {
11
- return typeof object === "object" && object != null && "$mode" in object && (object.$mode === "sync" || object.$mode === "async") && "metavar" in object && typeof object.metavar === "string" && "parse" in object && typeof object.parse === "function" && "format" in object && typeof object.format === "function";
14
+ if (typeof object !== "object" || object == null || !("$mode" in object) || object.$mode !== "sync" && object.$mode !== "async") return false;
15
+ const hasMetavar = "metavar" in object && typeof object.metavar === "string";
16
+ const hasParse = "parse" in object && typeof object.parse === "function";
17
+ const hasFormat = "format" in object && typeof object.format === "function";
18
+ const hasPlaceholder = "placeholder" in object;
19
+ if (hasMetavar && hasParse && hasFormat && !hasPlaceholder) throw new TypeError("Value parser is missing the required placeholder property. All value parsers must define a placeholder value.");
20
+ return hasMetavar && hasParse && hasFormat && hasPlaceholder;
12
21
  }
13
22
  /**
14
23
  * Implementation of the choice parser for both string and number types.
@@ -49,6 +58,7 @@ function choice(choices, options = {}) {
49
58
  return {
50
59
  $mode: "sync",
51
60
  metavar,
61
+ placeholder: choices[0],
52
62
  choices: frozenNumberChoices,
53
63
  parse(input) {
54
64
  const index = numberStrings.indexOf(input);
@@ -130,6 +140,7 @@ function choice(choices, options = {}) {
130
140
  return {
131
141
  $mode: "sync",
132
142
  metavar,
143
+ placeholder: choices[0],
133
144
  choices: stringChoices,
134
145
  parse(input) {
135
146
  const normalizedInput = caseInsensitive ? input.toLowerCase() : input;
@@ -288,6 +299,7 @@ function string(options = {}) {
288
299
  return {
289
300
  $mode: "sync",
290
301
  metavar,
302
+ placeholder: options.placeholder ?? "",
291
303
  parse(input) {
292
304
  if (patternSource != null && patternFlags != null) {
293
305
  const pattern = new RegExp(patternSource, patternFlags);
@@ -344,6 +356,8 @@ function string(options = {}) {
344
356
  * integer type.
345
357
  * @throws {TypeError} If `options.type` is provided but is neither `"number"`
346
358
  * nor `"bigint"`.
359
+ * @throws {RangeError} If the configured min/max range for number mode contains
360
+ * no safe integers.
347
361
  */
348
362
  function integer(options) {
349
363
  if (options?.type !== void 0 && options.type !== "number" && options.type !== "bigint") throw new TypeError(`Expected type to be "number" or "bigint", but got: ${String(options.type)}.`);
@@ -358,6 +372,7 @@ function integer(options) {
358
372
  return {
359
373
  $mode: "sync",
360
374
  metavar: metavar$1,
375
+ placeholder: options?.placeholder ?? (options?.min != null && options.min > 0n ? options.min : options?.max != null && options.max < 0n ? options.max : 0n),
361
376
  parse(input) {
362
377
  if (!input.match(/^-?\d+$/)) return {
363
378
  success: false,
@@ -386,6 +401,11 @@ function integer(options) {
386
401
  require_nonempty.ensureNonEmptyString(metavar);
387
402
  const maxSafe = BigInt(Number.MAX_SAFE_INTEGER);
388
403
  const minSafe = BigInt(Number.MIN_SAFE_INTEGER);
404
+ const safeMin = Math.max(options?.min ?? Number.MIN_SAFE_INTEGER, Number.MIN_SAFE_INTEGER);
405
+ const safeMax = Math.min(options?.max ?? Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER);
406
+ const firstAllowed = Math.ceil(safeMin);
407
+ const lastAllowed = Math.floor(safeMax);
408
+ if (firstAllowed > lastAllowed) throw new RangeError("The configured integer range contains no safe integers. Use type: \"bigint\" instead.");
389
409
  const unsafeIntegerError = options?.errors?.unsafeInteger;
390
410
  function makeUnsafeIntegerError(input) {
391
411
  return {
@@ -396,6 +416,7 @@ function integer(options) {
396
416
  return {
397
417
  $mode: "sync",
398
418
  metavar,
419
+ placeholder: options?.placeholder ?? (firstAllowed > 0 ? firstAllowed : lastAllowed < 0 ? lastAllowed : 0),
399
420
  parse(input) {
400
421
  if (!input.match(/^-?\d+$/)) return {
401
422
  success: false,
@@ -446,6 +467,7 @@ function float(options = {}) {
446
467
  return {
447
468
  $mode: "sync",
448
469
  metavar,
470
+ placeholder: options?.placeholder ?? (options?.min != null && options.min > 0 ? options.min : options?.max != null && options.max < 0 ? options.max : 0),
449
471
  parse(input) {
450
472
  const invalidNumber = (i) => ({
451
473
  success: false,
@@ -530,6 +552,9 @@ function url(options = {}) {
530
552
  return {
531
553
  $mode: "sync",
532
554
  metavar,
555
+ get placeholder() {
556
+ return new URL(`${allowedProtocols?.[0] ?? "http:"}//0.invalid`);
557
+ },
533
558
  parse(input) {
534
559
  if (!URL.canParse(input)) return {
535
560
  success: false,
@@ -596,6 +621,7 @@ function locale(options = {}) {
596
621
  return {
597
622
  $mode: "sync",
598
623
  metavar,
624
+ placeholder: new Intl.Locale("und"),
599
625
  parse(input) {
600
626
  let locale$1;
601
627
  try {
@@ -891,6 +917,7 @@ function uuid(options = {}) {
891
917
  return {
892
918
  $mode: "sync",
893
919
  metavar,
920
+ placeholder: "00000000-0000-0000-0000-000000000000",
894
921
  parse(input) {
895
922
  if (!uuidRegex.test(input)) return {
896
923
  success: false,
@@ -994,9 +1021,11 @@ function port(options) {
994
1021
  const min$1 = options.min ?? 1n;
995
1022
  const max$1 = options.max ?? 65535n;
996
1023
  if (min$1 > max$1) throw new RangeError(`Expected min to be less than or equal to max, but got min: ${min$1} and max: ${max$1}.`);
1024
+ if (options.disallowWellKnown && min$1 < 1024n && max$1 < 1024n) throw new RangeError(`disallowWellKnown is incompatible with the configured port range: all ports ${min$1}..${max$1} are well-known.`);
997
1025
  return {
998
1026
  $mode: "sync",
999
1027
  metavar: metavar$1,
1028
+ placeholder: options.placeholder ?? (options.disallowWellKnown && min$1 < 1024n ? 1024n > min$1 ? 1024n : min$1 : min$1),
1000
1029
  parse(input) {
1001
1030
  if (!input.match(/^-?\d+$/)) return {
1002
1031
  success: false,
@@ -1032,9 +1061,11 @@ function port(options) {
1032
1061
  const min = options?.min ?? 1;
1033
1062
  const max = options?.max ?? 65535;
1034
1063
  if (min > max) throw new RangeError(`Expected min to be less than or equal to max, but got min: ${min} and max: ${max}.`);
1064
+ if (options?.disallowWellKnown && min < 1024 && max < 1024) throw new RangeError(`disallowWellKnown is incompatible with the configured port range: all ports ${min}..${max} are well-known.`);
1035
1065
  return {
1036
1066
  $mode: "sync",
1037
1067
  metavar,
1068
+ placeholder: options?.placeholder ?? (options?.disallowWellKnown && min < 1024 ? Math.max(1024, min) : min),
1038
1069
  parse(input) {
1039
1070
  if (!input.match(/^-?\d+$/)) return {
1040
1071
  success: false,
@@ -1128,6 +1159,7 @@ function ipv4(options) {
1128
1159
  return {
1129
1160
  $mode: "sync",
1130
1161
  metavar,
1162
+ placeholder: allowZero ? "0.0.0.0" : allowLoopback ? "127.0.0.1" : "192.0.2.1",
1131
1163
  parse(input) {
1132
1164
  const parts = input.split(".");
1133
1165
  if (parts.length !== 4) {
@@ -1289,6 +1321,7 @@ function hostname(options) {
1289
1321
  return {
1290
1322
  $mode: "sync",
1291
1323
  metavar,
1324
+ placeholder: options?.placeholder ?? (allowLocalhost ? maxLength >= 9 ? "localhost" : "a.bc" : maxLength >= 11 ? "example.com" : "a.bc"),
1292
1325
  parse(input) {
1293
1326
  if (input.length > maxLength) {
1294
1327
  const errorMsg = options?.errors?.tooLong;
@@ -1408,6 +1441,10 @@ function email(options) {
1408
1441
  const metavar = options?.metavar ?? "EMAIL";
1409
1442
  require_nonempty.ensureNonEmptyString(metavar);
1410
1443
  const allowMultiple = options?.allowMultiple ?? false;
1444
+ if (options?.placeholder != null) {
1445
+ if (allowMultiple && !Array.isArray(options.placeholder)) throw new TypeError("email() placeholder must be an array when allowMultiple is true.");
1446
+ if (!allowMultiple && typeof options.placeholder !== "string") throw new TypeError("email() placeholder must be a string when allowMultiple is false.");
1447
+ }
1411
1448
  const allowDisplayName = options?.allowDisplayName ?? false;
1412
1449
  const lowercase = options?.lowercase ?? false;
1413
1450
  const allowedDomains = options?.allowedDomains != null ? Object.freeze([...options.allowedDomains]) : void 0;
@@ -1504,6 +1541,7 @@ function email(options) {
1504
1541
  return {
1505
1542
  $mode: "sync",
1506
1543
  metavar,
1544
+ placeholder: options?.placeholder ?? (options?.allowMultiple ? [`user@${options?.allowedDomains?.[0] ?? "example.com"}`] : `user@${options?.allowedDomains?.[0] ?? "example.com"}`),
1507
1545
  parse(input) {
1508
1546
  if (allowMultiple) {
1509
1547
  const emails = splitEmails(input).map((e) => e.trim());
@@ -1724,6 +1762,12 @@ function socketAddress(options) {
1724
1762
  return {
1725
1763
  $mode: "sync",
1726
1764
  metavar,
1765
+ get placeholder() {
1766
+ return {
1767
+ host: hostType === "ip" ? ipParser.placeholder : hostnameParser.placeholder,
1768
+ port: defaultPort ?? portParser.placeholder
1769
+ };
1770
+ },
1727
1771
  parse(input) {
1728
1772
  const trimmed = input.trim();
1729
1773
  const canOmitPort = defaultPort !== void 0 && !requirePort;
@@ -1936,6 +1980,15 @@ function portRange(options) {
1936
1980
  return {
1937
1981
  $mode: "sync",
1938
1982
  metavar,
1983
+ get placeholder() {
1984
+ return isBigInt ? {
1985
+ start: portParser.placeholder,
1986
+ end: portParser.placeholder
1987
+ } : {
1988
+ start: portParser.placeholder,
1989
+ end: portParser.placeholder
1990
+ };
1991
+ },
1939
1992
  parse(input) {
1940
1993
  const trimmed = input.trim();
1941
1994
  const separatorIndex = trimmed.indexOf(separator);
@@ -2094,6 +2147,20 @@ function macAddress(options) {
2094
2147
  return {
2095
2148
  $mode: "sync",
2096
2149
  metavar,
2150
+ get placeholder() {
2151
+ const octets = [
2152
+ "00",
2153
+ "00",
2154
+ "00",
2155
+ "00",
2156
+ "00",
2157
+ "00"
2158
+ ];
2159
+ const sep = outputSeparator ?? (separator === "any" ? ":" : separator);
2160
+ if (sep === ".") return `${octets[0]}${octets[1]}.${octets[2]}${octets[3]}.${octets[4]}${octets[5]}`;
2161
+ if (sep === "none") return octets.join("");
2162
+ return octets.join(sep);
2163
+ },
2097
2164
  parse(input) {
2098
2165
  let octets = [];
2099
2166
  let inputSeparator;
@@ -2250,6 +2317,7 @@ function domain(options) {
2250
2317
  return {
2251
2318
  $mode: "sync",
2252
2319
  metavar,
2320
+ placeholder: options?.placeholder ?? `example.${allowedTldsLower?.[0] ?? "com"}`,
2253
2321
  parse(input) {
2254
2322
  if (input.length > maxLength) {
2255
2323
  const errorMsg = tooLong;
@@ -2489,6 +2557,7 @@ function ipv6(options) {
2489
2557
  return {
2490
2558
  $mode: "sync",
2491
2559
  metavar,
2560
+ placeholder: allowZero ? "::" : allowLoopback ? "::1" : "2001:db8::1",
2492
2561
  parse(input) {
2493
2562
  const normalized = parseAndNormalizeIpv6(input);
2494
2563
  if (normalized === null) {
@@ -2905,6 +2974,7 @@ function ip(options) {
2905
2974
  return {
2906
2975
  $mode: "sync",
2907
2976
  metavar,
2977
+ placeholder: version === 6 ? ipv6Parser.placeholder : ipv4Parser.placeholder,
2908
2978
  parse(input) {
2909
2979
  let ipv4Error = null;
2910
2980
  let ipv6Error = null;
@@ -3046,6 +3116,17 @@ function cidr(options) {
3046
3116
  return {
3047
3117
  $mode: "sync",
3048
3118
  metavar,
3119
+ get placeholder() {
3120
+ return version === 6 || version === "both" && (minPrefix ?? 0) > 32 ? {
3121
+ address: ipv6Parser.placeholder,
3122
+ prefix: minPrefix ?? 0,
3123
+ version: 6
3124
+ } : {
3125
+ address: ipv4Parser.placeholder,
3126
+ prefix: minPrefix ?? 0,
3127
+ version: 4
3128
+ };
3129
+ },
3049
3130
  parse(input) {
3050
3131
  const slashIndex = input.lastIndexOf("/");
3051
3132
  if (slashIndex === -1) {