@optique/core 1.4.0-dev.2708 → 1.4.0-dev.2711

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.
@@ -0,0 +1,246 @@
1
+ //#region src/internal/usage.ts
2
+ const EMPTY = {
3
+ visible: false,
4
+ omissible: true,
5
+ hidesRequired: false
6
+ };
7
+ function isHiddenLeaf(term, isHidden) {
8
+ return (term.type === "argument" || term.type === "option" || term.type === "command" || term.type === "passthrough") && isHidden(term.hidden);
9
+ }
10
+ function isDegenerateLeaf(term) {
11
+ return term.type === "option" && term.names.length === 0 || term.type === "command" && term.name === "" || term.type === "argument" && term.metavar.length === 0 || term.type === "literal" && term.value === "";
12
+ }
13
+ function projectTerms(terms, isHidden) {
14
+ let visible = false;
15
+ let omissible = true;
16
+ let hidesRequired = false;
17
+ for (const term of terms) {
18
+ const projection = projectTerm(term, isHidden);
19
+ hidesRequired ||= projection.hidesRequired;
20
+ if (!projection.visible) continue;
21
+ visible = true;
22
+ omissible &&= projection.omissible;
23
+ }
24
+ return {
25
+ visible,
26
+ omissible,
27
+ hidesRequired
28
+ };
29
+ }
30
+ /**
31
+ * Projects an alternative of an exclusive term. Display filtering drops an
32
+ * alternative whose leading command is hidden as a whole, trailing terms
33
+ * included.
34
+ */
35
+ function projectBranch(branch, isHidden) {
36
+ const first = branch[0];
37
+ if (first?.type === "command" && isHidden(first.hidden)) return {
38
+ ...EMPTY,
39
+ hidesRequired: true
40
+ };
41
+ return projectTerms(branch, isHidden);
42
+ }
43
+ function projectTerm(term, isHidden) {
44
+ if (isHiddenLeaf(term, isHidden)) return {
45
+ ...EMPTY,
46
+ hidesRequired: term.type !== "passthrough"
47
+ };
48
+ if (isDegenerateLeaf(term)) return EMPTY;
49
+ switch (term.type) {
50
+ case "argument":
51
+ case "option":
52
+ case "command":
53
+ case "literal": return {
54
+ visible: true,
55
+ omissible: false,
56
+ hidesRequired: false
57
+ };
58
+ case "passthrough":
59
+ case "ellipsis": return {
60
+ visible: true,
61
+ omissible: true,
62
+ hidesRequired: false
63
+ };
64
+ case "optional": {
65
+ const inner = projectTerms(term.terms, isHidden);
66
+ return {
67
+ ...inner,
68
+ omissible: true,
69
+ hidesRequired: false
70
+ };
71
+ }
72
+ case "sequence": return projectTerms(term.terms, isHidden);
73
+ case "multiple": {
74
+ const inner = projectTerms(term.terms, isHidden);
75
+ return {
76
+ ...inner,
77
+ omissible: term.min < 1 || inner.omissible,
78
+ hidesRequired: term.min > 0 && inner.hidesRequired
79
+ };
80
+ }
81
+ case "exclusive": {
82
+ let visible = false;
83
+ let omissible = false;
84
+ let hidesRequired = false;
85
+ for (const branch of term.terms) {
86
+ const projection = projectBranch(branch, isHidden);
87
+ hidesRequired ||= projection.hidesRequired;
88
+ if (!projection.visible) continue;
89
+ visible = true;
90
+ omissible ||= projection.omissible;
91
+ }
92
+ return {
93
+ visible,
94
+ omissible,
95
+ hidesRequired
96
+ };
97
+ }
98
+ }
99
+ }
100
+ /**
101
+ * Returns the terms `X` when the visible part of `terms` is exactly
102
+ * `optional(X)`, possibly nested, with a required `X`, and no required term
103
+ * was hidden.
104
+ */
105
+ function unwrapSingleOptional(terms, isHidden) {
106
+ if (projectTerms(terms, isHidden).hidesRequired) return void 0;
107
+ const visible = terms.filter((t) => projectTerm(t, isHidden).visible);
108
+ if (visible.length !== 1) return void 0;
109
+ const [only] = visible;
110
+ if (only.type !== "optional") return void 0;
111
+ const inner = projectTerms(only.terms, isHidden);
112
+ if (!inner.visible || inner.hidesRequired) return void 0;
113
+ if (inner.omissible) return unwrapSingleOptional(only.terms, isHidden);
114
+ return only.terms;
115
+ }
116
+ function resolveTerms(terms, isHidden) {
117
+ return terms.map((term) => resolveTerm(term, isHidden));
118
+ }
119
+ function resolveTerm(term, isHidden) {
120
+ switch (term.type) {
121
+ case "optional": return {
122
+ type: "optional",
123
+ terms: resolveTerms(term.terms, isHidden)
124
+ };
125
+ case "sequence": return {
126
+ type: "sequence",
127
+ terms: resolveTerms(term.terms, isHidden)
128
+ };
129
+ case "multiple": {
130
+ const terms = resolveTerms(term.terms, isHidden);
131
+ const resolved = {
132
+ type: "multiple",
133
+ terms,
134
+ min: term.min
135
+ };
136
+ if (term.acceptsEmpty == null) return resolved;
137
+ const projection = projectTerm(resolved, isHidden);
138
+ if (!projection.visible || projection.omissible === term.acceptsEmpty) return resolved;
139
+ if (term.acceptsEmpty) return {
140
+ type: "optional",
141
+ terms: [resolved]
142
+ };
143
+ if (term.min < 1) return resolved;
144
+ const required = unwrapSingleOptional(terms, isHidden);
145
+ return required == null ? resolved : {
146
+ ...resolved,
147
+ terms: required
148
+ };
149
+ }
150
+ case "exclusive": {
151
+ const branches = term.terms.map((branch) => resolveTerms(branch, isHidden));
152
+ const resolved = {
153
+ type: "exclusive",
154
+ terms: branches
155
+ };
156
+ if (term.acceptsEmpty == null) return resolved;
157
+ const projection = projectTerm(resolved, isHidden);
158
+ if (!projection.visible || projection.omissible === term.acceptsEmpty) return resolved;
159
+ if (term.acceptsEmpty) {
160
+ const visible = branches.filter((branch) => projectBranch(branch, isHidden).visible);
161
+ return {
162
+ type: "optional",
163
+ terms: visible.length === 1 ? visible[0] : [{
164
+ type: "exclusive",
165
+ terms: visible
166
+ }]
167
+ };
168
+ }
169
+ const required = [];
170
+ for (const branch of branches) {
171
+ const branchProjection = projectBranch(branch, isHidden);
172
+ if (!branchProjection.visible || !branchProjection.omissible) {
173
+ required.push(branch);
174
+ continue;
175
+ }
176
+ const unwrapped = unwrapSingleOptional(branch, isHidden);
177
+ if (unwrapped == null) return resolved;
178
+ required.push(unwrapped);
179
+ }
180
+ return {
181
+ type: "exclusive",
182
+ terms: required
183
+ };
184
+ }
185
+ default: return term;
186
+ }
187
+ }
188
+ /**
189
+ * Rewrites a usage so that plain synopsis notation reflects the recorded
190
+ * empty-input behavior of its groups.
191
+ *
192
+ * Each `exclusive` or `multiple` term that records `acceptsEmpty` is drawn
193
+ * as omissible exactly when its parser accepts an empty argument list,
194
+ * using only exact local rewrites: a group that should be omissible is
195
+ * wrapped in an optional term, and a group that should be required has
196
+ * `optional(X)` alternatives or repeated items replaced by `X`. A group
197
+ * whose visible terms cannot be drawn that way keeps its declared notation.
198
+ * Enclosing groups are resolved after their children, so their constraints
199
+ * take precedence.
200
+ *
201
+ * The result records no `acceptsEmpty` fields, so resolving it again
202
+ * returns an equal usage. Hidden terms are kept; callers filter them for
203
+ * display afterwards.
204
+ * @param usage The usage to resolve.
205
+ * @param isHidden Reports whether a term is hidden in the rendering context.
206
+ * @returns The resolved usage.
207
+ * @internal
208
+ */
209
+ function resolveUsageForDisplay(usage, isHidden) {
210
+ return resolveTerms(usage, isHidden);
211
+ }
212
+ /**
213
+ * Removes every `acceptsEmpty` record from a usage without changing how it
214
+ * is drawn otherwise.
215
+ * @param usage The usage to copy.
216
+ * @returns The usage without records.
217
+ * @internal
218
+ */
219
+ function forgetEmptyInput(usage) {
220
+ return usage.map(forgetEmptyInputTerm);
221
+ }
222
+ function forgetEmptyInputTerm(term) {
223
+ switch (term.type) {
224
+ case "optional": return {
225
+ type: "optional",
226
+ terms: forgetEmptyInput(term.terms)
227
+ };
228
+ case "sequence": return {
229
+ type: "sequence",
230
+ terms: forgetEmptyInput(term.terms)
231
+ };
232
+ case "multiple": return {
233
+ type: "multiple",
234
+ terms: forgetEmptyInput(term.terms),
235
+ min: term.min
236
+ };
237
+ case "exclusive": return {
238
+ type: "exclusive",
239
+ terms: term.terms.map(forgetEmptyInput)
240
+ };
241
+ default: return term;
242
+ }
243
+ }
244
+
245
+ //#endregion
246
+ export { forgetEmptyInput, resolveUsageForDisplay };
@@ -4,6 +4,7 @@ const require_mode_dispatch = require('./internal/mode-dispatch.cjs');
4
4
  const require_dependency_runtime = require('./dependency-runtime.cjs');
