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

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.
@@ -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, hasInactiveCompletion, resolveDerivedSourceValues, resolveDerivedSourceValuesAsync, resolveStateWithRuntime, resolveStateWithRuntimeAsync, serializeSchedulingPath, sourceCollectionExpansionKey, staticSourceScopeKey } from "./dependency-runtime.js";
8
+ import { buildRuntimeNodesFromArray, buildRuntimeNodesFromPairs, collectExplicitSourceValues, collectExplicitSourceValuesAsync, collectSourcesFromState, completeEffectfulSourcesAsync, createDependencyRuntimeContext, effectfulSchedulingNodesKey, exclusiveSourceScopeKey, fillMissingSourceDefaults, fillMissingSourceDefaultsAsync, guaranteedStaticSourceIdsKey, 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";
@@ -169,21 +169,47 @@ function expandSourceCollectionNodes(nodes) {
169
169
  }
170
170
  let conditionalInstanceCounter = 0;
171
171
  /**
172
+ * Whether a source occurrence publishes a value even when its state
173
+ * holds no parsed one: its composed effectful completion always fires
174
+ * (`completesWhenMissing`), or it exposes a publishable missing-value
175
+ * default.
176
+ */
177
+ function isGuaranteedSourceProvider(source) {
178
+ return source.completeSource != null && source.completesWhenMissing === true || source.getMissingSourceValue != null && source.preservesSourceValue !== false;
179
+ }
180
+ /**
172
181
  * Statically collects the dependency source IDs reachable through a
173
- * parser's composed metadata and flattened field pairs. Used as the
174
- * `providesSourceIds` estimate for a conditional()'s branches: an
175
- * undercount only delays an effectful completion from the seed pass to
176
- * the final pass of a `runWith()` run.
182
+ * parser's composed metadata and flattened field pairs, classifying each
183
+ * as merely possible or guaranteed.
184
+ *
185
+ * `exclusive` marks descent through a mutually exclusive edge (an `or()`
186
+ * alternative, a `conditional()` branch, an unmatched `command()`); an
187
+ * occurrence reached that way never counts as guaranteed. `suppressId`
188
+ * carries the source ID of an enclosing wrapper whose composed metadata
189
+ * already decided that chain's classification, so the raw inner
190
+ * occurrence (e.g., the bare prompt inside `optional(prompt(...))`) does
191
+ * not overrule the wrapper.
177
192
  */
178
- function collectStaticSourceIds(parser, out, seen) {
179
- if (seen.has(parser)) return;
180
- seen.add(parser);
193
+ function collectStaticProvides(parser, out, exclusive, suppressId, seen) {
194
+ const visitBit = 1 << ((exclusive ? 1 : 0) | (suppressId != null ? 2 : 0));
195
+ const visited = seen.get(parser) ?? 0;
196
+ if ((visited & visitBit) !== 0) return;
197
+ seen.set(parser, visited | visitBit);
181
198
  const source = parser.dependencyMetadata?.source;
182
- if (source != null) out.add(source.sourceId);
199
+ let childSuppressId = suppressId;
200
+ if (source != null) {
201
+ out.may.add(source.sourceId);
202
+ if (!exclusive && source.sourceId !== suppressId && isGuaranteedSourceProvider(source)) out.guaranteed.add(source.sourceId);
203
+ childSuppressId = source.sourceId;
204
+ }
205
+ const routeGuaranteed = parser[guaranteedStaticSourceIdsKey];
206
+ if (routeGuaranteed != null && !exclusive) for (const id of routeGuaranteed) out.guaranteed.add(id);
183
207
  const pairs = parser[fieldParsersKey];
184
- if (pairs != null) for (const [, child] of pairs) collectStaticSourceIds(child, out, seen);
208
+ if (pairs != null) for (const [, child] of pairs) collectStaticProvides(child, out, exclusive, childSuppressId, seen);
185
209
  const scope = parser[staticSourceScopeKey];
186
- if (scope != null) for (const child of scope) collectStaticSourceIds(child, out, seen);
210
+ if (scope != null) for (const child of scope) collectStaticProvides(child, out, exclusive, childSuppressId, seen);
211
+ const exclusiveScope = parser[exclusiveSourceScopeKey];
212
+ if (exclusiveScope != null) for (const child of exclusiveScope) collectStaticProvides(child, out, true, childSuppressId, seen);
187
213
  }
188
214
  /**
189
215
  * Statically collects the completion dependencies reachable through a
@@ -211,16 +237,55 @@ function collectStaticCompletionDependencies(parser, orderingDependencyIds, dema
211
237
  if (pairs != null) for (const [, child] of pairs) collectStaticCompletionDependencies(child, orderingDependencyIds, demandEdges, seen);
212
238
  const scope = parser[staticSourceScopeKey];
213
239
  if (scope != null) for (const child of scope) collectStaticCompletionDependencies(child, orderingDependencyIds, demandEdges, seen);
240
+ const exclusiveScope = parser[exclusiveSourceScopeKey];
241
+ if (exclusiveScope != null) for (const child of exclusiveScope) collectStaticCompletionDependencies(child, orderingDependencyIds, demandEdges, seen);
242
+ }
243
+ /**
244
+ * Collects the source IDs a set of expanded branch nodes will actively
245
+ * publish: a guaranteed occurrence (an unconditional effectful
246
+ * completion or a publishable default), an occurrence whose state
247
+ * already extracts a value, or a nested barrier's every-route
248
+ * guarantees. Synchronous extraction only;
249
+ * {@link computeActiveBarrierDependencies} additionally awaits
250
+ * promise-shaped extractions, so a bound value that validates
251
+ * asynchronously still counts as active.
252
+ */
253
+ function collectActiveProvidesSync(nodes) {
254
+ const active = /* @__PURE__ */ new Set();
255
+ for (const node of nodes) {
256
+ const source = node.parser.dependencyMetadata?.source;
257
+ if (source != null && !active.has(source.sourceId)) {
258
+ if (isGuaranteedSourceProvider(source)) active.add(source.sourceId);
259
+ else if (source.extractSourceValue != null) {
260
+ let extracted;
261
+ try {
262
+ extracted = source.extractSourceValue(node.state);
263
+ } catch {
264
+ extracted = void 0;
265
+ }
266
+ if (extracted != null && isPromiseLike(extracted)) Promise.resolve(extracted).catch(() => {});
267
+ else if (extracted?.success === true && extracted.value !== void 0) active.add(source.sourceId);
268
+ }
269
+ }
270
+ for (const id of node.guaranteedProvidesSourceIds ?? []) active.add(id);
271
+ }
272
+ return active;
273
+ }
274
+ function isPromiseLike(value) {
275
+ return typeof value?.then === "function";
214
276
  }
215
277
  /**
216
278
  * Recomputes a speculative barrier's completion dependencies from the
217
279
  * guessed branch's expanded runtime nodes, so an occurrence already
218
280
  * satisfied by the command line or a binding contributes neither
219
281
  * ordering edges nor demand, mirroring the flat inactive-completion
220
- * rule. Nested barriers contribute their own aggregates, which remain
221
- * static estimates.
282
+ * rule. Only *active* provides are subtracted from the ordering
283
+ * union—a merely possible provider (an absent `optional()`, an
284
+ * unselected nested alternative) must not hide a matching provider
285
+ * declared after the conditional (issue #924). Nested barriers
286
+ * contribute their own aggregates, which remain static estimates.
222
287
  */
223
- function computeBarrierDependenciesFromNodes(nodes, provides) {
288
+ function computeBarrierDependenciesFromNodes(nodes) {
224
289
  const ordering = /* @__PURE__ */ new Set();
225
290
  const demandEdges = [];
226
291
  for (const node of nodes) {
@@ -240,13 +305,52 @@ function computeBarrierDependenciesFromNodes(nodes, provides) {
240
305
  demandEdges.push(...nested.demandEdges);
241
306
  }
242
307
  }
243
- const orderingDependencyIds = [...ordering].filter((id) => !provides.has(id));
308
+ const active = collectActiveProvidesSync(nodes);
309
+ const orderingDependencyIds = [...ordering].filter((id) => !active.has(id));
244
310
  if (orderingDependencyIds.length < 1 && demandEdges.length < 1) return void 0;
245
311
  return {
246
312
  orderingDependencyIds,
247
313
  demandEdges
248
314
  };
249
315
  }
316
+ /**
317
+ * Computes a resolved barrier's concrete dependencies from the selected
318
+ * branch's expanded runtime nodes: the completion dependencies its
319
+ * active consumers read, minus the sources the branch actively
320
+ * provides, and the active provides themselves. Unlike the
321
+ * synchronous estimate, promise-shaped state extractions are awaited,
322
+ * so an asynchronously validating bound value counts as active.
323
+ */
324
+ async function computeActiveBarrierDependencies(nodes) {
325
+ const active = /* @__PURE__ */ new Set();
326
+ const ordering = /* @__PURE__ */ new Set();
327
+ for (const node of nodes) {
328
+ const metadata = node.parser.dependencyMetadata;
329
+ const source = metadata?.source;
330
+ const completion = metadata?.completion;
331
+ const guaranteed = source != null && isGuaranteedSourceProvider(source);
332
+ let extracted;
333
+ if (source?.extractSourceValue != null && (completion != null || !guaranteed && !active.has(source.sourceId))) try {
334
+ extracted = await source.extractSourceValue(node.state);
335
+ } catch {
336
+ extracted = void 0;
337
+ }
338
+ if (source != null) {
339
+ if (guaranteed || extracted?.success === true && extracted.value !== void 0) active.add(source.sourceId);
340
+ }
341
+ for (const id of node.guaranteedProvidesSourceIds ?? []) active.add(id);
342
+ if (completion != null) {
343
+ const inactive = source?.extractSourceValue != null ? extracted?.success === true : hasInactiveCompletion(node);
344
+ if (!inactive) for (const id of completion.dependencyIds) ordering.add(id);
345
+ }
346
+ const nested = node.barrierCompletionDependencies;
347
+ if (nested != null) for (const id of nested.orderingDependencyIds) ordering.add(id);
348
+ }
349
+ return {
350
+ orderingDependencyIds: [...ordering].filter((id) => !active.has(id)),
351
+ activeProvidesSourceIds: active
352
+ };
353
+ }
250
354
  function isDeferredCompletionResult(result) {
251
355
  return typeof result === "object" && result !== null && "success" in result && result.success === true && "deferred" in result && result.deferred === true;
252
356
  }
@@ -1064,7 +1168,7 @@ function defineExclusiveSchedulingNodes(exclusiveParser, parsers) {
1064
1168
  configurable: true,
1065
1169
  enumerable: false
1066
1170
  });
1067
- Object.defineProperty(exclusiveParser, staticSourceScopeKey, {
1171
+ Object.defineProperty(exclusiveParser, exclusiveSourceScopeKey, {
1068
1172
  value: parsers,
1069
1173
  configurable: true,
1070
1174
  enumerable: false
@@ -1095,6 +1199,7 @@ function composeExclusiveDependencyMetadata(parsers) {
1095
1199
  return { source: {
1096
1200
  ...sharedSource,
1097
1201
  getMissingSourceValue: void 0,
1202
+ completesWhenMissing: false,
1098
1203
  preservesSourceValue: everyBranchPreserves,
1099
1204
  extractSourceValue(state) {
1100
1205
  if (!Array.isArray(state) || state.length !== 2 || typeof state[0] !== "number") return void 0;
@@ -6505,21 +6610,25 @@ function conditional(discriminator, branches, defaultBranch, options) {
6505
6610
  };
6506
6611
  const preparedKeyPrefix = `c${++conditionalInstanceCounter}|`;
6507
6612
  const preparedKeyFor = (parentPath) => preparedKeyPrefix + serializeSchedulingPath(parentPath ?? []);
6508
- const { branchProvidedSourceIds, mergedBarrierDependencies, barrierDependenciesByKey } = (() => {
6613
+ const { branchProvidedSourceIds, branchGuaranteedIntersection, mergedBarrierDependencies, barrierDependenciesByKey } = (() => {
6509
6614
  const provided = /* @__PURE__ */ new Set();
6510
6615
  const byParser = /* @__PURE__ */ new Map();
6511
6616
  const computeBranch = (branch) => {
6512
6617
  const memoized = byParser.get(branch);
6513
6618
  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);
6619
+ const provides = {
6620
+ may: /* @__PURE__ */ new Set(),
6621
+ guaranteed: /* @__PURE__ */ new Set()
6622
+ };
6623
+ collectStaticProvides(branch, provides, false, void 0, /* @__PURE__ */ new Map());
6624
+ for (const id of provides.may) provided.add(id);
6517
6625
  const ordering = /* @__PURE__ */ new Set();
6518
6626
  const demandEdges = [];
6519
6627
  collectStaticCompletionDependencies(branch, ordering, demandEdges, /* @__PURE__ */ new Set());
6520
- const orderingDependencyIds = [...ordering].filter((id) => !provides.has(id));
6628
+ const orderingDependencyIds = [...ordering].filter((id) => !provides.guaranteed.has(id));
6521
6629
  const estimate = {
6522
- provides,
6630
+ mayProvides: provides.may,
6631
+ guaranteedProvides: provides.guaranteed,
6523
6632
  ...orderingDependencyIds.length < 1 && demandEdges.length < 1 ? {} : { dependencies: {
6524
6633
  orderingDependencyIds,
6525
6634
  demandEdges
@@ -6528,12 +6637,17 @@ function conditional(discriminator, branches, defaultBranch, options) {
6528
6637
  byParser.set(branch, estimate);
6529
6638
  return estimate;
6530
6639
  };
6640
+ const routes = [];
6531
6641
  const byKey = /* @__PURE__ */ new Map();
6532
6642
  for (const key of Object.keys(branches)) {
6533
6643
  const estimate = computeBranch(branches[key]);
6534
- if (estimate.dependencies != null) byKey.set(key, estimate);
6644
+ routes.push(estimate);
6645
+ byKey.set(key, estimate);
6535
6646
  }
6536
- if (defaultBranch != null) computeBranch(defaultBranch);
6647
+ if (defaultBranch != null) routes.push(computeBranch(defaultBranch));
6648
+ let intersection;
6649
+ for (const route of routes) if (intersection == null) intersection = new Set(route.guaranteedProvides);
6650
+ else for (const id of intersection) if (!route.guaranteedProvides.has(id)) intersection.delete(id);
6537
6651
  const orderingUnion = /* @__PURE__ */ new Set();
6538
6652
  const mergedEdges = [];
6539
6653
  for (const estimate of new Set(byParser.values())) {
@@ -6547,6 +6661,7 @@ function conditional(discriminator, branches, defaultBranch, options) {
6547
6661
  };
6548
6662
  return {
6549
6663
  branchProvidedSourceIds: provided,
6664
+ branchGuaranteedIntersection: intersection ?? /* @__PURE__ */ new Set(),
6550
6665
  mergedBarrierDependencies: merged,
6551
6666
  barrierDependenciesByKey: byKey
6552
6667
  };
@@ -6595,16 +6710,20 @@ function conditional(discriminator, branches, defaultBranch, options) {
6595
6710
  if (selected?.kind !== "branch" || innerCompleteSource == null) return nodes;
6596
6711
  teeDiscriminatorNode();
6597
6712
  const speculativeKey = selected.key;
6598
- const speculativeBarrierDependencies = (() => {
6599
- const estimate = barrierDependenciesByKey.get(speculativeKey);
6600
- if (estimate?.dependencies == null) return void 0;
6713
+ const expandGuessedBranchNodes = () => {
6601
6714
  const branchParser = branches[speculativeKey];
6602
- if (branchParser == null) return estimate.dependencies;
6603
- return computeBarrierDependenciesFromNodes(expandEffectfulRuntimeNodes([{
6715
+ if (branchParser == null) return [];
6716
+ return expandEffectfulRuntimeNodes([{
6604
6717
  path: [...parentPath ?? [], "_branch"],
6605
6718
  parser: branchParser,
6606
6719
  state: getWrappedChildState(conditionalState, conditionalState.branchState, branchParser)
6607
- }]), estimate.provides);
6720
+ }]);
6721
+ };
6722
+ const speculativeEstimate = barrierDependenciesByKey.get(speculativeKey);
6723
+ const speculativeBarrierDependencies = (() => {
6724
+ if (speculativeEstimate?.dependencies == null) return void 0;
6725
+ if (branches[speculativeKey] == null) return speculativeEstimate.dependencies;
6726
+ return computeBarrierDependenciesFromNodes(expandGuessedBranchNodes());
6608
6727
  })();
6609
6728
  nodes.push({
6610
6729
  path: [...parentPath ?? [], "_branch"],
@@ -6612,20 +6731,27 @@ function conditional(discriminator, branches, defaultBranch, options) {
6612
6731
  state: conditionalState,
6613
6732
  ...discriminatorSource != null ? { requiresSourceId: discriminatorSource.sourceId } : {},
6614
6733
  providesSourceIds: branchProvidedSourceIds,
6734
+ ...speculativeEstimate != null && speculativeEstimate.guaranteedProvides.size > 0 ? { guaranteedProvidesSourceIds: speculativeEstimate.guaranteedProvides } : {},
6615
6735
  ...speculativeBarrierDependencies != null ? { barrierCompletionDependencies: speculativeBarrierDependencies } : {},
6736
+ resolveBarrier: async (ctx) => {
6737
+ const session = ctx.exec?.effectfulCompletionSession;
6738
+ if (session == null) return void 0;
6739
+ const result = cell.result ?? session.completedByPath.get(discriminatorPathKey);
6740
+ if (result == null || isDeferredCompletionResult(result) || result.success !== true) return void 0;
6741
+ if (String(result.value) !== speculativeKey || branches[speculativeKey] == null) return {
6742
+ orderingDependencyIds: [],
6743
+ activeProvidesSourceIds: /* @__PURE__ */ new Set()
6744
+ };
6745
+ return await computeActiveBarrierDependencies(expandGuessedBranchNodes());
6746
+ },
6616
6747
  prepare: async (ctx) => {
6617
6748
  const session = ctx.exec?.effectfulCompletionSession;
6618
6749
  if (session == null) return void 0;
6619
6750
  const result = cell.result ?? session.completedByPath.get(discriminatorPathKey);
6620
6751
  if (result == null || isDeferredCompletionResult(result) || result.success !== true) return void 0;
6621
6752
  if (String(result.value) !== speculativeKey) return void 0;
6622
- const branchParser = branches[speculativeKey];
6623
- if (branchParser == null) return void 0;
6624
- return await ctx.schedule(expandEffectfulRuntimeNodes([{
6625
- path: [...parentPath ?? [], "_branch"],
6626
- parser: branchParser,
6627
- state: getWrappedChildState(conditionalState, conditionalState.branchState, branchParser)
6628
- }]));
6753
+ if (branches[speculativeKey] == null) return void 0;
6754
+ return await ctx.schedule(expandGuessedBranchNodes());
6629
6755
  }
6630
6756
  });
6631
6757
  return nodes;
@@ -6633,61 +6759,79 @@ function conditional(discriminator, branches, defaultBranch, options) {
6633
6759
  if (selected !== void 0) return nodes;
6634
6760
  if (discriminatorSource == null && typeof discriminator.shouldDeferCompletion === "function") return nodes;
6635
6761
  teeDiscriminatorNode();
6762
+ const resolvePreparedSelection = async (ctx) => {
6763
+ const session = ctx.exec?.effectfulCompletionSession;
6764
+ if (session == null) return void 0;
6765
+ const key = preparedKeyFor(parentPath);
6766
+ let prepared = session.preparedByPath.get(key);
6767
+ if (prepared == null) {
6768
+ let discriminatorResult = cell.result ?? session.completedByPath.get(discriminatorPathKey);
6769
+ if (discriminatorResult == null && innerCompleteSource == null) {
6770
+ const discriminatorExec = ctx.exec == null ? void 0 : {
6771
+ ...ctx.exec,
6772
+ path: [...parentPath ?? [], "_discriminator"]
6773
+ };
6774
+ discriminatorResult = unwrapCompleteResult(await discriminator.complete(annotatedDiscriminatorState, discriminatorExec));
6775
+ }
6776
+ if (discriminatorResult == null || isDeferredCompletionResult(discriminatorResult)) return void 0;
6777
+ const namedBranch = discriminatorResult.success ? branches[String(discriminatorResult.value)] : void 0;
6778
+ const branchParser = namedBranch ?? defaultBranch;
6779
+ let branchState;
6780
+ if (branchParser != null) {
6781
+ const branchExec = ctx.exec == null ? void 0 : {
6782
+ ...ctx.exec,
6783
+ path: [...parentPath ?? [], "_branch"]
6784
+ };
6785
+ const annotatedInitial = getWrappedChildState(conditionalState, branchParser.initialState, branchParser);
6786
+ const replayResult = await branchParser.parse({
6787
+ buffer: [],
6788
+ optionsTerminated: false,
6789
+ usage: [],
6790
+ exec: branchExec,
6791
+ dependencyRegistry: ctx.exec?.dependencyRegistry,
6792
+ state: annotatedInitial
6793
+ });
6794
+ branchState = replayResult.success ? replayResult.next.state : annotatedInitial;
6795
+ }
6796
+ prepared = {
6797
+ discriminatorResult,
6798
+ branchParser,
6799
+ branchKey: namedBranch != null ? String(discriminatorResult.value) : void 0,
6800
+ branchState
6801
+ };
6802
+ session.preparedByPath.set(key, prepared);
6803
+ }
6804
+ return prepared;
6805
+ };
6806
+ const expandPreparedBranchNodes = (prepared) => {
6807
+ if (prepared.branchParser == null) return [];
6808
+ return expandEffectfulRuntimeNodes([{
6809
+ path: [...parentPath ?? [], "_branch"],
6810
+ parser: prepared.branchParser,
6811
+ state: getWrappedChildState(conditionalState, prepared.branchState, prepared.branchParser)
6812
+ }]);
6813
+ };
6636
6814
  nodes.push({
6637
6815
  path: [...parentPath ?? [], "_branch"],
6638
6816
  parser: {},
6639
6817
  state: conditionalState,
6640
6818
  ...discriminatorSource != null ? { requiresSourceId: discriminatorSource.sourceId } : {},
6641
6819
  providesSourceIds: branchProvidedSourceIds,
6820
+ ...branchGuaranteedIntersection.size > 0 ? { guaranteedProvidesSourceIds: branchGuaranteedIntersection } : {},
6642
6821
  ...mergedBarrierDependencies != null ? { barrierCompletionDependencies: mergedBarrierDependencies } : {},
6822
+ resolveBarrier: async (ctx) => {
6823
+ const prepared = await resolvePreparedSelection(ctx);
6824
+ if (prepared == null) return void 0;
6825
+ if (prepared.branchParser == null) return {
6826
+ orderingDependencyIds: [],
6827
+ activeProvidesSourceIds: /* @__PURE__ */ new Set()
6828
+ };
6829
+ return await computeActiveBarrierDependencies(expandPreparedBranchNodes(prepared));
6830
+ },
6643
6831
  prepare: async (ctx) => {
6644
- const session = ctx.exec?.effectfulCompletionSession;
6645
- if (session == null) return void 0;
6646
- const key = preparedKeyFor(parentPath);
6647
- let prepared = session.preparedByPath.get(key);
6648
- if (prepared == null) {
6649
- let discriminatorResult = cell.result ?? session.completedByPath.get(discriminatorPathKey);
6650
- if (discriminatorResult == null && innerCompleteSource == null) {
6651
- const discriminatorExec = ctx.exec == null ? void 0 : {
6652
- ...ctx.exec,
6653
- path: [...parentPath ?? [], "_discriminator"]
6654
- };
6655
- discriminatorResult = unwrapCompleteResult(await discriminator.complete(annotatedDiscriminatorState, discriminatorExec));
6656
- }
6657
- if (discriminatorResult == null || isDeferredCompletionResult(discriminatorResult)) return void 0;
6658
- const namedBranch = discriminatorResult.success ? branches[String(discriminatorResult.value)] : void 0;
6659
- const branchParser = namedBranch ?? defaultBranch;
6660
- let branchState;
6661
- if (branchParser != null) {
6662
- const branchExec = ctx.exec == null ? void 0 : {
6663
- ...ctx.exec,
6664
- path: [...parentPath ?? [], "_branch"]
6665
- };
6666
- const annotatedInitial = getWrappedChildState(conditionalState, branchParser.initialState, branchParser);
6667
- const replayResult = await branchParser.parse({
6668
- buffer: [],
6669
- optionsTerminated: false,
6670
- usage: [],
6671
- exec: branchExec,
6672
- dependencyRegistry: ctx.exec?.dependencyRegistry,
6673
- state: annotatedInitial
6674
- });
6675
- branchState = replayResult.success ? replayResult.next.state : annotatedInitial;
6676
- }
6677
- prepared = {
6678
- discriminatorResult,
6679
- branchParser,
6680
- branchKey: namedBranch != null ? String(discriminatorResult.value) : void 0,
6681
- branchState
6682
- };
6683
- session.preparedByPath.set(key, prepared);
6684
- }
6685
- if (prepared.branchParser == null) return void 0;
6686
- return await ctx.schedule(expandEffectfulRuntimeNodes([{
6687
- path: [...parentPath ?? [], "_branch"],
6688
- parser: prepared.branchParser,
6689
- state: getWrappedChildState(conditionalState, prepared.branchState, prepared.branchParser)
6690
- }]));
6832
+ const prepared = await resolvePreparedSelection(ctx);
6833
+ if (prepared?.branchParser == null) return void 0;
6834
+ return await ctx.schedule(expandPreparedBranchNodes(prepared));
6691
6835
  }
6692
6836
  });
6693
6837
  return nodes;
@@ -7093,11 +7237,17 @@ function conditional(discriminator, branches, defaultBranch, options) {
7093
7237
  enumerable: false
7094
7238
  });
7095
7239
  Object.defineProperty(conditionalParser, staticSourceScopeKey, {
7096
- value: [
7097
- discriminator,
7098
- ...Object.values(branches),
7099
- ...defaultBranch != null ? [defaultBranch] : []
7100
- ],
7240
+ value: [discriminator],
7241
+ configurable: true,
7242
+ enumerable: false
7243
+ });
7244
+ Object.defineProperty(conditionalParser, exclusiveSourceScopeKey, {
7245
+ value: [...Object.values(branches), ...defaultBranch != null ? [defaultBranch] : []],
7246
+ configurable: true,
7247
+ enumerable: false
7248
+ });
7249
+ if (branchGuaranteedIntersection.size > 0) Object.defineProperty(conditionalParser, guaranteedStaticSourceIdsKey, {
7250
+ value: branchGuaranteedIntersection,
7101
7251
  configurable: true,
7102
7252
  enumerable: false
7103
7253
  });
@@ -117,6 +117,7 @@ function composeDependencyMetadata(inner, wrapperKind, options) {
117
117
  source: {
118
118
  ...inner.source,
119
119
  extractSourceValue: unwrapArrayThenExtract(inner.source.extractSourceValue),
120
+ completesWhenMissing: false,
120
121
  ...inner.source.completeSource != null && { completeSource: unwrapArrayThenComplete(inner.source.completeSource) }
121
122
  }
122
123
  };
@@ -58,6 +58,21 @@ interface DependencySourceCapability {
58
58
  * @since 1.3.0
59
59
  */
60
60
  readonly completeSource?: (state: unknown, exec?: ExecutionContext) => Promise<ValueParserResult<unknown> | undefined>;
61
+ /**
62
+ * Whether the composed {@link completeSource}, invoked on a state with
63
+ * no parsed value, publishes a registrable result.
64
+ *
65
+ * A prompt wrapper without a runtime condition sets this to `true`: the
66
+ * prompt always asks and registers its answer. An `optional()` wrapper
67
+ * clears it, because an absent optional occurrence declines its inner
68
+ * completion and publishes nothing. Scheduling barriers use this flag
69
+ * to tell a *guaranteed* branch provider (one that publishes whenever
70
+ * its branch runs) apart from a merely *possible* one, without running
71
+ * any effect.
72
+ *
73
+ * @since 1.3.0
74
+ */
75
+ readonly completesWhenMissing?: boolean;
61
76
  /**
62
77
  * Whether the parser's output value is the actual dependency source value.
63
78
  * `false` when a transform like `map()` has been applied.
@@ -58,6 +58,21 @@ interface DependencySourceCapability {
58
58
  * @since 1.3.0
59
59
  */
60
60
  readonly completeSource?: (state: unknown, exec?: ExecutionContext) => Promise<ValueParserResult<unknown> | undefined>;
61
+ /**
62
+ * Whether the composed {@link completeSource}, invoked on a state with
63
+ * no parsed value, publishes a registrable result.
64
+ *
65
+ * A prompt wrapper without a runtime condition sets this to `true`: the
66
+ * prompt always asks and registers its answer. An `optional()` wrapper
67
+ * clears it, because an absent optional occurrence declines its inner
68
+ * completion and publishes nothing. Scheduling barriers use this flag
69
+ * to tell a *guaranteed* branch provider (one that publishes whenever
70
+ * its branch runs) apart from a merely *possible* one, without running
71
+ * any effect.
72
+ *
73
+ * @since 1.3.0
74
+ */
75
+ readonly completesWhenMissing?: boolean;
61
76
  /**
62
77
  * Whether the parser's output value is the actual dependency source value.
63
78
  * `false` when a transform like `map()` has been applied.
@@ -117,6 +117,7 @@ function composeDependencyMetadata(inner, wrapperKind, options) {
117
117
  source: {
118
118
  ...inner.source,
119
119
  extractSourceValue: unwrapArrayThenExtract(inner.source.extractSourceValue),
120
+ completesWhenMissing: false,
120
121
  ...inner.source.completeSource != null && { completeSource: unwrapArrayThenComplete(inner.source.completeSource) }
121
122
  }
122
123
  };