@optique/core 1.3.0-dev.2430 → 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.
- package/dist/constructs.cjs +238 -88
- package/dist/constructs.js +239 -89
- package/dist/dependency-metadata.cjs +1 -0
- package/dist/dependency-metadata.d.cts +15 -0
- package/dist/dependency-metadata.d.ts +15 -0
- package/dist/dependency-metadata.js +1 -0
- package/dist/dependency-runtime.cjs +250 -33
- package/dist/dependency-runtime.d.cts +92 -1
- package/dist/dependency-runtime.d.ts +92 -1
- package/dist/dependency-runtime.js +249 -34
- package/dist/internal/parser.cjs +2 -1
- package/dist/internal/parser.d.cts +16 -0
- package/dist/internal/parser.d.ts +16 -0
- package/dist/internal/parser.js +2 -1
- package/dist/primitives.cjs +1 -1
- package/dist/primitives.js +2 -2
- package/package.json +2 -2
|
@@ -404,6 +404,17 @@ async function collectExplicitSourceValuesAsync(nodes, runtime) {
|
|
|
404
404
|
* @since 1.3.0
|
|
405
405
|
*/
|
|
406
406
|
function orderDependencyNodes(nodes) {
|
|
407
|
+
return drainDependencyGraph(buildDependencyGraph(nodes));
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Builds the provider-edge graph over runtime nodes.
|
|
411
|
+
*
|
|
412
|
+
* Scheduling barriers act as providers for the source IDs their
|
|
413
|
+
* selected subtree may expose and depend on their discriminator source
|
|
414
|
+
* as well as on outer providers of the completion dependencies their
|
|
415
|
+
* selectable subtrees aggregate.
|
|
416
|
+
*/
|
|
417
|
+
function buildDependencyGraph(nodes) {
|
|
407
418
|
const providers = /* @__PURE__ */ new Map();
|
|
408
419
|
const addProvider = (sourceId, node) => {
|
|
409
420
|
const existing = providers.get(sourceId);
|
|
@@ -418,51 +429,162 @@ function orderDependencyNodes(nodes) {
|
|
|
418
429
|
const outgoing = /* @__PURE__ */ new Map();
|
|
419
430
|
const indegree = /* @__PURE__ */ new Map();
|
|
420
431
|
for (const node of nodes) indegree.set(node, 0);
|
|
421
|
-
const addEdge = (provider, consumer) => {
|
|
422
|
-
const edges = outgoing.get(provider) ?? /* @__PURE__ */ new
|
|
423
|
-
|
|
424
|
-
|
|
432
|
+
const addEdge = (provider, consumer, advisory) => {
|
|
433
|
+
const edges = outgoing.get(provider) ?? /* @__PURE__ */ new Map();
|
|
434
|
+
const existing = edges.get(consumer);
|
|
435
|
+
if (existing != null) {
|
|
436
|
+
if (existing && !advisory) edges.set(consumer, false);
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
439
|
+
edges.set(consumer, advisory);
|
|
425
440
|
outgoing.set(provider, edges);
|
|
426
441
|
indegree.set(consumer, (indegree.get(consumer) ?? 0) + 1);
|
|
427
442
|
};
|
|
428
443
|
for (const node of nodes) {
|
|
429
444
|
const derived = node.parser.dependencyMetadata?.derived;
|
|
430
|
-
if (derived != null) for (const dependencySourceId of derived.dependencyIds) for (const provider of providers.get(dependencySourceId) ?? []) addEdge(provider, node);
|
|
445
|
+
if (derived != null) for (const dependencySourceId of derived.dependencyIds) for (const provider of providers.get(dependencySourceId) ?? []) addEdge(provider, node, false);
|
|
431
446
|
const completion = node.parser.dependencyMetadata?.completion;
|
|
432
447
|
if (completion != null && !hasInactiveCompletion(node)) {
|
|
433
448
|
const ownSourceId = node.parser.dependencyMetadata?.source?.sourceId;
|
|
434
449
|
for (const dependencySourceId of completion.dependencyIds) {
|
|
435
450
|
if (dependencySourceId === ownSourceId) continue;
|
|
436
|
-
for (const provider of providers.get(dependencySourceId) ?? []) if (provider !== node) addEdge(provider, node);
|
|
451
|
+
for (const provider of providers.get(dependencySourceId) ?? []) if (provider !== node) addEdge(provider, node, false);
|
|
437
452
|
}
|
|
438
453
|
}
|
|
439
454
|
if (node.requiresSourceId != null) {
|
|
440
|
-
for (const provider of providers.get(node.requiresSourceId) ?? []) if (provider !== node) addEdge(provider, node);
|
|
455
|
+
for (const provider of providers.get(node.requiresSourceId) ?? []) if (provider !== node) addEdge(provider, node, false);
|
|
441
456
|
}
|
|
442
457
|
const barrierDeps = node.barrierCompletionDependencies;
|
|
443
458
|
if (barrierDeps != null) {
|
|
444
|
-
|
|
459
|
+
const advisory = node.barrierResolutionState !== "resolved";
|
|
460
|
+
for (const dependencySourceId of barrierDeps.orderingDependencyIds) for (const provider of providers.get(dependencySourceId) ?? []) if (provider !== node) addEdge(provider, node, advisory);
|
|
445
461
|
}
|
|
446
462
|
}
|
|
447
463
|
const declarationOrder = new Map(nodes.map((node, index) => [node, index]));
|
|
464
|
+
return {
|
|
465
|
+
nodes,
|
|
466
|
+
outgoing,
|
|
467
|
+
indegree,
|
|
468
|
+
declarationOrder
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
* Drains the graph in stable topological order.
|
|
473
|
+
*
|
|
474
|
+
* A stall—no node with indegree zero remains—normally means a genuine
|
|
475
|
+
* cycle. When the stalled region contains an unresolved barrier's
|
|
476
|
+
* advisory ordering edges within one strongly connected component, the
|
|
477
|
+
* apparent cycle aggregates edges from branches that cannot be selected
|
|
478
|
+
* together, so those advisory edges are relaxed and the drain resumes;
|
|
479
|
+
* the barrier's own resolution later replaces the estimate with
|
|
480
|
+
* concrete edges, and a cycle among those still throws.
|
|
481
|
+
*
|
|
482
|
+
* @throws {TypeError} If active provider edges contain a cycle.
|
|
483
|
+
*/
|
|
484
|
+
function drainDependencyGraph(graph) {
|
|
485
|
+
const { nodes, outgoing, declarationOrder } = graph;
|
|
486
|
+
const indegree = new Map(graph.indegree);
|
|
448
487
|
const ready = nodes.filter((node) => indegree.get(node) === 0);
|
|
449
488
|
const ordered = [];
|
|
450
|
-
while (
|
|
451
|
-
ready.
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
const
|
|
456
|
-
|
|
457
|
-
|
|
489
|
+
while (true) {
|
|
490
|
+
while (ready.length > 0) {
|
|
491
|
+
ready.sort((left, right) => declarationOrder.get(left) - declarationOrder.get(right));
|
|
492
|
+
const node = ready.shift();
|
|
493
|
+
ordered.push(node);
|
|
494
|
+
for (const consumer of outgoing.get(node)?.keys() ?? []) {
|
|
495
|
+
const next = (indegree.get(consumer) ?? 0) - 1;
|
|
496
|
+
indegree.set(consumer, next);
|
|
497
|
+
if (next === 0) ready.push(consumer);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
if (ordered.length === nodes.length) return ordered;
|
|
501
|
+
const residual = nodes.filter((node) => (indegree.get(node) ?? 0) > 0);
|
|
502
|
+
const sccOf = findStronglyConnected(residual, outgoing);
|
|
503
|
+
let relaxed = false;
|
|
504
|
+
for (const provider of residual) {
|
|
505
|
+
const edges = outgoing.get(provider);
|
|
506
|
+
if (edges == null) continue;
|
|
507
|
+
for (const [consumer, advisory] of edges) {
|
|
508
|
+
if (!advisory) continue;
|
|
509
|
+
const providerScc = sccOf.get(provider);
|
|
510
|
+
if (providerScc == null || providerScc !== sccOf.get(consumer)) continue;
|
|
511
|
+
edges.delete(consumer);
|
|
512
|
+
const next = (indegree.get(consumer) ?? 0) - 1;
|
|
513
|
+
indegree.set(consumer, next);
|
|
514
|
+
if (next === 0) ready.push(consumer);
|
|
515
|
+
relaxed = true;
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
if (!relaxed) {
|
|
519
|
+
const labels = residual.map(formatDependencyNodeLabel).join(" -> ");
|
|
520
|
+
throw new TypeError(`Circular dependency detected among dependency sources: ${labels}.`);
|
|
458
521
|
}
|
|
459
522
|
}
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* Assigns each node in `subset` to a strongly connected component of
|
|
526
|
+
* the induced subgraph. Only components with more than one node are
|
|
527
|
+
* recorded—an acyclic node cannot be part of an apparent cycle.
|
|
528
|
+
*/
|
|
529
|
+
function findStronglyConnected(subset, outgoing) {
|
|
530
|
+
const inSubset = new Set(subset);
|
|
531
|
+
const index = /* @__PURE__ */ new Map();
|
|
532
|
+
const lowLink = /* @__PURE__ */ new Map();
|
|
533
|
+
const onStack = /* @__PURE__ */ new Set();
|
|
534
|
+
const stack = [];
|
|
535
|
+
const sccOf = /* @__PURE__ */ new Map();
|
|
536
|
+
let nextIndex = 0;
|
|
537
|
+
let nextScc = 0;
|
|
538
|
+
const strongConnect = (root) => {
|
|
539
|
+
const frames = [{
|
|
540
|
+
node: root,
|
|
541
|
+
children: [...outgoing.get(root)?.keys() ?? []].filter((child) => inSubset.has(child)),
|
|
542
|
+
i: 0
|
|
543
|
+
}];
|
|
544
|
+
index.set(root, nextIndex);
|
|
545
|
+
lowLink.set(root, nextIndex);
|
|
546
|
+
nextIndex++;
|
|
547
|
+
stack.push(root);
|
|
548
|
+
onStack.add(root);
|
|
549
|
+
while (frames.length > 0) {
|
|
550
|
+
const frame = frames[frames.length - 1];
|
|
551
|
+
if (frame.i < frame.children.length) {
|
|
552
|
+
const child = frame.children[frame.i++];
|
|
553
|
+
if (!index.has(child)) {
|
|
554
|
+
index.set(child, nextIndex);
|
|
555
|
+
lowLink.set(child, nextIndex);
|
|
556
|
+
nextIndex++;
|
|
557
|
+
stack.push(child);
|
|
558
|
+
onStack.add(child);
|
|
559
|
+
frames.push({
|
|
560
|
+
node: child,
|
|
561
|
+
children: [...outgoing.get(child)?.keys() ?? []].filter((c) => inSubset.has(c)),
|
|
562
|
+
i: 0
|
|
563
|
+
});
|
|
564
|
+
} else if (onStack.has(child)) lowLink.set(frame.node, Math.min(lowLink.get(frame.node), index.get(child)));
|
|
565
|
+
continue;
|
|
566
|
+
}
|
|
567
|
+
frames.pop();
|
|
568
|
+
const node = frame.node;
|
|
569
|
+
if (lowLink.get(node) === index.get(node)) {
|
|
570
|
+
const members = [];
|
|
571
|
+
while (true) {
|
|
572
|
+
const member = stack.pop();
|
|
573
|
+
onStack.delete(member);
|
|
574
|
+
members.push(member);
|
|
575
|
+
if (member === node) break;
|
|
576
|
+
}
|
|
577
|
+
if (members.length > 1) {
|
|
578
|
+
for (const member of members) sccOf.set(member, nextScc);
|
|
579
|
+
nextScc++;
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
const parent = frames[frames.length - 1];
|
|
583
|
+
if (parent != null) lowLink.set(parent.node, Math.min(lowLink.get(parent.node), lowLink.get(node)));
|
|
584
|
+
}
|
|
585
|
+
};
|
|
586
|
+
for (const node of subset) if (!index.has(node)) strongConnect(node);
|
|
587
|
+
return sccOf;
|
|
466
588
|
}
|
|
467
589
|
/** Resolves and publishes matched derived sources in stable dependency order. */
|
|
468
590
|
function resolveDerivedSourceValues(nodes, runtime, options) {
|
|
@@ -921,6 +1043,35 @@ const sourceCollectionExpansionKey = Symbol("@optique/core/dependency-runtime/so
|
|
|
921
1043
|
*/
|
|
922
1044
|
const staticSourceScopeKey = Symbol("@optique/core/dependency-runtime/staticSourceScope");
|
|
923
1045
|
/**
|
|
1046
|
+
* Static child parsers reachable only through a mutually exclusive
|
|
1047
|
+
* selection, such as `or()` alternatives, a `conditional()`'s branches,
|
|
1048
|
+
* or a `command()`'s inner parser.
|
|
1049
|
+
*
|
|
1050
|
+
* The walk unions sources and completion dependencies from this scope
|
|
1051
|
+
* exactly like {@link staticSourceScopeKey}, but a source occurrence
|
|
1052
|
+
* reached through an exclusive edge is never a *guaranteed* provider:
|
|
1053
|
+
* the run may select a sibling instead, so a scheduling barrier must not
|
|
1054
|
+
* assume the occurrence will publish. See issue #924.
|
|
1055
|
+
*
|
|
1056
|
+
* @internal
|
|
1057
|
+
* @since 1.3.0
|
|
1058
|
+
*/
|
|
1059
|
+
const exclusiveSourceScopeKey = Symbol("@optique/core/dependency-runtime/exclusiveSourceScope");
|
|
1060
|
+
/**
|
|
1061
|
+
* Source IDs a parser guarantees to provide on every successful route
|
|
1062
|
+
* through it, regardless of which internal alternative is selected.
|
|
1063
|
+
*
|
|
1064
|
+
* A `conditional()` installs the intersection of its branches' guaranteed
|
|
1065
|
+
* provides here (including the default branch when present), so an outer
|
|
1066
|
+
* scheduling barrier can treat a source that *every* selectable route
|
|
1067
|
+
* publishes as guaranteed even though each individual branch edge is
|
|
1068
|
+
* exclusive. See issue #924.
|
|
1069
|
+
*
|
|
1070
|
+
* @internal
|
|
1071
|
+
* @since 1.3.0
|
|
1072
|
+
*/
|
|
1073
|
+
const guaranteedStaticSourceIdsKey = Symbol("@optique/core/dependency-runtime/guaranteedStaticSourceIds");
|
|
1074
|
+
/**
|
|
924
1075
|
* Forwards effectful scheduling through a shape-preserving wrapper such
|
|
925
1076
|
* as `map()`, `optional()`, `withDefault()`, or `nonEmpty()`, so the
|
|
926
1077
|
* wrapped parser—a selected exclusive or command branch, or an ordinary
|
|
@@ -1098,17 +1249,54 @@ async function completeEffectfulSourcesAsync(nodes, state, runtime, exec, option
|
|
|
1098
1249
|
const schedulable = nodes.filter((node) => node.parser.dependencyMetadata?.source?.completeSource != null || node.prepare != null);
|
|
1099
1250
|
if (schedulable.length === 0 && options?.includeStructural !== true) return empty;
|
|
1100
1251
|
const completed = [];
|
|
1101
|
-
|
|
1252
|
+
const barrierContext = {
|
|
1253
|
+
runtime,
|
|
1254
|
+
exec,
|
|
1255
|
+
schedule: (barrierNodes) => completeEffectfulSourcesAsync(barrierNodes, state, runtime, exec, {
|
|
1256
|
+
isReusable: () => false,
|
|
1257
|
+
isCollected: () => true,
|
|
1258
|
+
includeStructural: true
|
|
1259
|
+
}).then((result) => result.success ? void 0 : result)
|
|
1260
|
+
};
|
|
1261
|
+
const pending = [...nodes];
|
|
1262
|
+
const providesId = (node, id) => node.parser.dependencyMetadata?.source?.sourceId === id || node.providesSourceIds?.has(id) === true;
|
|
1263
|
+
const resolveBarrierAt = async (index) => {
|
|
1264
|
+
const candidate = pending[index];
|
|
1265
|
+
const resolution = await candidate.resolveBarrier(barrierContext);
|
|
1266
|
+
if (resolution != null && "success" in resolution) return resolution;
|
|
1267
|
+
pending[index] = resolution == null ? {
|
|
1268
|
+
...candidate,
|
|
1269
|
+
barrierResolutionState: "unresolvable"
|
|
1270
|
+
} : {
|
|
1271
|
+
...candidate,
|
|
1272
|
+
barrierResolutionState: "resolved",
|
|
1273
|
+
providesSourceIds: resolution.activeProvidesSourceIds,
|
|
1274
|
+
barrierCompletionDependencies: {
|
|
1275
|
+
orderingDependencyIds: resolution.orderingDependencyIds,
|
|
1276
|
+
demandEdges: []
|
|
1277
|
+
}
|
|
1278
|
+
};
|
|
1279
|
+
return void 0;
|
|
1280
|
+
};
|
|
1281
|
+
while (pending.length > 0) {
|
|
1282
|
+
for (let i = 0; i < pending.length; i++) {
|
|
1283
|
+
const candidate = pending[i];
|
|
1284
|
+
if (candidate.resolveBarrier == null || candidate.barrierResolutionState != null) continue;
|
|
1285
|
+
const requires = candidate.requiresSourceId;
|
|
1286
|
+
if (requires == null) continue;
|
|
1287
|
+
if (pending.some((other) => other !== candidate && providesId(other, requires))) continue;
|
|
1288
|
+
const failure = await resolveBarrierAt(i);
|
|
1289
|
+
if (failure != null) return failure;
|
|
1290
|
+
}
|
|
1291
|
+
const node = orderDependencyNodes(pending)[0];
|
|
1292
|
+
if (node.resolveBarrier != null && node.barrierResolutionState == null) {
|
|
1293
|
+
const failure = await resolveBarrierAt(pending.indexOf(node));
|
|
1294
|
+
if (failure != null) return failure;
|
|
1295
|
+
continue;
|
|
1296
|
+
}
|
|
1297
|
+
pending.splice(pending.indexOf(node), 1);
|
|
1102
1298
|
if (node.prepare != null) {
|
|
1103
|
-
const barrierFailure = await node.prepare(
|
|
1104
|
-
runtime,
|
|
1105
|
-
exec,
|
|
1106
|
-
schedule: (barrierNodes) => completeEffectfulSourcesAsync(barrierNodes, state, runtime, exec, {
|
|
1107
|
-
isReusable: () => false,
|
|
1108
|
-
isCollected: () => true,
|
|
1109
|
-
includeStructural: true
|
|
1110
|
-
}).then((result$1) => result$1.success ? void 0 : result$1)
|
|
1111
|
-
});
|
|
1299
|
+
const barrierFailure = await node.prepare(barrierContext);
|
|
1112
1300
|
if (barrierFailure != null) return barrierFailure;
|
|
1113
1301
|
continue;
|
|
1114
1302
|
}
|
|
@@ -1140,7 +1328,34 @@ async function completeEffectfulSourcesAsync(nodes, state, runtime, exec, option
|
|
|
1140
1328
|
const collected = options?.isCollected?.(node) ?? options?.isReusable?.(node) ?? true;
|
|
1141
1329
|
if (source.extractSourceValue == null || collected === false) continue;
|
|
1142
1330
|
const extracted = await source.extractSourceValue(node.state);
|
|
1143
|
-
if (extracted?.success === true && extracted.value !== void 0)
|
|
1331
|
+
if (extracted?.success === true && extracted.value !== void 0) {
|
|
1332
|
+
runtime.registerSource(source.sourceId, extracted.value);
|
|
1333
|
+
continue;
|
|
1334
|
+
}
|
|
1335
|
+
if (options?.includeStructural === true && extracted?.success !== false && node.matched !== true && source.preservesSourceValue && source.getMissingSourceValue != null && !runtime.hasSource(source.sourceId) && !runtime.isSourceFailed(source.sourceId)) {
|
|
1336
|
+
let fallback = session?.missingDefaultsByOccurrence.get(source.getMissingSourceValue);
|
|
1337
|
+
if (fallback == null) {
|
|
1338
|
+
try {
|
|
1339
|
+
fallback = await source.getMissingSourceValue();
|
|
1340
|
+
} catch (e) {
|
|
1341
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
1342
|
+
fallback = {
|
|
1343
|
+
success: false,
|
|
1344
|
+
error: require_message.message`Default value evaluation failed: ${msg}`
|
|
1345
|
+
};
|
|
1346
|
+
}
|
|
1347
|
+
session?.missingDefaultsByOccurrence.set(source.getMissingSourceValue, fallback);
|
|
1348
|
+
}
|
|
1349
|
+
if (fallback.success) runtime.registerSource(source.sourceId, fallback.value);
|
|
1350
|
+
else {
|
|
1351
|
+
runtime.markSourceFailed(source.sourceId);
|
|
1352
|
+
propagateRuntimeSourceFailures(nodes, runtime);
|
|
1353
|
+
return {
|
|
1354
|
+
success: false,
|
|
1355
|
+
error: includeSourceFailureChain(fallback.error, source.sourceId, runtime)
|
|
1356
|
+
};
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1144
1359
|
continue;
|
|
1145
1360
|
}
|
|
1146
1361
|
const pathKey = serializeSchedulingPath(node.path);
|
|
@@ -1479,9 +1694,11 @@ exports.createReplayKey = createReplayKey;
|
|
|
1479
1694
|
exports.defineForwardedEffectfulSchedulingNodes = defineForwardedEffectfulSchedulingNodes;
|
|
1480
1695
|
exports.derivedRawInputKey = derivedRawInputKey;
|
|
1481
1696
|
exports.effectfulSchedulingNodesKey = effectfulSchedulingNodesKey;
|
|
1697
|
+
exports.exclusiveSourceScopeKey = exclusiveSourceScopeKey;
|
|
1482
1698
|
exports.extractRawInputFromState = extractRawInputFromState;
|
|
1483
1699
|
exports.fillMissingSourceDefaults = fillMissingSourceDefaults;
|
|
1484
1700
|
exports.fillMissingSourceDefaultsAsync = fillMissingSourceDefaultsAsync;
|
|
1701
|
+
exports.guaranteedStaticSourceIdsKey = guaranteedStaticSourceIdsKey;
|
|
1485
1702
|
exports.hasInactiveCompletion = hasInactiveCompletion;
|
|
1486
1703
|
exports.includeSourceFailureChain = includeSourceFailureChain;
|
|
1487
1704
|
exports.orderDependencyNodes = orderDependencyNodes;
|
|
@@ -148,6 +148,36 @@ interface RuntimeNode {
|
|
|
148
148
|
readonly success: false;
|
|
149
149
|
readonly error: Message;
|
|
150
150
|
} | undefined>;
|
|
151
|
+
/**
|
|
152
|
+
* Resolves this barrier's *active* dependencies once its control
|
|
153
|
+
* dependencies (see {@link RuntimeNode.requiresSourceId}) are
|
|
154
|
+
* satisfied, before the barrier itself executes.
|
|
155
|
+
*
|
|
156
|
+
* A `conditional()` resolves the selected branch here—completing an
|
|
157
|
+
* effect-free discriminator, or reading an effectful one's cached
|
|
158
|
+
* completion—and reports which completion dependencies the branch
|
|
159
|
+
* actually has and which sources it will actively provide, so the
|
|
160
|
+
* scheduler can replace the static advisory estimate with concrete
|
|
161
|
+
* edges (see issue #924). Returning `undefined` leaves the static
|
|
162
|
+
* estimate in place; a failure aborts the pass. The scheduler calls
|
|
163
|
+
* this at most once per pass, and never lets advisory ordering edges
|
|
164
|
+
* delay it.
|
|
165
|
+
*
|
|
166
|
+
* @since 1.3.0
|
|
167
|
+
*/
|
|
168
|
+
readonly resolveBarrier?: (ctx: SchedulingBarrierContext) => Promise<{
|
|
169
|
+
readonly success: false;
|
|
170
|
+
readonly error: Message;
|
|
171
|
+
} | BarrierResolution | undefined>;
|
|
172
|
+
/**
|
|
173
|
+
* Internal marker the effectful scheduler sets on the replacement
|
|
174
|
+
* node after {@link RuntimeNode.resolveBarrier} ran: `"resolved"`
|
|
175
|
+
* edges are concrete (a cycle through them is genuine), while
|
|
176
|
+
* `"unresolvable"` keeps the advisory estimate.
|
|
177
|
+
*
|
|
178
|
+
* @since 1.3.0
|
|
179
|
+
*/
|
|
180
|
+
readonly barrierResolutionState?: "resolved" | "unresolvable";
|
|
151
181
|
/**
|
|
152
182
|
* Source IDs that the subtree guarded by this barrier can provide.
|
|
153
183
|
* Used by the demand-only pass: when any of them is demanded, the
|
|
@@ -158,6 +188,15 @@ interface RuntimeNode {
|
|
|
158
188
|
* @since 1.3.0
|
|
159
189
|
*/
|
|
160
190
|
readonly providesSourceIds?: ReadonlySet<symbol>;
|
|
191
|
+
/**
|
|
192
|
+
* Source IDs every selectable route through this barrier's subtree is
|
|
193
|
+
* guaranteed to provide, regardless of which branch is selected. An
|
|
194
|
+
* enclosing barrier's resolution treats these as active provides; the
|
|
195
|
+
* rest of {@link RuntimeNode.providesSourceIds} stays merely possible.
|
|
196
|
+
*
|
|
197
|
+
* @since 1.3.0
|
|
198
|
+
*/
|
|
199
|
+
readonly guaranteedProvidesSourceIds?: ReadonlySet<symbol>;
|
|
161
200
|
/**
|
|
162
201
|
* The source ID whose completion this barrier's preparation depends
|
|
163
202
|
* on (a `conditional()` discriminator). See
|
|
@@ -224,6 +263,29 @@ interface BarrierCompletionDependencies {
|
|
|
224
263
|
*/
|
|
225
264
|
readonly demandEdges: readonly BarrierCompletionDemandEdge[];
|
|
226
265
|
}
|
|
266
|
+
/**
|
|
267
|
+
* The concrete dependencies a scheduling barrier reports from
|
|
268
|
+
* {@link RuntimeNode.resolveBarrier} once its selection is known.
|
|
269
|
+
*
|
|
270
|
+
* @internal
|
|
271
|
+
* @since 1.3.0
|
|
272
|
+
*/
|
|
273
|
+
interface BarrierResolution {
|
|
274
|
+
/**
|
|
275
|
+
* The completion dependency IDs the selected subtree actually reads,
|
|
276
|
+
* minus the sources it actively provides itself. These replace the
|
|
277
|
+
* static advisory ordering estimate with concrete edges.
|
|
278
|
+
*/
|
|
279
|
+
readonly orderingDependencyIds: readonly symbol[];
|
|
280
|
+
/**
|
|
281
|
+
* The source IDs the selected subtree will actively publish: a
|
|
282
|
+
* guaranteed occurrence (an unconditional prompt or a publishable
|
|
283
|
+
* default) or one whose state already extracts a value. These
|
|
284
|
+
* replace {@link RuntimeNode.providesSourceIds} for ordering, so a
|
|
285
|
+
* sibling consumer no longer waits for a merely possible provider.
|
|
286
|
+
*/
|
|
287
|
+
readonly activeProvidesSourceIds: ReadonlySet<symbol>;
|
|
288
|
+
}
|
|
227
289
|
/**
|
|
228
290
|
* Options for resolving matched derived source values.
|
|
229
291
|
*
|
|
@@ -524,6 +586,35 @@ declare const sourceCollectionExpansionKey: unique symbol;
|
|
|
524
586
|
* @since 1.3.0
|
|
525
587
|
*/
|
|
526
588
|
declare const staticSourceScopeKey: unique symbol;
|
|
589
|
+
/**
|
|
590
|
+
* Static child parsers reachable only through a mutually exclusive
|
|
591
|
+
* selection, such as `or()` alternatives, a `conditional()`'s branches,
|
|
592
|
+
* or a `command()`'s inner parser.
|
|
593
|
+
*
|
|
594
|
+
* The walk unions sources and completion dependencies from this scope
|
|
595
|
+
* exactly like {@link staticSourceScopeKey}, but a source occurrence
|
|
596
|
+
* reached through an exclusive edge is never a *guaranteed* provider:
|
|
597
|
+
* the run may select a sibling instead, so a scheduling barrier must not
|
|
598
|
+
* assume the occurrence will publish. See issue #924.
|
|
599
|
+
*
|
|
600
|
+
* @internal
|
|
601
|
+
* @since 1.3.0
|
|
602
|
+
*/
|
|
603
|
+
declare const exclusiveSourceScopeKey: unique symbol;
|
|
604
|
+
/**
|
|
605
|
+
* Source IDs a parser guarantees to provide on every successful route
|
|
606
|
+
* through it, regardless of which internal alternative is selected.
|
|
607
|
+
*
|
|
608
|
+
* A `conditional()` installs the intersection of its branches' guaranteed
|
|
609
|
+
* provides here (including the default branch when present), so an outer
|
|
610
|
+
* scheduling barrier can treat a source that *every* selectable route
|
|
611
|
+
* publishes as guaranteed even though each individual branch edge is
|
|
612
|
+
* exclusive. See issue #924.
|
|
613
|
+
*
|
|
614
|
+
* @internal
|
|
615
|
+
* @since 1.3.0
|
|
616
|
+
*/
|
|
617
|
+
declare const guaranteedStaticSourceIdsKey: unique symbol;
|
|
527
618
|
/**
|
|
528
619
|
* Forwards effectful scheduling through a shape-preserving wrapper such
|
|
529
620
|
* as `map()`, `optional()`, `withDefault()`, or `nonEmpty()`, so the
|
|
@@ -767,4 +858,4 @@ declare function buildRuntimeNodesFromArray(parsers: ReadonlyArray<{
|
|
|
767
858
|
readonly initialState?: unknown;
|
|
768
859
|
}>, stateArray: readonly unknown[], parentPath?: readonly PropertyKey[]): readonly RuntimeNode[];
|
|
769
860
|
//#endregion
|
|
770
|
-
export { BarrierCompletionDemandEdge, BarrierCompletionDependencies, CompleteEffectfulSourcesOptions, DependencyRequest, DependencyResolution, DependencyRuntimeContext, EffectfulSchedulingNodesFn, EffectfulSourceCompletion, EffectfulSourceCompletionResult, ReplayKey, ResolveDerivedSourceValuesOptions, RuntimeNode, SchedulingBarrierContext, SourceDefaultFailure, buildRuntimeNodesFromArray, buildRuntimeNodesFromPairs, collectDemandedDependencyIds, collectExplicitSourceValues, collectExplicitSourceValuesAsync, collectSourcesFromState, completeEffectfulSourcesAsync, createDependencyFingerprint, createDependencyRuntimeContext, createReplayKey, defineForwardedEffectfulSchedulingNodes, derivedRawInputKey, effectfulSchedulingNodesKey, extractRawInputFromState, fillMissingSourceDefaults, fillMissingSourceDefaultsAsync, hasInactiveCompletion, includeSourceFailureChain, orderDependencyNodes, recordDerivedRawInput, replayDerivedParser, replayDerivedParserAsync, resolveDerivedSourceValues, resolveDerivedSourceValuesAsync, resolveStateWithRuntime, resolveStateWithRuntimeAsync, serializeSchedulingPath, sourceCollectionExpansionKey, staticSourceScopeKey };
|
|
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 };
|
|
@@ -148,6 +148,36 @@ interface RuntimeNode {
|
|
|
148
148
|
readonly success: false;
|
|
149
149
|
readonly error: Message;
|
|
150
150
|
} | undefined>;
|
|
151
|
+
/**
|
|
152
|
+
* Resolves this barrier's *active* dependencies once its control
|
|
153
|
+
* dependencies (see {@link RuntimeNode.requiresSourceId}) are
|
|
154
|
+
* satisfied, before the barrier itself executes.
|
|
155
|
+
*
|
|
156
|
+
* A `conditional()` resolves the selected branch here—completing an
|
|
157
|
+
* effect-free discriminator, or reading an effectful one's cached
|
|
158
|
+
* completion—and reports which completion dependencies the branch
|
|
159
|
+
* actually has and which sources it will actively provide, so the
|
|
160
|
+
* scheduler can replace the static advisory estimate with concrete
|
|
161
|
+
* edges (see issue #924). Returning `undefined` leaves the static
|
|
162
|
+
* estimate in place; a failure aborts the pass. The scheduler calls
|
|
163
|
+
* this at most once per pass, and never lets advisory ordering edges
|
|
164
|
+
* delay it.
|
|
165
|
+
*
|
|
166
|
+
* @since 1.3.0
|
|
167
|
+
*/
|
|
168
|
+
readonly resolveBarrier?: (ctx: SchedulingBarrierContext) => Promise<{
|
|
169
|
+
readonly success: false;
|
|
170
|
+
readonly error: Message;
|
|
171
|
+
} | BarrierResolution | undefined>;
|
|
172
|
+
/**
|
|
173
|
+
* Internal marker the effectful scheduler sets on the replacement
|
|
174
|
+
* node after {@link RuntimeNode.resolveBarrier} ran: `"resolved"`
|
|
175
|
+
* edges are concrete (a cycle through them is genuine), while
|
|
176
|
+
* `"unresolvable"` keeps the advisory estimate.
|
|
177
|
+
*
|
|
178
|
+
* @since 1.3.0
|
|
179
|
+
*/
|
|
180
|
+
readonly barrierResolutionState?: "resolved" | "unresolvable";
|
|
151
181
|
/**
|
|
152
182
|
* Source IDs that the subtree guarded by this barrier can provide.
|
|
153
183
|
* Used by the demand-only pass: when any of them is demanded, the
|
|
@@ -158,6 +188,15 @@ interface RuntimeNode {
|
|
|
158
188
|
* @since 1.3.0
|
|
159
189
|
*/
|
|
160
190
|
readonly providesSourceIds?: ReadonlySet<symbol>;
|
|
191
|
+
/**
|
|
192
|
+
* Source IDs every selectable route through this barrier's subtree is
|
|
193
|
+
* guaranteed to provide, regardless of which branch is selected. An
|
|
194
|
+
* enclosing barrier's resolution treats these as active provides; the
|
|
195
|
+
* rest of {@link RuntimeNode.providesSourceIds} stays merely possible.
|
|
196
|
+
*
|
|
197
|
+
* @since 1.3.0
|
|
198
|
+
*/
|
|
199
|
+
readonly guaranteedProvidesSourceIds?: ReadonlySet<symbol>;
|
|
161
200
|
/**
|
|
162
201
|
* The source ID whose completion this barrier's preparation depends
|
|
163
202
|
* on (a `conditional()` discriminator). See
|
|
@@ -224,6 +263,29 @@ interface BarrierCompletionDependencies {
|
|
|
224
263
|
*/
|
|
225
264
|
readonly demandEdges: readonly BarrierCompletionDemandEdge[];
|
|
226
265
|
}
|
|
266
|
+
/**
|
|
267
|
+
* The concrete dependencies a scheduling barrier reports from
|
|
268
|
+
* {@link RuntimeNode.resolveBarrier} once its selection is known.
|
|
269
|
+
*
|
|
270
|
+
* @internal
|
|
271
|
+
* @since 1.3.0
|
|
272
|
+
*/
|
|
273
|
+
interface BarrierResolution {
|
|
274
|
+
/**
|
|
275
|
+
* The completion dependency IDs the selected subtree actually reads,
|
|
276
|
+
* minus the sources it actively provides itself. These replace the
|
|
277
|
+
* static advisory ordering estimate with concrete edges.
|
|
278
|
+
*/
|
|
279
|
+
readonly orderingDependencyIds: readonly symbol[];
|
|
280
|
+
/**
|
|
281
|
+
* The source IDs the selected subtree will actively publish: a
|
|
282
|
+
* guaranteed occurrence (an unconditional prompt or a publishable
|
|
283
|
+
* default) or one whose state already extracts a value. These
|
|
284
|
+
* replace {@link RuntimeNode.providesSourceIds} for ordering, so a
|
|
285
|
+
* sibling consumer no longer waits for a merely possible provider.
|
|
286
|
+
*/
|
|
287
|
+
readonly activeProvidesSourceIds: ReadonlySet<symbol>;
|
|
288
|
+
}
|
|
227
289
|
/**
|
|
228
290
|
* Options for resolving matched derived source values.
|
|
229
291
|
*
|
|
@@ -524,6 +586,35 @@ declare const sourceCollectionExpansionKey: unique symbol;
|
|
|
524
586
|
* @since 1.3.0
|
|
525
587
|
*/
|
|
526
588
|
declare const staticSourceScopeKey: unique symbol;
|
|
589
|
+
/**
|
|
590
|
+
* Static child parsers reachable only through a mutually exclusive
|
|
591
|
+
* selection, such as `or()` alternatives, a `conditional()`'s branches,
|
|
592
|
+
* or a `command()`'s inner parser.
|
|
593
|
+
*
|
|
594
|
+
* The walk unions sources and completion dependencies from this scope
|
|
595
|
+
* exactly like {@link staticSourceScopeKey}, but a source occurrence
|
|
596
|
+
* reached through an exclusive edge is never a *guaranteed* provider:
|
|
597
|
+
* the run may select a sibling instead, so a scheduling barrier must not
|
|
598
|
+
* assume the occurrence will publish. See issue #924.
|
|
599
|
+
*
|
|
600
|
+
* @internal
|
|
601
|
+
* @since 1.3.0
|
|
602
|
+
*/
|
|
603
|
+
declare const exclusiveSourceScopeKey: unique symbol;
|
|
604
|
+
/**
|
|
605
|
+
* Source IDs a parser guarantees to provide on every successful route
|
|
606
|
+
* through it, regardless of which internal alternative is selected.
|
|
607
|
+
*
|
|
608
|
+
* A `conditional()` installs the intersection of its branches' guaranteed
|
|
609
|
+
* provides here (including the default branch when present), so an outer
|
|
610
|
+
* scheduling barrier can treat a source that *every* selectable route
|
|
611
|
+
* publishes as guaranteed even though each individual branch edge is
|
|
612
|
+
* exclusive. See issue #924.
|
|
613
|
+
*
|
|
614
|
+
* @internal
|
|
615
|
+
* @since 1.3.0
|
|
616
|
+
*/
|
|
617
|
+
declare const guaranteedStaticSourceIdsKey: unique symbol;
|
|
527
618
|
/**
|
|
528
619
|
* Forwards effectful scheduling through a shape-preserving wrapper such
|
|
529
620
|
* as `map()`, `optional()`, `withDefault()`, or `nonEmpty()`, so the
|
|
@@ -767,4 +858,4 @@ declare function buildRuntimeNodesFromArray(parsers: ReadonlyArray<{
|
|
|
767
858
|
readonly initialState?: unknown;
|
|
768
859
|
}>, stateArray: readonly unknown[], parentPath?: readonly PropertyKey[]): readonly RuntimeNode[];
|
|
769
860
|
//#endregion
|
|
770
|
-
export { BarrierCompletionDemandEdge, BarrierCompletionDependencies, CompleteEffectfulSourcesOptions, DependencyRequest, DependencyResolution, DependencyRuntimeContext, EffectfulSchedulingNodesFn, EffectfulSourceCompletion, EffectfulSourceCompletionResult, ReplayKey, ResolveDerivedSourceValuesOptions, RuntimeNode, SchedulingBarrierContext, SourceDefaultFailure, buildRuntimeNodesFromArray, buildRuntimeNodesFromPairs, collectDemandedDependencyIds, collectExplicitSourceValues, collectExplicitSourceValuesAsync, collectSourcesFromState, completeEffectfulSourcesAsync, createDependencyFingerprint, createDependencyRuntimeContext, createReplayKey, defineForwardedEffectfulSchedulingNodes, derivedRawInputKey, effectfulSchedulingNodesKey, extractRawInputFromState, fillMissingSourceDefaults, fillMissingSourceDefaultsAsync, hasInactiveCompletion, includeSourceFailureChain, orderDependencyNodes, recordDerivedRawInput, replayDerivedParser, replayDerivedParserAsync, resolveDerivedSourceValues, resolveDerivedSourceValuesAsync, resolveStateWithRuntime, resolveStateWithRuntimeAsync, serializeSchedulingPath, sourceCollectionExpansionKey, staticSourceScopeKey };
|
|
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 };
|