5
5
  const require_internal_parser = require('./internal/parser.cjs');
6
6
  const require_annotation_state = require('./annotation-state.cjs');
7
+ const require_empty_input = require('./internal/empty-input.cjs');
7
8
  const require_execution_context = require('./execution-context.cjs');
8
9
  const require_short_option = require('./short-option.cjs');
9
10
  const require_passthrough = require('./internal/passthrough.cjs');
@@ -27,6 +28,14 @@ function isUnstartedMultipleItemState(state, originalState) {
27
28
  const unwrappedOriginalState = unwrapMultipleItemState(originalState);
28
29
  return unwrappedState.value === unwrappedOriginalState.value && (unwrappedState.viaBoundCliWrapper || unwrappedOriginalState.viaBoundCliWrapper || unwrappedOriginalState.value != null && typeof unwrappedOriginalState.value === "object");
29
30
  }
31
+ /**
32
+ * Returns whether an optional-style wrapper around the given parser is known
33
+ * to complete successfully from its initial state without delegating to a
34
+ * source binding or a deferred completion.
35
+ */
36
+ function completesOptionalLikeFallback(parser) {
37
+ return typeof parser.shouldDeferCompletion !== "function" && parser.dependencyMetadata?.source == null && parser[require_internal_parser.unmatchedNonCliDependencySourceStateMarker] !== true ? true : void 0;
38
+ }
30
39
  function isBoundCliWrapperState(state) {
31
40
  return state != null && typeof state === "object" && Object.hasOwn(state, "hasCliValue") && typeof state.hasCliValue === "boolean" && (Object.hasOwn(state, "cliState") || state.hasCliValue === false);
32
41
  }
