@optique/core 1.3.0-dev.2436 → 1.3.0-dev.2439

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.
@@ -1356,6 +1356,20 @@ async function completeEffectfulSourcesAsync(nodes, state, runtime, exec, option
1356
1356
  };
1357
1357
  const pending = [...nodes];
1358
1358
  const metadataUniverse = [...nodes];
1359
+ const declarationIndex = new Map(nodes.map((node, index) => [node, index]));
1360
+ const lastPublicationBySource = /* @__PURE__ */ new Map();
1361
+ const recordPublication = (index, sourceId, value) => {
1362
+ const existing = lastPublicationBySource.get(sourceId);
1363
+ if (existing == null || index >= existing.index) lastPublicationBySource.set(sourceId, {
1364
+ index,
1365
+ value
1366
+ });
1367
+ options?.onPublish?.(sourceId, value);
1368
+ };
1369
+ const publishFromNode = (node, sourceId, value) => {
1370
+ runtime.registerSource(sourceId, value);
1371
+ recordPublication(declarationIndex.get(node) ?? 0, sourceId, value);
1372
+ };
1359
1373
  const providesId = (node, id) => node.parser.dependencyMetadata?.source?.sourceId === id || node.providesSourceIds?.has(id) === true;
1360
1374
  const demandSession = session?.policy === "demand-only" ? session : void 0;
1361
1375
  const resolveBarrierAt = async (index) => {
@@ -1378,6 +1392,8 @@ async function completeEffectfulSourcesAsync(nodes, state, runtime, exec, option
1378
1392
  }
1379
1393
  };
1380
1394
  pending[index] = replacement;
1395
+ const originalIndex = declarationIndex.get(candidate);
1396
+ if (originalIndex != null) declarationIndex.set(replacement, originalIndex);
1381
1397
  const universeIndex = metadataUniverse.indexOf(candidate);
1382
1398
  if (universeIndex >= 0) metadataUniverse[universeIndex] = replacement;
1383
1399
  if (resolution != null && demandSession != null) propagateDemand(metadataUniverse, demandSession);
@@ -1419,8 +1435,29 @@ async function completeEffectfulSourcesAsync(nodes, state, runtime, exec, option
1419
1435
  }
1420
1436
  pending.splice(pending.indexOf(node), 1);
1421
1437
  if (node.prepare != null) {
1422
- const barrierFailure = await node.prepare(barrierContext);
1438
+ const barrierIndex = declarationIndex.get(node) ?? 0;
1439
+ const publishedDuringPrepare = /* @__PURE__ */ new Set();
1440
+ const nodeBarrierContext = {
1441
+ ...barrierContext,
1442
+ schedule: (barrierNodes) => completeEffectfulSourcesAsync(barrierNodes, state, runtime, exec, {
1443
+ isReusable: () => false,
1444
+ isCollected: () => true,
1445
+ includeStructural: true,
1446
+ onPublish: (sourceId, value) => {
1447
+ publishedDuringPrepare.add(sourceId);
1448
+ recordPublication(barrierIndex, sourceId, value);
1449
+ }
1450
+ }).then((result$1) => result$1.success ? void 0 : result$1)
1451
+ };
1452
+ const barrierFailure = await node.prepare(nodeBarrierContext);
1423
1453
  if (barrierFailure != null) return barrierFailure;
1454
+ for (const sourceId of publishedDuringPrepare) {
1455
+ const entry = lastPublicationBySource.get(sourceId);
1456
+ if (entry != null && entry.index > barrierIndex) {
1457
+ runtime.registerSource(sourceId, entry.value);
1458
+ options?.onPublish?.(sourceId, entry.value);
1459
+ }
1460
+ }
1424
1461
  continue;
1425
1462
  }
1426
1463
  const source = node.parser.dependencyMetadata?.source;
@@ -1441,7 +1478,7 @@ async function completeEffectfulSourcesAsync(nodes, state, runtime, exec, option
1441
1478
  };
1442
1479
  }
1443
1480
  if (replayed.deferred === true) continue;
