@optique/core 1.3.0-dev.2433 → 1.3.0-dev.2434
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/constructs.cjs +101 -25
- package/dist/constructs.js +102 -26
- package/dist/dependency-runtime.cjs +160 -36
- package/dist/dependency-runtime.d.cts +56 -2
- package/dist/dependency-runtime.d.ts +56 -2
- package/dist/dependency-runtime.js +160 -37
- package/package.json +2 -2
package/dist/constructs.cjs
CHANGED
|
@@ -89,6 +89,14 @@ function filterSchedulableRuntimeNodes(nodes, preCompleted) {
|
|
|
89
89
|
* failure. Returns `undefined` on success.
|
|
90
90
|
*/
|
|
91
91
|
/**
|
|
92
|
+
* Nodes a `conditional()` pre-expanded from a committed branch so a
|
|
93
|
+
* `branchError` wrap can ride on the nested barrier nodes. They are
|
|
94
|
+
* expanded descendants despite appearing in a scheduling call's direct
|
|
95
|
+
* node list, so {@link scheduleEffectfulSourceCompletions} must not
|
|
96
|
+
* treat them as reusable or collected.
|
|
97
|
+
*/
|
|
98
|
+
const preExpandedBranchNodes = /* @__PURE__ */ new WeakSet();
|
|
99
|
+
/**
|
|
92
100
|
* Expands runtime nodes through nested constructs for effectful source
|
|
93
101
|
* scheduling and demand detection.
|
|
94
102
|
*
|
|
@@ -317,13 +325,18 @@ function computeBarrierDependenciesFromNodes(nodes) {
|
|
|
317
325
|
* Computes a resolved barrier's concrete dependencies from the selected
|
|
318
326
|
* branch's expanded runtime nodes: the completion dependencies its
|
|
319
327
|
* active consumers read, minus the sources the branch actively
|
|
320
|
-
* provides,
|
|
321
|
-
* synchronous estimate, promise-shaped state
|
|
322
|
-
* so an asynchronously validating bound value
|
|
328
|
+
* provides, the active provides themselves, and the selection's demand
|
|
329
|
+
* edges. Unlike the synchronous estimate, promise-shaped state
|
|
330
|
+
* extractions are awaited, so an asynchronously validating bound value
|
|
331
|
+
* counts as active. A nested barrier whose demand activation is
|
|
332
|
+
* `"after-resolution"` contributes only its ordering aggregate: its
|
|
333
|
+
* demand edges stay behind its own confirmation boundary and are
|
|
334
|
+
* promoted by its own resolution within the nested scheduling pass.
|
|
323
335
|
*/
|
|
324
336
|
async function computeActiveBarrierDependencies(nodes) {
|
|
325
337
|
const active = /* @__PURE__ */ new Set();
|
|
326
338
|
const ordering = /* @__PURE__ */ new Set();
|
|
339
|
+
const demandEdges = [];
|
|
327
340
|
for (const node of nodes) {
|
|
328
341
|
const metadata = node.parser.dependencyMetadata;
|
|
329
342
|
const source = metadata?.source;
|
|
@@ -341,14 +354,24 @@ async function computeActiveBarrierDependencies(nodes) {
|
|
|
341
354
|
for (const id of node.guaranteedProvidesSourceIds ?? []) active.add(id);
|
|
342
355
|
if (completion != null) {
|
|
343
356
|
const inactive = source?.extractSourceValue != null ? extracted?.success === true : require_dependency_runtime.hasInactiveCompletion(node);
|
|
344
|
-
if (!inactive)
|
|
357
|
+
if (!inactive) {
|
|
358
|
+
for (const id of completion.dependencyIds) ordering.add(id);
|
|
359
|
+
if (source != null) demandEdges.push({
|
|
360
|
+
consumerSourceId: source.sourceId,
|
|
361
|
+
dependencyIds: completion.dependencyIds
|
|
362
|
+
});
|
|
363
|
+
}
|
|
345
364
|
}
|
|
346
365
|
const nested = node.barrierCompletionDependencies;
|
|
347
|
-
if (nested != null)
|
|
366
|
+
if (nested != null) {
|
|
367
|
+
for (const id of nested.orderingDependencyIds) ordering.add(id);
|
|
368
|
+
if (node.barrierDemandActivation !== "after-resolution" || node.barrierResolutionState === "resolved") demandEdges.push(...nested.demandEdges);
|
|
369
|
+
}
|
|
348
370
|
}
|
|
349
371
|
return {
|
|
350
372
|
orderingDependencyIds: [...ordering].filter((id) => !active.has(id)),
|
|
351
|
-
activeProvidesSourceIds: active
|
|
373
|
+
activeProvidesSourceIds: active,
|
|
374
|
+
demandEdges
|
|
352
375
|
};
|
|
353
376
|
}
|
|
354
377
|
function isDeferredCompletionResult(result) {
|
|
@@ -395,13 +418,15 @@ async function scheduleEffectfulSourceCompletions(nodes, state, runtime, exec, p
|
|
|
395
418
|
success: false,
|
|
396
419
|
error: preCompletedFailure
|
|
397
420
|
};
|
|
398
|
-
const direct = new Set(nodes);
|
|
421
|
+
const direct = new Set(nodes.filter((node) => !preExpandedBranchNodes.has(node)));
|
|
399
422
|
const collected = /* @__PURE__ */ new Set();
|
|
400
423
|
const expandedNodes = [];
|
|
401
424
|
for (const node of nodes) {
|
|
402
425
|
const optedIn = node.parser[require_dependency_runtime.sourceCollectionExpansionKey] === true;
|
|
426
|
+
const preExpanded = preExpandedBranchNodes.has(node);
|
|
403
427
|
for (const expandedNode of expandEffectfulRuntimeNodes([node])) {
|
|
404
428
|
expandedNodes.push(expandedNode);
|
|
429
|
+
if (preExpanded) continue;
|
|
405
430
|
if (optedIn || expandedNode === node) collected.add(expandedNode);
|
|
406
431
|
}
|
|
407
432
|
}
|
|
@@ -6610,6 +6635,47 @@ function conditional(discriminator, branches, defaultBranch, options) {
|
|
|
6610
6635
|
};
|
|
6611
6636
|
const preparedKeyPrefix = `c${++conditionalInstanceCounter}|`;
|
|
6612
6637
|
const preparedKeyFor = (parentPath) => preparedKeyPrefix + require_dependency_runtime.serializeSchedulingPath(parentPath ?? []);
|
|
6638
|
+
const branchMismatchFailure = (resolvedValue, speculativeKey) => {
|
|
6639
|
+
const resolvedKey = typeof resolvedValue === "string" ? resolvedValue : "<unknown>";
|
|
6640
|
+
return {
|
|
6641
|
+
success: false,
|
|
6642
|
+
error: options?.errors?.branchMismatch ? options.errors.branchMismatch(resolvedKey, speculativeKey) : require_message.message`Branch mismatch: tokens for ${speculativeKey} were consumed, but the discriminator resolved to ${resolvedKey}.`
|
|
6643
|
+
};
|
|
6644
|
+
};
|
|
6645
|
+
const wrapCachedBarrierFailure = (session, failure, branchKey) => {
|
|
6646
|
+
const branchError = options?.errors?.branchError;
|
|
6647
|
+
if (branchKey == null || branchError == null) return failure;
|
|
6648
|
+
const wrapped = {
|
|
6649
|
+
success: false,
|
|
6650
|
+
error: branchError(branchKey, failure.error)
|
|
6651
|
+
};
|
|
6652
|
+
return require_dependency_runtime.replaceCachedBarrierFailure(session, failure, wrapped) ? wrapped : failure;
|
|
6653
|
+
};
|
|
6654
|
+
const wrapBranchBarrierNodes = (nodes, branchKey) => {
|
|
6655
|
+
const branchError = options?.errors?.branchError;
|
|
6656
|
+
if (branchKey == null || branchError == null) return nodes;
|
|
6657
|
+
return nodes.map((node) => {
|
|
6658
|
+
const resolveBarrier = node.resolveBarrier;
|
|
6659
|
+
const prepare = node.prepare;
|
|
6660
|
+
if (resolveBarrier == null && prepare == null) return node;
|
|
6661
|
+
return {
|
|
6662
|
+
...node,
|
|
6663
|
+
...resolveBarrier == null ? {} : { resolveBarrier: async (ctx) => {
|
|
6664
|
+
const result = await resolveBarrier(ctx);
|
|
6665
|
+
if (result != null && "success" in result) return {
|
|
6666
|
+
success: false,
|
|
6667
|
+
error: branchError(branchKey, result.error)
|
|
6668
|
+
};
|
|
6669
|
+
return result;
|
|
6670
|
+
} },
|
|
6671
|
+
...prepare == null ? {} : { prepare: async (ctx) => {
|
|
6672
|
+
const failure = await prepare(ctx);
|
|
6673
|
+
if (failure == null) return void 0;
|
|
6674
|
+
return wrapCachedBarrierFailure(ctx.exec?.effectfulCompletionSession, failure, branchKey);
|
|
6675
|
+
} }
|
|
6676
|
+
};
|
|
6677
|
+
});
|
|
6678
|
+
};
|
|
6613
6679
|
const { branchProvidedSourceIds, branchGuaranteedIntersection, mergedBarrierDependencies, barrierDependenciesByKey } = (() => {
|
|
6614
6680
|
const provided = /* @__PURE__ */ new Set();
|
|
6615
6681
|
const byParser = /* @__PURE__ */ new Map();
|
|
@@ -6678,11 +6744,19 @@ function conditional(discriminator, branches, defaultBranch, options) {
|
|
|
6678
6744
|
const selected = conditionalState.selectedBranch;
|
|
6679
6745
|
if (selected !== void 0 && conditionalState.speculative !== true) {
|
|
6680
6746
|
const branchParser = selected.kind === "default" ? defaultBranch : branches[selected.key];
|
|
6681
|
-
if (branchParser != null)
|
|
6682
|
-
|
|
6683
|
-
|
|
6684
|
-
|
|
6685
|
-
|
|
6747
|
+
if (branchParser != null) {
|
|
6748
|
+
const branchNode = {
|
|
6749
|
+
path: [...parentPath ?? [], "_branch"],
|
|
6750
|
+
parser: branchParser,
|
|
6751
|
+
state: require_annotation_state.getWrappedChildState(conditionalState, conditionalState.branchState, branchParser)
|
|
6752
|
+
};
|
|
6753
|
+
const branchKey = selected.kind === "branch" ? selected.key : void 0;
|
|
6754
|
+
if (branchKey != null && options?.errors?.branchError != null) for (const expanded of wrapBranchBarrierNodes(expandEffectfulRuntimeNodes([branchNode]), branchKey)) {
|
|
6755
|
+
preExpandedBranchNodes.add(expanded);
|
|
6756
|
+
nodes.push(expanded);
|
|
6757
|
+
}
|
|
6758
|
+
else nodes.push(branchNode);
|
|
6759
|
+
}
|
|
6686
6760
|
return nodes;
|
|
6687
6761
|
}
|
|
6688
6762
|
const discriminatorSource = discriminator.dependencyMetadata?.source;
|
|
@@ -6733,14 +6807,17 @@ function conditional(discriminator, branches, defaultBranch, options) {
|
|
|
6733
6807
|
providesSourceIds: branchProvidedSourceIds,
|
|
6734
6808
|
...speculativeEstimate != null && speculativeEstimate.guaranteedProvides.size > 0 ? { guaranteedProvidesSourceIds: speculativeEstimate.guaranteedProvides } : {},
|
|
6735
6809
|
...speculativeBarrierDependencies != null ? { barrierCompletionDependencies: speculativeBarrierDependencies } : {},
|
|
6810
|
+
barrierDemandActivation: "after-resolution",
|
|
6736
6811
|
resolveBarrier: async (ctx) => {
|
|
6737
6812
|
const session = ctx.exec?.effectfulCompletionSession;
|
|
6738
6813
|
if (session == null) return void 0;
|
|
6739
6814
|
const result = cell.result ?? session.completedByPath.get(discriminatorPathKey);
|
|
6740
6815
|
if (result == null || isDeferredCompletionResult(result) || result.success !== true) return void 0;
|
|
6741
|
-
if (String(result.value) !== speculativeKey
|
|
6816
|
+
if (String(result.value) !== speculativeKey) return branchMismatchFailure(result.value, speculativeKey);
|
|
6817
|
+
if (branches[speculativeKey] == null) return {
|
|
6742
6818
|
orderingDependencyIds: [],
|
|
6743
|
-
activeProvidesSourceIds: /* @__PURE__ */ new Set()
|
|
6819
|
+
activeProvidesSourceIds: /* @__PURE__ */ new Set(),
|
|
6820
|
+
demandEdges: []
|
|
6744
6821
|
};
|
|
6745
6822
|
return await computeActiveBarrierDependencies(expandGuessedBranchNodes());
|
|
6746
6823
|
},
|
|
@@ -6751,7 +6828,9 @@ function conditional(discriminator, branches, defaultBranch, options) {
|
|
|
6751
6828
|
if (result == null || isDeferredCompletionResult(result) || result.success !== true) return void 0;
|
|
6752
6829
|
if (String(result.value) !== speculativeKey) return void 0;
|
|
6753
6830
|
if (branches[speculativeKey] == null) return void 0;
|
|
6754
|
-
|
|
6831
|
+
const failure = await ctx.schedule(expandGuessedBranchNodes());
|
|
6832
|
+
if (failure == null) return void 0;
|
|
6833
|
+
return wrapCachedBarrierFailure(session, failure, speculativeKey);
|
|
6755
6834
|
}
|
|
6756
6835
|
});
|
|
6757
6836
|
return nodes;
|
|
@@ -6819,19 +6898,23 @@ function conditional(discriminator, branches, defaultBranch, options) {
|
|
|
6819
6898
|
providesSourceIds: branchProvidedSourceIds,
|
|
6820
6899
|
...branchGuaranteedIntersection.size > 0 ? { guaranteedProvidesSourceIds: branchGuaranteedIntersection } : {},
|
|
6821
6900
|
...mergedBarrierDependencies != null ? { barrierCompletionDependencies: mergedBarrierDependencies } : {},
|
|
6901
|
+
barrierDemandActivation: "after-resolution",
|
|
6822
6902
|
resolveBarrier: async (ctx) => {
|
|
6823
6903
|
const prepared = await resolvePreparedSelection(ctx);
|
|
6824
6904
|
if (prepared == null) return void 0;
|
|
6825
6905
|
if (prepared.branchParser == null) return {
|
|
6826
6906
|
orderingDependencyIds: [],
|
|
6827
|
-
activeProvidesSourceIds: /* @__PURE__ */ new Set()
|
|
6907
|
+
activeProvidesSourceIds: /* @__PURE__ */ new Set(),
|
|
6908
|
+
demandEdges: []
|
|
6828
6909
|
};
|
|
6829
6910
|
return await computeActiveBarrierDependencies(expandPreparedBranchNodes(prepared));
|
|
6830
6911
|
},
|
|
6831
6912
|
prepare: async (ctx) => {
|
|
6832
6913
|
const prepared = await resolvePreparedSelection(ctx);
|
|
6833
6914
|
if (prepared?.branchParser == null) return void 0;
|
|
6834
|
-
|
|
6915
|
+
const failure = await ctx.schedule(expandPreparedBranchNodes(prepared));
|
|
6916
|
+
if (failure == null) return void 0;
|
|
6917
|
+
return wrapCachedBarrierFailure(ctx.exec?.effectfulCompletionSession, failure, prepared.branchKey);
|
|
6835
6918
|
}
|
|
6836
6919
|
});
|
|
6837
6920
|
return nodes;
|
|
@@ -6979,14 +7062,7 @@ function conditional(discriminator, branches, defaultBranch, options) {
|
|
|
6979
7062
|
else if (wasSpeculative) return completedDiscriminator;
|
|
6980
7063
|
else discriminatorValue = state.selectedBranch.key;
|
|
6981
7064
|
}
|
|
6982
|
-
if (wasSpeculative && state.selectedBranch.kind === "branch" && discriminatorValue !== state.selectedBranch.key)
|
|
6983
|
-
const speculativeKey = state.selectedBranch.key;
|
|
6984
|
-
const resolvedKey = typeof discriminatorValue === "string" ? discriminatorValue : "<unknown>";
|
|
6985
|
-
return {
|
|
6986
|
-
success: false,
|
|
6987
|
-
error: options?.errors?.branchMismatch ? options.errors.branchMismatch(resolvedKey, speculativeKey) : require_message.message`Branch mismatch: tokens for ${speculativeKey} were consumed, but the discriminator resolved to ${resolvedKey}.`
|
|
6988
|
-
};
|
|
6989
|
-
}
|
|
7065
|
+
if (wasSpeculative && state.selectedBranch.kind === "branch" && discriminatorValue !== state.selectedBranch.key) return branchMismatchFailure(discriminatorValue, state.selectedBranch.key);
|
|
6990
7066
|
const resolvedBranchState = require_annotation_state.getWrappedChildState(state, await require_dependency_runtime.resolveStateWithRuntimeAsync(state.branchState, runtime), branchParser);
|
|
6991
7067
|
const branchResult = unwrapCompleteResult(await branchParser.complete(resolvedBranchState, require_execution_context.withChildExecPath(completionExec, "_branch")));
|
|
6992
7068
|
if (!branchResult.success) {
|
package/dist/constructs.js
CHANGED
|
@@ -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, exclusiveSourceScopeKey, fillMissingSourceDefaults, fillMissingSourceDefaultsAsync, guaranteedStaticSourceIdsKey, 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, 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";
|
|
@@ -89,6 +89,14 @@ function filterSchedulableRuntimeNodes(nodes, preCompleted) {
|
|
|
89
89
|
* failure. Returns `undefined` on success.
|
|
90
90
|
*/
|
|
91
91
|
/**
|
|
92
|
+
* Nodes a `conditional()` pre-expanded from a committed branch so a
|
|
93
|
+
* `branchError` wrap can ride on the nested barrier nodes. They are
|
|
94
|
+
* expanded descendants despite appearing in a scheduling call's direct
|
|
95
|
+
* node list, so {@link scheduleEffectfulSourceCompletions} must not
|
|
96
|
+
* treat them as reusable or collected.
|
|
97
|
+
*/
|
|
98
|
+
const preExpandedBranchNodes = /* @__PURE__ */ new WeakSet();
|
|
99
|
+
/**
|
|
92
100
|
* Expands runtime nodes through nested constructs for effectful source
|
|
93
101
|
* scheduling and demand detection.
|
|
94
102
|
*
|
|
@@ -317,13 +325,18 @@ function computeBarrierDependenciesFromNodes(nodes) {
|
|
|
317
325
|
* Computes a resolved barrier's concrete dependencies from the selected
|
|
318
326
|
* branch's expanded runtime nodes: the completion dependencies its
|
|
319
327
|
* active consumers read, minus the sources the branch actively
|
|
320
|
-
* provides,
|
|
321
|
-
* synchronous estimate, promise-shaped state
|
|
322
|
-
* so an asynchronously validating bound value
|
|
328
|
+
* provides, the active provides themselves, and the selection's demand
|
|
329
|
+
* edges. Unlike the synchronous estimate, promise-shaped state
|
|
330
|
+
* extractions are awaited, so an asynchronously validating bound value
|
|
331
|
+
* counts as active. A nested barrier whose demand activation is
|
|
332
|
+
* `"after-resolution"` contributes only its ordering aggregate: its
|
|
333
|
+
* demand edges stay behind its own confirmation boundary and are
|
|
334
|
+
* promoted by its own resolution within the nested scheduling pass.
|
|
323
335
|
*/
|
|
324
336
|
async function computeActiveBarrierDependencies(nodes) {
|
|
325
337
|
const active = /* @__PURE__ */ new Set();
|
|
326
338
|
const ordering = /* @__PURE__ */ new Set();
|
|
339
|
+
const demandEdges = [];
|
|
327
340
|
for (const node of nodes) {
|
|
328
341
|
const metadata = node.parser.dependencyMetadata;
|
|
329
342
|
const source = metadata?.source;
|
|
@@ -341,14 +354,24 @@ async function computeActiveBarrierDependencies(nodes) {
|
|
|
341
354
|
for (const id of node.guaranteedProvidesSourceIds ?? []) active.add(id);
|
|
342
355
|
if (completion != null) {
|
|
343
356
|
const inactive = source?.extractSourceValue != null ? extracted?.success === true : hasInactiveCompletion(node);
|
|
344
|
-
if (!inactive)
|
|
357
|
+
if (!inactive) {
|
|
358
|
+
for (const id of completion.dependencyIds) ordering.add(id);
|
|
359
|
+
if (source != null) demandEdges.push({
|
|
360
|
+
consumerSourceId: source.sourceId,
|
|
361
|
+
dependencyIds: completion.dependencyIds
|
|
362
|
+
});
|
|
363
|
+
}
|
|
345
364
|
}
|
|
346
365
|
const nested = node.barrierCompletionDependencies;
|
|
347
|
-
if (nested != null)
|
|
366
|
+
if (nested != null) {
|
|
367
|
+
for (const id of nested.orderingDependencyIds) ordering.add(id);
|
|
368
|
+
if (node.barrierDemandActivation !== "after-resolution" || node.barrierResolutionState === "resolved") demandEdges.push(...nested.demandEdges);
|
|
369
|
+
}
|
|
348
370
|
}
|
|
349
371
|
return {
|
|
350
372
|
orderingDependencyIds: [...ordering].filter((id) => !active.has(id)),
|
|
351
|
-
activeProvidesSourceIds: active
|
|
373
|
+
activeProvidesSourceIds: active,
|
|
374
|
+
demandEdges
|
|
352
375
|
};
|
|
353
376
|
}
|
|
354
377
|
function isDeferredCompletionResult(result) {
|
|
@@ -395,13 +418,15 @@ async function scheduleEffectfulSourceCompletions(nodes, state, runtime, exec, p
|
|
|
395
418
|
success: false,
|
|
396
419
|
error: preCompletedFailure
|
|
397
420
|
};
|
|
398
|
-
const direct = new Set(nodes);
|
|
421
|
+
const direct = new Set(nodes.filter((node) => !preExpandedBranchNodes.has(node)));
|
|
399
422
|
const collected = /* @__PURE__ */ new Set();
|
|
400
423
|
const expandedNodes = [];
|
|
401
424
|
for (const node of nodes) {
|
|
402
425
|
const optedIn = node.parser[sourceCollectionExpansionKey] === true;
|
|
426
|
+
const preExpanded = preExpandedBranchNodes.has(node);
|
|
403
427
|
for (const expandedNode of expandEffectfulRuntimeNodes([node])) {
|
|
404
428
|
expandedNodes.push(expandedNode);
|
|
429
|
+
if (preExpanded) continue;
|
|
405
430
|
if (optedIn || expandedNode === node) collected.add(expandedNode);
|
|
406
431
|
}
|
|
407
432
|
}
|
|
@@ -6610,6 +6635,47 @@ function conditional(discriminator, branches, defaultBranch, options) {
|
|
|
6610
6635
|
};
|
|
6611
6636
|
const preparedKeyPrefix = `c${++conditionalInstanceCounter}|`;
|
|
6612
6637
|
const preparedKeyFor = (parentPath) => preparedKeyPrefix + serializeSchedulingPath(parentPath ?? []);
|
|
6638
|
+
const branchMismatchFailure = (resolvedValue, speculativeKey) => {
|
|
6639
|
+
const resolvedKey = typeof resolvedValue === "string" ? resolvedValue : "<unknown>";
|
|
6640
|
+
return {
|
|
6641
|
+
success: false,
|
|
6642
|
+
error: options?.errors?.branchMismatch ? options.errors.branchMismatch(resolvedKey, speculativeKey) : message`Branch mismatch: tokens for ${speculativeKey} were consumed, but the discriminator resolved to ${resolvedKey}.`
|
|
6643
|
+
};
|
|
6644
|
+
};
|
|
6645
|
+
const wrapCachedBarrierFailure = (session, failure, branchKey) => {
|
|
6646
|
+
const branchError = options?.errors?.branchError;
|
|
6647
|
+
if (branchKey == null || branchError == null) return failure;
|
|
6648
|
+
const wrapped = {
|
|
6649
|
+
success: false,
|
|
6650
|
+
error: branchError(branchKey, failure.error)
|
|
6651
|
+
};
|
|
6652
|
+
return replaceCachedBarrierFailure(session, failure, wrapped) ? wrapped : failure;
|
|
6653
|
+
};
|
|
6654
|
+
const wrapBranchBarrierNodes = (nodes, branchKey) => {
|
|
6655
|
+
const branchError = options?.errors?.branchError;
|
|
6656
|
+
if (branchKey == null || branchError == null) return nodes;
|
|
6657
|
+
return nodes.map((node) => {
|
|
6658
|
+
const resolveBarrier = node.resolveBarrier;
|
|
6659
|
+
const prepare = node.prepare;
|
|
6660
|
+
if (resolveBarrier == null && prepare == null) return node;
|
|
6661
|
+
return {
|
|
6662
|
+
...node,
|
|
6663
|
+
...resolveBarrier == null ? {} : { resolveBarrier: async (ctx) => {
|
|
6664
|
+
const result = await resolveBarrier(ctx);
|
|
6665
|
+
if (result != null && "success" in result) return {
|
|
6666
|
+
success: false,
|
|
6667
|
+
error: branchError(branchKey, result.error)
|
|
6668
|
+
};
|
|
6669
|
+
return result;
|
|
6670
|
+
} },
|
|
6671
|
+
...prepare == null ? {} : { prepare: async (ctx) => {
|
|
6672
|
+
const failure = await prepare(ctx);
|
|
6673
|
+
if (failure == null) return void 0;
|
|
6674
|
+
return wrapCachedBarrierFailure(ctx.exec?.effectfulCompletionSession, failure, branchKey);
|
|
6675
|
+
} }
|
|
6676
|
+
};
|
|
6677
|
+
});
|
|
6678
|
+
};
|
|
6613
6679
|
const { branchProvidedSourceIds, branchGuaranteedIntersection, mergedBarrierDependencies, barrierDependenciesByKey } = (() => {
|
|
6614
6680
|
const provided = /* @__PURE__ */ new Set();
|
|
6615
6681
|
const byParser = /* @__PURE__ */ new Map();
|
|
@@ -6678,11 +6744,19 @@ function conditional(discriminator, branches, defaultBranch, options) {
|
|
|
6678
6744
|
const selected = conditionalState.selectedBranch;
|
|
6679
6745
|
if (selected !== void 0 && conditionalState.speculative !== true) {
|
|
6680
6746
|
const branchParser = selected.kind === "default" ? defaultBranch : branches[selected.key];
|
|
6681
|
-
if (branchParser != null)
|
|
6682
|
-
|
|
6683
|
-
|
|
6684
|
-
|
|
6685
|
-
|
|
6747
|
+
if (branchParser != null) {
|
|
6748
|
+
const branchNode = {
|
|
6749
|
+
path: [...parentPath ?? [], "_branch"],
|
|
6750
|
+
parser: branchParser,
|
|
6751
|
+
state: getWrappedChildState(conditionalState, conditionalState.branchState, branchParser)
|
|
6752
|
+
};
|
|
6753
|
+
const branchKey = selected.kind === "branch" ? selected.key : void 0;
|
|
6754
|
+
if (branchKey != null && options?.errors?.branchError != null) for (const expanded of wrapBranchBarrierNodes(expandEffectfulRuntimeNodes([branchNode]), branchKey)) {
|
|
6755
|
+
preExpandedBranchNodes.add(expanded);
|
|
6756
|
+
nodes.push(expanded);
|
|
6757
|
+
}
|
|
6758
|
+
else nodes.push(branchNode);
|
|
6759
|
+
}
|
|
6686
6760
|
return nodes;
|
|
6687
6761
|
}
|
|
6688
6762
|
const discriminatorSource = discriminator.dependencyMetadata?.source;
|
|
@@ -6733,14 +6807,17 @@ function conditional(discriminator, branches, defaultBranch, options) {
|
|
|
6733
6807
|
providesSourceIds: branchProvidedSourceIds,
|
|
6734
6808
|
...speculativeEstimate != null && speculativeEstimate.guaranteedProvides.size > 0 ? { guaranteedProvidesSourceIds: speculativeEstimate.guaranteedProvides } : {},
|
|
6735
6809
|
...speculativeBarrierDependencies != null ? { barrierCompletionDependencies: speculativeBarrierDependencies } : {},
|
|
6810
|
+
barrierDemandActivation: "after-resolution",
|
|
6736
6811
|
resolveBarrier: async (ctx) => {
|
|
6737
6812
|
const session = ctx.exec?.effectfulCompletionSession;
|
|
6738
6813
|
if (session == null) return void 0;
|
|
6739
6814
|
const result = cell.result ?? session.completedByPath.get(discriminatorPathKey);
|
|
6740
6815
|
if (result == null || isDeferredCompletionResult(result) || result.success !== true) return void 0;
|
|
6741
|
-
if (String(result.value) !== speculativeKey
|
|
6816
|
+
if (String(result.value) !== speculativeKey) return branchMismatchFailure(result.value, speculativeKey);
|
|
6817
|
+
if (branches[speculativeKey] == null) return {
|
|
6742
6818
|
orderingDependencyIds: [],
|
|
6743
|
-
activeProvidesSourceIds: /* @__PURE__ */ new Set()
|
|
6819
|
+
activeProvidesSourceIds: /* @__PURE__ */ new Set(),
|
|
6820
|
+
demandEdges: []
|
|
6744
6821
|
};
|
|
6745
6822
|
return await computeActiveBarrierDependencies(expandGuessedBranchNodes());
|
|
6746
6823
|
},
|
|
@@ -6751,7 +6828,9 @@ function conditional(discriminator, branches, defaultBranch, options) {
|
|
|
6751
6828
|
if (result == null || isDeferredCompletionResult(result) || result.success !== true) return void 0;
|
|
6752
6829
|
if (String(result.value) !== speculativeKey) return void 0;
|
|
6753
6830
|
if (branches[speculativeKey] == null) return void 0;
|
|
6754
|
-
|
|
6831
|
+
const failure = await ctx.schedule(expandGuessedBranchNodes());
|
|
6832
|
+
if (failure == null) return void 0;
|
|
6833
|
+
return wrapCachedBarrierFailure(session, failure, speculativeKey);
|
|
6755
6834
|
}
|
|
6756
6835
|
});
|
|
6757
6836
|
return nodes;
|
|
@@ -6819,19 +6898,23 @@ function conditional(discriminator, branches, defaultBranch, options) {
|
|
|
6819
6898
|
providesSourceIds: branchProvidedSourceIds,
|
|
6820
6899
|
...branchGuaranteedIntersection.size > 0 ? { guaranteedProvidesSourceIds: branchGuaranteedIntersection } : {},
|
|
6821
6900
|
...mergedBarrierDependencies != null ? { barrierCompletionDependencies: mergedBarrierDependencies } : {},
|
|
6901
|
+
barrierDemandActivation: "after-resolution",
|
|
6822
6902
|
resolveBarrier: async (ctx) => {
|
|
6823
6903
|
const prepared = await resolvePreparedSelection(ctx);
|
|
6824
6904
|
if (prepared == null) return void 0;
|
|
6825
6905
|
if (prepared.branchParser == null) return {
|
|
6826
6906
|
orderingDependencyIds: [],
|
|
6827
|
-
activeProvidesSourceIds: /* @__PURE__ */ new Set()
|
|
6907
|
+
activeProvidesSourceIds: /* @__PURE__ */ new Set(),
|
|
6908
|
+
demandEdges: []
|
|
6828
6909
|
};
|
|
6829
6910
|
return await computeActiveBarrierDependencies(expandPreparedBranchNodes(prepared));
|
|
6830
6911
|
},
|
|
6831
6912
|
prepare: async (ctx) => {
|
|
6832
6913
|
const prepared = await resolvePreparedSelection(ctx);
|
|
6833
6914
|
if (prepared?.branchParser == null) return void 0;
|
|
6834
|
-
|
|
6915
|
+
const failure = await ctx.schedule(expandPreparedBranchNodes(prepared));
|
|
6916
|
+
if (failure == null) return void 0;
|
|
6917
|
+
return wrapCachedBarrierFailure(ctx.exec?.effectfulCompletionSession, failure, prepared.branchKey);
|
|
6835
6918
|
}
|
|
6836
6919
|
});
|
|
6837
6920
|
return nodes;
|
|
@@ -6979,14 +7062,7 @@ function conditional(discriminator, branches, defaultBranch, options) {
|
|
|
6979
7062
|
else if (wasSpeculative) return completedDiscriminator;
|
|
6980
7063
|
else discriminatorValue = state.selectedBranch.key;
|
|
6981
7064
|
}
|
|
6982
|
-
if (wasSpeculative && state.selectedBranch.kind === "branch" && discriminatorValue !== state.selectedBranch.key)
|
|
6983
|
-
const speculativeKey = state.selectedBranch.key;
|
|
6984
|
-
const resolvedKey = typeof discriminatorValue === "string" ? discriminatorValue : "<unknown>";
|
|
6985
|
-
return {
|
|
6986
|
-
success: false,
|
|
6987
|
-
error: options?.errors?.branchMismatch ? options.errors.branchMismatch(resolvedKey, speculativeKey) : message`Branch mismatch: tokens for ${speculativeKey} were consumed, but the discriminator resolved to ${resolvedKey}.`
|
|
6988
|
-
};
|
|
6989
|
-
}
|
|
7065
|
+
if (wasSpeculative && state.selectedBranch.kind === "branch" && discriminatorValue !== state.selectedBranch.key) return branchMismatchFailure(discriminatorValue, state.selectedBranch.key);
|
|
6990
7066
|
const resolvedBranchState = getWrappedChildState(state, await resolveStateWithRuntimeAsync(state.branchState, runtime), branchParser);
|
|
6991
7067
|
const branchResult = unwrapCompleteResult(await branchParser.complete(resolvedBranchState, withChildExecPath(completionExec, "_branch")));
|
|
6992
7068
|
if (!branchResult.success) {
|
|
@@ -1151,6 +1151,133 @@ function collectDeferredDemand(state, demanded, visited) {
|
|
|
1151
1151
|
for (const key of Reflect.ownKeys(state)) collectDeferredDemand(state[key], demanded, visited);
|
|
1152
1152
|
}
|
|
1153
1153
|
/**
|
|
1154
|
+
* Runs the monotonic demand fixed point over the given nodes' metadata,
|
|
1155
|
+
* adding to the session's demanded set until nothing changes:
|
|
1156
|
+
*
|
|
1157
|
+
* - a demanded derived source demands its dependency IDs;
|
|
1158
|
+
* - a demanded source whose effectful completion consumes other
|
|
1159
|
+
* dependency values (a prompt with a derived configuration) demands
|
|
1160
|
+
* those prerequisites, unless this occurrence is already satisfied;
|
|
1161
|
+
* - a barrier's demand edges mirror the flat completion rule for the
|
|
1162
|
+
* consumers hidden behind it—only when a branch consumer's own source
|
|
1163
|
+
* is demanded do its completion prerequisites become demanded—but a
|
|
1164
|
+
* barrier whose {@link RuntimeNode.barrierDemandActivation} is
|
|
1165
|
+
* `"after-resolution"` contributes its edges only once it has
|
|
1166
|
+
* resolved, so an unselected branch's prerequisites are never
|
|
1167
|
+
* demanded on the strength of a static estimate;
|
|
1168
|
+
* - when a source a barrier can provide is demanded, the barrier's
|
|
1169
|
+
* control dependency (its discriminator source) becomes demanded.
|
|
1170
|
+
*
|
|
1171
|
+
* One barrier's branches can provide another barrier's discriminator
|
|
1172
|
+
* source, and the chain must propagate regardless of declaration order.
|
|
1173
|
+
* Each iteration adds at least one demanded ID, so the loop terminates.
|
|
1174
|
+
* The scheduler re-runs this after a gated barrier resolves, passing
|
|
1175
|
+
* the pass's metadata universe with resolved replacement nodes swapped
|
|
1176
|
+
* in, so promotion chains through already-visited metadata as well.
|
|
1177
|
+
*/
|
|
1178
|
+
function propagateDemand(universe, session) {
|
|
1179
|
+
let demandAdded = true;
|
|
1180
|
+
while (demandAdded) {
|
|
1181
|
+
demandAdded = false;
|
|
1182
|
+
for (const node of universe) {
|
|
1183
|
+
const metadata = node.parser.dependencyMetadata;
|
|
1184
|
+
if (metadata?.source != null && metadata.derived != null && session.demanded.has(metadata.source.sourceId)) for (const dependencySourceId of metadata.derived.dependencyIds) {
|
|
1185
|
+
if (session.demanded.has(dependencySourceId)) continue;
|
|
1186
|
+
session.demanded.add(dependencySourceId);
|
|
1187
|
+
demandAdded = true;
|
|
1188
|
+
}
|
|
1189
|
+
if (metadata?.source != null && metadata.completion != null && session.demanded.has(metadata.source.sourceId) && !hasInactiveCompletion(node)) for (const dependencySourceId of metadata.completion.dependencyIds) {
|
|
1190
|
+
if (session.demanded.has(dependencySourceId)) continue;
|
|
1191
|
+
session.demanded.add(dependencySourceId);
|
|
1192
|
+
demandAdded = true;
|
|
1193
|
+
}
|
|
1194
|
+
if (node.barrierCompletionDependencies != null && (node.barrierDemandActivation !== "after-resolution" || node.barrierResolutionState === "resolved")) for (const edge of node.barrierCompletionDependencies.demandEdges) {
|
|
1195
|
+
if (!session.demanded.has(edge.consumerSourceId)) continue;
|
|
1196
|
+
for (const dependencySourceId of edge.dependencyIds) {
|
|
1197
|
+
if (session.demanded.has(dependencySourceId)) continue;
|
|
1198
|
+
session.demanded.add(dependencySourceId);
|
|
1199
|
+
demandAdded = true;
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
if (node.requiresSourceId == null || node.providesSourceIds == null) continue;
|
|
1203
|
+
if (session.demanded.has(node.requiresSourceId)) continue;
|
|
1204
|
+
for (const provided of node.providesSourceIds) if (session.demanded.has(provided)) {
|
|
1205
|
+
session.demanded.add(node.requiresSourceId);
|
|
1206
|
+
demandAdded = true;
|
|
1207
|
+
break;
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
const barrierFailureDescription = "optique.barrierFailure";
|
|
1213
|
+
/**
|
|
1214
|
+
* Replaces a barrier failure cached in the run-scoped session with a
|
|
1215
|
+
* wrapped version of the same failure, identified by object identity.
|
|
1216
|
+
* A construct that decorates a nested barrier failure on its way out—a
|
|
1217
|
+
* `conditional()` applying its `branchError` hook—uses this so later
|
|
1218
|
+
* passes sharing the session (the eager fallback after a failed seed
|
|
1219
|
+
* pass) surface the wrapped error rather than the raw one.
|
|
1220
|
+
*
|
|
1221
|
+
* @param session The run-scoped effectful completion session.
|
|
1222
|
+
* @param original The failure exactly as the scheduling pass returned
|
|
1223
|
+
* it.
|
|
1224
|
+
* @param replacement The decorated failure to cache in its place.
|
|
1225
|
+
* @returns Whether a cached barrier failure was found and replaced;
|
|
1226
|
+
* `false` means the failure did not come from a barrier resolution
|
|
1227
|
+
* and should propagate untouched.
|
|
1228
|
+
* @internal
|
|
1229
|
+
* @since 1.3.0
|
|
1230
|
+
*/
|
|
1231
|
+
function replaceCachedBarrierFailure(session, original, replacement) {
|
|
1232
|
+
if (session == null) return false;
|
|
1233
|
+
let replaced = false;
|
|
1234
|
+
for (const [key, value] of session.results) if (key.description === barrierFailureDescription && value === original) {
|
|
1235
|
+
session.results.set(key, replacement);
|
|
1236
|
+
replaced = true;
|
|
1237
|
+
}
|
|
1238
|
+
return replaced;
|
|
1239
|
+
}
|
|
1240
|
+
/**
|
|
1241
|
+
* Whether the node is a scheduling barrier whose demand edges are gated
|
|
1242
|
+
* on resolution and whose resolution has not happened yet. While any
|
|
1243
|
+
* such barrier is pending in a demand-only pass, the scheduler parks
|
|
1244
|
+
* not-yet-demanded effectful nodes instead of letting them defer, so a
|
|
1245
|
+
* later resolution can still demand them into their dependency slot.
|
|
1246
|
+
*/
|
|
1247
|
+
function isGatedUnresolvedBarrier(node) {
|
|
1248
|
+
return node.resolveBarrier != null && node.barrierDemandActivation === "after-resolution" && node.barrierResolutionState == null;
|
|
1249
|
+
}
|
|
1250
|
+
/**
|
|
1251
|
+
* Collects the nodes a demand-only pass must not visit yet while a
|
|
1252
|
+
* gated barrier is unresolved: every effectful source node whose own
|
|
1253
|
+
* source is not demanded (its completion would defer irrevocably for
|
|
1254
|
+
* this pass, yet a barrier's resolution may still demand it), plus
|
|
1255
|
+
* every node reachable from a parked node along hard (non-advisory)
|
|
1256
|
+
* dependency edges, so a dependent—a derived replay, a barrier whose
|
|
1257
|
+
* control dependency is a parked discriminator—never runs ahead of a
|
|
1258
|
+
* parked provider. The scheduling loop's all-blocked fallback keeps
|
|
1259
|
+
* the pass progressing even when this parks every pending node.
|
|
1260
|
+
*/
|
|
1261
|
+
function collectParkedNodes(pending, session) {
|
|
1262
|
+
const parked = /* @__PURE__ */ new Set();
|
|
1263
|
+
for (const node of pending) {
|
|
1264
|
+
const source = node.parser.dependencyMetadata?.source;
|
|
1265
|
+
if (source?.completeSource != null && !session.demanded.has(source.sourceId)) parked.add(node);
|
|
1266
|
+
}
|
|
1267
|
+
if (parked.size === 0) return parked;
|
|
1268
|
+
const graph = buildDependencyGraph(pending);
|
|
1269
|
+
const queue = [...parked];
|
|
1270
|
+
while (queue.length > 0) {
|
|
1271
|
+
const provider = queue.pop();
|
|
1272
|
+
for (const [consumer, advisoryOnly] of graph.outgoing.get(provider) ?? []) {
|
|
1273
|
+
if (advisoryOnly || parked.has(consumer)) continue;
|
|
1274
|
+
parked.add(consumer);
|
|
1275
|
+
queue.push(consumer);
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
return parked;
|
|
1279
|
+
}
|
|
1280
|
+
/**
|
|
1154
1281
|
* Runs effectful source completions (e.g., interactive prompts) serially
|
|
1155
1282
|
* in declaration order for source nodes whose value is not yet registered.
|
|
1156
1283
|
*
|
|
@@ -1207,38 +1334,7 @@ async function completeEffectfulSourcesAsync(nodes, state, runtime, exec, option
|
|
|
1207
1334
|
const demandNodes = options?.demandNodes ?? nodes;
|
|
1208
1335
|
const demanded = collectDemandedDependencyIds(demandNodes, state, exec.trace);
|
|
1209
1336
|
for (const id of demanded) session.demanded.add(id);
|
|
1210
|
-
|
|
1211
|
-
while (demandAdded) {
|
|
1212
|
-
demandAdded = false;
|
|
1213
|
-
for (const node of nodes) {
|
|
1214
|
-
const metadata = node.parser.dependencyMetadata;
|
|
1215
|
-
if (metadata?.source != null && metadata.derived != null && session.demanded.has(metadata.source.sourceId)) for (const dependencySourceId of metadata.derived.dependencyIds) {
|
|
1216
|
-
if (session.demanded.has(dependencySourceId)) continue;
|
|
1217
|
-
session.demanded.add(dependencySourceId);
|
|
1218
|
-
demandAdded = true;
|
|
1219
|
-
}
|
|
1220
|
-
if (metadata?.source != null && metadata.completion != null && session.demanded.has(metadata.source.sourceId) && !hasInactiveCompletion(node)) for (const dependencySourceId of metadata.completion.dependencyIds) {
|
|
1221
|
-
if (session.demanded.has(dependencySourceId)) continue;
|
|
1222
|
-
session.demanded.add(dependencySourceId);
|
|
1223
|
-
demandAdded = true;
|
|
1224
|
-
}
|
|
1225
|
-
if (node.barrierCompletionDependencies != null) for (const edge of node.barrierCompletionDependencies.demandEdges) {
|
|
1226
|
-
if (!session.demanded.has(edge.consumerSourceId)) continue;
|
|
1227
|
-
for (const dependencySourceId of edge.dependencyIds) {
|
|
1228
|
-
if (session.demanded.has(dependencySourceId)) continue;
|
|
1229
|
-
session.demanded.add(dependencySourceId);
|
|
1230
|
-
demandAdded = true;
|
|
1231
|
-
}
|
|
1232
|
-
}
|
|
1233
|
-
if (node.requiresSourceId == null || node.providesSourceIds == null) continue;
|
|
1234
|
-
if (session.demanded.has(node.requiresSourceId)) continue;
|
|
1235
|
-
for (const provided of node.providesSourceIds) if (session.demanded.has(provided)) {
|
|
1236
|
-
session.demanded.add(node.requiresSourceId);
|
|
1237
|
-
demandAdded = true;
|
|
1238
|
-
break;
|
|
1239
|
-
}
|
|
1240
|
-
}
|
|
1241
|
-
}
|
|
1337
|
+
propagateDemand(nodes, session);
|
|
1242
1338
|
}
|
|
1243
1339
|
if (session != null) {
|
|
1244
1340
|
for (const result of session.results.values()) if (!result.success) return {
|
|
@@ -1259,12 +1355,17 @@ async function completeEffectfulSourcesAsync(nodes, state, runtime, exec, option
|
|
|
1259
1355
|
}).then((result) => result.success ? void 0 : result)
|
|
1260
1356
|
};
|
|
1261
1357
|
const pending = [...nodes];
|
|
1358
|
+
const metadataUniverse = [...nodes];
|
|
1262
1359
|
const providesId = (node, id) => node.parser.dependencyMetadata?.source?.sourceId === id || node.providesSourceIds?.has(id) === true;
|
|
1360
|
+
const demandSession = session?.policy === "demand-only" ? session : void 0;
|
|
1263
1361
|
const resolveBarrierAt = async (index) => {
|
|
1264
1362
|
const candidate = pending[index];
|
|
1265
1363
|
const resolution = await candidate.resolveBarrier(barrierContext);
|
|
1266
|
-
if (resolution != null && "success" in resolution)
|
|
1267
|
-
|
|
1364
|
+
if (resolution != null && "success" in resolution) {
|
|
1365
|
+
session?.results.set(Symbol(barrierFailureDescription), resolution);
|
|
1366
|
+
return resolution;
|
|
1367
|
+
}
|
|
1368
|
+
const replacement = resolution == null ? {
|
|
1268
1369
|
...candidate,
|
|
1269
1370
|
barrierResolutionState: "unresolvable"
|
|
1270
1371
|
} : {
|
|
@@ -1273,22 +1374,44 @@ async function completeEffectfulSourcesAsync(nodes, state, runtime, exec, option
|
|
|
1273
1374
|
providesSourceIds: resolution.activeProvidesSourceIds,
|
|
1274
1375
|
barrierCompletionDependencies: {
|
|
1275
1376
|
orderingDependencyIds: resolution.orderingDependencyIds,
|
|
1276
|
-
demandEdges:
|
|
1377
|
+
demandEdges: resolution.demandEdges
|
|
1277
1378
|
}
|
|
1278
1379
|
};
|
|
1380
|
+
pending[index] = replacement;
|
|
1381
|
+
const universeIndex = metadataUniverse.indexOf(candidate);
|
|
1382
|
+
if (universeIndex >= 0) metadataUniverse[universeIndex] = replacement;
|
|
1383
|
+
if (resolution != null && demandSession != null) propagateDemand(metadataUniverse, demandSession);
|
|
1279
1384
|
return void 0;
|
|
1280
1385
|
};
|
|
1281
1386
|
while (pending.length > 0) {
|
|
1387
|
+
const parkingActive = demandSession != null && pending.some(isGatedUnresolvedBarrier);
|
|
1282
1388
|
for (let i = 0; i < pending.length; i++) {
|
|
1283
1389
|
const candidate = pending[i];
|
|
1284
1390
|
if (candidate.resolveBarrier == null || candidate.barrierResolutionState != null) continue;
|
|
1285
1391
|
const requires = candidate.requiresSourceId;
|
|
1286
1392
|
if (requires == null) continue;
|
|
1393
|
+
if (parkingActive && demandSession != null && !demandSession.demanded.has(requires)) continue;
|
|
1287
1394
|
if (pending.some((other) => other !== candidate && providesId(other, requires))) continue;
|
|
1288
1395
|
const failure = await resolveBarrierAt(i);
|
|
1289
1396
|
if (failure != null) return failure;
|
|
1290
1397
|
}
|
|
1291
|
-
const
|
|
1398
|
+
const ordered = orderDependencyNodes(pending);
|
|
1399
|
+
let node = ordered[0];
|
|
1400
|
+
const parkingStillActive = demandSession != null && pending.some(isGatedUnresolvedBarrier);
|
|
1401
|
+
if (parkingStillActive && demandSession != null) {
|
|
1402
|
+
const parked = collectParkedNodes(pending, demandSession);
|
|
1403
|
+
const isSkippedBarrier = (candidate) => candidate.resolveBarrier != null && candidate.barrierResolutionState == null && candidate.requiresSourceId != null && !demandSession.demanded.has(candidate.requiresSourceId);
|
|
1404
|
+
const selectable = ordered.find((candidate) => !parked.has(candidate) && !isSkippedBarrier(candidate));
|
|
1405
|
+
if (selectable != null) node = selectable;
|
|
1406
|
+
else {
|
|
1407
|
+
const stalled = ordered.find((candidate) => candidate.resolveBarrier != null && candidate.barrierResolutionState == null);
|
|
1408
|
+
if (stalled != null) {
|
|
1409
|
+
const failure = await resolveBarrierAt(pending.indexOf(stalled));
|
|
1410
|
+
if (failure != null) return failure;
|
|
1411
|
+
continue;
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1292
1415
|
if (node.resolveBarrier != null && node.barrierResolutionState == null) {
|
|
1293
1416
|
const failure = await resolveBarrierAt(pending.indexOf(node));
|
|
1294
1417
|
if (failure != null) return failure;
|
|
@@ -1703,6 +1826,7 @@ exports.hasInactiveCompletion = hasInactiveCompletion;
|
|
|
1703
1826
|
exports.includeSourceFailureChain = includeSourceFailureChain;
|
|
1704
1827
|
exports.orderDependencyNodes = orderDependencyNodes;
|
|
1705
1828
|
exports.recordDerivedRawInput = recordDerivedRawInput;
|
|
1829
|
+
exports.replaceCachedBarrierFailure = replaceCachedBarrierFailure;
|
|
1706
1830
|
exports.replayDerivedParser = replayDerivedParser;
|
|
1707
1831
|
exports.replayDerivedParserAsync = replayDerivedParserAsync;
|
|
1708
1832
|
exports.resolveDerivedSourceValues = resolveDerivedSourceValues;
|
|
@@ -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 { ExecutionContext } from "./internal/parser.cjs";
|
|
6
|
+
import { EffectfulCompletionSession, ExecutionContext } from "./internal/parser.cjs";
|
|
7
7
|
|
|
8
8
|
//#region src/dependency-runtime.d.ts
|
|
9
9
|
|
|
@@ -220,6 +220,22 @@ interface RuntimeNode {
|
|
|
220
220
|
* @since 1.3.0
|
|
221
221
|
*/
|
|
222
222
|
readonly barrierCompletionDependencies?: BarrierCompletionDependencies;
|
|
223
|
+
/**
|
|
224
|
+
* When `"after-resolution"`, the demand edges in
|
|
225
|
+
* {@link RuntimeNode.barrierCompletionDependencies} join demand
|
|
226
|
+
* propagation only once {@link RuntimeNode.resolveBarrier} has
|
|
227
|
+
* replaced the static estimate with the resolved selection's actual
|
|
228
|
+
* edges. Until then the demand-only pass parks not-yet-demanded
|
|
229
|
+
* effectful nodes instead of letting them defer, so a prerequisite
|
|
230
|
+
* demanded only by an unselected branch never runs, while one the
|
|
231
|
+
* resolved selection reads still completes in its dependency slot.
|
|
232
|
+
* A barrier the pass marks `"unresolvable"` never activates its
|
|
233
|
+
* edges within that pass. Ordering and control edges are unaffected.
|
|
234
|
+
* Default: `"immediate"`.
|
|
235
|
+
*
|
|
236
|
+
* @since 1.3.0
|
|
237
|
+
*/
|
|
238
|
+
readonly barrierDemandActivation?: "immediate" | "after-resolution";
|
|
223
239
|
}
|
|
224
240
|
/**
|
|
225
241
|
* An exact demand edge inside a scheduling barrier's subtree: when the
|
|
@@ -285,6 +301,19 @@ interface BarrierResolution {
|
|
|
285
301
|
* sibling consumer no longer waits for a merely possible provider.
|
|
286
302
|
*/
|
|
287
303
|
readonly activeProvidesSourceIds: ReadonlySet<symbol>;
|
|
304
|
+
/**
|
|
305
|
+
* The demand edges of the resolved selection: empty when the
|
|
306
|
+
* selection was rejected or has no completion consumers. A
|
|
307
|
+
* demand-only pass applies them once the barrier resolves, so a
|
|
308
|
+
* prerequisite is demanded only for a selection that actually reads
|
|
309
|
+
* it. Edges belonging to a nested barrier whose own demand
|
|
310
|
+
* activation is `"after-resolution"` and which is still unresolved
|
|
311
|
+
* must not appear here; the nested barrier promotes them itself
|
|
312
|
+
* within its own scheduling pass.
|
|
313
|
+
*
|
|
314
|
+
* @since 1.3.0
|
|
315
|
+
*/
|
|
316
|
+
readonly demandEdges: readonly BarrierCompletionDemandEdge[];
|
|
288
317
|
}
|
|
289
318
|
/**
|
|
290
319
|
* Options for resolving matched derived source values.
|
|
@@ -723,6 +752,31 @@ interface CompleteEffectfulSourcesOptions {
|
|
|
723
752
|
*/
|
|
724
753
|
readonly includeStructural?: boolean;
|
|
725
754
|
}
|
|
755
|
+
/**
|
|
756
|
+
* Replaces a barrier failure cached in the run-scoped session with a
|
|
757
|
+
* wrapped version of the same failure, identified by object identity.
|
|
758
|
+
* A construct that decorates a nested barrier failure on its way out—a
|
|
759
|
+
* `conditional()` applying its `branchError` hook—uses this so later
|
|
760
|
+
* passes sharing the session (the eager fallback after a failed seed
|
|
761
|
+
* pass) surface the wrapped error rather than the raw one.
|
|
762
|
+
*
|
|
763
|
+
* @param session The run-scoped effectful completion session.
|
|
764
|
+
* @param original The failure exactly as the scheduling pass returned
|
|
765
|
+
* it.
|
|
766
|
+
* @param replacement The decorated failure to cache in its place.
|
|
767
|
+
* @returns Whether a cached barrier failure was found and replaced;
|
|
768
|
+
* `false` means the failure did not come from a barrier resolution
|
|
769
|
+
* and should propagate untouched.
|
|
770
|
+
* @internal
|
|
771
|
+
* @since 1.3.0
|
|
772
|
+
*/
|
|
773
|
+
declare function replaceCachedBarrierFailure(session: EffectfulCompletionSession | undefined, original: {
|
|
774
|
+
readonly success: false;
|
|
775
|
+
readonly error: Message;
|
|
776
|
+
}, replacement: {
|
|
777
|
+
readonly success: false;
|
|
778
|
+
readonly error: Message;
|
|
779
|
+
}): boolean;
|
|
726
780
|
/**
|
|
727
781
|
* Runs effectful source completions (e.g., interactive prompts) serially
|
|
728
782
|
* in declaration order for source nodes whose value is not yet registered.
|
|
@@ -858,4 +912,4 @@ declare function buildRuntimeNodesFromArray(parsers: ReadonlyArray<{
|
|
|
858
912
|
readonly initialState?: unknown;
|
|
859
913
|
}>, stateArray: readonly unknown[], parentPath?: readonly PropertyKey[]): readonly RuntimeNode[];
|
|
860
914
|
//#endregion
|
|
861
|
-
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, guaranteedStaticSourceIdsKey, hasInactiveCompletion, includeSourceFailureChain, orderDependencyNodes, recordDerivedRawInput, replayDerivedParser, replayDerivedParserAsync, resolveDerivedSourceValues, resolveDerivedSourceValuesAsync, resolveStateWithRuntime, resolveStateWithRuntimeAsync, serializeSchedulingPath, sourceCollectionExpansionKey, staticSourceScopeKey };
|
|
915
|
+
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, guaranteedStaticSourceIdsKey, hasInactiveCompletion, includeSourceFailureChain, orderDependencyNodes, recordDerivedRawInput, replaceCachedBarrierFailure, replayDerivedParser, replayDerivedParserAsync, resolveDerivedSourceValues, resolveDerivedSourceValuesAsync, resolveStateWithRuntime, resolveStateWithRuntimeAsync, serializeSchedulingPath, sourceCollectionExpansionKey, staticSourceScopeKey };
|
|
@@ -3,7 +3,7 @@ import { DependencyRegistryLike } from "./registry-types.js";
|
|
|
3
3
|
import { ValueParserResult } from "./valueparser.js";
|
|
4
4
|
import { ParserDependencyMetadata } from "./dependency-metadata.js";
|
|
5
5
|
import { InputTrace } from "./input-trace.js";
|
|
6
|
-
import { ExecutionContext } from "./internal/parser.js";
|
|
6
|
+
import { EffectfulCompletionSession, ExecutionContext } from "./internal/parser.js";
|
|
7
7
|
|
|
8
8
|
//#region src/dependency-runtime.d.ts
|
|
9
9
|
|
|
@@ -220,6 +220,22 @@ interface RuntimeNode {
|
|
|
220
220
|
* @since 1.3.0
|
|
221
221
|
*/
|
|
222
222
|
readonly barrierCompletionDependencies?: BarrierCompletionDependencies;
|
|
223
|
+
/**
|
|
224
|
+
* When `"after-resolution"`, the demand edges in
|
|
225
|
+
* {@link RuntimeNode.barrierCompletionDependencies} join demand
|
|
226
|
+
* propagation only once {@link RuntimeNode.resolveBarrier} has
|
|
227
|
+
* replaced the static estimate with the resolved selection's actual
|
|
228
|
+
* edges. Until then the demand-only pass parks not-yet-demanded
|
|
229
|
+
* effectful nodes instead of letting them defer, so a prerequisite
|
|
230
|
+
* demanded only by an unselected branch never runs, while one the
|
|
231
|
+
* resolved selection reads still completes in its dependency slot.
|
|
232
|
+
* A barrier the pass marks `"unresolvable"` never activates its
|
|
233
|
+
* edges within that pass. Ordering and control edges are unaffected.
|
|
234
|
+
* Default: `"immediate"`.
|
|
235
|
+
*
|
|
236
|
+
* @since 1.3.0
|
|
237
|
+
*/
|
|
238
|
+
readonly barrierDemandActivation?: "immediate" | "after-resolution";
|
|
223
239
|
}
|
|
224
240
|
/**
|
|
225
241
|
* An exact demand edge inside a scheduling barrier's subtree: when the
|
|
@@ -285,6 +301,19 @@ interface BarrierResolution {
|
|
|
285
301
|
* sibling consumer no longer waits for a merely possible provider.
|
|
286
302
|
*/
|
|
287
303
|
readonly activeProvidesSourceIds: ReadonlySet<symbol>;
|
|
304
|
+
/**
|
|
305
|
+
* The demand edges of the resolved selection: empty when the
|
|
306
|
+
* selection was rejected or has no completion consumers. A
|
|
307
|
+
* demand-only pass applies them once the barrier resolves, so a
|
|
308
|
+
* prerequisite is demanded only for a selection that actually reads
|
|
309
|
+
* it. Edges belonging to a nested barrier whose own demand
|
|
310
|
+
* activation is `"after-resolution"` and which is still unresolved
|
|
311
|
+
* must not appear here; the nested barrier promotes them itself
|
|
312
|
+
* within its own scheduling pass.
|
|
313
|
+
*
|
|
314
|
+
* @since 1.3.0
|
|
315
|
+
*/
|
|
316
|
+
readonly demandEdges: readonly BarrierCompletionDemandEdge[];
|
|
288
317
|
}
|
|
289
318
|
/**
|
|
290
319
|
* Options for resolving matched derived source values.
|
|
@@ -723,6 +752,31 @@ interface CompleteEffectfulSourcesOptions {
|
|
|
723
752
|
*/
|
|
724
753
|
readonly includeStructural?: boolean;
|
|
725
754
|
}
|
|
755
|
+
/**
|
|
756
|
+
* Replaces a barrier failure cached in the run-scoped session with a
|
|
757
|
+
* wrapped version of the same failure, identified by object identity.
|
|
758
|
+
* A construct that decorates a nested barrier failure on its way out—a
|
|
759
|
+
* `conditional()` applying its `branchError` hook—uses this so later
|
|
760
|
+
* passes sharing the session (the eager fallback after a failed seed
|
|
761
|
+
* pass) surface the wrapped error rather than the raw one.
|
|
762
|
+
*
|
|
763
|
+
* @param session The run-scoped effectful completion session.
|
|
764
|
+
* @param original The failure exactly as the scheduling pass returned
|
|
765
|
+
* it.
|
|
766
|
+
* @param replacement The decorated failure to cache in its place.
|
|
767
|
+
* @returns Whether a cached barrier failure was found and replaced;
|
|
768
|
+
* `false` means the failure did not come from a barrier resolution
|
|
769
|
+
* and should propagate untouched.
|
|
770
|
+
* @internal
|
|
771
|
+
* @since 1.3.0
|
|
772
|
+
*/
|
|
773
|
+
declare function replaceCachedBarrierFailure(session: EffectfulCompletionSession | undefined, original: {
|
|
774
|
+
readonly success: false;
|
|
775
|
+
readonly error: Message;
|
|
776
|
+
}, replacement: {
|
|
777
|
+
readonly success: false;
|
|
778
|
+
readonly error: Message;
|
|
779
|
+
}): boolean;
|
|
726
780
|
/**
|
|
727
781
|
* Runs effectful source completions (e.g., interactive prompts) serially
|
|
728
782
|
* in declaration order for source nodes whose value is not yet registered.
|
|
@@ -858,4 +912,4 @@ declare function buildRuntimeNodesFromArray(parsers: ReadonlyArray<{
|
|
|
858
912
|
readonly initialState?: unknown;
|
|
859
913
|
}>, stateArray: readonly unknown[], parentPath?: readonly PropertyKey[]): readonly RuntimeNode[];
|
|
860
914
|
//#endregion
|
|
861
|
-
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, guaranteedStaticSourceIdsKey, hasInactiveCompletion, includeSourceFailureChain, orderDependencyNodes, recordDerivedRawInput, replayDerivedParser, replayDerivedParserAsync, resolveDerivedSourceValues, resolveDerivedSourceValuesAsync, resolveStateWithRuntime, resolveStateWithRuntimeAsync, serializeSchedulingPath, sourceCollectionExpansionKey, staticSourceScopeKey };
|
|
915
|
+
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, guaranteedStaticSourceIdsKey, hasInactiveCompletion, includeSourceFailureChain, orderDependencyNodes, recordDerivedRawInput, replaceCachedBarrierFailure, replayDerivedParser, replayDerivedParserAsync, resolveDerivedSourceValues, resolveDerivedSourceValuesAsync, resolveStateWithRuntime, resolveStateWithRuntimeAsync, serializeSchedulingPath, sourceCollectionExpansionKey, staticSourceScopeKey };
|
|
@@ -1151,6 +1151,133 @@ function collectDeferredDemand(state, demanded, visited) {
|
|
|
1151
1151
|
for (const key of Reflect.ownKeys(state)) collectDeferredDemand(state[key], demanded, visited);
|
|
1152
1152
|
}
|
|
1153
1153
|
/**
|
|
1154
|
+
* Runs the monotonic demand fixed point over the given nodes' metadata,
|
|
1155
|
+
* adding to the session's demanded set until nothing changes:
|
|
1156
|
+
*
|
|
1157
|
+
* - a demanded derived source demands its dependency IDs;
|
|
1158
|
+
* - a demanded source whose effectful completion consumes other
|
|
1159
|
+
* dependency values (a prompt with a derived configuration) demands
|
|
1160
|
+
* those prerequisites, unless this occurrence is already satisfied;
|
|
1161
|
+
* - a barrier's demand edges mirror the flat completion rule for the
|
|
1162
|
+
* consumers hidden behind it—only when a branch consumer's own source
|
|
1163
|
+
* is demanded do its completion prerequisites become demanded—but a
|
|
1164
|
+
* barrier whose {@link RuntimeNode.barrierDemandActivation} is
|
|
1165
|
+
* `"after-resolution"` contributes its edges only once it has
|
|
1166
|
+
* resolved, so an unselected branch's prerequisites are never
|
|
1167
|
+
* demanded on the strength of a static estimate;
|
|
1168
|
+
* - when a source a barrier can provide is demanded, the barrier's
|
|
1169
|
+
* control dependency (its discriminator source) becomes demanded.
|
|
1170
|
+
*
|
|
1171
|
+
* One barrier's branches can provide another barrier's discriminator
|
|
1172
|
+
* source, and the chain must propagate regardless of declaration order.
|
|
1173
|
+
* Each iteration adds at least one demanded ID, so the loop terminates.
|
|
1174
|
+
* The scheduler re-runs this after a gated barrier resolves, passing
|
|
1175
|
+
* the pass's metadata universe with resolved replacement nodes swapped
|
|
1176
|
+
* in, so promotion chains through already-visited metadata as well.
|
|
1177
|
+
*/
|
|
1178
|
+
function propagateDemand(universe, session) {
|
|
1179
|
+
let demandAdded = true;
|
|
1180
|
+
while (demandAdded) {
|
|
1181
|
+
demandAdded = false;
|
|
1182
|
+
for (const node of universe) {
|
|
1183
|
+
const metadata = node.parser.dependencyMetadata;
|
|
1184
|
+
if (metadata?.source != null && metadata.derived != null && session.demanded.has(metadata.source.sourceId)) for (const dependencySourceId of metadata.derived.dependencyIds) {
|
|
1185
|
+
if (session.demanded.has(dependencySourceId)) continue;
|
|
1186
|
+
session.demanded.add(dependencySourceId);
|
|
1187
|
+
demandAdded = true;
|
|
1188
|
+
}
|
|
1189
|
+
if (metadata?.source != null && metadata.completion != null && session.demanded.has(metadata.source.sourceId) && !hasInactiveCompletion(node)) for (const dependencySourceId of metadata.completion.dependencyIds) {
|
|
1190
|
+
if (session.demanded.has(dependencySourceId)) continue;
|
|
1191
|
+
session.demanded.add(dependencySourceId);
|
|
1192
|
+
demandAdded = true;
|
|
1193
|
+
}
|
|
1194
|
+
if (node.barrierCompletionDependencies != null && (node.barrierDemandActivation !== "after-resolution" || node.barrierResolutionState === "resolved")) for (const edge of node.barrierCompletionDependencies.demandEdges) {
|
|
1195
|
+
if (!session.demanded.has(edge.consumerSourceId)) continue;
|
|
1196
|
+
for (const dependencySourceId of edge.dependencyIds) {
|
|
1197
|
+
if (session.demanded.has(dependencySourceId)) continue;
|
|
1198
|
+
session.demanded.add(dependencySourceId);
|
|
1199
|
+
demandAdded = true;
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
if (node.requiresSourceId == null || node.providesSourceIds == null) continue;
|
|
1203
|
+
if (session.demanded.has(node.requiresSourceId)) continue;
|
|
1204
|
+
for (const provided of node.providesSourceIds) if (session.demanded.has(provided)) {
|
|
1205
|
+
session.demanded.add(node.requiresSourceId);
|
|
1206
|
+
demandAdded = true;
|
|
1207
|
+
break;
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
const barrierFailureDescription = "optique.barrierFailure";
|
|
1213
|
+
/**
|
|
1214
|
+
* Replaces a barrier failure cached in the run-scoped session with a
|
|
1215
|
+
* wrapped version of the same failure, identified by object identity.
|
|
1216
|
+
* A construct that decorates a nested barrier failure on its way out—a
|
|
1217
|
+
* `conditional()` applying its `branchError` hook—uses this so later
|
|
1218
|
+
* passes sharing the session (the eager fallback after a failed seed
|
|
1219
|
+
* pass) surface the wrapped error rather than the raw one.
|
|
1220
|
+
*
|
|
1221
|
+
* @param session The run-scoped effectful completion session.
|
|
1222
|
+
* @param original The failure exactly as the scheduling pass returned
|
|
1223
|
+
* it.
|
|
1224
|
+
* @param replacement The decorated failure to cache in its place.
|
|
1225
|
+
* @returns Whether a cached barrier failure was found and replaced;
|
|
1226
|
+
* `false` means the failure did not come from a barrier resolution
|
|
1227
|
+
* and should propagate untouched.
|
|
1228
|
+
* @internal
|
|
1229
|
+
* @since 1.3.0
|
|
1230
|
+
*/
|
|
1231
|
+
function replaceCachedBarrierFailure(session, original, replacement) {
|
|
1232
|
+
if (session == null) return false;
|
|
1233
|
+
let replaced = false;
|
|
1234
|
+
for (const [key, value] of session.results) if (key.description === barrierFailureDescription && value === original) {
|
|
1235
|
+
session.results.set(key, replacement);
|
|
1236
|
+
replaced = true;
|
|
1237
|
+
}
|
|
1238
|
+
return replaced;
|
|
1239
|
+
}
|
|
1240
|
+
/**
|
|
1241
|
+
* Whether the node is a scheduling barrier whose demand edges are gated
|
|
1242
|
+
* on resolution and whose resolution has not happened yet. While any
|
|
1243
|
+
* such barrier is pending in a demand-only pass, the scheduler parks
|
|
1244
|
+
* not-yet-demanded effectful nodes instead of letting them defer, so a
|
|
1245
|
+
* later resolution can still demand them into their dependency slot.
|
|
1246
|
+
*/
|
|
1247
|
+
function isGatedUnresolvedBarrier(node) {
|
|
1248
|
+
return node.resolveBarrier != null && node.barrierDemandActivation === "after-resolution" && node.barrierResolutionState == null;
|
|
1249
|
+
}
|
|
1250
|
+
/**
|
|
1251
|
+
* Collects the nodes a demand-only pass must not visit yet while a
|
|
1252
|
+
* gated barrier is unresolved: every effectful source node whose own
|
|
1253
|
+
* source is not demanded (its completion would defer irrevocably for
|
|
1254
|
+
* this pass, yet a barrier's resolution may still demand it), plus
|
|
1255
|
+
* every node reachable from a parked node along hard (non-advisory)
|
|
1256
|
+
* dependency edges, so a dependent—a derived replay, a barrier whose
|
|
1257
|
+
* control dependency is a parked discriminator—never runs ahead of a
|
|
1258
|
+
* parked provider. The scheduling loop's all-blocked fallback keeps
|
|
1259
|
+
* the pass progressing even when this parks every pending node.
|
|
1260
|
+
*/
|
|
1261
|
+
function collectParkedNodes(pending, session) {
|
|
1262
|
+
const parked = /* @__PURE__ */ new Set();
|
|
1263
|
+
for (const node of pending) {
|
|
1264
|
+
const source = node.parser.dependencyMetadata?.source;
|
|
1265
|
+
if (source?.completeSource != null && !session.demanded.has(source.sourceId)) parked.add(node);
|
|
1266
|
+
}
|
|
1267
|
+
if (parked.size === 0) return parked;
|
|
1268
|
+
const graph = buildDependencyGraph(pending);
|
|
1269
|
+
const queue = [...parked];
|
|
1270
|
+
while (queue.length > 0) {
|
|
1271
|
+
const provider = queue.pop();
|
|
1272
|
+
for (const [consumer, advisoryOnly] of graph.outgoing.get(provider) ?? []) {
|
|
1273
|
+
if (advisoryOnly || parked.has(consumer)) continue;
|
|
1274
|
+
parked.add(consumer);
|
|
1275
|
+
queue.push(consumer);
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
return parked;
|
|
1279
|
+
}
|
|
1280
|
+
/**
|
|
1154
1281
|
* Runs effectful source completions (e.g., interactive prompts) serially
|
|
1155
1282
|
* in declaration order for source nodes whose value is not yet registered.
|
|
1156
1283
|
*
|
|
@@ -1207,38 +1334,7 @@ async function completeEffectfulSourcesAsync(nodes, state, runtime, exec, option
|
|
|
1207
1334
|
const demandNodes = options?.demandNodes ?? nodes;
|
|
1208
1335
|
const demanded = collectDemandedDependencyIds(demandNodes, state, exec.trace);
|
|
1209
1336
|
for (const id of demanded) session.demanded.add(id);
|
|
1210
|
-
|
|
1211
|
-
while (demandAdded) {
|
|
1212
|
-
demandAdded = false;
|
|
1213
|
-
for (const node of nodes) {
|
|
1214
|
-
const metadata = node.parser.dependencyMetadata;
|
|
1215
|
-
if (metadata?.source != null && metadata.derived != null && session.demanded.has(metadata.source.sourceId)) for (const dependencySourceId of metadata.derived.dependencyIds) {
|
|
1216
|
-
if (session.demanded.has(dependencySourceId)) continue;
|
|
1217
|
-
session.demanded.add(dependencySourceId);
|
|
1218
|
-
demandAdded = true;
|
|
1219
|
-
}
|
|
1220
|
-
if (metadata?.source != null && metadata.completion != null && session.demanded.has(metadata.source.sourceId) && !hasInactiveCompletion(node)) for (const dependencySourceId of metadata.completion.dependencyIds) {
|
|
1221
|
-
if (session.demanded.has(dependencySourceId)) continue;
|
|
1222
|
-
session.demanded.add(dependencySourceId);
|
|
1223
|
-
demandAdded = true;
|
|
1224
|
-
}
|
|
1225
|
-
if (node.barrierCompletionDependencies != null) for (const edge of node.barrierCompletionDependencies.demandEdges) {
|
|
1226
|
-
if (!session.demanded.has(edge.consumerSourceId)) continue;
|
|
1227
|
-
for (const dependencySourceId of edge.dependencyIds) {
|
|
1228
|
-
if (session.demanded.has(dependencySourceId)) continue;
|
|
1229
|
-
session.demanded.add(dependencySourceId);
|
|
1230
|
-
demandAdded = true;
|
|
1231
|
-
}
|
|
1232
|
-
}
|
|
1233
|
-
if (node.requiresSourceId == null || node.providesSourceIds == null) continue;
|
|
1234
|
-
if (session.demanded.has(node.requiresSourceId)) continue;
|
|
1235
|
-
for (const provided of node.providesSourceIds) if (session.demanded.has(provided)) {
|
|
1236
|
-
session.demanded.add(node.requiresSourceId);
|
|
1237
|
-
demandAdded = true;
|
|
1238
|
-
break;
|
|
1239
|
-
}
|
|
1240
|
-
}
|
|
1241
|
-
}
|
|
1337
|
+
propagateDemand(nodes, session);
|
|
1242
1338
|
}
|
|
1243
1339
|
if (session != null) {
|
|
1244
1340
|
for (const result of session.results.values()) if (!result.success) return {
|
|
@@ -1259,12 +1355,17 @@ async function completeEffectfulSourcesAsync(nodes, state, runtime, exec, option
|
|
|
1259
1355
|
}).then((result) => result.success ? void 0 : result)
|
|
1260
1356
|
};
|
|
1261
1357
|
const pending = [...nodes];
|
|
1358
|
+
const metadataUniverse = [...nodes];
|
|
1262
1359
|
const providesId = (node, id) => node.parser.dependencyMetadata?.source?.sourceId === id || node.providesSourceIds?.has(id) === true;
|
|
1360
|
+
const demandSession = session?.policy === "demand-only" ? session : void 0;
|
|
1263
1361
|
const resolveBarrierAt = async (index) => {
|
|
1264
1362
|
const candidate = pending[index];
|
|
1265
1363
|
const resolution = await candidate.resolveBarrier(barrierContext);
|
|
1266
|
-
if (resolution != null && "success" in resolution)
|
|
1267
|
-
|
|
1364
|
+
if (resolution != null && "success" in resolution) {
|
|
1365
|
+
session?.results.set(Symbol(barrierFailureDescription), resolution);
|
|
1366
|
+
return resolution;
|
|
1367
|
+
}
|
|
1368
|
+
const replacement = resolution == null ? {
|
|
1268
1369
|
...candidate,
|
|
1269
1370
|
barrierResolutionState: "unresolvable"
|
|
1270
1371
|
} : {
|
|
@@ -1273,22 +1374,44 @@ async function completeEffectfulSourcesAsync(nodes, state, runtime, exec, option
|
|
|
1273
1374
|
providesSourceIds: resolution.activeProvidesSourceIds,
|
|
1274
1375
|
barrierCompletionDependencies: {
|
|
1275
1376
|
orderingDependencyIds: resolution.orderingDependencyIds,
|
|
1276
|
-
demandEdges:
|
|
1377
|
+
demandEdges: resolution.demandEdges
|
|
1277
1378
|
}
|
|
1278
1379
|
};
|
|
1380
|
+
pending[index] = replacement;
|
|
1381
|
+
const universeIndex = metadataUniverse.indexOf(candidate);
|
|
1382
|
+
if (universeIndex >= 0) metadataUniverse[universeIndex] = replacement;
|
|
1383
|
+
if (resolution != null && demandSession != null) propagateDemand(metadataUniverse, demandSession);
|
|
1279
1384
|
return void 0;
|
|
1280
1385
|
};
|
|
1281
1386
|
while (pending.length > 0) {
|
|
1387
|
+
const parkingActive = demandSession != null && pending.some(isGatedUnresolvedBarrier);
|
|
1282
1388
|
for (let i = 0; i < pending.length; i++) {
|
|
1283
1389
|
const candidate = pending[i];
|
|
1284
1390
|
if (candidate.resolveBarrier == null || candidate.barrierResolutionState != null) continue;
|
|
1285
1391
|
const requires = candidate.requiresSourceId;
|
|
1286
1392
|
if (requires == null) continue;
|
|
1393
|
+
if (parkingActive && demandSession != null && !demandSession.demanded.has(requires)) continue;
|
|
1287
1394
|
if (pending.some((other) => other !== candidate && providesId(other, requires))) continue;
|
|
1288
1395
|
const failure = await resolveBarrierAt(i);
|
|
1289
1396
|
if (failure != null) return failure;
|
|
1290
1397
|
}
|
|
1291
|
-
const
|
|
1398
|
+
const ordered = orderDependencyNodes(pending);
|
|
1399
|
+
let node = ordered[0];
|
|
1400
|
+
const parkingStillActive = demandSession != null && pending.some(isGatedUnresolvedBarrier);
|
|
1401
|
+
if (parkingStillActive && demandSession != null) {
|
|
1402
|
+
const parked = collectParkedNodes(pending, demandSession);
|
|
1403
|
+
const isSkippedBarrier = (candidate) => candidate.resolveBarrier != null && candidate.barrierResolutionState == null && candidate.requiresSourceId != null && !demandSession.demanded.has(candidate.requiresSourceId);
|
|
1404
|
+
const selectable = ordered.find((candidate) => !parked.has(candidate) && !isSkippedBarrier(candidate));
|
|
1405
|
+
if (selectable != null) node = selectable;
|
|
1406
|
+
else {
|
|
1407
|
+
const stalled = ordered.find((candidate) => candidate.resolveBarrier != null && candidate.barrierResolutionState == null);
|
|
1408
|
+
if (stalled != null) {
|
|
1409
|
+
const failure = await resolveBarrierAt(pending.indexOf(stalled));
|
|
1410
|
+
if (failure != null) return failure;
|
|
1411
|
+
continue;
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1292
1415
|
if (node.resolveBarrier != null && node.barrierResolutionState == null) {
|
|
1293
1416
|
const failure = await resolveBarrierAt(pending.indexOf(node));
|
|
1294
1417
|
if (failure != null) return failure;
|
|
@@ -1681,4 +1804,4 @@ function getDefaultDependencySnapshotInner(state, visited) {
|
|
|
1681
1804
|
}
|
|
1682
1805
|
|
|
1683
1806
|
//#endregion
|
|
1684
|
-
export { buildRuntimeNodesFromArray, buildRuntimeNodesFromPairs, collectDemandedDependencyIds, collectExplicitSourceValues, collectExplicitSourceValuesAsync, collectSourcesFromState, completeEffectfulSourcesAsync, createDependencyFingerprint, createDependencyRuntimeContext, createReplayKey, defineForwardedEffectfulSchedulingNodes, derivedRawInputKey, effectfulSchedulingNodesKey, exclusiveSourceScopeKey, extractRawInputFromState, fillMissingSourceDefaults, fillMissingSourceDefaultsAsync, guaranteedStaticSourceIdsKey, hasInactiveCompletion, includeSourceFailureChain, orderDependencyNodes, recordDerivedRawInput, replayDerivedParser, replayDerivedParserAsync, resolveDerivedSourceValues, resolveDerivedSourceValuesAsync, resolveStateWithRuntime, resolveStateWithRuntimeAsync, serializeSchedulingPath, sourceCollectionExpansionKey, staticSourceScopeKey };
|
|
1807
|
+
export { buildRuntimeNodesFromArray, buildRuntimeNodesFromPairs, collectDemandedDependencyIds, collectExplicitSourceValues, collectExplicitSourceValuesAsync, collectSourcesFromState, completeEffectfulSourcesAsync, createDependencyFingerprint, createDependencyRuntimeContext, createReplayKey, defineForwardedEffectfulSchedulingNodes, derivedRawInputKey, effectfulSchedulingNodesKey, exclusiveSourceScopeKey, extractRawInputFromState, fillMissingSourceDefaults, fillMissingSourceDefaultsAsync, guaranteedStaticSourceIdsKey, hasInactiveCompletion, includeSourceFailureChain, orderDependencyNodes, recordDerivedRawInput, replaceCachedBarrierFailure, 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.
|
|
3
|
+
"version": "1.3.0-dev.2434",
|
|
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.
|
|
224
|
+
"@optique/env": "1.3.0-dev.2434+6eaf6d6e",
|
|
225
225
|
"@types/node": "^24.0.0",
|
|
226
226
|
"fast-check": "^4.7.0",
|
|
227
227
|
"tsdown": "^0.13.0",
|