@@ -485,7 +494,9 @@ function optional(parser) {
485
494
  state: deriveOptionalInnerParseState(state, parser)
486
495
  }]);
487
496
  require_passthrough.defineKnownCompletionLookup(optionalParser, (state) => require_passthrough.getKnownCompletion(parser, deriveOptionalInnerParseState(state, parser)));
488
- return fluent(require_short_option.scopeParser(optionalParser, require_short_option.combinedOptionScope([parser])));
497
+ const scopedOptionalParser = require_short_option.scopeParser(optionalParser, require_short_option.combinedOptionScope([parser]));
498
+ require_empty_input.defineEmptyInputFacts(scopedOptionalParser, require_empty_input.optionalLikeFacts(require_empty_input.getEmptyInputFacts(parser), parser.initialState, completesOptionalLikeFallback(parser)));
499
+ return fluent(scopedOptionalParser);
489
500
  }
490
501
  /**
491
502
  * Error type for structured error messages in {@link withDefault} default value callbacks.
@@ -741,7 +752,9 @@ function withDefault(parser, defaultValue, options) {
741
752
  state: deriveOptionalInnerParseState(state, parser)
742
753
  }]);
743
754
  require_passthrough.defineKnownCompletionLookup(withDefaultParser, (state) => require_passthrough.getKnownCompletion(parser, deriveOptionalInnerParseState(state, parser)));
744
- return fluent(require_short_option.scopeParser(withDefaultParser, require_short_option.combinedOptionScope([parser])));
755
+ const scopedWithDefaultParser = require_short_option.scopeParser(withDefaultParser, require_short_option.combinedOptionScope([parser]));
756
+ require_empty_input.defineEmptyInputFacts(scopedWithDefaultParser, require_empty_input.optionalLikeFacts(require_empty_input.getEmptyInputFacts(parser), parser.initialState, typeof defaultValue === "function" ? void 0 : completesOptionalLikeFallback(parser)));
757
+ return fluent(scopedWithDefaultParser);
745
758
  }
746
759
  /**
747
760
  * Creates a parser that transforms the result value of another parser using
@@ -930,6 +943,13 @@ function map(parser, transform) {
930
943
  if (composed != null) mappedParser.dependencyMetadata = composed;
931
944
  }
932
945
  require_dependency_runtime.defineForwardedEffectfulSchedulingNodes(mappedParser, parser);
946
+ const innerFacts = require_empty_input.getEmptyInputFacts(parser);
947
+ require_empty_input.defineEmptyInputFacts(mappedParser, {
948
+ ...innerFacts.step == null ? {} : { step: innerFacts.step },
949
+ ...innerFacts.next == null ? {} : { next: innerFacts.next },
950
+ ...innerFacts.afterStep === false ? { afterStep: false } : {},
951
+ ...innerFacts.fromInitial === false ? { fromInitial: false } : {}
952
+ });
933
953
  return fluent(mappedParser);
934
954
  }
935
955
  const deferredValueBrand = Symbol.for("@optique/core/deferredValue");
@@ -1104,6 +1124,17 @@ function isDeferredValue(value) {
1104
1124
  return typeof value === "function" && value[deferredValueBrand] === true;
1105
1125
  }
1106
1126
  /**
1127
+ * Computes the empty-input facts of `multiple(parser, options)`.
1128
+ */
1129
+ function getMultipleEmptyInputFacts(parser, options) {
1130
+ const min = options.min ?? 0;
1131
+ if (options.max != null || !Number.isInteger(min) || min < 0) return {};
1132
+ const facts = require_empty_input.getEmptyInputFacts(parser);
1133
+ const childInitialState = parser.initialState;
1134
+ const retainsItem = facts.step === "success" || facts.step === "provisional" ? facts.next == null ? void 0 : !isUnstartedMultipleItemState(require_empty_input.resolveEmptyStepState(facts.next, childInitialState), childInitialState) : false;
1135
+ return require_empty_input.multipleFacts(facts, retainsItem, childInitialState, min);
1136
+ }
1137
+ /**
1107
1138
  * Creates a parser that allows multiple occurrences of a given parser.
1108
1139
  * This parser can be used to parse multiple values of the same type,
1109
1140
  * such as multiple command-line arguments or options.
@@ -1380,6 +1411,9 @@ function multiple(parser, options = {}) {
1380
1411
  consumed: result.consumed
1381
1412
  };
1382
1413
  };
1414
+ const initialState = [];
1415
+ const emptyInputFacts = getMultipleEmptyInputFacts(parser, options);
1416
+ const acceptsEmpty = require_empty_input.acceptsEmptyInput(emptyInputFacts);
1383
1417
  const resultParser = {
1384
1418
  mode: parser.mode,
1385
1419
  $valueType: [],
@@ -1388,11 +1422,12 @@ function multiple(parser, options = {}) {
1388
1422
  usage: [{
1389
1423
  type: "multiple",
1390
1424
  terms: parser.usage,
1391
- min
1425
+ min,
1426
+ ...acceptsEmpty == null ? {} : { acceptsEmpty }
1392
1427
  }],
1393
1428
  leadingNames: parser.leadingNames,
1394
1429
  acceptingAnyToken: min > 0 && (parser.acceptingAnyToken ?? false),
1395
- initialState: [],
1430
+ initialState,
1396
1431
  canSkip(state, exec) {
1397
1432
  if (state.length < min) return false;
1398
1433
  const currentItemState = state.at(-1);
@@ -1748,7 +1783,9 @@ function multiple(parser, options = {}) {
1748
1783
  parser,
1749
1784
  state: unwrapInjectedWrapper(item.state)
1750
1785
  })));
1751
- return fluent(require_short_option.scopeParser(resultParser, require_short_option.repeatedOptionScope(parser)));
1786
+ const scopedResultParser = require_short_option.scopeParser(resultParser, require_short_option.repeatedOptionScope(parser));
1787
+ require_empty_input.defineEmptyInputFacts(scopedResultParser, emptyInputFacts);
1788
+ return fluent(scopedResultParser);
1752
1789
  }
1753
1790
  /**
1754
1791
  * Creates a parser that requires the wrapped parser to consume at least one
@@ -1888,7 +1925,13 @@ function nonEmpty(parser) {
1888
1925
  parser,
1889
1926
  state
1890
1927
  }]);
1891
- return fluent(require_short_option.scopeParser(nonEmptyParser, require_short_option.combinedOptionScope([parser])));
1928
+ const scopedNonEmptyParser = require_short_option.scopeParser(nonEmptyParser, require_short_option.combinedOptionScope([parser]));
1929
+ const innerFacts = require_empty_input.getEmptyInputFacts(parser);
1930
+ require_empty_input.defineEmptyInputFacts(scopedNonEmptyParser, {
1931
+ step: "failure",
1932
+ ...innerFacts.fromInitial == null ? {} : { fromInitial: innerFacts.fromInitial }
1933
+ });
1934
+ return fluent(scopedNonEmptyParser);
1892
1935
  }
1893
1936
  const fluentParserMarker = Symbol.for("@optique/core/fluent");
1894
1937
  /**
package/dist/modifiers.js CHANGED
@@ -4,6 +4,7 @@ import { dispatchByMode, dispatchIterableByMode, mapModeValue, wrapForMode } fro
4
4
  import { defineForwardedEffectfulSchedulingNodes } from "./dependency-runtime.js";
5
5
  import { defineInheritedAnnotationParser, defineParseLanes, defineSourceBindingOnlyAnnotationCompletionParser, getOwnParseLanes, unmatchedNonCliDependencySourceStateMarker } from "./internal/parser.js";
6
6
  import { getDelegatedAnnotationState, hasDelegatedAnnotationCarrier, isAnnotationWrappedInitialState, normalizeDelegatedAnnotationState, normalizeNestedDelegatedAnnotationState } from "./annotation-state.js";
7
+ import { acceptsEmptyInput, defineEmptyInputFacts, getEmptyInputFacts, multipleFacts, optionalLikeFacts, resolveEmptyStepState } from "./internal/empty-input.js";
7
8
  import { mergeChildExec, withChildContext, withChildExecPath } from "./execution-context.js";
8
9
  import { combinedOptionScope, repeatedOptionScope, scopeParser } from "./short-option.js";
9
10
  import { defineKnownCompletionLookup, defineReachableChildren, getKnownCompletion } from "./internal/passthrough.js";
@@ -27,6 +28,14 @@ function isUnstartedMultipleItemState(state, originalState) {
27
28
  const unwrappedOriginalState = unwrapMultipleItemState(originalState);
28
29
  return unwrappedState.value === unwrappedOriginalState.value && (unwrappedState.viaBoundCliWrapper || unwrappedOriginalState.viaBoundCliWrapper || unwrappedOriginalState.value != null && typeof unwrappedOriginalState.value === "object");
29
30
  }
31
+ /**
32
+ * Returns whether an optional-style wrapper around the given parser is known
33
+ * to complete successfully from its initial state without delegating to a
34
+ * source binding or a deferred completion.
35
+ */
36
+ function completesOptionalLikeFallback(parser) {
37
+ return typeof parser.shouldDeferCompletion !== "function" && parser.dependencyMetadata?.source == null && parser[unmatchedNonCliDependencySourceStateMarker] !== true ? true : void 0;
38
+ }
30
39
  function isBoundCliWrapperState(state) {
31
40
  return state != null && typeof state === "object" && Object.hasOwn(state, "hasCliValue") && typeof state.hasCliValue === "boolean" && (Object.hasOwn(state, "cliState") || state.hasCliValue === false);
32
41
  }