1444
- runtime.registerSource(source.sourceId, replayed.value);
1481
+ publishFromNode(node, source.sourceId, replayed.value);
1445
1482
  if (source.preservesSourceValue && (options?.isReusable?.(node) ?? true)) completed.push({
1446
1483
  key: node.path[node.path.length - 1],
1447
1484
  result: replayed
@@ -1451,8 +1488,8 @@ async function completeEffectfulSourcesAsync(nodes, state, runtime, exec, option
1451
1488
  const collected = options?.isCollected?.(node) ?? options?.isReusable?.(node) ?? true;
1452
1489
  if (source.extractSourceValue == null || collected === false) continue;
1453
1490
  const extracted = await source.extractSourceValue(node.state);
1454
- if (extracted?.success === true && extracted.value !== void 0) {
1455
- runtime.registerSource(source.sourceId, extracted.value);
1491
+ if (extracted?.success === true) {
1492
+ publishFromNode(node, source.sourceId, extracted.value);
1456
1493
  continue;
1457
1494
  }
1458
1495
  if (options?.includeStructural === true && extracted?.success !== false && node.matched !== true && source.preservesSourceValue && source.getMissingSourceValue != null && !runtime.hasSource(source.sourceId) && !runtime.isSourceFailed(source.sourceId)) {
@@ -1469,7 +1506,7 @@ async function completeEffectfulSourcesAsync(nodes, state, runtime, exec, option
1469
1506
  }
1470
1507
  session?.missingDefaultsByOccurrence.set(source.getMissingSourceValue, fallback);
1471
1508
  }
1472
- if (fallback.success) runtime.registerSource(source.sourceId, fallback.value);
1509
+ if (fallback.success) publishFromNode(node, source.sourceId, fallback.value);
1473
1510
  else {
1474
1511
  runtime.markSourceFailed(source.sourceId);
1475
1512
  propagateRuntimeSourceFailures(nodes, runtime);
@@ -1512,7 +1549,7 @@ async function completeEffectfulSourcesAsync(nodes, state, runtime, exec, option
1512
1549
  key: node.path[node.path.length - 1],
1513
1550
  result
1514
1551
  });
1515
- if (result.value !== void 0) runtime.registerSource(source.sourceId, result.value);
1552
+ if (result.value !== void 0) publishFromNode(node, source.sourceId, result.value);
1516
1553
  }
1517
1554
  return {
1518
1555
  success: true,
@@ -751,6 +751,17 @@ interface CompleteEffectfulSourcesOptions {
751
751
  * return.
752
752
  */
753
753
  readonly includeStructural?: boolean;
754
+ /**
755
+ * Observes every source value this pass registers, including values a
756
+ * nested barrier pass reports through its own callback and values a
757
+ * barrier exit re-asserts. A barrier's nested scheduling call sets
758
+ * this so the enclosing pass can attribute the branch's publications
759
+ * to the barrier's declaration position and restore a later-declared
760
+ * occurrence the branch overwrote; the callback carries no positional
761
+ * information of its own, because declaration indices are meaningful
762
+ * only within a single pass.
763
+ */
764
+ readonly onPublish?: (sourceId: symbol, value: unknown) => void;
754
765
  }
755
766
  /**
756
767
  * Replaces a barrier failure cached in the run-scoped session with a
@@ -751,6 +751,17 @@ interface CompleteEffectfulSourcesOptions {
751
751
  * return.
752
752
  */
753
753
  readonly includeStructural?: boolean;
754
+ /**
755
+ * Observes every source value this pass registers, including values a
756
+ * nested barrier pass reports through its own callback and values a
757
+ * barrier exit re-asserts. A barrier's nested scheduling call sets
758
+ * this so the enclosing pass can attribute the branch's publications
759
+ * to the barrier's declaration position and restore a later-declared
760
+ * occurrence the branch overwrote; the callback carries no positional
761
+ * information of its own, because declaration indices are meaningful
762
+ * only within a single pass.
763
+ */
764
+ readonly onPublish?: (sourceId: symbol, value: unknown) => void;
754
765
  }
755
766
  /**
756
767
  * Replaces a barrier failure cached in the run-scoped session with a
@@ -1356,6 +1356,20 @@ async function completeEffectfulSourcesAsync(nodes, state, runtime, exec, option
1356
1356
  };
1357
1357
  const pending = [...nodes];
1358
1358
  const metadataUniverse = [...nodes];
1359
+ const declarationIndex = new Map(nodes.map((node, index) => [node, index]));
1360
+ const lastPublicationBySource = /* @__PURE__ */ new Map();
1361
+ const recordPublication = (index, sourceId, value) => {
1362
+ const existing = lastPublicationBySource.get(sourceId);
1363
+ if (existing == null || index >= existing.index) lastPublicationBySource.set(sourceId, {
1364
+ index,
1365
+ value
1366
+ });
1367
+ options?.onPublish?.(sourceId, value);
1368
+ };
1369
+ const publishFromNode = (node, sourceId, value) => {
1370
+ runtime.registerSource(sourceId, value);
1371
+ recordPublication(declarationIndex.get(node) ?? 0, sourceId, value);
1372
+ };
1359
1373
  const providesId = (node, id) => node.parser.dependencyMetadata?.source?.sourceId === id || node.providesSourceIds?.has(id) === true;
1360
1374
  const demandSession = session?.policy === "demand-only" ? session : void 0;
1361
1375
  const resolveBarrierAt = async (index) => {
@@ -1378,6 +1392,8 @@ async function completeEffectfulSourcesAsync(nodes, state, runtime, exec, option
1378
1392
  }
1379
1393
  };
1380
1394
  pending[index] = replacement;
1395
+ const originalIndex = declarationIndex.get(candidate);
1396
+ if (originalIndex != null) declarationIndex.set(replacement, originalIndex);
1381
1397
  const universeIndex = metadataUniverse.indexOf(candidate);
1382
1398
  if (universeIndex >= 0) metadataUniverse[universeIndex] = replacement;
1383
1399
  if (resolution != null && demandSession != null) propagateDemand(metadataUniverse, demandSession);
@@ -1419,8 +1435,29 @@ async function completeEffectfulSourcesAsync(nodes, state, runtime, exec, option
1419
1435
  }
1420
1436
  pending.splice(pending.indexOf(node), 1);
1421
1437
  if (node.prepare != null) {
1422
- const barrierFailure = await node.prepare(barrierContext);
1438
+ const barrierIndex = declarationIndex.get(node) ?? 0;
1439
+ const publishedDuringPrepare = /* @__PURE__ */ new Set();
1440
+ const nodeBarrierContext = {
1441
+ ...barrierContext,
1442
+ schedule: (barrierNodes) => completeEffectfulSourcesAsync(barrierNodes, state, runtime, exec, {
1443
+ isReusable: () => false,
1444
+ isCollected: () => true,
1445
+ includeStructural: true,
1446
+ onPublish: (sourceId, value) => {
1447
+ publishedDuringPrepare.add(sourceId);
1448
+ recordPublication(barrierIndex, sourceId, value);
1449
+ }
1450
+ }).then((result$1) => result$1.success ? void 0 : result$1)
1451
+ };
1452
+ const barrierFailure = await node.prepare(nodeBarrierContext);
1423
1453
  if (barrierFailure != null) return barrierFailure;
1454
+ for (const sourceId of publishedDuringPrepare) {
1455
+ const entry = lastPublicationBySource.get(sourceId);
1456
+ if (entry != null && entry.index > barrierIndex) {
1457
+ runtime.registerSource(sourceId, entry.value);
1458
+ options?.onPublish?.(sourceId, entry.value);
1459
+ }
1460
+ }
1424
1461
  continue;
1425
1462
  }
1426
1463
  const source = node.parser.dependencyMetadata?.source;
@@ -1441,7 +1478,7 @@ async function completeEffectfulSourcesAsync(nodes, state, runtime, exec, option
1441
1478
  };
1442
1479
  }
1443
1480
  if (replayed.deferred === true) continue;
1444
- runtime.registerSource(source.sourceId, replayed.value);
1481
+ publishFromNode(node, source.sourceId, replayed.value);
1445
1482
  if (source.preservesSourceValue && (options?.isReusable?.(node) ?? true)) completed.push({
1446
1483
  key: node.path[node.path.length - 1],
1447
1484
  result: replayed
@@ -1451,8 +1488,8 @@ async function completeEffectfulSourcesAsync(nodes, state, runtime, exec, option
1451
1488
  const collected = options?.isCollected?.(node) ?? options?.isReusable?.(node) ?? true;
1452
1489
  if (source.extractSourceValue == null || collected === false) continue;
1453
1490
  const extracted = await source.extractSourceValue(node.state);
1454
- if (extracted?.success === true && extracted.value !== void 0) {
1455
- runtime.registerSource(source.sourceId, extracted.value);
1491
+ if (extracted?.success === true) {
1492
+ publishFromNode(node, source.sourceId, extracted.value);
1456
1493
  continue;
1457
1494
  }
1458
1495
  if (options?.includeStructural === true && extracted?.success !== false && node.matched !== true && source.preservesSourceValue && source.getMissingSourceValue != null && !runtime.hasSource(source.sourceId) && !runtime.isSourceFailed(source.sourceId)) {
@@ -1469,7 +1506,7 @@ async function completeEffectfulSourcesAsync(nodes, state, runtime, exec, option
1469
1506
  }
1470
1507
  session?.missingDefaultsByOccurrence.set(source.getMissingSourceValue, fallback);
1471
1508
  }
1472
- if (fallback.success) runtime.registerSource(source.sourceId, fallback.value);
1509
+ if (fallback.success) publishFromNode(node, source.sourceId, fallback.value);
1473
1510
  else {
1474
1511
  runtime.markSourceFailed(source.sourceId);
1475
1512
  propagateRuntimeSourceFailures(nodes, runtime);
@@ -1512,7 +1549,7 @@ async function completeEffectfulSourcesAsync(nodes, state, runtime, exec, option
1512
1549
  key: node.path[node.path.length - 1],
1513
1550
  result
1514
1551
  });
1515
- if (result.value !== void 0) runtime.registerSource(source.sourceId, result.value);
1552
+ if (result.value !== void 0) publishFromNode(node, source.sourceId, result.value);
1516
1553
  }
1517
1554
  return {
1518
1555
  success: true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optique/core",
3
- "version": "1.3.0-dev.2436",
3
+ "version": "1.3.0-dev.2439",
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.2436+93bd3330",
224
+ "@optique/env": "1.3.0-dev.2439+27e12fd8",
225
225
  "@types/node": "^24.0.0",
226
226
  "fast-check": "^4.7.0",
227
227
  "tsdown": "^0.13.0",