@optique/core 1.3.0-dev.2581 → 1.3.0-dev.2583

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.
@@ -115,18 +115,23 @@ const preExpandedBranchNodes = /* @__PURE__ */ new WeakSet();
115
115
  * numeric path segments and record states use their field keys, matching
116
116
  * the paths that child completion (and parse-time tracing) uses.
117
117
  */
118
- function expandEffectfulRuntimeNodes(nodes) {
118
+ function expandEffectfulRuntimeNodes(nodes, options) {
119
119
  const expanded = [];
120
120
  const visit = (node) => {
121
121
  const meta = node.parser.dependencyMetadata;
122
+ const optedIntoCollection = node.parser[require_dependency_runtime.sourceCollectionExpansionKey] === true;
123
+ if (options?.structuralOnly === true && !optedIntoCollection && require_dependency_runtime.exclusiveSourceScopeKey in node.parser) {
124
+ expanded.push(node);
125
+ return;
126
+ }
122
127
  const schedulingNodes = node.parser[require_dependency_runtime.effectfulSchedulingNodesKey];
123
- const expandsThroughHook = schedulingNodes != null && meta?.derived == null && node.parser[require_dependency_runtime.sourceCollectionExpansionKey] === true;
128
+ const expandsThroughHook = schedulingNodes != null && meta?.derived == null && optedIntoCollection;
124
129
  if (!expandsThroughHook && (meta?.source?.completeSource != null || meta?.derived != null)) {
125
130
  expanded.push(node);
126
131
  return;
127
132
  }
128
133
  if (schedulingNodes != null) {
129
- const children = schedulingNodes(node.state, node.path);
134
+ const children = schedulingNodes(node.state, node.path, options);
130
135
  if (children.length === 0 && meta?.source != null) {
131
136
  expanded.push(node);
132
137
  return;
@@ -145,10 +150,16 @@ function expandEffectfulRuntimeNodes(nodes) {
145
150
  if (duplicateFieldNames.has(field)) continue;
146
151
  const segment = Array.isArray(state) && typeof field === "string" ? Number(field) : field;
147
152
  const childState = Array.isArray(state) ? state[segment] : state[field];
153
+ const annotatedChildState = require_annotation_state.getWrappedChildState(state, childState, childParser);
154
+ const isDerived = childParser.dependencyMetadata?.derived != null;
155
+ const rawInput = isDerived ? require_dependency_runtime.extractRawInputFromState(annotatedChildState) : void 0;
156
+ const defaultDependencyValues = isDerived ? require_dependency_runtime.getDefaultDependencySnapshot(annotatedChildState) : void 0;
148
157
  visit({
149
158
  path: [...node.path, segment],
150
159
  parser: childParser,
151
- state: require_annotation_state.getWrappedChildState(state, childState, childParser)
160
+ state: annotatedChildState,
161
+ ...rawInput != null ? { rawInput } : {},
162
+ ...defaultDependencyValues != null ? { defaultDependencyValues } : {}
152
163
  });
153
164
  }
154
165
  };
@@ -173,15 +184,101 @@ function expandSourceCollectionNodes(nodes) {
173
184
  for (let i = 0; i < nodes.length; i++) {
174
185
  const node = nodes[i];
175
186
  const optedIn = node.parser[require_dependency_runtime.sourceCollectionExpansionKey] === true;
176
- if (!optedIn) {
187
+ if (optedIn) {
188
+ if (expanded == null) expanded = nodes.slice(0, i);
189
+ expanded.push(...expandEffectfulRuntimeNodes([node]));
190
+ continue;
191
+ }
192
+ const nested = nestedCommandLineSourceNodes(node);
193
+ if (nested.length < 1) {
177
194
  expanded?.push(node);
178
195
  continue;
179
196
  }
180
197
  if (expanded == null) expanded = nodes.slice(0, i);
181
- expanded.push(...expandEffectfulRuntimeNodes([node]));
198
+ expanded.push(node, ...nested);
182
199
  }
183
200
  return expanded ?? nodes;
184
201
  }
202
+ /**
203
+ * The command-line source occurrences a structural child contributes to
204
+ * its enclosing construct's collection scope.
205
+ *
206
+ * A plain nested construct—an `object()`, `tuple()`, `concat()`,
207
+ * `merge()`, or one of those behind a transparent wrapper—is part of the
208
+ * enclosing declaration sequence, so a source it holds must reach a
209
+ * consumer declared before it, exactly as a source declared directly
210
+ * beside that consumer does. Only occurrences that consumed
211
+ * command-line input are contributed: an absent occurrence has no value
212
+ * to publish, and a bound or defaulted one publishes through its own
213
+ * construct's pre-completion, whose fallback readers and lazy defaults
214
+ * must keep evaluating exactly once
215
+ * (https://github.com/dahlia/optique/issues/958).
216
+ *
217
+ * @param node The structural child to look inside.
218
+ * @returns The nested command-line source occurrences, in declaration
219
+ * order. Empty for a child that holds none.
220
+ */
221
+ function nestedCommandLineSourceNodes(node) {
222
+ const nested = [];
223
+ for (const descendant of expandEffectfulRuntimeNodes([node], { structuralOnly: true })) {
224
+ if (descendant === node) continue;
225
+ if (descendant.parser.dependencyMetadata?.source == null) continue;
226
+ if ((require_dependency_runtime.exclusiveSourceScopeKey in descendant.parser || descendant.parser.dependencyMetadata?.source?.exclusiveOccurrence === true) && descendant.parser[require_dependency_runtime.sourceCollectionExpansionKey] !== true) continue;
227
+ if (!hasCommandLineSourceInput(descendant)) continue;
228
+ nested.push({
229
+ ...descendant,
230
+ matched: true
231
+ });
232
+ }
233
+ return nested;
234
+ }
235
+ /**
236
+ * Whether a source occurrence's state holds no parsed input at all.
237
+ *
238
+ * State identity against `initialState` misses two shapes a wrapper
239
+ * produces afresh on every parse: a repetition that matched nothing,
240
+ * whose state is a new empty array, and a wrapper whose own initial
241
+ * state is `undefined`, which annotation injection turns into an object
242
+ * carrying nothing but annotation symbols. Both look matched by
243
+ * identity while holding nothing to publish, and neither forwards the
244
+ * binding marker that would otherwise catch them.
245
+ *
246
+ * Treating an unrecognized shape as non-empty is the safe direction: the
247
+ * occurrence is then subject to the remaining provenance checks.
248
+ *
249
+ * @param state The occurrence's state, already unwrapped one level.
250
+ * @returns `true` when the state carries no parsed input.
251
+ */
252
+ function isEmptySourceState(state) {
253
+ if (state == null) return true;
254
+ if (Array.isArray(state)) return state.length < 1;
255
+ if (typeof state !== "object") return false;
256
+ return Object.getOwnPropertyNames(state).length < 1;
257
+ }
258
+ /**
259
+ * Whether a nested source occurrence holds a value the command line
260
+ * supplied.
261
+ *
262
+ * A source that can complete from a binding (`bindEnv()`,
263
+ * `bindConfig()`, and other `completesFromSource` wrappers) reaches an
264
+ * annotated state even when nothing on the command line matched, so an
265
+ * unmatched state alone does not prove provenance. Such an occurrence
266
+ * has to say so through its own state; otherwise extracting from it here
267
+ * would run the binding's fallback reader in addition to the run its
268
+ * owning construct's pre-completion already performs.
269
+ *
270
+ * @param node The nested source occurrence.
271
+ * @returns `true` when the occurrence's state came from the command
272
+ * line, whether or not that input passed validation.
273
+ */
274
+ function hasCommandLineSourceInput(node) {
275
+ const parser = node.parser;
276
+ if (!require_dependency_runtime.isMatchedState(node.state, parser)) return false;
277
+ const innerState = Array.isArray(node.state) && node.state.length === 1 ? node.state[0] : node.state;
278
+ if (isEmptySourceState(innerState)) return false;
279
+ if (parser[require_internal_parser.unmatchedNonCliDependencySourceStateMarker] !== true) return true;
280
+ return innerState != null && typeof innerState === "object" && innerState.hasCliValue === true;
281
+ }
185
282
  let conditionalInstanceCounter = 0;
186
283
  /**
187
284
  * Whether a source occurrence publishes a value even when its state
@@ -431,10 +528,11 @@ async function scheduleEffectfulSourceCompletions(nodes, state, runtime, exec, p
431
528
  for (const node of nodes) {
432
529
  const optedIn = node.parser[require_dependency_runtime.sourceCollectionExpansionKey] === true;
433
530
  const preExpanded = preExpandedBranchNodes.has(node);
531
+ const nestedPaths = optedIn || preExpanded ? void 0 : new Set(nestedCommandLineSourceNodes(node).map((nested) => require_dependency_runtime.serializeSchedulingPath(nested.path)));
434
532
  for (const expandedNode of expandEffectfulRuntimeNodes([node])) {
435
533
  expandedNodes.push(expandedNode);
436
534
  if (preExpanded) continue;
437
- if (optedIn || expandedNode === node) collected.add(expandedNode);
535
+ if (optedIn || expandedNode === node || nestedPaths?.has(require_dependency_runtime.serializeSchedulingPath(expandedNode.path)) === true) collected.add(expandedNode);
438
536
  }
439
537
  }
440
538
  const republish = exec?.republishCachedCompletions === true;
@@ -1245,6 +1343,7 @@ function composeExclusiveDependencyMetadata(parsers) {
1245
1343
  ...sharedSource,
1246
1344
  getMissingSourceValue: void 0,
1247
1345
  completesWhenMissing: false,
1346
+ exclusiveOccurrence: true,
1248
1347
  preservesSourceValue: everyBranchPreserves,
1249
1348
  extractSourceValue(state) {
1250
1349
  if (!Array.isArray(state) || state.length !== 2 || typeof state[0] !== "number") return void 0;
@@ -5981,6 +6080,7 @@ function group(label, parser, options = {}) {
5981
6080
  ...fieldParsersKey in parser ? { [fieldParsersKey]: parser[fieldParsersKey] } : {},
5982
6081
  ...require_dependency_runtime.effectfulSchedulingNodesKey in parser ? { [require_dependency_runtime.effectfulSchedulingNodesKey]: parser[require_dependency_runtime.effectfulSchedulingNodesKey] } : {},
5983
6082
  ...require_dependency_runtime.sourceCollectionExpansionKey in parser ? { [require_dependency_runtime.sourceCollectionExpansionKey]: parser[require_dependency_runtime.sourceCollectionExpansionKey] } : {},
6083
+ ...require_dependency_runtime.exclusiveSourceScopeKey in parser ? { [require_dependency_runtime.exclusiveSourceScopeKey]: parser[require_dependency_runtime.exclusiveSourceScopeKey] } : {},
5984
6084
  ...typeof parser.shouldDeferCompletion === "function" ? { shouldDeferCompletion: parser.shouldDeferCompletion.bind(parser) } : {},
5985
6085
  ...typeof parser.canSkip === "function" ? { canSkip: parser.canSkip.bind(parser) } : {},
5986
6086
  getSuggestRuntimeNodes(state, path) {
@@ -6052,6 +6152,11 @@ function group(label, parser, options = {}) {
6052
6152
  configurable: true,
6053
6153
  enumerable: false
6054
6154
  });
6155
+ if (parser[require_internal_parser.unmatchedNonCliDependencySourceStateMarker] === true) Object.defineProperty(groupParser, require_internal_parser.unmatchedNonCliDependencySourceStateMarker, {
6156
+ value: true,
6157
+ configurable: true,
6158
+ enumerable: true
6159
+ });
6055
6160
  if ("placeholder" in parser) Object.defineProperty(groupParser, "placeholder", {
6056
6161
  get() {
6057
6162
  return parser.placeholder;
@@ -6778,7 +6883,7 @@ function conditional(discriminator, branches, defaultBranch, options) {
6778
6883
  }
6779
6884
  return ids;
6780
6885
  };
6781
- const buildConditionalSchedulingNodes = (state, parentPath) => {
6886
+ const buildConditionalSchedulingNodes = (state, parentPath, expandOptions) => {
6782
6887
  if (state == null || typeof state !== "object") return [];
6783
6888
  const conditionalState = state;
6784
6889
  const annotatedDiscriminatorState = require_annotation_state.getWrappedChildState(conditionalState, conditionalState.discriminatorState, discriminator);
@@ -6797,7 +6902,7 @@ function conditional(discriminator, branches, defaultBranch, options) {
6797
6902
  state: require_annotation_state.getWrappedChildState(conditionalState, conditionalState.branchState, branchParser)
6798
6903
  };
6799
6904
  const branchKey = selected.kind === "branch" ? selected.key : void 0;
6800
- if (branchKey != null && options?.errors?.branchError != null) for (const expanded of wrapBranchBarrierNodes(expandEffectfulRuntimeNodes([branchNode]), branchKey)) {
6905
+ if (branchKey != null && options?.errors?.branchError != null) for (const expanded of wrapBranchBarrierNodes(expandEffectfulRuntimeNodes([branchNode], expandOptions), branchKey)) {
6801
6906
  preExpandedBranchNodes.add(expanded);
6802
6907
  nodes.push(expanded);
6803
6908
  }
@@ -5,7 +5,7 @@ import { message, optionName, text, values } from "./message.js";
5
5
  import { deduplicateDocFragments } from "./doc.js";
6
6
  import { dispatchByMode, dispatchIterableByMode } from "./internal/mode-dispatch.js";
7
7
  import { createDependencySourceState, dependencyId, isDependencySourceState, isPendingDependencySourceState, isWrappedDependencySource, wrappedDependencySourceMarker } from "./internal/dependency.js";
8
- import { buildRuntimeNodesFromArray, buildRuntimeNodesFromPairs, collectExplicitSourceValues, collectExplicitSourceValuesAsync, collectSourcesFromState, completeEffectfulSourcesAsync, createDependencyRuntimeContext, effectfulSchedulingNodesKey, exclusiveSourceScopeKey, fillMissingSourceDefaults, fillMissingSourceDefaultsAsync, forkRuntimeForCommittedSubtree, guaranteedStaticSourceIdsKey, hasInactiveCompletion, recordSourceScope, releaseUncollectedSourceValues, replaceCachedBarrierFailure, resolveDerivedSourceValues, resolveDerivedSourceValuesAsync, resolveStateWithRuntime, resolveStateWithRuntimeAsync, serializeSchedulingPath, sourceCollectionExpansionKey, staticSourceScopeKey } from "./dependency-runtime.js";
8
+ import { buildRuntimeNodesFromArray, buildRuntimeNodesFromPairs, collectExplicitSourceValues, collectExplicitSourceValuesAsync, collectSourcesFromState, completeEffectfulSourcesAsync, createDependencyRuntimeContext, effectfulSchedulingNodesKey, exclusiveSourceScopeKey, extractRawInputFromState, fillMissingSourceDefaults, fillMissingSourceDefaultsAsync, forkRuntimeForCommittedSubtree, getDefaultDependencySnapshot, guaranteedStaticSourceIdsKey, hasInactiveCompletion, isMatchedState, recordSourceScope, releaseUncollectedSourceValues, replaceCachedBarrierFailure, resolveDerivedSourceValues, resolveDerivedSourceValuesAsync, resolveStateWithRuntime, resolveStateWithRuntimeAsync, serializeSchedulingPath, sourceCollectionExpansionKey, staticSourceScopeKey } from "./dependency-runtime.js";
9
9
  import { defineInheritedAnnotationParser, defineParseLanes, getOwnParseLanes, getParserSuggestRuntimeNodes, unmatchedNonCliDependencySourceStateMarker } from "./internal/parser.js";
10
10
  import { annotationViewTargets, getWrappedChildParseState, getWrappedChildState, reconcileObjectChildState, unwrapAnnotationView } from "./annotation-state.js";
11
11
  import { allowDuplicateLeadingCommandNamesKey } from "./internal/command-alias.js";
@@ -115,18 +115,23 @@ const preExpandedBranchNodes = /* @__PURE__ */ new WeakSet();
115
115
  * numeric path segments and record states use their field keys, matching
116
116
  * the paths that child completion (and parse-time tracing) uses.
117
117
  */
118
- function expandEffectfulRuntimeNodes(nodes) {
118
+ function expandEffectfulRuntimeNodes(nodes, options) {
119
119
  const expanded = [];
120
120
  const visit = (node) => {
121
121
  const meta = node.parser.dependencyMetadata;
122
+ const optedIntoCollection = node.parser[sourceCollectionExpansionKey] === true;
123
+ if (options?.structuralOnly === true && !optedIntoCollection && exclusiveSourceScopeKey in node.parser) {
124
+ expanded.push(node);
125
+ return;
126
+ }
122
127
  const schedulingNodes = node.parser[effectfulSchedulingNodesKey];
123
- const expandsThroughHook = schedulingNodes != null && meta?.derived == null && node.parser[sourceCollectionExpansionKey] === true;
128
+ const expandsThroughHook = schedulingNodes != null && meta?.derived == null && optedIntoCollection;
124
129
  if (!expandsThroughHook && (meta?.source?.completeSource != null || meta?.derived != null)) {
125
130
  expanded.push(node);
126
131
  return;
127
132
  }
128
133
  if (schedulingNodes != null) {
129
- const children = schedulingNodes(node.state, node.path);
134
+ const children = schedulingNodes(node.state, node.path, options);
130
135
  if (children.length === 0 && meta?.source != null) {
131
136
  expanded.push(node);
132
137
  return;
@@ -145,10 +150,16 @@ function expandEffectfulRuntimeNodes(nodes) {
145
150
  if (duplicateFieldNames.has(field)) continue;
146
151
  const segment = Array.isArray(state) && typeof field === "string" ? Number(field) : field;
147
152
  const childState = Array.isArray(state) ? state[segment] : state[field];
153
+ const annotatedChildState = getWrappedChildState(state, childState, childParser);
154
+ const isDerived = childParser.dependencyMetadata?.derived != null;
155
+ const rawInput = isDerived ? extractRawInputFromState(annotatedChildState) : void 0;
156
+ const defaultDependencyValues = isDerived ? getDefaultDependencySnapshot(annotatedChildState) : void 0;
148
157
  visit({
149
158
  path: [...node.path, segment],
150
159
  parser: childParser,
151
- state: getWrappedChildState(state, childState, childParser)
160
+ state: annotatedChildState,
161
+ ...rawInput != null ? { rawInput } : {},
162
+ ...defaultDependencyValues != null ? { defaultDependencyValues } : {}
152
163
  });
153
164
  }
154
165
  };
@@ -173,15 +184,101 @@ function expandSourceCollectionNodes(nodes) {
173
184
  for (let i = 0; i < nodes.length; i++) {
174
185
  const node = nodes[i];
175
186
  const optedIn = node.parser[sourceCollectionExpansionKey] === true;
176
- if (!optedIn) {
187
+ if (optedIn) {
188
+ if (expanded == null) expanded = nodes.slice(0, i);
189
+ expanded.push(...expandEffectfulRuntimeNodes([node]));
190
+ continue;
191
+ }
192
+ const nested = nestedCommandLineSourceNodes(node);
193
+ if (nested.length < 1) {
177
194
  expanded?.push(node);
178
195
  continue;
179
196
  }
180
197
  if (expanded == null) expanded = nodes.slice(0, i);
181
- expanded.push(...expandEffectfulRuntimeNodes([node]));
198
+ expanded.push(node, ...nested);
182
199
  }
183
200
  return expanded ?? nodes;
184
201
  }
202
+ /**
203
+ * The command-line source occurrences a structural child contributes to
204
+ * its enclosing construct's collection scope.
205
+ *
206
+ * A plain nested construct—an `object()`, `tuple()`, `concat()`,
207
+ * `merge()`, or one of those behind a transparent wrapper—is part of the
208
+ * enclosing declaration sequence, so a source it holds must reach a
209
+ * consumer declared before it, exactly as a source declared directly
210
+ * beside that consumer does. Only occurrences that consumed
211
+ * command-line input are contributed: an absent occurrence has no value
212
+ * to publish, and a bound or defaulted one publishes through its own
213
+ * construct's pre-completion, whose fallback readers and lazy defaults
214
+ * must keep evaluating exactly once
215
+ * (https://github.com/dahlia/optique/issues/958).
216
+ *
217
+ * @param node The structural child to look inside.
218
+ * @returns The nested command-line source occurrences, in declaration
219
+ * order. Empty for a child that holds none.
220
+ */
221
+ function nestedCommandLineSourceNodes(node) {
222
+ const nested = [];
223
+ for (const descendant of expandEffectfulRuntimeNodes([node], { structuralOnly: true })) {
224
+ if (descendant === node) continue;
225
+ if (descendant.parser.dependencyMetadata?.source == null) continue;
226
+ if ((exclusiveSourceScopeKey in descendant.parser || descendant.parser.dependencyMetadata?.source?.exclusiveOccurrence === true) && descendant.parser[sourceCollectionExpansionKey] !== true) continue;
227
+ if (!hasCommandLineSourceInput(descendant)) continue;
228
+ nested.push({
229
+ ...descendant,
230
+ matched: true
231
+ });
232
+ }
233
+ return nested;
234
+ }
235
+ /**
236
+ * Whether a source occurrence's state holds no parsed input at all.
237
+ *
238
+ * State identity against `initialState` misses two shapes a wrapper
239
+ * produces afresh on every parse: a repetition that matched nothing,
240
+ * whose state is a new empty array, and a wrapper whose own initial
241
+ * state is `undefined`, which annotation injection turns into an object
242
+ * carrying nothing but annotation symbols. Both look matched by
243
+ * identity while holding nothing to publish, and neither forwards the
244
+ * binding marker that would otherwise catch them.
245
+ *
246
+ * Treating an unrecognized shape as non-empty is the safe direction: the
247
+ * occurrence is then subject to the remaining provenance checks.
248
+ *
249
+ * @param state The occurrence's state, already unwrapped one level.
250
+ * @returns `true` when the state carries no parsed input.
251
+ */
252
+ function isEmptySourceState(state) {
253
+ if (state == null) return true;
254
+ if (Array.isArray(state)) return state.length < 1;
255
+ if (typeof state !== "object") return false;
256
+ return Object.getOwnPropertyNames(state).length < 1;
257
+ }
258
+ /**
259
+ * Whether a nested source occurrence holds a value the command line
260
+ * supplied.
261
+ *
262
+ * A source that can complete from a binding (`bindEnv()`,
263
+ * `bindConfig()`, and other `completesFromSource` wrappers) reaches an
264
+ * annotated state even when nothing on the command line matched, so an
265
+ * unmatched state alone does not prove provenance. Such an occurrence
266
+ * has to say so through its own state; otherwise extracting from it here
267
+ * would run the binding's fallback reader in addition to the run its
268
+ * owning construct's pre-completion already performs.
269
+ *
270
+ * @param node The nested source occurrence.
271
+ * @returns `true` when the occurrence's state came from the command
272
+ * line, whether or not that input passed validation.
273
+ */
274
+ function hasCommandLineSourceInput(node) {
275
+ const parser = node.parser;
276
+ if (!isMatchedState(node.state, parser)) return false;
277
+ const innerState = Array.isArray(node.state) && node.state.length === 1 ? node.state[0] : node.state;
278
+ if (isEmptySourceState(innerState)) return false;
279
+ if (parser[unmatchedNonCliDependencySourceStateMarker] !== true) return true;
280
+ return innerState != null && typeof innerState === "object" && innerState.hasCliValue === true;
281
+ }
185
282
  let conditionalInstanceCounter = 0;
186
283
  /**
187
284
  * Whether a source occurrence publishes a value even when its state
@@ -431,10 +528,11 @@ async function scheduleEffectfulSourceCompletions(nodes, state, runtime, exec, p
431
528
  for (const node of nodes) {
432
529
  const optedIn = node.parser[sourceCollectionExpansionKey] === true;
433
530
  const preExpanded = preExpandedBranchNodes.has(node);
531
+ const nestedPaths = optedIn || preExpanded ? void 0 : new Set(nestedCommandLineSourceNodes(node).map((nested) => serializeSchedulingPath(nested.path)));
434
532
  for (const expandedNode of expandEffectfulRuntimeNodes([node])) {
435
533
  expandedNodes.push(expandedNode);
436
534
  if (preExpanded) continue;
437
- if (optedIn || expandedNode === node) collected.add(expandedNode);
535
+ if (optedIn || expandedNode === node || nestedPaths?.has(serializeSchedulingPath(expandedNode.path)) === true) collected.add(expandedNode);
438
536
  }
439
537
  }
440
538
  const republish = exec?.republishCachedCompletions === true;
@@ -1245,6 +1343,7 @@ function composeExclusiveDependencyMetadata(parsers) {
1245
1343
  ...sharedSource,
1246
1344
  getMissingSourceValue: void 0,
1247
1345
  completesWhenMissing: false,
1346
+ exclusiveOccurrence: true,
1248
1347
  preservesSourceValue: everyBranchPreserves,
1249
1348
  extractSourceValue(state) {
1250
1349
  if (!Array.isArray(state) || state.length !== 2 || typeof state[0] !== "number") return void 0;
@@ -5981,6 +6080,7 @@ function group(label, parser, options = {}) {
5981
6080
  ...fieldParsersKey in parser ? { [fieldParsersKey]: parser[fieldParsersKey] } : {},
5982
6081
  ...effectfulSchedulingNodesKey in parser ? { [effectfulSchedulingNodesKey]: parser[effectfulSchedulingNodesKey] } : {},
5983
6082
  ...sourceCollectionExpansionKey in parser ? { [sourceCollectionExpansionKey]: parser[sourceCollectionExpansionKey] } : {},
6083
+ ...exclusiveSourceScopeKey in parser ? { [exclusiveSourceScopeKey]: parser[exclusiveSourceScopeKey] } : {},
5984
6084
  ...typeof parser.shouldDeferCompletion === "function" ? { shouldDeferCompletion: parser.shouldDeferCompletion.bind(parser) } : {},
5985
6085
  ...typeof parser.canSkip === "function" ? { canSkip: parser.canSkip.bind(parser) } : {},
5986
6086
  getSuggestRuntimeNodes(state, path) {
@@ -6052,6 +6152,11 @@ function group(label, parser, options = {}) {
6052
6152
  configurable: true,
6053
6153
  enumerable: false
6054
6154
  });
6155
+ if (parser[unmatchedNonCliDependencySourceStateMarker] === true) Object.defineProperty(groupParser, unmatchedNonCliDependencySourceStateMarker, {
6156
+ value: true,
6157
+ configurable: true,
6158
+ enumerable: true
6159
+ });
6055
6160
  if ("placeholder" in parser) Object.defineProperty(groupParser, "placeholder", {
6056
6161
  get() {
6057
6162
  return parser.placeholder;
@@ -6778,7 +6883,7 @@ function conditional(discriminator, branches, defaultBranch, options) {
6778
6883
  }
6779
6884
  return ids;
6780
6885
  };
6781
- const buildConditionalSchedulingNodes = (state, parentPath) => {
6886
+ const buildConditionalSchedulingNodes = (state, parentPath, expandOptions) => {
6782
6887
  if (state == null || typeof state !== "object") return [];
6783
6888
  const conditionalState = state;
6784
6889
  const annotatedDiscriminatorState = getWrappedChildState(conditionalState, conditionalState.discriminatorState, discriminator);
@@ -6797,7 +6902,7 @@ function conditional(discriminator, branches, defaultBranch, options) {
6797
6902
  state: getWrappedChildState(conditionalState, conditionalState.branchState, branchParser)
6798
6903
  };
6799
6904
  const branchKey = selected.kind === "branch" ? selected.key : void 0;
6800
- if (branchKey != null && options?.errors?.branchError != null) for (const expanded of wrapBranchBarrierNodes(expandEffectfulRuntimeNodes([branchNode]), branchKey)) {
6905
+ if (branchKey != null && options?.errors?.branchError != null) for (const expanded of wrapBranchBarrierNodes(expandEffectfulRuntimeNodes([branchNode], expandOptions), branchKey)) {
6801
6906
  preExpandedBranchNodes.add(expanded);
6802
6907
  nodes.push(expanded);
6803
6908
  }
@@ -73,6 +73,21 @@ interface DependencySourceCapability {
73
73
  * @since 1.3.0
74
74
  */
75
75
  readonly completesWhenMissing?: boolean;
76
+ /**
77
+ * Whether this capability publishes whichever mutually exclusive
78
+ * alternative the run selected, rather than an occurrence of its own.
79
+ *
80
+ * An `or()`/`longestMatch()` whose alternatives share one source
81
+ * composes such a capability. The alternative is a scope of its own,
82
+ * so an enclosing construct's explicit source collection leaves it
83
+ * alone unless the exclusive parser opted into collection. Wrappers
84
+ * compose source capabilities by spreading them, so the flag travels
85
+ * through `optional()`, `withDefault()`, `map()`, and `multiple()`
86
+ * without each having to forward it.
87
+ *
88
+ * @since 1.3.0
89
+ */
90
+ readonly exclusiveOccurrence?: boolean;
76
91
  /**
77
92
  * Whether the parser's output value is the actual dependency source value.
78
93
  * `false` when a transform like `map()` has been applied.
@@ -73,6 +73,21 @@ interface DependencySourceCapability {
73
73
  * @since 1.3.0
74
74
  */
75
75
  readonly completesWhenMissing?: boolean;
76
+ /**
77
+ * Whether this capability publishes whichever mutually exclusive
78
+ * alternative the run selected, rather than an occurrence of its own.
79
+ *
80
+ * An `or()`/`longestMatch()` whose alternatives share one source
81
+ * composes such a capability. The alternative is a scope of its own,
82
+ * so an enclosing construct's explicit source collection leaves it
83
+ * alone unless the exclusive parser opted into collection. Wrappers
84
+ * compose source capabilities by spreading them, so the flag travels
85
+ * through `optional()`, `withDefault()`, `map()`, and `multiple()`
86
+ * without each having to forward it.
87
+ *
88
+ * @since 1.3.0
89
+ */
90
+ readonly exclusiveOccurrence?: boolean;
76
91
  /**
77
92
  * Whether the parser's output value is the actual dependency source value.
78
93
  * `false` when a transform like `map()` has been applied.
@@ -1850,6 +1850,13 @@ async function resolveDeferredInStateAsync(state, runtime, visited = /* @__PURE_
1850
1850
  /**
1851
1851
  * Determines whether a parser state represents an explicit match (the user
1852
1852
  * provided input) rather than an initial/pending state.
1853
+ *
1854
+ * @param fieldState The parser's state.
1855
+ * @param parser The parser owning that state.
1856
+ * @returns `true` when the state reflects command-line input, whether or
1857
+ * not that input passed validation.
1858
+ * @internal
1859
+ * @since 1.3.0
1853
1860
  */
1854
1861
  function isMatchedState(fieldState, parser) {
1855
1862
  if (fieldState === void 0) return false;
@@ -1922,6 +1929,18 @@ function buildRuntimeNodesFromArray(parsers, stateArray, parentPath) {
1922
1929
  }
1923
1930
  return nodes;
1924
1931
  }
1932
+ /**
1933
+ * Reads a derived parser state's snapshotted default dependency values.
1934
+ *
1935
+ * Nodes must carry the parse-time snapshot so replay never re-evaluates a
1936
+ * dynamic `getDefaultDependencyValues()` thunk.
1937
+ *
1938
+ * @param state The derived parser's state.
1939
+ * @returns The snapshotted values, or `undefined` when the state carries
1940
+ * none.
1941
+ * @internal
1942
+ * @since 1.3.0
1943
+ */
1925
1944
  function getDefaultDependencySnapshot(state) {
1926
1945
  return getDefaultDependencySnapshotInner(state, /* @__PURE__ */ new Set());
1927
1946
  }
@@ -1965,9 +1984,11 @@ exports.extractRawInputFromState = extractRawInputFromState;
1965
1984
  exports.fillMissingSourceDefaults = fillMissingSourceDefaults;
1966
1985
  exports.fillMissingSourceDefaultsAsync = fillMissingSourceDefaultsAsync;
1967
1986
  exports.forkRuntimeForCommittedSubtree = forkRuntimeForCommittedSubtree;
1987
+ exports.getDefaultDependencySnapshot = getDefaultDependencySnapshot;
1968
1988
  exports.guaranteedStaticSourceIdsKey = guaranteedStaticSourceIdsKey;
1969
1989
  exports.hasInactiveCompletion = hasInactiveCompletion;
1970
1990
  exports.includeSourceFailureChain = includeSourceFailureChain;
1991
+ exports.isMatchedState = isMatchedState;
1971
1992
  exports.orderDependencyNodes = orderDependencyNodes;
1972
1993
  exports.recordDerivedRawInput = recordDerivedRawInput;
1973
1994
  exports.recordSourceScope = recordSourceScope;
@@ -3,7 +3,7 @@ import { DependencyRegistryLike } from "./registry-types.cjs";
3
3
  import { ValueParserResult } from "./valueparser.cjs";
4
4
  import { ParserDependencyMetadata } from "./dependency-metadata.cjs";
5
5
  import { InputTrace } from "./input-trace.cjs";
6
- import { EffectfulCompletionSession, ExecutionContext } from "./internal/parser.cjs";
6
+ import { EffectfulCompletionSession, ExecutionContext, unmatchedNonCliDependencySourceStateMarker } from "./internal/parser.cjs";
7
7
 
8
8
  //#region src/dependency-runtime.d.ts
9
9
 
@@ -612,13 +612,29 @@ declare function extractRawInputFromState(state: unknown): string | undefined;
612
612
  * @since 1.3.0
613
613
  */
614
614
  declare const effectfulSchedulingNodesKey: unique symbol;
615
+ /**
616
+ * Options that steer how far a runtime-node expansion walks.
617
+ *
618
+ * @internal
619
+ * @since 1.3.0
620
+ */
621
+ interface ExpandRuntimeNodesOptions {
622
+ /**
623
+ * Stops the walk at an exclusive parser—an `or()`, `longestMatch()`, or
624
+ * `conditional()` alternative—that has not opted into source
625
+ * collection, so a scope of its own is not folded into the enclosing
626
+ * one. A hook that expands descendants itself must forward this to
627
+ * its own expansion.
628
+ */
629
+ readonly structuralOnly?: boolean;
630
+ }
615
631
  /**
616
632
  * The shape of the {@link effectfulSchedulingNodesKey} hook.
617
633
  *
618
634
  * @internal
619
635
  * @since 1.3.0
620
636
  */
621
- type EffectfulSchedulingNodesFn = (state: unknown, parentPath: readonly PropertyKey[] | undefined) => readonly RuntimeNode[];
637
+ type EffectfulSchedulingNodesFn = (state: unknown, parentPath: readonly PropertyKey[] | undefined, options?: ExpandRuntimeNodesOptions) => readonly RuntimeNode[];
622
638
  /**
623
639
  * Opt-in marker for parsers whose {@link effectfulSchedulingNodesKey}
624
640
  * hook also defines their explicit-source *collection* scope.
@@ -929,6 +945,22 @@ declare function resolveStateWithRuntime(state: unknown, runtime: DependencyRunt
929
945
  * @since 1.0.0
930
946
  */
931
947
  declare function resolveStateWithRuntimeAsync(state: unknown, runtime: DependencyRuntimeContext): Promise<unknown>;
948
+ /**
949
+ * Determines whether a parser state represents an explicit match (the user
950
+ * provided input) rather than an initial/pending state.
951
+ *
952
+ * @param fieldState The parser's state.
953
+ * @param parser The parser owning that state.
954
+ * @returns `true` when the state reflects command-line input, whether or
955
+ * not that input passed validation.
956
+ * @internal
957
+ * @since 1.3.0
958
+ */
959
+ declare function isMatchedState(fieldState: unknown, parser: {
960
+ readonly dependencyMetadata?: ParserDependencyMetadata;
961
+ readonly initialState?: unknown;
962
+ readonly [unmatchedNonCliDependencySourceStateMarker]?: true;
963
+ }): boolean;
932
964
  /**
933
965
  * Builds {@link RuntimeNode}s from field→parser pairs and a state record.
934
966
  *
@@ -961,5 +993,18 @@ declare function buildRuntimeNodesFromArray(parsers: ReadonlyArray<{
961
993
  readonly dependencyMetadata?: ParserDependencyMetadata;
962
994
  readonly initialState?: unknown;
963
995
  }>, stateArray: readonly unknown[], parentPath?: readonly PropertyKey[]): readonly RuntimeNode[];
996
+ /**
997
+ * Reads a derived parser state's snapshotted default dependency values.
998
+ *
999
+ * Nodes must carry the parse-time snapshot so replay never re-evaluates a
1000
+ * dynamic `getDefaultDependencyValues()` thunk.
1001
+ *
1002
+ * @param state The derived parser's state.
1003
+ * @returns The snapshotted values, or `undefined` when the state carries
1004
+ * none.
1005
+ * @internal
1006
+ * @since 1.3.0
1007
+ */
1008
+ declare function getDefaultDependencySnapshot(state: unknown): readonly unknown[] | undefined;
964
1009
  //#endregion
965
- export { BarrierCompletionDemandEdge, BarrierCompletionDependencies, BarrierResolution, CompleteEffectfulSourcesOptions, DependencyRequest, DependencyResolution, DependencyRuntimeContext, EffectfulSchedulingNodesFn, EffectfulSourceCompletion, EffectfulSourceCompletionResult, ReplayKey, ResolveDerivedSourceValuesOptions, RuntimeNode, SchedulingBarrierContext, SourceDefaultFailure, buildRuntimeNodesFromArray, buildRuntimeNodesFromPairs, collectDemandedDependencyIds, collectExplicitSourceValues, collectExplicitSourceValuesAsync, collectSourcesFromState, completeEffectfulSourcesAsync, createDependencyFingerprint, createDependencyRuntimeContext, createReplayKey, defineForwardedEffectfulSchedulingNodes, derivedRawInputKey, effectfulSchedulingNodesKey, exclusiveSourceScopeKey, extractRawInputFromState, fillMissingSourceDefaults, fillMissingSourceDefaultsAsync, forkRuntimeForCommittedSubtree, guaranteedStaticSourceIdsKey, hasInactiveCompletion, includeSourceFailureChain, orderDependencyNodes, recordDerivedRawInput, recordSourceScope, releaseUncollectedSourceValues, replaceCachedBarrierFailure, replayDerivedParser, replayDerivedParserAsync, resolveDerivedSourceValues, resolveDerivedSourceValuesAsync, resolveStateWithRuntime, resolveStateWithRuntimeAsync, serializeSchedulingPath, sourceCollectionExpansionKey, staticSourceScopeKey };
1010
+ export { BarrierCompletionDemandEdge, BarrierCompletionDependencies, BarrierResolution, CompleteEffectfulSourcesOptions, DependencyRequest, DependencyResolution, DependencyRuntimeContext, EffectfulSchedulingNodesFn, EffectfulSourceCompletion, EffectfulSourceCompletionResult, ExpandRuntimeNodesOptions, ReplayKey, ResolveDerivedSourceValuesOptions, RuntimeNode, SchedulingBarrierContext, SourceDefaultFailure, buildRuntimeNodesFromArray, buildRuntimeNodesFromPairs, collectDemandedDependencyIds, collectExplicitSourceValues, collectExplicitSourceValuesAsync, collectSourcesFromState, completeEffectfulSourcesAsync, createDependencyFingerprint, createDependencyRuntimeContext, createReplayKey, defineForwardedEffectfulSchedulingNodes, derivedRawInputKey, effectfulSchedulingNodesKey, exclusiveSourceScopeKey, extractRawInputFromState, fillMissingSourceDefaults, fillMissingSourceDefaultsAsync, forkRuntimeForCommittedSubtree, getDefaultDependencySnapshot, guaranteedStaticSourceIdsKey, hasInactiveCompletion, includeSourceFailureChain, isMatchedState, orderDependencyNodes, recordDerivedRawInput, recordSourceScope, releaseUncollectedSourceValues, replaceCachedBarrierFailure, replayDerivedParser, replayDerivedParserAsync, resolveDerivedSourceValues, resolveDerivedSourceValuesAsync, resolveStateWithRuntime, resolveStateWithRuntimeAsync, serializeSchedulingPath, sourceCollectionExpansionKey, staticSourceScopeKey };