@@ -485,7 +494,9 @@ function optional(parser) {
485
494
  state: deriveOptionalInnerParseState(state, parser)
486
495
  }]);
487
496
  defineKnownCompletionLookup(optionalParser, (state) => getKnownCompletion(parser, deriveOptionalInnerParseState(state, parser)));
488
- return fluent(scopeParser(optionalParser, combinedOptionScope([parser])));
497
+ const scopedOptionalParser = scopeParser(optionalParser, combinedOptionScope([parser]));
498
+ defineEmptyInputFacts(scopedOptionalParser, optionalLikeFacts(getEmptyInputFacts(parser), parser.initialState, completesOptionalLikeFallback(parser)));
499
+ return fluent(scopedOptionalParser);
489
500
  }
490
501
  /**
491
502
  * Error type for structured error messages in {@link withDefault} default value callbacks.
@@ -741,7 +752,9 @@ function withDefault(parser, defaultValue, options) {
741
752
  state: deriveOptionalInnerParseState(state, parser)
742
753
  }]);
743
754
  defineKnownCompletionLookup(withDefaultParser, (state) => getKnownCompletion(parser, deriveOptionalInnerParseState(state, parser)));
744
- return fluent(scopeParser(withDefaultParser, combinedOptionScope([parser])));
755
+ const scopedWithDefaultParser = scopeParser(withDefaultParser, combinedOptionScope([parser]));
756
+ defineEmptyInputFacts(scopedWithDefaultParser, optionalLikeFacts(getEmptyInputFacts(parser), parser.initialState, typeof defaultValue === "function" ? void 0 : completesOptionalLikeFallback(parser)));
757
+ return fluent(scopedWithDefaultParser);
745
758
  }
746
759
  /**
747
760
  * Creates a parser that transforms the result value of another parser using
@@ -930,6 +943,13 @@ function map(parser, transform) {
930
943
  if (composed != null) mappedParser.dependencyMetadata = composed;
931
944
  }
932
945
  defineForwardedEffectfulSchedulingNodes(mappedParser, parser);
946
+ const innerFacts = getEmptyInputFacts(parser);
947
+ defineEmptyInputFacts(mappedParser, {
948
+ ...innerFacts.step == null ? {} : { step: innerFacts.step },
949
+ ...innerFacts.next == null ? {} : { next: innerFacts.next },
950
+ ...innerFacts.afterStep === false ? { afterStep: false } : {},
951
+ ...innerFacts.fromInitial === false ? { fromInitial: false } : {}
952
+ });
933
953
  return fluent(mappedParser);
934
954
  }
935
955
  const deferredValueBrand = Symbol.for("@optique/core/deferredValue");
@@ -1104,6 +1124,17 @@ function isDeferredValue(value) {
1104
1124
  return typeof value === "function" && value[deferredValueBrand] === true;
1105
1125
  }
1106
1126
  /**
1127
+ * Computes the empty-input facts of `multiple(parser, options)`.
1128
+ */
1129
+ function getMultipleEmptyInputFacts(parser, options) {
1130
+ const min = options.min ?? 0;
1131
+ if (options.max != null || !Number.isInteger(min) || min < 0) return {};
1132
+ const facts = getEmptyInputFacts(parser);
1133
+ const childInitialState = parser.initialState;
1134
+ const retainsItem = facts.step === "success" || facts.step === "provisional" ? facts.next == null ? void 0 : !isUnstartedMultipleItemState(resolveEmptyStepState(facts.next, childInitialState), childInitialState) : false;
1135
+ return multipleFacts(facts, retainsItem, childInitialState, min);
1136
+ }
1137
+ /**
1107
1138
  * Creates a parser that allows multiple occurrences of a given parser.
1108
1139
  * This parser can be used to parse multiple values of the same type,
1109
1140
  * such as multiple command-line arguments or options.
@@ -1380,6 +1411,9 @@ function multiple(parser, options = {}) {
1380
1411
  consumed: result.consumed
1381
1412
  };
1382
1413
  };
1414
+ const initialState = [];
1415
+ const emptyInputFacts = getMultipleEmptyInputFacts(parser, options);
1416
+ const acceptsEmpty = acceptsEmptyInput(emptyInputFacts);
1383
1417
  const resultParser = {
1384
1418
  mode: parser.mode,
1385
1419
  $valueType: [],
@@ -1388,11 +1422,12 @@ function multiple(parser, options = {}) {
1388
1422
  usage: [{
1389
1423
  type: "multiple",
1390
1424
  terms: parser.usage,
1391
- min
1425
+ min,
1426
+ ...acceptsEmpty == null ? {} : { acceptsEmpty }
1392
1427
  }],
1393
1428
  leadingNames: parser.leadingNames,
1394
1429
  acceptingAnyToken: min > 0 && (parser.acceptingAnyToken ?? false),
1395
- initialState: [],
1430
+ initialState,
1396
1431
  canSkip(state, exec) {
1397
1432
  if (state.length < min) return false;
1398
1433
  const currentItemState = state.at(-1);
@@ -1748,7 +1783,9 @@ function multiple(parser, options = {}) {
1748
1783
  parser,
1749
1784
  state: unwrapInjectedWrapper(item.state)
1750
1785
  })));
1751
- return fluent(scopeParser(resultParser, repeatedOptionScope(parser)));
1786
+ const scopedResultParser = scopeParser(resultParser, repeatedOptionScope(parser));
1787
+ defineEmptyInputFacts(scopedResultParser, emptyInputFacts);
1788
+ return fluent(scopedResultParser);
1752
1789
  }
1753
1790
  /**
1754
1791
  * Creates a parser that requires the wrapped parser to consume at least one
@@ -1888,7 +1925,13 @@ function nonEmpty(parser) {
1888
1925
  parser,
1889
1926
  state
1890
1927
  }]);
1891
- return fluent(scopeParser(nonEmptyParser, combinedOptionScope([parser])));
1928
+ const scopedNonEmptyParser = scopeParser(nonEmptyParser, combinedOptionScope([parser]));
1929
+ const innerFacts = getEmptyInputFacts(parser);
1930
+ defineEmptyInputFacts(scopedNonEmptyParser, {
1931
+ step: "failure",
1932
+ ...innerFacts.fromInitial == null ? {} : { fromInitial: innerFacts.fromInitial }
1933
+ });
1934
+ return fluent(scopedNonEmptyParser);
1892
1935
  }
1893
1936
  const fluentParserMarker = Symbol.for("@optique/core/fluent");
1894
1937
  /**
@@ -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_empty_input = require('./internal/empty-input.cjs');
9
10
  const require_execution_context = require('./execution-context.cjs');
10
11
  const require_usage_internals = require('./usage-internals.cjs');
11
12
  const require_short_option = require('./short-option.cjs');
@@ -174,6 +175,12 @@ function constant(value) {
174
175
  }
175
176
  };
176
177
  require_passthrough.defineKnownCompletion(result, (state) => require_annotation_state.normalizeInjectedAnnotationState(state));
178
+ require_empty_input.defineEmptyInputFacts(result, {
179
+ step: "success",
180
+ next: require_empty_input.UNCHANGED,
181
+ afterStep: true,
182
+ fromInitial: true
183
+ });
177
184
  Object.defineProperty(result, "placeholder", {
178
185
  value,
179
186
  configurable: true,
@@ -202,7 +209,7 @@ function constant(value) {
202
209
  * @since 1.0.0
203
210
  */
