@optique/core 1.3.0-dev.2425 → 1.3.0-dev.2428

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.
@@ -185,6 +185,68 @@ function collectStaticSourceIds(parser, out, seen) {
185
185
  const scope = parser[require_dependency_runtime.staticSourceScopeKey];
186
186
  if (scope != null) for (const child of scope) collectStaticSourceIds(child, out, seen);
187
187
  }
188
+ /**
189
+ * Statically collects the completion dependencies reachable through a
190
+ * parser's composed metadata and flattened field pairs, mirroring
191
+ * {@link collectStaticSourceIds}. Every completion consumer joins the
192
+ * ordering union, while demand edges record only parsers that are both
193
+ * a source and a completion consumer—the flat demand rule never
194
+ * demands a consumer-only completion's prerequisites, because such a
195
+ * completion defers itself out of the seed pass.
196
+ */
197
+ function collectStaticCompletionDependencies(parser, orderingDependencyIds, demandEdges, seen) {
198
+ if (seen.has(parser)) return;
199
+ seen.add(parser);
200
+ const metadata = parser.dependencyMetadata;
201
+ const completion = metadata?.completion;
202
+ if (completion != null) {
203
+ for (const id of completion.dependencyIds) orderingDependencyIds.add(id);
204
+ const consumerSourceId = metadata?.source?.sourceId;
205
+ if (consumerSourceId != null) demandEdges.push({
206
+ consumerSourceId,
207
+ dependencyIds: completion.dependencyIds
208
+ });
209
+ }
210
+ const pairs = parser[fieldParsersKey];
211
+ if (pairs != null) for (const [, child] of pairs) collectStaticCompletionDependencies(child, orderingDependencyIds, demandEdges, seen);
212
+ const scope = parser[require_dependency_runtime.staticSourceScopeKey];
213
+ if (scope != null) for (const child of scope) collectStaticCompletionDependencies(child, orderingDependencyIds, demandEdges, seen);
214
+ }
215
+ /**
216
+ * Recomputes a speculative barrier's completion dependencies from the
217
+ * guessed branch's expanded runtime nodes, so an occurrence already
218
+ * satisfied by the command line or a binding contributes neither
219
+ * ordering edges nor demand, mirroring the flat inactive-completion
220
+ * rule. Nested barriers contribute their own aggregates, which remain
221
+ * static estimates.
222
+ */
223
+ function computeBarrierDependenciesFromNodes(nodes, provides) {
224
+ const ordering = /* @__PURE__ */ new Set();
225
+ const demandEdges = [];
226
+ for (const node of nodes) {
227
+ const metadata = node.parser.dependencyMetadata;
228
+ const completion = metadata?.completion;
229
+ if (completion != null && !require_dependency_runtime.hasInactiveCompletion(node)) {
230
+ for (const id of completion.dependencyIds) ordering.add(id);
231
+ const consumerSourceId = metadata?.source?.sourceId;
232
+ if (consumerSourceId != null) demandEdges.push({
233
+ consumerSourceId,
234
+ dependencyIds: completion.dependencyIds
235
+ });
236
+ }
237
+ const nested = node.barrierCompletionDependencies;
238
+ if (nested != null) {
239
+ for (const id of nested.orderingDependencyIds) ordering.add(id);
240
+ demandEdges.push(...nested.demandEdges);
241
+ }
242
+ }
243
+ const orderingDependencyIds = [...ordering].filter((id) => !provides.has(id));
244
+ if (orderingDependencyIds.length < 1 && demandEdges.length < 1) return void 0;
245
+ return {
246
+ orderingDependencyIds,
247
+ demandEdges
248
+ };
249
+ }
188
250
  function isDeferredCompletionResult(result) {
189
251
  return typeof result === "object" && result !== null && "success" in result && result.success === true && "deferred" in result && result.deferred === true;
190
252
  }
@@ -6443,12 +6505,51 @@ function conditional(discriminator, branches, defaultBranch, options) {
6443
6505
  };
6444
6506
  const preparedKeyPrefix = `c${++conditionalInstanceCounter}|`;
6445
6507
  const preparedKeyFor = (parentPath) => preparedKeyPrefix + require_dependency_runtime.serializeSchedulingPath(parentPath ?? []);