204
211
  function fail() {
205
- return require_modifiers.fluent({
212
+ const result = {
206
213
  $valueType: [],
207
214
  $stateType: [],
208
215
  mode: "sync",
@@ -236,7 +243,9 @@ function fail() {
236
243
  sourceOnly: true
237
244
  };
238
245
  }
239
- });
246
+ };
247
+ require_empty_input.defineEmptyInputFacts(result, require_empty_input.failingStepFacts(false));
248
+ return require_modifiers.fluent(result);
240
249
  }
241
250
  /**
242
251
  * Internal helper to get suggestions from a value parser, using dependency values
@@ -794,6 +803,7 @@ function option(...args) {
794
803
  configurable: true,
795
804
  enumerable: false
796
805
  });
806
+ else require_empty_input.defineEmptyInputFacts(result, require_empty_input.failingStepFacts(valueParser == null));
797
807
  return require_modifiers.fluent(result);
798
808
  }
799
809
  /**
@@ -983,6 +993,7 @@ function flag(...args) {
983
993
  enumerable: false,
984
994
  writable: false
985
995
  });
996
+ require_empty_input.defineEmptyInputFacts(result, require_empty_input.failingStepFacts(false));
986
997
  return require_modifiers.fluent(result);
987
998
  }
988
999
  function normalizeNegatableFlagNameList(names) {
@@ -1395,6 +1406,7 @@ function argument(valueParser, options = {}) {
1395
1406
  configurable: true,
1396
1407
  enumerable: false
1397
1408
  });
1409
+ else require_empty_input.defineEmptyInputFacts(result, require_empty_input.failingStepFacts(false));
1398
1410
  return require_modifiers.fluent(result);
1399
1411
  }
1400
1412
  function normalizeCommandState(state) {
@@ -1770,9 +1782,11 @@ function command(name, parser, options = {}) {
1770
1782
  const child = selectChild(state);
1771
1783
  return child == null ? [] : [child];
1772
1784
  });
1773
- return require_modifiers.fluent(require_short_option.scopeParser(result, commandScope.source, (context) => {
1785
+ const scoped = require_short_option.scopeParser(result, commandScope.source, (context) => {
1774
1786
  if (normalizeCommandState(context.state) != null) commandScope.select(context, enteredScope);
1775
- }));
1787
+ });
1788
+ require_empty_input.defineEmptyInputFacts(scoped, require_empty_input.failingStepFacts(false));
1789
+ return require_modifiers.fluent(scoped);
1776
1790
  }
1777
1791
  /**
1778
1792
  * Creates a parser that collects unrecognized options and passes them through.
@@ -1963,6 +1977,7 @@ function passThrough(options = {}) {
1963
1977
  const accepts = format === "equalsOnly" ? equalsOptionPattern.test(token) : format === "nextToken" && optionPattern.test(token);
1964
1978
  return accepts ? result.priority : void 0;
1965
1979
  });
1980
+ require_empty_input.defineEmptyInputFacts(result, require_empty_input.failingStepFacts(true));
1966
1981
  return require_modifiers.fluent(result);
1967
1982
  }
1968
1983
 
@@ -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 { UNCHANGED, defineEmptyInputFacts, failingStepFacts } from "./internal/empty-input.js";
9
10
  import { mergeChildExec, withChildContext, withChildExecPath } from "./execution-context.js";
10
11
  import { extractLeadingCommandNames } from "./usage-internals.js";
11
12
  import { attachedValuePrefix, combinedOptionScope, fullNameOwnsToken, prefixSuggestion, scopeParser, selectableOptionScope } from "./short-option.js";
@@ -174,6 +175,12 @@ function constant(value) {
174
175
  }
175
176
  };
176
177
  defineKnownCompletion(result, (state) => normalizeInjectedAnnotationState(state));
178
+ defineEmptyInputFacts(result, {
179
+ step: "success",
180
+ next: UNCHANGED,
181
+ afterStep: true,
182
+ fromInitial: true
183
+ });
177
184
  Object.defineProperty(result, "placeholder", {
178
185
  value,
179
186
  configurable: true,
@@ -202,7 +209,7 @@ function constant(value) {
202
209
  * @since 1.0.0
203
210
  */
204
211
  function fail() {
205
- return fluent({
212
+ const result = {
206
213
  $valueType: [],
207
214
  $stateType: [],
208
215
  mode: "sync",
@@ -236,7 +243,9 @@ function fail() {
236
243
  sourceOnly: true
237
244
  };
238
245
  }
239
- });
246
+ };
247
+ defineEmptyInputFacts(result, failingStepFacts(false));
248
+ return fluent(result);
240
249
  }
241
250
  /**
242
251
  * Internal helper to get suggestions from a value parser, using dependency values
@@ -794,6 +803,7 @@ function option(...args) {
794
803
  configurable: true,
795
804
  enumerable: false
796
805
  });
806
+ else defineEmptyInputFacts(result, failingStepFacts(valueParser == null));
797
807
  return fluent(result);
798
808
  }
799
809
  /**
@@ -983,6 +993,7 @@ function flag(...args) {
983
993
  enumerable: false,
984
994
  writable: false
985
995
  });
996
+ defineEmptyInputFacts(result, failingStepFacts(false));
986
997
  return fluent(result);
987
998
  }
988
999
  function normalizeNegatableFlagNameList(names) {
@@ -1395,6 +1406,7 @@ function argument(valueParser, options = {}) {
1395
1406
  configurable: true,
1396
1407
  enumerable: false
1397
1408
  });
1409
+ else defineEmptyInputFacts(result, failingStepFacts(false));
1398
1410
  return fluent(result);
1399
1411
  }
1400
1412
  function normalizeCommandState(state) {
@@ -1770,9 +1782,11 @@ function command(name, parser, options = {}) {
1770
1782
  const child = selectChild(state);
1771
1783
  return child == null ? [] : [child];
1772
1784
  });
1773
- return fluent(scopeParser(result, commandScope.source, (context) => {
1785
+ const scoped = scopeParser(result, commandScope.source, (context) => {
1774
1786
  if (normalizeCommandState(context.state) != null) commandScope.select(context, enteredScope);
1775
- }));
1787
+ });
1788
+ defineEmptyInputFacts(scoped, failingStepFacts(false));
1789
+ return fluent(scoped);
1776
1790
  }
1777
1791
  /**
1778
1792
  * Creates a parser that collects unrecognized options and passes them through.
@@ -1963,6 +1977,7 @@ function passThrough(options = {}) {
1963
1977
  const accepts = format === "equalsOnly" ? equalsOptionPattern.test(token) : format === "nextToken" && optionPattern.test(token);
1964
1978
  return accepts ? result.priority : void 0;
1965
1979
  });
1980
+ defineEmptyInputFacts(result, failingStepFacts(true));
1966
1981
  return fluent(result);
1967
1982
  }
1968
1983