6446
- const branchProvidedSourceIds = (() => {
6447
- const out = /* @__PURE__ */ new Set();
6448
- const seen = /* @__PURE__ */ new Set();
6449
- for (const key of Object.keys(branches)) collectStaticSourceIds(branches[key], out, seen);
6450
- if (defaultBranch != null) collectStaticSourceIds(defaultBranch, out, seen);
6451
- return out;
6508
+ const { branchProvidedSourceIds, mergedBarrierDependencies, barrierDependenciesByKey } = (() => {
6509
+ const provided = /* @__PURE__ */ new Set();
6510
+ const byParser = /* @__PURE__ */ new Map();
6511
+ const computeBranch = (branch) => {
6512
+ const memoized = byParser.get(branch);
6513
+ if (memoized != null) return memoized;
6514
+ const provides = /* @__PURE__ */ new Set();
6515
+ collectStaticSourceIds(branch, provides, /* @__PURE__ */ new Set());
6516
+ for (const id of provides) provided.add(id);
6517
+ const ordering = /* @__PURE__ */ new Set();
6518
+ const demandEdges = [];
6519
+ collectStaticCompletionDependencies(branch, ordering, demandEdges, /* @__PURE__ */ new Set());
6520
+ const orderingDependencyIds = [...ordering].filter((id) => !provides.has(id));
6521
+ const estimate = {
6522
+ provides,
6523
+ ...orderingDependencyIds.length < 1 && demandEdges.length < 1 ? {} : { dependencies: {
6524
+ orderingDependencyIds,
6525
+ demandEdges
6526
+ } }
6527
+ };
6528
+ byParser.set(branch, estimate);
6529
+ return estimate;
6530
+ };
6531
+ const byKey = /* @__PURE__ */ new Map();
6532
+ for (const key of Object.keys(branches)) {
6533
+ const estimate = computeBranch(branches[key]);
6534
+ if (estimate.dependencies != null) byKey.set(key, estimate);
6535
+ }
6536
+ if (defaultBranch != null) computeBranch(defaultBranch);
6537
+ const orderingUnion = /* @__PURE__ */ new Set();
6538
+ const mergedEdges = [];
6539
+ for (const estimate of new Set(byParser.values())) {
6540
+ if (estimate.dependencies == null) continue;
6541
+ for (const id of estimate.dependencies.orderingDependencyIds) orderingUnion.add(id);
6542
+ mergedEdges.push(...estimate.dependencies.demandEdges);
6543
+ }
6544
+ const merged = orderingUnion.size < 1 && mergedEdges.length < 1 ? void 0 : {
6545
+ orderingDependencyIds: [...orderingUnion],
6546
+ demandEdges: mergedEdges
6547
+ };
6548
+ return {
6549
+ branchProvidedSourceIds: provided,
6550
+ mergedBarrierDependencies: merged,
6551
+ barrierDependenciesByKey: byKey
6552
+ };
6452
6553
  })();
6453
6554
  const buildConditionalSchedulingNodes = (state, parentPath) => {
6454
6555
  if (state == null || typeof state !== "object") return [];
@@ -6494,12 +6595,24 @@ function conditional(discriminator, branches, defaultBranch, options) {
6494
6595
  if (selected?.kind !== "branch" || innerCompleteSource == null) return nodes;
6495
6596
  teeDiscriminatorNode();
6496
6597
  const speculativeKey = selected.key;
6598
+ const speculativeBarrierDependencies = (() => {
6599
+ const estimate = barrierDependenciesByKey.get(speculativeKey);
6600
+ if (estimate?.dependencies == null) return void 0;
6601
+ const branchParser = branches[speculativeKey];
6602
+ if (branchParser == null) return estimate.dependencies;
6603
+ return computeBarrierDependenciesFromNodes(expandEffectfulRuntimeNodes([{
6604
+ path: [...parentPath ?? [], "_branch"],
6605
+ parser: branchParser,
6606
+ state: require_annotation_state.getWrappedChildState(conditionalState, conditionalState.branchState, branchParser)
6607
+ }]), estimate.provides);
6608
+ })();
6497
6609
  nodes.push({
6498
6610
  path: [...parentPath ?? [], "_branch"],
6499
6611
  parser: {},
6500
6612
  state: conditionalState,
6501
6613
  ...discriminatorSource != null ? { requiresSourceId: discriminatorSource.sourceId } : {},
6502
6614
  providesSourceIds: branchProvidedSourceIds,
6615
+ ...speculativeBarrierDependencies != null ? { barrierCompletionDependencies: speculativeBarrierDependencies } : {},
6503
6616
  prepare: async (ctx) => {
6504
6617
  const session = ctx.exec?.effectfulCompletionSession;
6505
6618
  if (session == null) return void 0;
@@ -6526,6 +6639,7 @@ function conditional(discriminator, branches, defaultBranch, options) {
6526
6639
  state: conditionalState,
6527
6640
  ...discriminatorSource != null ? { requiresSourceId: discriminatorSource.sourceId } : {},
6528
6641
  providesSourceIds: branchProvidedSourceIds,
6642
+ ...mergedBarrierDependencies != null ? { barrierCompletionDependencies: mergedBarrierDependencies } : {},
6529
6643
  prepare: async (ctx) => {
6530
6644
  const session = ctx.exec?.effectfulCompletionSession;
6531
6645
  if (session == null) return void 0;
@@ -5,7 +5,7 @@ import { extractArgumentMetavars, extractCommandNames, extractOptionNames, isDoc
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, fillMissingSourceDefaults, fillMissingSourceDefaultsAsync, resolveDerivedSourceValues, resolveDerivedSourceValuesAsync, resolveStateWithRuntime, resolveStateWithRuntimeAsync, serializeSchedulingPath, sourceCollectionExpansionKey, staticSourceScopeKey } from "./dependency-runtime.js";
8
+ import { buildRuntimeNodesFromArray, buildRuntimeNodesFromPairs, collectExplicitSourceValues, collectExplicitSourceValuesAsync, collectSourcesFromState, completeEffectfulSourcesAsync, createDependencyRuntimeContext, effectfulSchedulingNodesKey, fillMissingSourceDefaults, fillMissingSourceDefaultsAsync, hasInactiveCompletion, 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";
@@ -185,6 +185,68 @@ function collectStaticSourceIds(parser, out, seen) {
185
185
  const scope = parser[staticSourceScopeKey];
186
186
  if (scope != null) for (const child of scope) collectStaticSourceIds(child, out, seen);
187
187
  }
188
+ /**
189
+ * Statically collects the completion dependencies reachable through a
190
+ * parser's composed metadata and flattened field pairs, mirroring
191
+ * {@link collectStaticSourceIds}. Every completion consumer joins the
192
+ * ordering union, while demand edges record only parsers that are both
193
+ * a source and a completion consumer—the flat demand rule never
194
+ * demands a consumer-only completion's prerequisites, because such a
195
+ * completion defers itself out of the seed pass.
196
+ */
197
+ function collectStaticCompletionDependencies(parser, orderingDependencyIds, demandEdges, seen) {
198
+ if (seen.has(parser)) return;
199
+ seen.add(parser);
200
+ const metadata = parser.dependencyMetadata;
201
+ const completion = metadata?.completion;
202
+ if (completion != null) {
203
+ for (const id of completion.dependencyIds) orderingDependencyIds.add(id);
204
+ const consumerSourceId = metadata?.source?.sourceId;
205
+ if (consumerSourceId != null) demandEdges.push({
206
+ consumerSourceId,
207
+ dependencyIds: completion.dependencyIds
208
+ });
209
+ }
210
+ const pairs = parser[fieldParsersKey];
211
+ if (pairs != null) for (const [, child] of pairs) collectStaticCompletionDependencies(child, orderingDependencyIds, demandEdges, seen);
212
+ const scope = parser[staticSourceScopeKey];
213
+ if (scope != null) for (const child of scope) collectStaticCompletionDependencies(child, orderingDependencyIds, demandEdges, seen);
214
+ }
215
+ /**
216
+ * Recomputes a speculative barrier's completion dependencies from the
217
+ * guessed branch's expanded runtime nodes, so an occurrence already
218
+ * satisfied by the command line or a binding contributes neither
219
+ * ordering edges nor demand, mirroring the flat inactive-completion
220
+ * rule. Nested barriers contribute their own aggregates, which remain
221
+ * static estimates.
222
+ */
223
+ function computeBarrierDependenciesFromNodes(nodes, provides) {
224
+ const ordering = /* @__PURE__ */ new Set();
225
+ const demandEdges = [];
226
+ for (const node of nodes) {
227
+ const metadata = node.parser.dependencyMetadata;
228
+ const completion = metadata?.completion;
229
+ if (completion != null && !hasInactiveCompletion(node)) {
230
+ for (const id of completion.dependencyIds) ordering.add(id);
231
+ const consumerSourceId = metadata?.source?.sourceId;
232
+ if (consumerSourceId != null) demandEdges.push({
233
+ consumerSourceId,
234
+ dependencyIds: completion.dependencyIds
235
+ });
236
+ }
237
+ const nested = node.barrierCompletionDependencies;
238
+ if (nested != null) {
239
+ for (const id of nested.orderingDependencyIds) ordering.add(id);
240
+ demandEdges.push(...nested.demandEdges);
241
+ }
242
+ }
243
+ const orderingDependencyIds = [...ordering].filter((id) => !provides.has(id));
244
+ if (orderingDependencyIds.length < 1 && demandEdges.length < 1) return void 0;
245
+ return {
246
+ orderingDependencyIds,
247
+ demandEdges
248
+ };
249
+ }
188
250
  function isDeferredCompletionResult(result) {
189
251
  return typeof result === "object" && result !== null && "success" in result && result.success === true && "deferred" in result && result.deferred === true;
190
252
  }
@@ -6443,12 +6505,51 @@ function conditional(discriminator, branches, defaultBranch, options) {
6443
6505
  };
6444
6506
  const preparedKeyPrefix = `c${++conditionalInstanceCounter}|`;
6445
6507
  const preparedKeyFor = (parentPath) => preparedKeyPrefix + serializeSchedulingPath(parentPath ?? []);
6446
- const branchProvidedSourceIds = (() => {
6447
- const out = /* @__PURE__ */ new Set();
6448
- const seen = /* @__PURE__ */ new Set();
6449
- for (const key of Object.keys(branches)) collectStaticSourceIds(branches[key], out, seen);
6450
- if (defaultBranch != null) collectStaticSourceIds(defaultBranch, out, seen);
6451
- return out;
6508
+ const { branchProvidedSourceIds, mergedBarrierDependencies, barrierDependenciesByKey } = (() => {
6509
+ const provided = /* @__PURE__ */ new Set();
6510
+ const byParser = /* @__PURE__ */ new Map();
6511
+ const computeBranch = (branch) => {
6512
+ const memoized = byParser.get(branch);
6513
+ if (memoized != null) return memoized;
6514
+ const provides = /* @__PURE__ */ new Set();
6515
+ collectStaticSourceIds(branch, provides, /* @__PURE__ */ new Set());
6516
+ for (const id of provides) provided.add(id);
6517
+ const ordering = /* @__PURE__ */ new Set();
6518
+ const demandEdges = [];
6519
+ collectStaticCompletionDependencies(branch, ordering, demandEdges, /* @__PURE__ */ new Set());
6520
+ const orderingDependencyIds = [...ordering].filter((id) => !provides.has(id));
6521
+ const estimate = {
6522
+ provides,
6523
+ ...orderingDependencyIds.length < 1 && demandEdges.length < 1 ? {} : { dependencies: {
6524
+ orderingDependencyIds,
6525
+ demandEdges
6526
+ } }
6527
+ };
6528
+ byParser.set(branch, estimate);
6529
+ return estimate;
6530
+ };
6531
+ const byKey = /* @__PURE__ */ new Map();
6532
+ for (const key of Object.keys(branches)) {
6533
+ const estimate = computeBranch(branches[key]);
6534
+ if (estimate.dependencies != null) byKey.set(key, estimate);
6535
+ }
6536
+ if (defaultBranch != null) computeBranch(defaultBranch);
6537
+ const orderingUnion = /* @__PURE__ */ new Set();
6538
+ const mergedEdges = [];
6539
+ for (const estimate of new Set(byParser.values())) {
6540
+ if (estimate.dependencies == null) continue;
6541
+ for (const id of estimate.dependencies.orderingDependencyIds) orderingUnion.add(id);
6542
+ mergedEdges.push(...estimate.dependencies.demandEdges);
6543
+ }
6544
+ const merged = orderingUnion.size < 1 && mergedEdges.length < 1 ? void 0 : {
6545
+ orderingDependencyIds: [...orderingUnion],
6546
+ demandEdges: mergedEdges
6547
+ };
6548
+ return {
6549
+ branchProvidedSourceIds: provided,
6550
+ mergedBarrierDependencies: merged,
6551
+ barrierDependenciesByKey: byKey
6552
+ };
6452
6553
  })();
6453
6554
  const buildConditionalSchedulingNodes = (state, parentPath) => {
6454
6555
  if (state == null || typeof state !== "object") return [];
@@ -6494,12 +6595,24 @@ function conditional(discriminator, branches, defaultBranch, options) {
6494
6595
  if (selected?.kind !== "branch" || innerCompleteSource == null) return nodes;
6495
6596
  teeDiscriminatorNode();
6496
6597
  const speculativeKey = selected.key;
6598
+ const speculativeBarrierDependencies = (() => {
6599
+ const estimate = barrierDependenciesByKey.get(speculativeKey);
6600
+ if (estimate?.dependencies == null) return void 0;
6601
+ const branchParser = branches[speculativeKey];
6602
+ if (branchParser == null) return estimate.dependencies;
6603
+ return computeBarrierDependenciesFromNodes(expandEffectfulRuntimeNodes([{
6604
+ path: [...parentPath ?? [], "_branch"],
6605
+ parser: branchParser,
6606
+ state: getWrappedChildState(conditionalState, conditionalState.branchState, branchParser)
6607
+ }]), estimate.provides);
6608
+ })();
6497
6609
  nodes.push({
6498
6610
  path: [...parentPath ?? [], "_branch"],
6499
6611
  parser: {},
6500
6612
  state: conditionalState,
6501
6613
  ...discriminatorSource != null ? { requiresSourceId: discriminatorSource.sourceId } : {},
6502
6614
  providesSourceIds: branchProvidedSourceIds,
6615
+ ...speculativeBarrierDependencies != null ? { barrierCompletionDependencies: speculativeBarrierDependencies } : {},
6503
6616
  prepare: async (ctx) => {
6504
6617
  const session = ctx.exec?.effectfulCompletionSession;
6505
6618
  if (session == null) return void 0;
@@ -6526,6 +6639,7 @@ function conditional(discriminator, branches, defaultBranch, options) {
6526
6639
  state: conditionalState,
6527
6640
  ...discriminatorSource != null ? { requiresSourceId: discriminatorSource.sourceId } : {},
6528
6641
  providesSourceIds: branchProvidedSourceIds,
6642
+ ...mergedBarrierDependencies != null ? { barrierCompletionDependencies: mergedBarrierDependencies } : {},
6529
6643
  prepare: async (ctx) => {
6530
6644
  const session = ctx.exec?.effectfulCompletionSession;
6531
6645
  if (session == null) return void 0;
@@ -393,7 +393,9 @@ async function collectExplicitSourceValuesAsync(nodes, runtime) {
393
393
  *
394
394
  * Missing providers create no edge because the consumer may use its declared
395
395
  * default. Scheduling barriers act as providers for the source IDs their
396
- * selected subtree may expose and depend on their discriminator source.
396
+ * selected subtree may expose and depend on their discriminator source as
397
+ * well as on outer providers of the completion dependencies their
398
+ * selectable subtrees aggregate.
397
399
  *
398
400
  * @param nodes Runtime nodes in declaration order.
399
401
  * @returns The same nodes in stable dependency order.
@@ -437,6 +439,10 @@ function orderDependencyNodes(nodes) {
437
439
  if (node.requiresSourceId != null) {
438
440
  for (const provider of providers.get(node.requiresSourceId) ?? []) if (provider !== node) addEdge(provider, node);
439
441
  }
442
+ const barrierDeps = node.barrierCompletionDependencies;
443
+ if (barrierDeps != null) {
444
+ for (const dependencySourceId of barrierDeps.orderingDependencyIds) for (const provider of providers.get(dependencySourceId) ?? []) if (provider !== node) addEdge(provider, node);
445
+ }
440
446
  }
441
447
  const declarationOrder = new Map(nodes.map((node, index) => [node, index]));
442
448
  const ready = nodes.filter((node) => indegree.get(node) === 0);
@@ -553,6 +559,9 @@ const inactiveCompletionCache = /* @__PURE__ */ new WeakMap();
553
559
  * the completion active (the effectful completion is its recovery
554
560
  * path), and an asynchronous extraction is conservatively treated as
555
561
  * active because it cannot be decided synchronously.
562
+ *
563
+ * @internal
564
+ * @since 1.3.0
556
565
  */
557
566
  function hasInactiveCompletion(node) {
558
567
  const cached = inactiveCompletionCache.get(node);
@@ -1062,6 +1071,14 @@ async function completeEffectfulSourcesAsync(nodes, state, runtime, exec, option
1062
1071
  session.demanded.add(dependencySourceId);
1063
1072
  demandAdded = true;
1064
1073
  }
1074
+ if (node.barrierCompletionDependencies != null) for (const edge of node.barrierCompletionDependencies.demandEdges) {
1075
+ if (!session.demanded.has(edge.consumerSourceId)) continue;
1076
+ for (const dependencySourceId of edge.dependencyIds) {
1077
+ if (session.demanded.has(dependencySourceId)) continue;
1078
+ session.demanded.add(dependencySourceId);
1079
+ demandAdded = true;
1080
+ }
1081
+ }
1065
1082
  if (node.requiresSourceId == null || node.providesSourceIds == null) continue;
1066
1083
  if (session.demanded.has(node.requiresSourceId)) continue;
1067
1084
  for (const provided of node.providesSourceIds) if (session.demanded.has(provided)) {
@@ -1465,6 +1482,7 @@ exports.effectfulSchedulingNodesKey = effectfulSchedulingNodesKey;
1465
1482
  exports.extractRawInputFromState = extractRawInputFromState;
1466
1483
  exports.fillMissingSourceDefaults = fillMissingSourceDefaults;
1467
1484
  exports.fillMissingSourceDefaultsAsync = fillMissingSourceDefaultsAsync;
1485
+ exports.hasInactiveCompletion = hasInactiveCompletion;
1468
1486
  exports.includeSourceFailureChain = includeSourceFailureChain;
1469
1487
  exports.orderDependencyNodes = orderDependencyNodes;
1470
1488
  exports.recordDerivedRawInput = recordDerivedRawInput;
@@ -166,6 +166,63 @@ interface RuntimeNode {
166
166
  * @since 1.3.0
167
167
  */
168
168
  readonly requiresSourceId?: symbol;
169
+ /**
170
+ * Completion dependencies aggregated from the selectable subtrees
171
+ * guarded by this barrier, so the outer pass can order the barrier
172
+ * after the providers those subtrees' effectful completions read and
173
+ * propagate demand to them.
174
+ *
175
+ * The sets are static estimates: they describe every selectable
176
+ * branch, not the one preparation will eventually choose. They are
177
+ * advisory for ordering and demand only and never join failure
178
+ * lineage—a failed prerequisite used only by an unselected branch
179
+ * must not fail the barrier.
180
+ *
181
+ * @since 1.3.0
182
+ */
183
+ readonly barrierCompletionDependencies?: BarrierCompletionDependencies;
184
+ }
185
+ /**
186
+ * An exact demand edge inside a scheduling barrier's subtree: when the
187
+ * consumer's own source is demanded, the completion dependencies its
188
+ * effectful completion reads become demanded as well, mirroring the
189
+ * flat demand rule for parsers whose completion consumes dependency
190
+ * values.
191
+ *
192
+ * @internal
193
+ * @since 1.3.0
194
+ */
195
+ interface BarrierCompletionDemandEdge {
196
+ /** The branch consumer's own source ID (the demand trigger). */
197
+ readonly consumerSourceId: symbol;
198
+ /** The completion dependency IDs that consumer reads. */
199
+ readonly dependencyIds: readonly symbol[];
200
+ }
201
+ /**
202
+ * Completion dependencies a scheduling barrier aggregates from its
203
+ * selectable subtrees. See
204
+ * {@link RuntimeNode.barrierCompletionDependencies}.
205
+ *
206
+ * @internal
207
+ * @since 1.3.0
208
+ */
209
+ interface BarrierCompletionDependencies {
210
+ /**
211
+ * Union of the branches' completion dependency IDs, with each
212
+ * branch's own statically providable IDs subtracted. Used only to
213
+ * order the barrier after outer providers; a missing provider
214
+ * creates no edge.
215
+ */
216
+ readonly orderingDependencyIds: readonly symbol[];
217
+ /**
218
+ * Exact demand edges collected from branch parsers that are both a
219
+ * source and a completion consumer. Unlike
220
+ * {@link BarrierCompletionDependencies.orderingDependencyIds}, these
221
+ * keep branch-internal prerequisites: a demanded consumer must also
222
+ * demand an internal source prompt so it does not defer out of the
223
+ * seed pass.
224
+ */
225
+ readonly demandEdges: readonly BarrierCompletionDemandEdge[];
169
226
  }
170
227
  /**
171
228
  * Options for resolving matched derived source values.
@@ -296,7 +353,9 @@ declare function collectExplicitSourceValuesAsync(nodes: readonly RuntimeNode[],
296
353
  *
297
354
  * Missing providers create no edge because the consumer may use its declared
298
355
  * default. Scheduling barriers act as providers for the source IDs their
299
- * selected subtree may expose and depend on their discriminator source.
356
+ * selected subtree may expose and depend on their discriminator source as
357
+ * well as on outer providers of the completion dependencies their
358
+ * selectable subtrees aggregate.
300
359
  *
301
360
  * @param nodes Runtime nodes in declaration order.
302
361
  * @returns The same nodes in stable dependency order.
@@ -309,6 +368,20 @@ declare function orderDependencyNodes(nodes: readonly RuntimeNode[]): readonly R
309
368
  declare function resolveDerivedSourceValues(nodes: readonly RuntimeNode[], runtime: DependencyRuntimeContext, options?: ResolveDerivedSourceValuesOptions): void;
310
369
  /** Async version of {@link resolveDerivedSourceValues}. */
311
370
  declare function resolveDerivedSourceValuesAsync(nodes: readonly RuntimeNode[], runtime: DependencyRuntimeContext, options?: ResolveDerivedSourceValuesOptions): Promise<void>;
371
+ /**
372
+ * Whether a node's completion dependencies are inactive because this
373
+ * occurrence's own state already extracts a value: a field satisfied by
374
+ * the command line or a binding never runs its configuration resolver,
375
+ * so its completion dependencies must not order scheduling, demand
376
+ * upstream sources, or join failure lineage. A failed extraction keeps
377
+ * the completion active (the effectful completion is its recovery
378
+ * path), and an asynchronous extraction is conservatively treated as
379
+ * active because it cannot be decided synchronously.
380
+ *
381
+ * @internal
382
+ * @since 1.3.0
383
+ */
384
+ declare function hasInactiveCompletion(node: RuntimeNode): boolean;
312
385
  /**
313
386
  * Appends the recorded dependency chain to a source failure.
314
387
  *
@@ -694,4 +767,4 @@ declare function buildRuntimeNodesFromArray(parsers: ReadonlyArray<{
694
767
  readonly initialState?: unknown;
695
768
  }>, stateArray: readonly unknown[], parentPath?: readonly PropertyKey[]): readonly RuntimeNode[];
696
769
  //#endregion
697
- export { 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, extractRawInputFromState, fillMissingSourceDefaults, fillMissingSourceDefaultsAsync, includeSourceFailureChain, orderDependencyNodes, recordDerivedRawInput, replayDerivedParser, replayDerivedParserAsync, resolveDerivedSourceValues, resolveDerivedSourceValuesAsync, resolveStateWithRuntime, resolveStateWithRuntimeAsync, serializeSchedulingPath, sourceCollectionExpansionKey, staticSourceScopeKey };
770
+ export { BarrierCompletionDemandEdge, BarrierCompletionDependencies, 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, extractRawInputFromState, fillMissingSourceDefaults, fillMissingSourceDefaultsAsync, hasInactiveCompletion, includeSourceFailureChain, orderDependencyNodes, recordDerivedRawInput, replayDerivedParser, replayDerivedParserAsync, resolveDerivedSourceValues, resolveDerivedSourceValuesAsync, resolveStateWithRuntime, resolveStateWithRuntimeAsync, serializeSchedulingPath, sourceCollectionExpansionKey, staticSourceScopeKey };
@@ -166,6 +166,63 @@ interface RuntimeNode {
166
166
  * @since 1.3.0
167
167
  */
168
168
  readonly requiresSourceId?: symbol;
169
+ /**
170
+ * Completion dependencies aggregated from the selectable subtrees
171
+ * guarded by this barrier, so the outer pass can order the barrier
172
+ * after the providers those subtrees' effectful completions read and
173
+ * propagate demand to them.
174
+ *
175
+ * The sets are static estimates: they describe every selectable
176
+ * branch, not the one preparation will eventually choose. They are
177
+ * advisory for ordering and demand only and never join failure
178
+ * lineage—a failed prerequisite used only by an unselected branch
179
+ * must not fail the barrier.
180
+ *
181
+ * @since 1.3.0
182
+ */
183
+ readonly barrierCompletionDependencies?: BarrierCompletionDependencies;
184
+ }
185
+ /**
186
+ * An exact demand edge inside a scheduling barrier's subtree: when the
187
+ * consumer's own source is demanded, the completion dependencies its
188
+ * effectful completion reads become demanded as well, mirroring the
189
+ * flat demand rule for parsers whose completion consumes dependency
190
+ * values.
191
+ *
192
+ * @internal
193
+ * @since 1.3.0
194
+ */
195
+ interface BarrierCompletionDemandEdge {
196
+ /** The branch consumer's own source ID (the demand trigger). */
197
+ readonly consumerSourceId: symbol;
198
+ /** The completion dependency IDs that consumer reads. */
199
+ readonly dependencyIds: readonly symbol[];
200
+ }
201
+ /**
202
+ * Completion dependencies a scheduling barrier aggregates from its
203
+ * selectable subtrees. See
204
+ * {@link RuntimeNode.barrierCompletionDependencies}.
205
+ *
206
+ * @internal
207
+ * @since 1.3.0
208
+ */
209
+ interface BarrierCompletionDependencies {
210
+ /**
211
+ * Union of the branches' completion dependency IDs, with each
212
+ * branch's own statically providable IDs subtracted. Used only to
213
+ * order the barrier after outer providers; a missing provider
214
+ * creates no edge.
215
+ */
216
+ readonly orderingDependencyIds: readonly symbol[];
217
+ /**
218
+ * Exact demand edges collected from branch parsers that are both a
219
+ * source and a completion consumer. Unlike
220
+ * {@link BarrierCompletionDependencies.orderingDependencyIds}, these
221
+ * keep branch-internal prerequisites: a demanded consumer must also
222
+ * demand an internal source prompt so it does not defer out of the
223
+ * seed pass.
224
+ */
225
+ readonly demandEdges: readonly BarrierCompletionDemandEdge[];
169
226
  }
170
227
  /**
171
228
  * Options for resolving matched derived source values.
@@ -296,7 +353,9 @@ declare function collectExplicitSourceValuesAsync(nodes: readonly RuntimeNode[],
296
353
  *
297
354
  * Missing providers create no edge because the consumer may use its declared
298
355
  * default. Scheduling barriers act as providers for the source IDs their
299
- * selected subtree may expose and depend on their discriminator source.
356
+ * selected subtree may expose and depend on their discriminator source as
357
+ * well as on outer providers of the completion dependencies their
358
+ * selectable subtrees aggregate.
300
359
  *
301
360
  * @param nodes Runtime nodes in declaration order.
302
361
  * @returns The same nodes in stable dependency order.
@@ -309,6 +368,20 @@ declare function orderDependencyNodes(nodes: readonly RuntimeNode[]): readonly R
309
368
  declare function resolveDerivedSourceValues(nodes: readonly RuntimeNode[], runtime: DependencyRuntimeContext, options?: ResolveDerivedSourceValuesOptions): void;
310
369
  /** Async version of {@link resolveDerivedSourceValues}. */
311
370
  declare function resolveDerivedSourceValuesAsync(nodes: readonly RuntimeNode[], runtime: DependencyRuntimeContext, options?: ResolveDerivedSourceValuesOptions): Promise<void>;
371
+ /**
372
+ * Whether a node's completion dependencies are inactive because this
373
+ * occurrence's own state already extracts a value: a field satisfied by
374
+ * the command line or a binding never runs its configuration resolver,
375
+ * so its completion dependencies must not order scheduling, demand
376
+ * upstream sources, or join failure lineage. A failed extraction keeps
377
+ * the completion active (the effectful completion is its recovery
378
+ * path), and an asynchronous extraction is conservatively treated as
379
+ * active because it cannot be decided synchronously.
380
+ *
381
+ * @internal
382
+ * @since 1.3.0
383
+ */
384
+ declare function hasInactiveCompletion(node: RuntimeNode): boolean;
312
385
  /**
313
386
  * Appends the recorded dependency chain to a source failure.
314
387
  *
@@ -694,4 +767,4 @@ declare function buildRuntimeNodesFromArray(parsers: ReadonlyArray<{
694
767
  readonly initialState?: unknown;
695
768
  }>, stateArray: readonly unknown[], parentPath?: readonly PropertyKey[]): readonly RuntimeNode[];
696
769
  //#endregion
697
- export { 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, extractRawInputFromState, fillMissingSourceDefaults, fillMissingSourceDefaultsAsync, includeSourceFailureChain, orderDependencyNodes, recordDerivedRawInput, replayDerivedParser, replayDerivedParserAsync, resolveDerivedSourceValues, resolveDerivedSourceValuesAsync, resolveStateWithRuntime, resolveStateWithRuntimeAsync, serializeSchedulingPath, sourceCollectionExpansionKey, staticSourceScopeKey };
770
+ export { BarrierCompletionDemandEdge, BarrierCompletionDependencies, 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, extractRawInputFromState, fillMissingSourceDefaults, fillMissingSourceDefaultsAsync, hasInactiveCompletion, includeSourceFailureChain, orderDependencyNodes, recordDerivedRawInput, replayDerivedParser, replayDerivedParserAsync, resolveDerivedSourceValues, resolveDerivedSourceValuesAsync, resolveStateWithRuntime, resolveStateWithRuntimeAsync, serializeSchedulingPath, sourceCollectionExpansionKey, staticSourceScopeKey };
@@ -393,7 +393,9 @@ async function collectExplicitSourceValuesAsync(nodes, runtime) {
393
393
  *
394
394
  * Missing providers create no edge because the consumer may use its declared
395
395
  * default. Scheduling barriers act as providers for the source IDs their
396
- * selected subtree may expose and depend on their discriminator source.
396
+ * selected subtree may expose and depend on their discriminator source as
397
+ * well as on outer providers of the completion dependencies their
398
+ * selectable subtrees aggregate.
397
399
  *
398
400
  * @param nodes Runtime nodes in declaration order.
399
401
  * @returns The same nodes in stable dependency order.
@@ -437,6 +439,10 @@ function orderDependencyNodes(nodes) {
437
439
  if (node.requiresSourceId != null) {
438
440
  for (const provider of providers.get(node.requiresSourceId) ?? []) if (provider !== node) addEdge(provider, node);
439
441
  }
442
+ const barrierDeps = node.barrierCompletionDependencies;
443
+ if (barrierDeps != null) {
444
+ for (const dependencySourceId of barrierDeps.orderingDependencyIds) for (const provider of providers.get(dependencySourceId) ?? []) if (provider !== node) addEdge(provider, node);
445
+ }
440
446
  }
441
447
  const declarationOrder = new Map(nodes.map((node, index) => [node, index]));
442
448
  const ready = nodes.filter((node) => indegree.get(node) === 0);
@@ -553,6 +559,9 @@ const inactiveCompletionCache = /* @__PURE__ */ new WeakMap();
553
559
  * the completion active (the effectful completion is its recovery
554
560
  * path), and an asynchronous extraction is conservatively treated as
555
561
  * active because it cannot be decided synchronously.
562
+ *
563
+ * @internal
564
+ * @since 1.3.0
556
565
  */
557
566
  function hasInactiveCompletion(node) {
558
567
  const cached = inactiveCompletionCache.get(node);
@@ -1062,6 +1071,14 @@ async function completeEffectfulSourcesAsync(nodes, state, runtime, exec, option
1062
1071
  session.demanded.add(dependencySourceId);
1063
1072
  demandAdded = true;
1064
1073
  }
1074
+ if (node.barrierCompletionDependencies != null) for (const edge of node.barrierCompletionDependencies.demandEdges) {
1075
+ if (!session.demanded.has(edge.consumerSourceId)) continue;
1076
+ for (const dependencySourceId of edge.dependencyIds) {
1077
+ if (session.demanded.has(dependencySourceId)) continue;
1078
+ session.demanded.add(dependencySourceId);
1079
+ demandAdded = true;
1080
+ }
1081
+ }
1065
1082
  if (node.requiresSourceId == null || node.providesSourceIds == null) continue;
1066
1083
  if (session.demanded.has(node.requiresSourceId)) continue;
1067
1084
  for (const provided of node.providesSourceIds) if (session.demanded.has(provided)) {
@@ -1449,4 +1466,4 @@ function getDefaultDependencySnapshotInner(state, visited) {
1449
1466
  }
1450
1467
 
1451
1468
  //#endregion
1452
- export { buildRuntimeNodesFromArray, buildRuntimeNodesFromPairs, collectDemandedDependencyIds, collectExplicitSourceValues, collectExplicitSourceValuesAsync, collectSourcesFromState, completeEffectfulSourcesAsync, createDependencyFingerprint, createDependencyRuntimeContext, createReplayKey, defineForwardedEffectfulSchedulingNodes, derivedRawInputKey, effectfulSchedulingNodesKey, extractRawInputFromState, fillMissingSourceDefaults, fillMissingSourceDefaultsAsync, includeSourceFailureChain, orderDependencyNodes, recordDerivedRawInput, replayDerivedParser, replayDerivedParserAsync, resolveDerivedSourceValues, resolveDerivedSourceValuesAsync, resolveStateWithRuntime, resolveStateWithRuntimeAsync, serializeSchedulingPath, sourceCollectionExpansionKey, staticSourceScopeKey };
1469
+ export { buildRuntimeNodesFromArray, buildRuntimeNodesFromPairs, collectDemandedDependencyIds, collectExplicitSourceValues, collectExplicitSourceValuesAsync, collectSourcesFromState, completeEffectfulSourcesAsync, createDependencyFingerprint, createDependencyRuntimeContext, createReplayKey, defineForwardedEffectfulSchedulingNodes, derivedRawInputKey, effectfulSchedulingNodesKey, extractRawInputFromState, fillMissingSourceDefaults, fillMissingSourceDefaultsAsync, hasInactiveCompletion, includeSourceFailureChain, orderDependencyNodes, recordDerivedRawInput, replayDerivedParser, replayDerivedParserAsync, resolveDerivedSourceValues, resolveDerivedSourceValuesAsync, resolveStateWithRuntime, resolveStateWithRuntimeAsync, serializeSchedulingPath, sourceCollectionExpansionKey, staticSourceScopeKey };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optique/core",
3
- "version": "1.3.0-dev.2425",
3
+ "version": "1.3.0-dev.2428",
4
4
  "description": "Type-safe combinatorial command-line interface parser",
5
5
  "keywords": [
6
6
  "CLI",
@@ -221,7 +221,7 @@
221
221
  },
222
222
  "sideEffects": false,
223
223
  "devDependencies": {
224
- "@optique/env": "1.3.0-dev.2425+957c64e5",
224
+ "@optique/env": "1.3.0-dev.2428+5fa651a4",
225
225
  "@types/node": "^24.0.0",
226
226
  "fast-check": "^4.7.0",
227
227
  "tsdown": "^0.13.0",