@softarc/native-federation-orchestrator 4.5.2 → 4.6.0
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/README.md +2 -2
- package/fesm2022/@softarc/native-federation-orchestrator.mjs +1310 -257
- package/fesm2022/@softarc/native-federation-orchestrator.mjs.map +4 -4
- package/fesm2022/module-federation.mjs +27 -8
- package/fesm2022/module-federation.mjs.map +3 -3
- package/fesm2022/node.mjs +1307 -254
- package/fesm2022/node.mjs.map +4 -4
- package/fesm2022/options.mjs +5 -10
- package/fesm2022/options.mjs.map +2 -2
- package/package.json +2 -2
- package/quickstart.mjs +15 -15
- package/types/lib/core/1.domain/externals/basis.d.ts +22 -0
- package/types/lib/core/1.domain/externals/version.contract.d.ts +1 -0
- package/types/lib/core/1.domain/remote-entry/remote-entry.contract.d.ts +2 -0
- package/types/lib/core/2.app/config/mode.contract.d.ts +1 -0
- package/types/lib/core/2.app/driver-ports/init/drivers.contract.d.ts +2 -0
- package/types/lib/core/2.app/driver-ports/init/for-determining-shared-externals.port.d.ts +8 -1
- package/types/lib/core/2.app/driver-ports/init/for-marking-pools-for-reelection.port.d.ts +6 -0
- package/types/lib/core/2.app/driver-ports/init/for-pooling-shared-externals.port.d.ts +3 -1
- package/types/lib/core/2.app/driving-ports/for-shared-chunks-storage.port.d.ts +1 -0
- package/types/lib/core/2.app/driving-ports/for-shared-externals-storage.port.d.ts +2 -3
- package/types/lib/core/2.app/steps/apply-winner.d.ts +18 -0
- package/types/lib/core/2.app/steps/pooling/anchoring.d.ts +69 -0
- package/types/lib/core/2.app/steps/pooling/mark-pools-for-reelection.d.ts +5 -0
- package/types/lib/core/2.app/steps/pooling/pool-dynamic-externals.d.ts +3 -1
- package/types/lib/core/2.app/steps/pooling/pool-graph.d.ts +10 -5
- package/types/lib/core/2.app/steps/pooling/pool-shared-externals.d.ts +30 -1
- package/types/lib/core/2.app/steps/pooling/pool-views.d.ts +63 -0
- package/types/lib/core/2.app/steps/pooling/pool.types.d.ts +30 -1
- package/types/lib/core/2.app/steps/pooling/pool.util.d.ts +15 -0
- package/types/lib/core/2.app/steps/store-remote-entry.d.ts +15 -7
- package/types/lib/testing/pooling/no-tear.d.ts +37 -0
|
@@ -1283,7 +1283,7 @@ var createInitFlow = ({
|
|
|
1283
1283
|
adapters,
|
|
1284
1284
|
config
|
|
1285
1285
|
}) => {
|
|
1286
|
-
return (remotesOrManifestUrl) => flow.getRemoteEntries(remotesOrManifestUrl).then(flow.processRemoteEntries).then(flow.determineSharedExternals).then(flow.poolSharedExternals).then(flow.generateImportMap).then((importMap) => flow.commitChanges(importMap, { override: true })).then(flow.exposeModuleLoader).then((loadRemoteModule) => ({
|
|
1286
|
+
return (remotesOrManifestUrl) => flow.getRemoteEntries(remotesOrManifestUrl).then(flow.processRemoteEntries).then(flow.markPoolsForReelection).then(flow.determineSharedExternals).then(flow.poolSharedExternals).then(flow.generateImportMap).then((importMap) => flow.commitChanges(importMap, { override: true })).then(flow.exposeModuleLoader).then((loadRemoteModule) => ({
|
|
1287
1287
|
config,
|
|
1288
1288
|
adapters,
|
|
1289
1289
|
loadRemoteModule
|
|
@@ -1426,19 +1426,92 @@ function createGetRemoteEntries(config, ports) {
|
|
|
1426
1426
|
}
|
|
1427
1427
|
}
|
|
1428
1428
|
|
|
1429
|
+
// src/lib/core/1.domain/externals/basis.ts
|
|
1430
|
+
var coverage = (remote) => Object.keys(remote.entries).length;
|
|
1431
|
+
function isBetterBasis(candidate, basis) {
|
|
1432
|
+
if (basis.cached !== candidate.cached) return candidate.cached;
|
|
1433
|
+
return coverage(candidate) > coverage(basis);
|
|
1434
|
+
}
|
|
1435
|
+
function addRemoteToVersion(version, remote, isHost = false) {
|
|
1436
|
+
if (isHost) {
|
|
1437
|
+
version.host = true;
|
|
1438
|
+
version.remotes.unshift(remote);
|
|
1439
|
+
return;
|
|
1440
|
+
}
|
|
1441
|
+
const basis = version.remotes[0];
|
|
1442
|
+
if (basis && !version.host && isBetterBasis(remote, basis)) {
|
|
1443
|
+
version.remotes.unshift(remote);
|
|
1444
|
+
return;
|
|
1445
|
+
}
|
|
1446
|
+
version.remotes.push(remote);
|
|
1447
|
+
}
|
|
1448
|
+
function versionDemands(version) {
|
|
1449
|
+
if (version.remotes.length < 2) return version.remotes;
|
|
1450
|
+
const distinct = /* @__PURE__ */ new Map();
|
|
1451
|
+
for (const remote of version.remotes) {
|
|
1452
|
+
const key = `${remote.requiredVersion}|${remote.strictVersion}|${remote.cached}`;
|
|
1453
|
+
if (!distinct.has(key)) distinct.set(key, remote);
|
|
1454
|
+
}
|
|
1455
|
+
return Array.from(distinct.values());
|
|
1456
|
+
}
|
|
1457
|
+
function findVersionForTag(versions, tag) {
|
|
1458
|
+
let scoped;
|
|
1459
|
+
for (const version of versions) {
|
|
1460
|
+
if (version.tag !== tag) continue;
|
|
1461
|
+
if (version.action !== "scope") return version;
|
|
1462
|
+
scoped ??= version;
|
|
1463
|
+
}
|
|
1464
|
+
return scoped;
|
|
1465
|
+
}
|
|
1466
|
+
function versionEntries(version) {
|
|
1467
|
+
return collectEntries(version, void 0);
|
|
1468
|
+
}
|
|
1469
|
+
function committedEntries(version) {
|
|
1470
|
+
return collectEntries(version, (remote) => remote.cached);
|
|
1471
|
+
}
|
|
1472
|
+
function forEachVersionEntry(version, accepts, visit) {
|
|
1473
|
+
for (const remote of version.remotes) {
|
|
1474
|
+
if (remote.servedBy) continue;
|
|
1475
|
+
if (accepts && !accepts(remote)) continue;
|
|
1476
|
+
for (const entrypoint in remote.entries) visit(entrypoint, remote);
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
function collectEntries(version, accepts) {
|
|
1480
|
+
const entries = /* @__PURE__ */ new Map();
|
|
1481
|
+
forEachVersionEntry(version, accepts, (entrypoint, remote) => {
|
|
1482
|
+
if (!entries.has(entrypoint)) entries.set(entrypoint, remote);
|
|
1483
|
+
});
|
|
1484
|
+
return entries;
|
|
1485
|
+
}
|
|
1486
|
+
function uncoveredEntrypoints(remote, covered) {
|
|
1487
|
+
return Object.keys(remote.entries).filter((entrypoint) => !covered.has(entrypoint));
|
|
1488
|
+
}
|
|
1489
|
+
function countUncoveredEntrypoints(remote, covered) {
|
|
1490
|
+
let uncovered = 0;
|
|
1491
|
+
for (const entrypoint in remote.entries) {
|
|
1492
|
+
if (!covered.has(entrypoint)) uncovered++;
|
|
1493
|
+
}
|
|
1494
|
+
return uncovered;
|
|
1495
|
+
}
|
|
1496
|
+
|
|
1429
1497
|
// src/lib/core/2.app/steps/store-remote-entry.ts
|
|
1498
|
+
function createRemoveCachedRemoteEntries(ports) {
|
|
1499
|
+
return (remoteNames) => {
|
|
1500
|
+
if (remoteNames.size === 0) return;
|
|
1501
|
+
for (const remoteName of remoteNames) {
|
|
1502
|
+
ports.remoteInfoRepo.remove(remoteName);
|
|
1503
|
+
ports.scopedExternalsRepo.remove(remoteName);
|
|
1504
|
+
ports.sharedChunksRepo.remove(remoteName);
|
|
1505
|
+
}
|
|
1506
|
+
ports.sharedExternalsRepo.removeFromAllScopes(remoteNames);
|
|
1507
|
+
};
|
|
1508
|
+
}
|
|
1430
1509
|
function createStoreRemoteEntry(config, ports, logStep) {
|
|
1431
1510
|
return (remoteEntry, onSharedExternal) => {
|
|
1432
|
-
if (remoteEntry?.override) removeCachedRemoteEntry(remoteEntry);
|
|
1433
1511
|
addRemoteInfoToStorage(remoteEntry);
|
|
1434
1512
|
addExternalsToStorage(remoteEntry, onSharedExternal);
|
|
1435
1513
|
addSharedChunksToStorage(remoteEntry);
|
|
1436
1514
|
};
|
|
1437
|
-
function removeCachedRemoteEntry(remoteEntry) {
|
|
1438
|
-
ports.remoteInfoRepo.remove(remoteEntry.name);
|
|
1439
|
-
ports.scopedExternalsRepo.remove(remoteEntry.name);
|
|
1440
|
-
ports.sharedExternalsRepo.removeFromAllScopes(remoteEntry.name);
|
|
1441
|
-
}
|
|
1442
1515
|
function addRemoteInfoToStorage({ name, url, exposes, integrity }) {
|
|
1443
1516
|
ports.remoteInfoRepo.addOrUpdate(name, {
|
|
1444
1517
|
scopeUrl: getScope(url),
|
|
@@ -1454,7 +1527,6 @@ function createStoreRemoteEntry(config, ports, logStep) {
|
|
|
1454
1527
|
const tag = resolveVersion(remoteEntry, external);
|
|
1455
1528
|
if (tag === null) return;
|
|
1456
1529
|
if (external.singleton) {
|
|
1457
|
-
if (external.pool?.trim()) ports.sharedExternalsRepo.markPoolTagPresent();
|
|
1458
1530
|
onSharedExternal(remoteEntry, external, sharedExternalContext(remoteEntry, external, tag));
|
|
1459
1531
|
} else {
|
|
1460
1532
|
addScopedExternal(remoteEntry, external, tag);
|
|
@@ -1538,14 +1610,35 @@ function createStoreRemoteEntry(config, ports, logStep) {
|
|
|
1538
1610
|
// src/lib/core/2.app/steps/process-remote-entries.ts
|
|
1539
1611
|
function createProcessRemoteEntries(config, ports) {
|
|
1540
1612
|
const storeRemoteEntry = createStoreRemoteEntry(config, ports, 2);
|
|
1613
|
+
const removeCachedRemoteEntries = createRemoveCachedRemoteEntries(ports);
|
|
1541
1614
|
return (remoteEntries) => {
|
|
1542
1615
|
try {
|
|
1543
|
-
|
|
1616
|
+
const evictPerEntry = evictOverriddenRemotes(remoteEntries);
|
|
1617
|
+
remoteEntries.forEach((remoteEntry) => {
|
|
1618
|
+
if (remoteEntry?.override && evictPerEntry.has(remoteEntry.name)) {
|
|
1619
|
+
removeCachedRemoteEntries(/* @__PURE__ */ new Set([remoteEntry.name]));
|
|
1620
|
+
}
|
|
1621
|
+
storeRemoteEntry(remoteEntry, addSharedExternal);
|
|
1622
|
+
});
|
|
1544
1623
|
return Promise.resolve(remoteEntries);
|
|
1545
1624
|
} catch (e) {
|
|
1546
1625
|
return Promise.reject(e);
|
|
1547
1626
|
}
|
|
1548
1627
|
};
|
|
1628
|
+
function evictOverriddenRemotes(remoteEntries) {
|
|
1629
|
+
const batched = /* @__PURE__ */ new Set();
|
|
1630
|
+
const repeated = /* @__PURE__ */ new Set();
|
|
1631
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1632
|
+
for (const remoteEntry of remoteEntries) {
|
|
1633
|
+
if (!remoteEntry) continue;
|
|
1634
|
+
if (seen.has(remoteEntry.name)) repeated.add(remoteEntry.name);
|
|
1635
|
+
seen.add(remoteEntry.name);
|
|
1636
|
+
if (remoteEntry.override) batched.add(remoteEntry.name);
|
|
1637
|
+
}
|
|
1638
|
+
for (const remoteName of repeated) batched.delete(remoteName);
|
|
1639
|
+
removeCachedRemoteEntries(batched);
|
|
1640
|
+
return repeated;
|
|
1641
|
+
}
|
|
1549
1642
|
function addSharedExternal(remoteEntry, _sharedInfo, {
|
|
1550
1643
|
tag,
|
|
1551
1644
|
remote,
|
|
@@ -1554,15 +1647,12 @@ function createProcessRemoteEntries(config, ports) {
|
|
|
1554
1647
|
assertSameVersionCompatibility,
|
|
1555
1648
|
commit
|
|
1556
1649
|
}) {
|
|
1557
|
-
|
|
1650
|
+
if (scopeType !== "strict") cached.dirty = true;
|
|
1651
|
+
const matchingVersion = findVersionForTag(cached.versions, tag);
|
|
1558
1652
|
if (matchingVersion) {
|
|
1559
1653
|
assertSameVersionCompatibility(matchingVersion);
|
|
1560
|
-
|
|
1561
|
-
matchingVersion.host = true;
|
|
1562
|
-
matchingVersion.remotes.unshift(remote);
|
|
1563
|
-
} else matchingVersion.remotes.push(remote);
|
|
1654
|
+
addRemoteToVersion(matchingVersion, remote, !matchingVersion.host && !!remoteEntry?.host);
|
|
1564
1655
|
} else {
|
|
1565
|
-
if (scopeType !== "strict") cached.dirty = true;
|
|
1566
1656
|
cached.versions.push({
|
|
1567
1657
|
tag,
|
|
1568
1658
|
action: scopeType === "strict" ? "share" : "skip",
|
|
@@ -1578,19 +1668,154 @@ function createProcessRemoteEntries(config, ports) {
|
|
|
1578
1668
|
var GLOBAL_SCOPE = "__GLOBAL__";
|
|
1579
1669
|
var STRICT_SCOPE = "strict";
|
|
1580
1670
|
|
|
1671
|
+
// src/lib/core/2.app/steps/apply-winner.ts
|
|
1672
|
+
function versionAcceptance(external, isCompatible) {
|
|
1673
|
+
const demands = new Map(
|
|
1674
|
+
external.versions.map((v) => [v, versionDemands(v)])
|
|
1675
|
+
);
|
|
1676
|
+
return {
|
|
1677
|
+
accepts: (version, tag) => demands.get(version).every((d) => isCompatible(tag, d.requiredVersion)),
|
|
1678
|
+
objector: (version, tag) => demands.get(version).find((d) => d.strictVersion && !isCompatible(tag, d.requiredVersion))
|
|
1679
|
+
};
|
|
1680
|
+
}
|
|
1681
|
+
function createApplyWinner(config) {
|
|
1682
|
+
return function applyWinner(externalName, external, winner, isCompatible, acceptance) {
|
|
1683
|
+
if (external.versions.length > 1) {
|
|
1684
|
+
const { accepts, objector } = acceptance ?? versionAcceptance(external, isCompatible);
|
|
1685
|
+
const rebuilt = [];
|
|
1686
|
+
for (const v of external.versions) {
|
|
1687
|
+
rebuilt.push(v);
|
|
1688
|
+
if (accepts(v, winner.tag)) {
|
|
1689
|
+
v.action = "skip";
|
|
1690
|
+
continue;
|
|
1691
|
+
}
|
|
1692
|
+
const strict = objector(v, winner.tag);
|
|
1693
|
+
if (config.strict.strictExternalCompatibility && strict) {
|
|
1694
|
+
config.log.error(
|
|
1695
|
+
3,
|
|
1696
|
+
`[${strict.name}][${externalName}@${v.tag}] Is not compatible with requiredRange '${strict.requiredVersion}' of shared ${externalName}@${winner.tag}.`
|
|
1697
|
+
);
|
|
1698
|
+
throw new NFError(`External ${externalName}@${v.tag} could not be shared.`);
|
|
1699
|
+
}
|
|
1700
|
+
if (v === winner) continue;
|
|
1701
|
+
if (!strict) {
|
|
1702
|
+
v.action = "skip";
|
|
1703
|
+
continue;
|
|
1704
|
+
}
|
|
1705
|
+
const objecting = new Set(
|
|
1706
|
+
v.remotes.filter((r) => r.strictVersion && !isCompatible(winner.tag, r.requiredVersion))
|
|
1707
|
+
);
|
|
1708
|
+
if (objecting.size === v.remotes.length) {
|
|
1709
|
+
v.action = "scope";
|
|
1710
|
+
continue;
|
|
1711
|
+
}
|
|
1712
|
+
v.remotes = v.remotes.filter((r) => !objecting.has(r));
|
|
1713
|
+
v.action = "skip";
|
|
1714
|
+
rebuilt.push({ tag: v.tag, host: false, action: "scope", remotes: [...objecting] });
|
|
1715
|
+
}
|
|
1716
|
+
const merged = /* @__PURE__ */ new Map();
|
|
1717
|
+
external.versions = rebuilt.filter((v) => {
|
|
1718
|
+
const first = merged.get(`${v.tag}|${v.action}`);
|
|
1719
|
+
if (!first) {
|
|
1720
|
+
merged.set(`${v.tag}|${v.action}`, v);
|
|
1721
|
+
return true;
|
|
1722
|
+
}
|
|
1723
|
+
first.remotes.push(...v.remotes);
|
|
1724
|
+
return false;
|
|
1725
|
+
});
|
|
1726
|
+
}
|
|
1727
|
+
winner.action = "share";
|
|
1728
|
+
applyEntrypointCoveragePolicy(externalName, external);
|
|
1729
|
+
external.dirty = false;
|
|
1730
|
+
return external;
|
|
1731
|
+
};
|
|
1732
|
+
function applyEntrypointCoveragePolicy(externalName, external) {
|
|
1733
|
+
const { strictEntryPointCoverage } = config.strict;
|
|
1734
|
+
if (!strictEntryPointCoverage && !config.profile.scopeUncoveredEntrypoints) return;
|
|
1735
|
+
const tears = findTears(external);
|
|
1736
|
+
if (tears.length === 0) return;
|
|
1737
|
+
if (strictEntryPointCoverage) {
|
|
1738
|
+
const { version, remote, uncovered } = tears[0];
|
|
1739
|
+
config.log.error(
|
|
1740
|
+
3,
|
|
1741
|
+
`[${externalName}@${version.tag}][${remote.name}] Entrypoints not covered by the shared version: ${uncovered.join(", ")}.`
|
|
1742
|
+
);
|
|
1743
|
+
throw new NFError(
|
|
1744
|
+
`External ${externalName} could not be shared without tearing entrypoints.`
|
|
1745
|
+
);
|
|
1746
|
+
}
|
|
1747
|
+
scopeTornRemotes(externalName, external, tears);
|
|
1748
|
+
}
|
|
1749
|
+
function findTears(external) {
|
|
1750
|
+
const shared = external.versions.find((v) => v.action === "share");
|
|
1751
|
+
if (!shared) return [];
|
|
1752
|
+
const basis = versionEntries(shared);
|
|
1753
|
+
const tears = [];
|
|
1754
|
+
for (const version of external.versions) {
|
|
1755
|
+
if (version.action === "scope") continue;
|
|
1756
|
+
if (version === shared) continue;
|
|
1757
|
+
version.remotes.forEach((remote) => {
|
|
1758
|
+
if (remote.servedBy) return;
|
|
1759
|
+
const uncovered = uncoveredEntrypoints(remote, basis);
|
|
1760
|
+
if (uncovered.length > 0) tears.push({ version, remote, uncovered });
|
|
1761
|
+
});
|
|
1762
|
+
}
|
|
1763
|
+
return tears;
|
|
1764
|
+
}
|
|
1765
|
+
function scopeTornRemotes(externalName, external, tears) {
|
|
1766
|
+
const torn = new Set(tears.map((t) => t.remote));
|
|
1767
|
+
const demotedByTag = /* @__PURE__ */ new Map();
|
|
1768
|
+
for (const { version, remote, uncovered } of tears) {
|
|
1769
|
+
const group = demotedByTag.get(version.tag);
|
|
1770
|
+
if (group) group.push(remote);
|
|
1771
|
+
else demotedByTag.set(version.tag, [remote]);
|
|
1772
|
+
config.log.debug(
|
|
1773
|
+
3,
|
|
1774
|
+
`[${externalName}@${version.tag}][${remote.name}] Scoped: entrypoints not covered by the shared version: ${uncovered.join(", ")}.`
|
|
1775
|
+
);
|
|
1776
|
+
}
|
|
1777
|
+
for (const version of external.versions) {
|
|
1778
|
+
if (version.remotes.some((r) => torn.has(r))) {
|
|
1779
|
+
version.remotes = version.remotes.filter((r) => !torn.has(r));
|
|
1780
|
+
}
|
|
1781
|
+
}
|
|
1782
|
+
external.versions = external.versions.filter((v) => v.remotes.length > 0);
|
|
1783
|
+
for (const [tag, remotes] of demotedByTag) {
|
|
1784
|
+
const scoped = external.versions.find((v) => v.tag === tag && v.action === "scope");
|
|
1785
|
+
if (scoped) scoped.remotes.push(...remotes);
|
|
1786
|
+
else external.versions.push({ tag, host: false, action: "scope", remotes });
|
|
1787
|
+
}
|
|
1788
|
+
}
|
|
1789
|
+
}
|
|
1790
|
+
|
|
1581
1791
|
// src/lib/core/2.app/steps/determine-shared-externals.ts
|
|
1582
1792
|
function createDetermineSharedExternals(config, ports) {
|
|
1793
|
+
const applyWinner = createApplyWinner(config);
|
|
1583
1794
|
return () => {
|
|
1795
|
+
const memo = /* @__PURE__ */ new Map();
|
|
1796
|
+
const isCompatible = (tag, requiredVersion) => {
|
|
1797
|
+
const key = `${tag}|${requiredVersion}`;
|
|
1798
|
+
let hit = memo.get(key);
|
|
1799
|
+
if (hit === void 0) {
|
|
1800
|
+
hit = ports.versionCheck.isCompatible(tag, requiredVersion);
|
|
1801
|
+
memo.set(key, hit);
|
|
1802
|
+
}
|
|
1803
|
+
return hit;
|
|
1804
|
+
};
|
|
1805
|
+
const touched = /* @__PURE__ */ new Map();
|
|
1584
1806
|
for (const shareScope of ports.sharedExternalsRepo.getScopes()) {
|
|
1585
1807
|
const sharedExternals = ports.sharedExternalsRepo.getFromScope(shareScope);
|
|
1586
1808
|
try {
|
|
1587
|
-
|
|
1588
|
-
|
|
1809
|
+
const elected = /* @__PURE__ */ new Set();
|
|
1810
|
+
Object.entries(sharedExternals).filter(([_, e]) => e.dirty).forEach(([name, external]) => {
|
|
1811
|
+
ports.sharedExternalsRepo.addOrUpdate(
|
|
1589
1812
|
name,
|
|
1590
|
-
setVersionActions(name, external),
|
|
1813
|
+
setVersionActions(name, external, isCompatible),
|
|
1591
1814
|
shareScope
|
|
1592
|
-
)
|
|
1593
|
-
|
|
1815
|
+
);
|
|
1816
|
+
elected.add(name);
|
|
1817
|
+
});
|
|
1818
|
+
if (elected.size > 0) touched.set(shareScope, elected);
|
|
1594
1819
|
} catch (error) {
|
|
1595
1820
|
config.log.error(
|
|
1596
1821
|
3,
|
|
@@ -1608,22 +1833,25 @@ function createDetermineSharedExternals(config, ports) {
|
|
|
1608
1833
|
);
|
|
1609
1834
|
}
|
|
1610
1835
|
}
|
|
1611
|
-
return Promise.resolve();
|
|
1836
|
+
return Promise.resolve(touched);
|
|
1612
1837
|
};
|
|
1613
|
-
function uncoveredTears(external, winner) {
|
|
1614
|
-
const basis = winner
|
|
1838
|
+
function uncoveredTears(external, winner, accepts) {
|
|
1839
|
+
const basis = versionEntries(winner);
|
|
1615
1840
|
return external.versions.reduce((sum, v) => {
|
|
1616
1841
|
if (v === winner) return sum;
|
|
1617
|
-
if (!
|
|
1618
|
-
return
|
|
1842
|
+
if (!accepts(v, winner.tag)) return sum;
|
|
1843
|
+
return v.remotes.reduce(
|
|
1844
|
+
(n, r) => r.servedBy ? n : n + countUncoveredEntrypoints(r, basis),
|
|
1845
|
+
sum
|
|
1846
|
+
);
|
|
1619
1847
|
}, 0);
|
|
1620
1848
|
}
|
|
1621
|
-
function setVersionActions(externalName, external) {
|
|
1849
|
+
function setVersionActions(externalName, external, isCompatible) {
|
|
1622
1850
|
if (external.versions.length === 1) {
|
|
1623
|
-
external.versions[0]
|
|
1624
|
-
external.dirty = false;
|
|
1625
|
-
return external;
|
|
1851
|
+
return applyWinner(externalName, external, external.versions[0], isCompatible);
|
|
1626
1852
|
}
|
|
1853
|
+
const acceptance = versionAcceptance(external, isCompatible);
|
|
1854
|
+
const { accepts } = acceptance;
|
|
1627
1855
|
let sharedVersion = external.versions.find((v) => v.host);
|
|
1628
1856
|
if (!sharedVersion && config.profile.latestSharedExternal) {
|
|
1629
1857
|
sharedVersion = external.versions[0];
|
|
@@ -1631,13 +1859,39 @@ function createDetermineSharedExternals(config, ports) {
|
|
|
1631
1859
|
if (!sharedVersion) {
|
|
1632
1860
|
let leastExtraDownloads = Number.MAX_VALUE;
|
|
1633
1861
|
let leastTears = Number.MAX_VALUE;
|
|
1862
|
+
const selfServing = new Map(
|
|
1863
|
+
external.versions.map((v) => {
|
|
1864
|
+
const groups = /* @__PURE__ */ new Map();
|
|
1865
|
+
for (const remote of v.remotes) {
|
|
1866
|
+
if (remote.cached || !remote.strictVersion) continue;
|
|
1867
|
+
const group = groups.get(remote.requiredVersion);
|
|
1868
|
+
if (group) group.copies++;
|
|
1869
|
+
else
|
|
1870
|
+
groups.set(remote.requiredVersion, {
|
|
1871
|
+
requiredVersion: remote.requiredVersion,
|
|
1872
|
+
copies: 1
|
|
1873
|
+
});
|
|
1874
|
+
}
|
|
1875
|
+
return [v, [...groups.values()]];
|
|
1876
|
+
})
|
|
1877
|
+
);
|
|
1878
|
+
const costOf = (version, tag) => selfServing.get(version).reduce((n, g) => isCompatible(tag, g.requiredVersion) ? n : n + g.copies, 0);
|
|
1634
1879
|
external.versions.forEach((vA) => {
|
|
1635
|
-
const extraDownloads = external.versions.
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1880
|
+
const extraDownloads = external.versions.reduce(
|
|
1881
|
+
// A copy of the winner is never redirected, so it never self-serves however its own range
|
|
1882
|
+
// reads — see `applyWinner`, which does not split the winner either.
|
|
1883
|
+
(sum, vB) => vB === vA ? sum : sum + costOf(vB, vA.tag),
|
|
1884
|
+
0
|
|
1885
|
+
);
|
|
1886
|
+
if (extraDownloads < leastExtraDownloads) {
|
|
1640
1887
|
leastExtraDownloads = extraDownloads;
|
|
1888
|
+
leastTears = uncoveredTears(external, vA, accepts);
|
|
1889
|
+
sharedVersion = vA;
|
|
1890
|
+
return;
|
|
1891
|
+
}
|
|
1892
|
+
if (extraDownloads > leastExtraDownloads) return;
|
|
1893
|
+
const tears = uncoveredTears(external, vA, accepts);
|
|
1894
|
+
if (tears < leastTears) {
|
|
1641
1895
|
leastTears = tears;
|
|
1642
1896
|
sharedVersion = vA;
|
|
1643
1897
|
}
|
|
@@ -1646,40 +1900,302 @@ function createDetermineSharedExternals(config, ports) {
|
|
|
1646
1900
|
if (!sharedVersion) {
|
|
1647
1901
|
throw new NFError(`[${externalName}] Could not determine shared version!`);
|
|
1648
1902
|
}
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1903
|
+
return applyWinner(externalName, external, sharedVersion, isCompatible, acceptance);
|
|
1904
|
+
}
|
|
1905
|
+
}
|
|
1906
|
+
|
|
1907
|
+
// src/lib/core/2.app/steps/pooling/pool-views.ts
|
|
1908
|
+
function liveBuilds(members, islanded) {
|
|
1909
|
+
return walkBuilds(members, islanded, true);
|
|
1910
|
+
}
|
|
1911
|
+
function committedView(members) {
|
|
1912
|
+
const global = /* @__PURE__ */ new Map();
|
|
1913
|
+
forEachGlobalClaim(members, void 0, (specifier, tag, meta) => {
|
|
1914
|
+
if (!global.has(specifier))
|
|
1915
|
+
global.set(specifier, { tag, remote: meta.name, file: meta.entries[specifier] });
|
|
1916
|
+
});
|
|
1917
|
+
return { builds: walkBuilds(members, void 0, false), global };
|
|
1918
|
+
}
|
|
1919
|
+
function walkBuilds(members, islanded, skipScoped) {
|
|
1920
|
+
const builds = /* @__PURE__ */ new Map();
|
|
1921
|
+
for (const member of members) {
|
|
1922
|
+
const versions = member.external.versions;
|
|
1923
|
+
for (let v = 0; v < versions.length; v++) {
|
|
1924
|
+
const version = versions[v];
|
|
1925
|
+
if (skipScoped && version.action === "scope") continue;
|
|
1926
|
+
const remotes = version.remotes;
|
|
1927
|
+
for (let r = 0; r < remotes.length; r++) {
|
|
1928
|
+
const meta = remotes[r];
|
|
1929
|
+
if (islanded?.has(meta.name)) continue;
|
|
1930
|
+
let own = builds.get(meta.name);
|
|
1931
|
+
if (!own) {
|
|
1932
|
+
builds.set(
|
|
1933
|
+
meta.name,
|
|
1934
|
+
own = { coverage: /* @__PURE__ */ new Map(), tags: /* @__PURE__ */ new Map(), instance: /* @__PURE__ */ new Map() }
|
|
1935
|
+
);
|
|
1936
|
+
}
|
|
1937
|
+
if (!own.instance.has(member.name)) own.instance.set(member.name, version.tag);
|
|
1938
|
+
for (const specifier in meta.entries) {
|
|
1939
|
+
own.coverage.set(specifier, meta.entries[specifier]);
|
|
1940
|
+
if (!own.tags.has(specifier)) own.tags.set(specifier, version.tag);
|
|
1941
|
+
}
|
|
1653
1942
|
}
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1943
|
+
}
|
|
1944
|
+
}
|
|
1945
|
+
return builds;
|
|
1946
|
+
}
|
|
1947
|
+
function ownCopies(members, only) {
|
|
1948
|
+
const own = /* @__PURE__ */ new Map();
|
|
1949
|
+
for (const member of members) {
|
|
1950
|
+
for (const version of member.external.versions) {
|
|
1951
|
+
for (const meta of version.remotes) {
|
|
1952
|
+
if (only && !only.has(meta.name)) continue;
|
|
1953
|
+
let copies = own.get(meta.name);
|
|
1954
|
+
if (!copies) own.set(meta.name, copies = []);
|
|
1955
|
+
if (!copies.some((c) => c.member === member.name))
|
|
1956
|
+
copies.push({ member: member.name, tag: version.tag, entries: meta.entries });
|
|
1660
1957
|
}
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1958
|
+
}
|
|
1959
|
+
}
|
|
1960
|
+
return own;
|
|
1961
|
+
}
|
|
1962
|
+
function consumedMembers(members) {
|
|
1963
|
+
const consumed = /* @__PURE__ */ new Map();
|
|
1964
|
+
for (const member of members) {
|
|
1965
|
+
const versions = member.external.versions;
|
|
1966
|
+
for (let v = 0; v < versions.length; v++) {
|
|
1967
|
+
const remotes = versions[v].remotes;
|
|
1968
|
+
for (let r = 0; r < remotes.length; r++) {
|
|
1969
|
+
const name = remotes[r].name;
|
|
1970
|
+
const list = consumed.get(name);
|
|
1971
|
+
if (!list) consumed.set(name, [member.name]);
|
|
1972
|
+
else if (list[list.length - 1] !== member.name) list.push(member.name);
|
|
1973
|
+
}
|
|
1974
|
+
}
|
|
1975
|
+
}
|
|
1976
|
+
return consumed;
|
|
1977
|
+
}
|
|
1978
|
+
function consumedSpecifiers(members) {
|
|
1979
|
+
const consumed = /* @__PURE__ */ new Map();
|
|
1980
|
+
for (const member of members) {
|
|
1981
|
+
const versions = member.external.versions;
|
|
1982
|
+
for (let v = 0; v < versions.length; v++) {
|
|
1983
|
+
const remotes = versions[v].remotes;
|
|
1984
|
+
for (let r = 0; r < remotes.length; r++) {
|
|
1985
|
+
const meta = remotes[r];
|
|
1986
|
+
let own = consumed.get(meta.name);
|
|
1987
|
+
if (!own) consumed.set(meta.name, own = /* @__PURE__ */ new Set());
|
|
1988
|
+
for (const specifier in meta.entries) own.add(specifier);
|
|
1989
|
+
}
|
|
1990
|
+
}
|
|
1991
|
+
}
|
|
1992
|
+
return consumed;
|
|
1993
|
+
}
|
|
1994
|
+
function coversWholePool(members, build, islanded) {
|
|
1995
|
+
const served = /* @__PURE__ */ new Set();
|
|
1996
|
+
const wanted = /* @__PURE__ */ new Set();
|
|
1997
|
+
for (const member of members) {
|
|
1998
|
+
for (const version of member.external.versions) {
|
|
1999
|
+
for (const meta of version.remotes) {
|
|
2000
|
+
if (islanded.has(meta.name)) continue;
|
|
2001
|
+
const into = meta.name === build ? served : wanted;
|
|
2002
|
+
for (const specifier in meta.entries) into.add(specifier);
|
|
2003
|
+
}
|
|
2004
|
+
}
|
|
2005
|
+
}
|
|
2006
|
+
for (const specifier of wanted) if (!served.has(specifier)) return false;
|
|
2007
|
+
return true;
|
|
2008
|
+
}
|
|
2009
|
+
function servingBuilds(members, islanded) {
|
|
2010
|
+
const serving = /* @__PURE__ */ new Map();
|
|
2011
|
+
for (const member of members) {
|
|
2012
|
+
const shared = member.external.versions.find((v) => v.action === "share");
|
|
2013
|
+
const basis = shared?.remotes.find((r) => !islanded.has(r.name));
|
|
2014
|
+
if (basis) serving.set(member.name, basis.name);
|
|
2015
|
+
}
|
|
2016
|
+
return serving;
|
|
2017
|
+
}
|
|
2018
|
+
function basisPerMember(members, islanded, dedupsElsewhere) {
|
|
2019
|
+
const basis = /* @__PURE__ */ new Map();
|
|
2020
|
+
for (const member of members) {
|
|
2021
|
+
const winner = member.external.versions.find((v) => v.action === "share");
|
|
2022
|
+
if (!winner) continue;
|
|
2023
|
+
const own = winner.remotes.find(
|
|
2024
|
+
(r) => !islanded?.has(r.name) && !dedupsElsewhere?.(r.name)
|
|
2025
|
+
);
|
|
2026
|
+
if (own) basis.set(member.name, own.name);
|
|
2027
|
+
}
|
|
2028
|
+
return basis;
|
|
2029
|
+
}
|
|
2030
|
+
function hostRemotes(members) {
|
|
2031
|
+
const hosts = /* @__PURE__ */ new Set();
|
|
2032
|
+
for (const member of members) {
|
|
2033
|
+
for (const version of member.external.versions) {
|
|
2034
|
+
if (version.host && version.remotes.length > 0) hosts.add(version.remotes[0].name);
|
|
2035
|
+
}
|
|
2036
|
+
}
|
|
2037
|
+
return hosts;
|
|
2038
|
+
}
|
|
2039
|
+
function arrivalOrder(members) {
|
|
2040
|
+
const arrival = /* @__PURE__ */ new Map();
|
|
2041
|
+
for (const member of members) {
|
|
2042
|
+
for (const version of member.external.versions) {
|
|
2043
|
+
for (const meta of version.remotes) {
|
|
2044
|
+
if (!arrival.has(meta.name)) arrival.set(meta.name, arrival.size);
|
|
2045
|
+
}
|
|
2046
|
+
}
|
|
2047
|
+
}
|
|
2048
|
+
return arrival;
|
|
2049
|
+
}
|
|
2050
|
+
function sharedTagPerSpecifier(members, islanded) {
|
|
2051
|
+
const shared = /* @__PURE__ */ new Map();
|
|
2052
|
+
forEachGlobalClaim(members, islanded, (specifier, tag) => {
|
|
2053
|
+
if (!shared.has(specifier)) shared.set(specifier, tag);
|
|
2054
|
+
});
|
|
2055
|
+
return shared;
|
|
2056
|
+
}
|
|
2057
|
+
function forEachGlobalClaim(members, islanded, visit) {
|
|
2058
|
+
const accepts = islanded ? (meta) => !islanded.has(meta.name) : void 0;
|
|
2059
|
+
const claim = (version) => forEachVersionEntry(version, accepts, (specifier, meta) => visit(specifier, version.tag, meta));
|
|
2060
|
+
for (const member of members) {
|
|
2061
|
+
const versions = member.external.versions;
|
|
2062
|
+
const winner = versions.find((v) => v.action === "share");
|
|
2063
|
+
if (winner) claim(winner);
|
|
2064
|
+
for (let v = 0; v < versions.length; v++) {
|
|
2065
|
+
if (versions[v].action === "skip") claim(versions[v]);
|
|
2066
|
+
}
|
|
2067
|
+
}
|
|
2068
|
+
}
|
|
2069
|
+
|
|
2070
|
+
// src/lib/core/2.app/steps/pooling/anchoring.ts
|
|
2071
|
+
function acceptanceTable(members, isCompatible) {
|
|
2072
|
+
const table = /* @__PURE__ */ new Map();
|
|
2073
|
+
for (const member of members) {
|
|
2074
|
+
const tags = [];
|
|
2075
|
+
for (const version of member.external.versions) tags.push(version.tag);
|
|
2076
|
+
for (const version of member.external.versions) {
|
|
2077
|
+
for (const meta of version.remotes) {
|
|
2078
|
+
let byMember = table.get(meta.name);
|
|
2079
|
+
if (!byMember) table.set(meta.name, byMember = /* @__PURE__ */ new Map());
|
|
2080
|
+
if (byMember.has(member.name)) continue;
|
|
2081
|
+
const accepted = /* @__PURE__ */ new Set();
|
|
2082
|
+
for (let t = 0; t < tags.length; t++) {
|
|
2083
|
+
if (isCompatible(tags[t], meta.requiredVersion)) accepted.add(tags[t]);
|
|
1677
2084
|
}
|
|
1678
|
-
|
|
2085
|
+
byMember.set(member.name, accepted);
|
|
2086
|
+
}
|
|
1679
2087
|
}
|
|
1680
|
-
external.dirty = false;
|
|
1681
|
-
return external;
|
|
1682
2088
|
}
|
|
2089
|
+
return table;
|
|
2090
|
+
}
|
|
2091
|
+
function covers(coverage2, consumed) {
|
|
2092
|
+
for (const specifier of consumed) if (!coverage2.has(specifier)) return false;
|
|
2093
|
+
return true;
|
|
2094
|
+
}
|
|
2095
|
+
function acceptsAll(acceptance, build, consumer, consumed) {
|
|
2096
|
+
const byMember = acceptance.get(consumer);
|
|
2097
|
+
if (!byMember) return false;
|
|
2098
|
+
for (let i = 0; i < consumed.length; i++) {
|
|
2099
|
+
const offered = build.get(consumed[i]);
|
|
2100
|
+
if (offered === void 0) return false;
|
|
2101
|
+
if (!byMember.get(consumed[i])?.has(offered)) return false;
|
|
2102
|
+
}
|
|
2103
|
+
return true;
|
|
2104
|
+
}
|
|
2105
|
+
function isWitnessed(specifiers, shared, builds) {
|
|
2106
|
+
const wanted = [...specifiers];
|
|
2107
|
+
for (const specifier of wanted) if (!shared.has(specifier)) return false;
|
|
2108
|
+
for (const [, build] of builds) {
|
|
2109
|
+
let matches = true;
|
|
2110
|
+
for (let i = 0; i < wanted.length; i++) {
|
|
2111
|
+
if (build.tags.get(wanted[i]) !== shared.get(wanted[i])) {
|
|
2112
|
+
matches = false;
|
|
2113
|
+
break;
|
|
2114
|
+
}
|
|
2115
|
+
}
|
|
2116
|
+
if (matches) return true;
|
|
2117
|
+
}
|
|
2118
|
+
return false;
|
|
2119
|
+
}
|
|
2120
|
+
function explainSelfServe(remote, wants, specifiers, pool) {
|
|
2121
|
+
let closest;
|
|
2122
|
+
let fewest = Number.MAX_SAFE_INTEGER;
|
|
2123
|
+
let gap = wants[0] ?? "";
|
|
2124
|
+
for (const [build, view] of pool.builds) {
|
|
2125
|
+
if (build === remote) continue;
|
|
2126
|
+
let missing;
|
|
2127
|
+
let count = 0;
|
|
2128
|
+
for (const specifier of specifiers) {
|
|
2129
|
+
if (view.coverage.has(specifier)) continue;
|
|
2130
|
+
count++;
|
|
2131
|
+
missing ??= specifier;
|
|
2132
|
+
}
|
|
2133
|
+
if (count >= fewest) continue;
|
|
2134
|
+
fewest = count;
|
|
2135
|
+
closest = build;
|
|
2136
|
+
gap = missing ?? rejectedMember(view.instance, remote, wants, pool.acceptance) ?? gap;
|
|
2137
|
+
}
|
|
2138
|
+
return { gap, closest };
|
|
2139
|
+
}
|
|
2140
|
+
function rejectedMember(offered, consumer, wants, acceptance) {
|
|
2141
|
+
const accepts = acceptance.get(consumer);
|
|
2142
|
+
if (!accepts) return void 0;
|
|
2143
|
+
for (const member of wants) {
|
|
2144
|
+
const tag = offered.get(member);
|
|
2145
|
+
if (tag === void 0) return member;
|
|
2146
|
+
if (!accepts.get(member)?.has(tag)) return `${member}@${tag}`;
|
|
2147
|
+
}
|
|
2148
|
+
return void 0;
|
|
2149
|
+
}
|
|
2150
|
+
function assignAnchors(input) {
|
|
2151
|
+
const { builds, acceptance, consumedMembers: consumedMembers2, hosts, arrival } = input;
|
|
2152
|
+
const assignment = /* @__PURE__ */ new Map();
|
|
2153
|
+
const byPriority = (a, b) => (arrival.get(a) ?? Number.MAX_SAFE_INTEGER) - (arrival.get(b) ?? Number.MAX_SAFE_INTEGER) || a.localeCompare(b);
|
|
2154
|
+
const candidates = [...builds.keys()].sort(byPriority);
|
|
2155
|
+
const pending = new Set([...consumedMembers2.keys()].sort(byPriority));
|
|
2156
|
+
const anchored = /* @__PURE__ */ new Set();
|
|
2157
|
+
const canServe = (candidate, consumer) => {
|
|
2158
|
+
const build = builds.get(candidate);
|
|
2159
|
+
if (!covers(build.coverage, input.consumedSpecifiers.get(consumer) ?? [])) return false;
|
|
2160
|
+
return acceptsAll(acceptance, build.instance, consumer, consumedMembers2.get(consumer) ?? []);
|
|
2161
|
+
};
|
|
2162
|
+
const electAnchor = (anchor, served) => {
|
|
2163
|
+
pending.delete(anchor);
|
|
2164
|
+
assignment.set(anchor, void 0);
|
|
2165
|
+
for (const consumer of served) {
|
|
2166
|
+
pending.delete(consumer);
|
|
2167
|
+
anchored.add(consumer);
|
|
2168
|
+
assignment.set(consumer, anchor);
|
|
2169
|
+
}
|
|
2170
|
+
};
|
|
2171
|
+
for (const host of [...hosts].sort(byPriority)) {
|
|
2172
|
+
const served = [];
|
|
2173
|
+
if (builds.has(host)) {
|
|
2174
|
+
for (const consumer of pending) {
|
|
2175
|
+
if (consumer !== host && canServe(host, consumer)) served.push(consumer);
|
|
2176
|
+
}
|
|
2177
|
+
}
|
|
2178
|
+
electAnchor(host, served);
|
|
2179
|
+
}
|
|
2180
|
+
for (; ; ) {
|
|
2181
|
+
let best;
|
|
2182
|
+
let bestServed = [];
|
|
2183
|
+
for (const candidate of candidates) {
|
|
2184
|
+
if (anchored.has(candidate)) continue;
|
|
2185
|
+
const served = [];
|
|
2186
|
+
for (const consumer of pending) {
|
|
2187
|
+
if (consumer !== candidate && canServe(candidate, consumer)) served.push(consumer);
|
|
2188
|
+
}
|
|
2189
|
+
if (served.length > bestServed.length) {
|
|
2190
|
+
best = candidate;
|
|
2191
|
+
bestServed = served;
|
|
2192
|
+
}
|
|
2193
|
+
}
|
|
2194
|
+
if (best === void 0) break;
|
|
2195
|
+
electAnchor(best, bestServed);
|
|
2196
|
+
}
|
|
2197
|
+
for (const consumer of pending) assignment.set(consumer, void 0);
|
|
2198
|
+
return assignment;
|
|
1683
2199
|
}
|
|
1684
2200
|
|
|
1685
2201
|
// src/lib/core/2.app/steps/pooling/pool-graph.ts
|
|
@@ -1722,34 +2238,56 @@ function createDSU() {
|
|
|
1722
2238
|
}
|
|
1723
2239
|
var extNode = (name) => `ext\0${name}`;
|
|
1724
2240
|
var tagNode = (remote, tag) => `tag\0${remote}\0${tag}`;
|
|
1725
|
-
var scopeNode = (scope) => `scope\0${scope}`;
|
|
2241
|
+
var scopeNode = (remote, scope) => `scope\0${remote}\0${scope}`;
|
|
1726
2242
|
function autoScope(name, useAutoExternalPooling) {
|
|
1727
2243
|
return useAutoExternalPooling ? SCOPED_PACKAGE.exec(name)?.[1] : void 0;
|
|
1728
2244
|
}
|
|
2245
|
+
function owningPackage(name) {
|
|
2246
|
+
const depth = name.startsWith("@") ? 2 : 1;
|
|
2247
|
+
let cut = -1;
|
|
2248
|
+
for (let seen = 0; seen < depth; seen++) {
|
|
2249
|
+
cut = name.indexOf("/", cut + 1);
|
|
2250
|
+
if (cut === -1) return void 0;
|
|
2251
|
+
}
|
|
2252
|
+
return name.slice(0, cut);
|
|
2253
|
+
}
|
|
1729
2254
|
function groupByMembership(candidates, log) {
|
|
1730
2255
|
const dsu = createDSU();
|
|
1731
2256
|
const tagged = /* @__PURE__ */ new Set();
|
|
1732
|
-
const
|
|
2257
|
+
const joined = /* @__PURE__ */ new Set();
|
|
2258
|
+
const tagRules = /* @__PURE__ */ new Set();
|
|
2259
|
+
for (const candidate of candidates) {
|
|
2260
|
+
if (candidate.scope === void 0) continue;
|
|
2261
|
+
for (const edge of candidate.tags) tagRules.add(`${edge.remote}\0${candidate.scope}`);
|
|
2262
|
+
}
|
|
1733
2263
|
for (const candidate of candidates) {
|
|
1734
|
-
let joined = false;
|
|
1735
|
-
if (candidate.scope !== void 0) {
|
|
1736
|
-
dsu.union(extNode(candidate.name), scopeNode(candidate.scope));
|
|
1737
|
-
joined = true;
|
|
1738
|
-
}
|
|
1739
2264
|
for (const edge of candidate.tags) {
|
|
1740
2265
|
dsu.union(extNode(candidate.name), tagNode(edge.remote, edge.tag));
|
|
1741
2266
|
tagged.add(candidate.name);
|
|
1742
|
-
joined
|
|
2267
|
+
joined.add(candidate.name);
|
|
2268
|
+
}
|
|
2269
|
+
if (candidate.scope === void 0) continue;
|
|
2270
|
+
for (const remote of candidate.remotes) {
|
|
2271
|
+
if (tagRules.has(`${remote}\0${candidate.scope}`)) continue;
|
|
2272
|
+
dsu.union(extNode(candidate.name), scopeNode(remote, candidate.scope));
|
|
2273
|
+
joined.add(candidate.name);
|
|
1743
2274
|
}
|
|
1744
|
-
|
|
2275
|
+
}
|
|
2276
|
+
const declared = new Set(candidates.map((c) => c.name));
|
|
2277
|
+
for (const candidate of candidates) {
|
|
2278
|
+
const owner = owningPackage(candidate.name);
|
|
2279
|
+
if (owner !== void 0 && declared.has(owner))
|
|
2280
|
+
dsu.union(extNode(candidate.name), extNode(owner));
|
|
1745
2281
|
}
|
|
1746
2282
|
const byComponent = /* @__PURE__ */ new Map();
|
|
1747
|
-
for (const
|
|
1748
|
-
const root = dsu.component(extNode(
|
|
1749
|
-
|
|
2283
|
+
for (const candidate of candidates) {
|
|
2284
|
+
const root = dsu.component(extNode(candidate.name));
|
|
2285
|
+
const members = byComponent.get(root) ?? byComponent.set(root, []).get(root);
|
|
2286
|
+
members.push({ name: candidate.name, value: candidate.value });
|
|
1750
2287
|
}
|
|
1751
2288
|
const pools = [];
|
|
1752
2289
|
for (const members of byComponent.values()) {
|
|
2290
|
+
if (!members.some((m) => joined.has(m.name))) continue;
|
|
1753
2291
|
members.sort((a, b) => a.name.localeCompare(b.name));
|
|
1754
2292
|
if (members.length < 2) {
|
|
1755
2293
|
const only = members[0];
|
|
@@ -1777,6 +2315,7 @@ function buildPools(sharedExternals, useAutoExternalPooling, log) {
|
|
|
1777
2315
|
return tag ? [{ remote: r.name, tag }] : [];
|
|
1778
2316
|
})
|
|
1779
2317
|
),
|
|
2318
|
+
remotes: external.versions.flatMap((v) => v.remotes.map((r) => r.name)),
|
|
1780
2319
|
value: { name, external }
|
|
1781
2320
|
})
|
|
1782
2321
|
);
|
|
@@ -1784,28 +2323,131 @@ function buildPools(sharedExternals, useAutoExternalPooling, log) {
|
|
|
1784
2323
|
}
|
|
1785
2324
|
|
|
1786
2325
|
// src/lib/core/2.app/steps/pooling/pool.util.ts
|
|
2326
|
+
function lazy(make) {
|
|
2327
|
+
let value;
|
|
2328
|
+
return () => value ??= make();
|
|
2329
|
+
}
|
|
1787
2330
|
function remotesInPool(members) {
|
|
1788
2331
|
return [
|
|
1789
2332
|
...new Set(members.flatMap((m) => m.external.versions.flatMap((v) => v.remotes.map((r) => r.name))))
|
|
1790
2333
|
];
|
|
1791
2334
|
}
|
|
2335
|
+
function poolableScopes(config, repo) {
|
|
2336
|
+
const { useAutoExternalPooling } = config.feature;
|
|
2337
|
+
return {
|
|
2338
|
+
useAutoExternalPooling,
|
|
2339
|
+
scopes: repo.getScopes().filter(
|
|
2340
|
+
(scope) => repo.scopeType(scope) !== "strict" && (useAutoExternalPooling || repo.hasPoolTag(scope))
|
|
2341
|
+
)
|
|
2342
|
+
};
|
|
2343
|
+
}
|
|
1792
2344
|
|
|
1793
2345
|
// src/lib/core/2.app/steps/pooling/pool-shared-externals.ts
|
|
1794
2346
|
function islandedRemotes(members) {
|
|
1795
|
-
const islanded = /* @__PURE__ */ new
|
|
2347
|
+
const islanded = /* @__PURE__ */ new Map();
|
|
1796
2348
|
for (const member of members)
|
|
1797
2349
|
for (const version of member.external.versions)
|
|
1798
|
-
if (version.action === "scope")
|
|
2350
|
+
if (version.action === "scope") {
|
|
2351
|
+
for (const remote of version.remotes)
|
|
2352
|
+
if (!islanded.has(remote.name))
|
|
2353
|
+
islanded.set(remote.name, {
|
|
2354
|
+
kind: "incompatible",
|
|
2355
|
+
member: member.name,
|
|
2356
|
+
tag: version.tag
|
|
2357
|
+
});
|
|
2358
|
+
}
|
|
1799
2359
|
return islanded;
|
|
1800
2360
|
}
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
2361
|
+
var basisFor = (members, islanded, serving) => basisPerMember(members, islanded, (remote) => {
|
|
2362
|
+
const build = serving.get(remote);
|
|
2363
|
+
return build !== void 0 && build !== remote;
|
|
2364
|
+
});
|
|
2365
|
+
function servedPerRemote(consumed, serving, basis) {
|
|
2366
|
+
const served = /* @__PURE__ */ new Map();
|
|
2367
|
+
for (const [remote, build] of serving) {
|
|
2368
|
+
const byMember = /* @__PURE__ */ new Map();
|
|
2369
|
+
for (const name of consumed.get(remote) ?? []) {
|
|
2370
|
+
if (basis.get(name) !== build) byMember.set(name, build);
|
|
2371
|
+
}
|
|
2372
|
+
if (byMember.size > 0) served.set(remote, byMember);
|
|
2373
|
+
}
|
|
2374
|
+
return served;
|
|
2375
|
+
}
|
|
2376
|
+
var anyAnchorStored = (members) => members.some((m) => m.external.versions.some((v) => v.remotes.some((r) => r.servedBy !== void 0)));
|
|
2377
|
+
function findTornRemotes(members, islanded, basis, served, hosts) {
|
|
2378
|
+
const { suspect, selfServed } = movedRemotes(members, islanded, basis, served, hosts);
|
|
2379
|
+
if (suspect.size === 0) return [];
|
|
2380
|
+
const builds = liveBuilds(members, islanded);
|
|
2381
|
+
const shared = sharedTagPerSpecifier(members, islanded);
|
|
2382
|
+
const own = ownCopies(members, suspect);
|
|
2383
|
+
const torn = [];
|
|
2384
|
+
for (const remote of suspect) {
|
|
2385
|
+
const copies = own.get(remote) ?? [];
|
|
2386
|
+
const mine = selfServed.get(remote);
|
|
2387
|
+
const anchors = served.get(remote);
|
|
2388
|
+
const ownTags = /* @__PURE__ */ new Map();
|
|
2389
|
+
const resolved = /* @__PURE__ */ new Map();
|
|
2390
|
+
for (const copy of copies) {
|
|
2391
|
+
const from = mine?.has(copy.member) ? remote : anchors?.get(copy.member);
|
|
2392
|
+
for (const specifier in copy.entries) {
|
|
2393
|
+
if (!ownTags.has(specifier)) ownTags.set(specifier, copy.tag);
|
|
2394
|
+
const tag = from === remote ? copy.tag : from !== void 0 ? builds.get(from)?.tags.get(specifier) : shared.get(specifier);
|
|
2395
|
+
if (tag !== void 0 && !resolved.has(specifier)) resolved.set(specifier, tag);
|
|
2396
|
+
}
|
|
2397
|
+
}
|
|
2398
|
+
if (resolved.size === 0) continue;
|
|
2399
|
+
if (ships(ownTags, resolved)) continue;
|
|
2400
|
+
let witnessed = false;
|
|
2401
|
+
for (const [, build] of builds) {
|
|
2402
|
+
if (ships(build.tags, resolved)) {
|
|
2403
|
+
witnessed = true;
|
|
2404
|
+
break;
|
|
2405
|
+
}
|
|
1806
2406
|
}
|
|
1807
|
-
|
|
1808
|
-
|
|
2407
|
+
if (!witnessed) {
|
|
2408
|
+
torn.push({
|
|
2409
|
+
remote,
|
|
2410
|
+
combination: [...resolved].map(([specifier, tag]) => `${specifier}@${tag}`).join(", ")
|
|
2411
|
+
});
|
|
2412
|
+
}
|
|
2413
|
+
}
|
|
2414
|
+
return torn;
|
|
2415
|
+
}
|
|
2416
|
+
var ships = (build, resolved) => {
|
|
2417
|
+
if (!build) return false;
|
|
2418
|
+
for (const [specifier, tag] of resolved) if (build.get(specifier) !== tag) return false;
|
|
2419
|
+
return true;
|
|
2420
|
+
};
|
|
2421
|
+
function movedRemotes(members, islanded, basis, served, hosts) {
|
|
2422
|
+
const suspect = /* @__PURE__ */ new Set();
|
|
2423
|
+
const selfServed = /* @__PURE__ */ new Map();
|
|
2424
|
+
const add = (remote) => {
|
|
2425
|
+
if (!islanded.has(remote) && !hosts.has(remote)) suspect.add(remote);
|
|
2426
|
+
};
|
|
2427
|
+
for (const remote of served.keys()) add(remote);
|
|
2428
|
+
for (const member of members) {
|
|
2429
|
+
const unpublished = !basis.has(member.name);
|
|
2430
|
+
for (const version of member.external.versions) {
|
|
2431
|
+
const scoped = version.action === "scope";
|
|
2432
|
+
if (!unpublished && !scoped) continue;
|
|
2433
|
+
for (const meta of version.remotes) {
|
|
2434
|
+
add(meta.name);
|
|
2435
|
+
const servesItself = scoped || unpublished && served.get(meta.name)?.get(member.name) === void 0;
|
|
2436
|
+
if (!servesItself) continue;
|
|
2437
|
+
let mine = selfServed.get(meta.name);
|
|
2438
|
+
if (!mine) selfServed.set(meta.name, mine = /* @__PURE__ */ new Set());
|
|
2439
|
+
mine.add(member.name);
|
|
2440
|
+
}
|
|
2441
|
+
}
|
|
2442
|
+
}
|
|
2443
|
+
return { suspect, selfServed };
|
|
2444
|
+
}
|
|
2445
|
+
function createPoolSharedExternals(config, ports) {
|
|
2446
|
+
return (touched) => {
|
|
2447
|
+
const { useAutoExternalPooling, scopes } = poolableScopes(config, ports.sharedExternalsRepo);
|
|
2448
|
+
for (const scope of scopes) {
|
|
2449
|
+
const touchedInScope = touched?.get(scope);
|
|
2450
|
+
if (touched && !touchedInScope) continue;
|
|
1809
2451
|
const sharedExternals = ports.sharedExternalsRepo.getFromScope(scope);
|
|
1810
2452
|
try {
|
|
1811
2453
|
for (const [poolName, members] of buildPools(
|
|
@@ -1813,6 +2455,7 @@ function createPoolSharedExternals(config, ports) {
|
|
|
1813
2455
|
useAutoExternalPooling,
|
|
1814
2456
|
config.log
|
|
1815
2457
|
)) {
|
|
2458
|
+
if (touchedInScope && !members.some((m) => touchedInScope.has(m.name))) continue;
|
|
1816
2459
|
poolFamily(poolName, members, scope);
|
|
1817
2460
|
}
|
|
1818
2461
|
} catch (error) {
|
|
@@ -1833,45 +2476,138 @@ function createPoolSharedExternals(config, ports) {
|
|
|
1833
2476
|
const allRemotes = remotesInPool(members);
|
|
1834
2477
|
if (allRemotes.length < 2) return;
|
|
1835
2478
|
const islanded = islandedRemotes(members);
|
|
2479
|
+
const consumed = consumedMembers(members);
|
|
2480
|
+
const hosts = hostRemotes(members);
|
|
2481
|
+
const views = {
|
|
2482
|
+
consumedSpecifiers: lazy(() => consumedSpecifiers(members)),
|
|
2483
|
+
acceptance: lazy(() => acceptanceTable(members, ports.versionCheck.isCompatible)),
|
|
2484
|
+
arrival: lazy(() => arrivalOrder(members))
|
|
2485
|
+
};
|
|
1836
2486
|
config.log.debug(
|
|
1837
2487
|
3,
|
|
1838
|
-
`[${scope}][pool:${poolName}] ${members.length} members across ${allRemotes.length} remotes,
|
|
2488
|
+
`[${scope}][pool:${poolName}] ${members.length} members across ${allRemotes.length} remotes, incompatible={${[...islanded.keys()].join(", ") || "\u2205"}}
|
|
1839
2489
|
` + members.map((m) => ` - ${m.name}`).join("\n")
|
|
1840
2490
|
);
|
|
1841
|
-
|
|
2491
|
+
const assign = () => {
|
|
2492
|
+
const serving2 = assignServingBuilds(members, islanded, consumed, hosts, views);
|
|
2493
|
+
const basis2 = basisFor(members, islanded, serving2);
|
|
2494
|
+
return { serving: serving2, basis: basis2, served: servedPerRemote(consumed, serving2, basis2) };
|
|
2495
|
+
};
|
|
2496
|
+
let { serving, basis, served } = assign();
|
|
2497
|
+
if (islanded.size === 0 && serving.size === 0 && !anyAnchorStored(members)) return;
|
|
2498
|
+
for (; ; ) {
|
|
2499
|
+
const torn = findTornRemotes(members, islanded, basis, served, hosts);
|
|
2500
|
+
if (torn.length === 0) break;
|
|
2501
|
+
for (const { remote, combination } of torn)
|
|
2502
|
+
islanded.set(remote, { kind: "torn", combination });
|
|
2503
|
+
({ serving, basis, served } = assign());
|
|
2504
|
+
}
|
|
2505
|
+
const incompatible = [...islanded].filter(([, cause]) => cause.kind === "incompatible").map(([remote]) => remote);
|
|
2506
|
+
if (config.strict.strictExternalCompatibility && incompatible.length > 0) {
|
|
1842
2507
|
config.log.error(
|
|
1843
2508
|
3,
|
|
1844
|
-
`[${scope}][pool:${poolName}] version-incompatible remotes cannot be pooled: {${
|
|
2509
|
+
`[${scope}][pool:${poolName}] version-incompatible remotes cannot be pooled: {${incompatible.join(", ")}}.`
|
|
1845
2510
|
);
|
|
1846
2511
|
throw new NFError(`Could not pool '${poolName}' in scope ${scope}.`);
|
|
1847
2512
|
}
|
|
2513
|
+
for (const [remote, cause] of islanded) {
|
|
2514
|
+
const scoped = consumed.get(remote)?.length ?? 0;
|
|
2515
|
+
config.log.warn(3, `[${scope}][pool:${poolName}] ${islandWarning(remote, cause, scoped)}`);
|
|
2516
|
+
}
|
|
1848
2517
|
for (const member of members) {
|
|
1849
|
-
const rebuilt = rebuildMember(member, islanded);
|
|
1850
|
-
warnIfScopedOnly(poolName, member
|
|
2518
|
+
const rebuilt = rebuildMember(member, islanded, served, basis.get(member.name));
|
|
2519
|
+
warnIfScopedOnly(poolName, member, rebuilt, islanded, scope);
|
|
1851
2520
|
ports.sharedExternalsRepo.addOrUpdate(member.name, rebuilt, scope);
|
|
1852
2521
|
}
|
|
1853
2522
|
}
|
|
1854
|
-
function
|
|
2523
|
+
function assignServingBuilds(members, islanded, consumed, hosts, views) {
|
|
2524
|
+
for (; ; ) {
|
|
2525
|
+
const bases = servingBuilds(members, islanded);
|
|
2526
|
+
const distinct = new Set(bases.values());
|
|
2527
|
+
if (distinct.size === 1 && bases.size === members.length) {
|
|
2528
|
+
const sole = [...distinct][0];
|
|
2529
|
+
if (coversWholePool(members, sole, islanded)) return /* @__PURE__ */ new Map();
|
|
2530
|
+
}
|
|
2531
|
+
const builds = liveBuilds(members, islanded);
|
|
2532
|
+
const shared = sharedTagPerSpecifier(members, islanded);
|
|
2533
|
+
const consumedSpec = views.consumedSpecifiers();
|
|
2534
|
+
const needAnchor = /* @__PURE__ */ new Map();
|
|
2535
|
+
const needSpecifiers = /* @__PURE__ */ new Map();
|
|
2536
|
+
for (const [remote, wants] of consumed) {
|
|
2537
|
+
if (islanded.has(remote)) continue;
|
|
2538
|
+
if (isWitnessed(consumedSpec.get(remote) ?? [], shared, builds)) continue;
|
|
2539
|
+
needAnchor.set(remote, wants);
|
|
2540
|
+
needSpecifiers.set(remote, consumedSpec.get(remote) ?? /* @__PURE__ */ new Set());
|
|
2541
|
+
}
|
|
2542
|
+
if (needAnchor.size === 0) return /* @__PURE__ */ new Map();
|
|
2543
|
+
const acceptance = views.acceptance();
|
|
2544
|
+
const assignment = assignAnchors({
|
|
2545
|
+
builds,
|
|
2546
|
+
acceptance,
|
|
2547
|
+
consumedSpecifiers: needSpecifiers,
|
|
2548
|
+
consumedMembers: needAnchor,
|
|
2549
|
+
hosts,
|
|
2550
|
+
arrival: views.arrival()
|
|
2551
|
+
});
|
|
2552
|
+
const anchors = /* @__PURE__ */ new Set();
|
|
2553
|
+
for (const build of assignment.values()) if (build !== void 0) anchors.add(build);
|
|
2554
|
+
const serving = /* @__PURE__ */ new Map();
|
|
2555
|
+
let scoped = false;
|
|
2556
|
+
for (const [remote, wants] of needAnchor) {
|
|
2557
|
+
const assigned = assignment.get(remote);
|
|
2558
|
+
const build = assigned ?? (anchors.has(remote) || hosts.has(remote) ? remote : void 0);
|
|
2559
|
+
if (build === void 0) {
|
|
2560
|
+
islanded.set(remote, {
|
|
2561
|
+
kind: "uncovered",
|
|
2562
|
+
...explainSelfServe(remote, wants, needSpecifiers.get(remote), { builds, acceptance })
|
|
2563
|
+
});
|
|
2564
|
+
scoped = true;
|
|
2565
|
+
continue;
|
|
2566
|
+
}
|
|
2567
|
+
serving.set(remote, build);
|
|
2568
|
+
}
|
|
2569
|
+
if (!scoped) return serving;
|
|
2570
|
+
}
|
|
2571
|
+
}
|
|
2572
|
+
function warnIfScopedOnly(poolName, member, rebuilt, islanded, scope) {
|
|
1855
2573
|
if (rebuilt.versions.some((v) => v.action === "share")) return;
|
|
1856
|
-
const consumers = new Set(
|
|
2574
|
+
const consumers = new Set(
|
|
2575
|
+
rebuilt.versions.filter((v) => v.action === "scope").flatMap((v) => v.remotes.map((r) => r.name))
|
|
2576
|
+
).size;
|
|
1857
2577
|
if (consumers < 2) return;
|
|
2578
|
+
const provider = member.external.versions.find((v) => v.action === "share");
|
|
2579
|
+
if (provider?.remotes.some((r) => islanded.has(r.name))) return;
|
|
1858
2580
|
config.log.warn(
|
|
1859
2581
|
3,
|
|
1860
|
-
`[${scope}][pool:${poolName}] '${
|
|
2582
|
+
`[${scope}][pool:${poolName}] '${member.name}' is scoped-only \u2014 no coherent shared build provides it; ${consumers} remotes download their own copy.`
|
|
1861
2583
|
);
|
|
1862
2584
|
}
|
|
1863
|
-
function rebuildMember(member, islanded) {
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
)
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
2585
|
+
function rebuildMember(member, islanded, served, basis) {
|
|
2586
|
+
let scoped = [];
|
|
2587
|
+
let clean = [];
|
|
2588
|
+
for (const version of member.external.versions) {
|
|
2589
|
+
for (const meta of version.remotes) {
|
|
2590
|
+
const entry = {
|
|
2591
|
+
remote: meta.name,
|
|
2592
|
+
tag: version.tag,
|
|
2593
|
+
host: version.host,
|
|
2594
|
+
action: version.action,
|
|
2595
|
+
meta
|
|
2596
|
+
};
|
|
2597
|
+
(islanded.has(entry.remote) || entry.action === "scope" ? scoped : clean).push(entry);
|
|
2598
|
+
}
|
|
2599
|
+
}
|
|
2600
|
+
for (const entry of scoped) delete entry.meta.servedBy;
|
|
2601
|
+
for (const entry of clean) {
|
|
2602
|
+
const build = served.get(entry.remote)?.get(member.name);
|
|
2603
|
+
if (build !== void 0) entry.meta.servedBy = build;
|
|
2604
|
+
else delete entry.meta.servedBy;
|
|
2605
|
+
}
|
|
2606
|
+
if (basis === void 0) {
|
|
2607
|
+
scoped = [...scoped, ...clean.filter((e) => e.meta.servedBy === void 0)];
|
|
2608
|
+
clean = clean.filter((e) => e.meta.servedBy !== void 0).map((e) => ({ ...e, action: "skip" }));
|
|
2609
|
+
}
|
|
2610
|
+
const shareEntries = clean.filter((e) => e.action === "share").sort((a, b) => Number(b.remote === basis) - Number(a.remote === basis));
|
|
1875
2611
|
const shareVersion = shareEntries.length > 0 ? [
|
|
1876
2612
|
{
|
|
1877
2613
|
tag: shareEntries[0].tag,
|
|
@@ -1880,37 +2616,70 @@ function createPoolSharedExternals(config, ports) {
|
|
|
1880
2616
|
remotes: shareEntries.map((e) => e.meta)
|
|
1881
2617
|
}
|
|
1882
2618
|
] : [];
|
|
1883
|
-
const
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
const version = scopeByTag.get(e.tag) ?? {
|
|
1898
|
-
tag: e.tag,
|
|
1899
|
-
host: false,
|
|
1900
|
-
action: "scope",
|
|
1901
|
-
remotes: []
|
|
1902
|
-
};
|
|
1903
|
-
version.remotes.push(e.meta);
|
|
1904
|
-
scopeByTag.set(e.tag, version);
|
|
1905
|
-
}
|
|
2619
|
+
const byTag = (entries, action) => {
|
|
2620
|
+
const versions = /* @__PURE__ */ new Map();
|
|
2621
|
+
for (const entry of entries) {
|
|
2622
|
+
const version = versions.get(entry.tag) ?? {
|
|
2623
|
+
tag: entry.tag,
|
|
2624
|
+
host: entry.host,
|
|
2625
|
+
action,
|
|
2626
|
+
remotes: []
|
|
2627
|
+
};
|
|
2628
|
+
version.remotes.push(entry.meta);
|
|
2629
|
+
versions.set(entry.tag, version);
|
|
2630
|
+
}
|
|
2631
|
+
return [...versions.values()];
|
|
2632
|
+
};
|
|
1906
2633
|
return {
|
|
1907
2634
|
dirty: false,
|
|
1908
|
-
versions: [
|
|
2635
|
+
versions: [
|
|
2636
|
+
...shareVersion,
|
|
2637
|
+
...byTag(
|
|
2638
|
+
clean.filter((e) => e.action === "skip"),
|
|
2639
|
+
"skip"
|
|
2640
|
+
),
|
|
2641
|
+
...byTag(scoped, "scope")
|
|
2642
|
+
].sort((a, b) => ports.versionCheck.compare(b.tag, a.tag))
|
|
1909
2643
|
};
|
|
1910
2644
|
}
|
|
1911
2645
|
}
|
|
2646
|
+
function islandWarning(remote, cause, scoped) {
|
|
2647
|
+
switch (cause.kind) {
|
|
2648
|
+
case "incompatible":
|
|
2649
|
+
return `'${remote}' is islanded: the resolver scoped its '${cause.member}@${cause.tag}', so all ${scoped} members it imports are scoped for it.`;
|
|
2650
|
+
case "torn":
|
|
2651
|
+
return `'${remote}' serves its own family: the mapping would have handed it ${cause.combination}, which no build shipped together, so all ${scoped} members it imports are scoped for it.`;
|
|
2652
|
+
default: {
|
|
2653
|
+
const closest = cause.closest ? `closest is '${cause.closest}'` : "no other build in the pool serves any of it";
|
|
2654
|
+
return `'${remote}' serves its own family: no shared build offers every entrypoint it imports at a version it accepts \u2014 '${cause.gap}' is the gap, ${closest}. All ${scoped} members it imports are scoped for it.`;
|
|
2655
|
+
}
|
|
2656
|
+
}
|
|
2657
|
+
}
|
|
1912
2658
|
|
|
1913
|
-
//
|
|
2659
|
+
// src/lib/core/2.app/steps/pooling/mark-pools-for-reelection.ts
|
|
2660
|
+
function createMarkPoolsForReelection(config, ports) {
|
|
2661
|
+
return () => {
|
|
2662
|
+
const { useAutoExternalPooling, scopes } = poolableScopes(config, ports.sharedExternalsRepo);
|
|
2663
|
+
for (const scope of scopes) {
|
|
2664
|
+
const sharedExternals = ports.sharedExternalsRepo.getFromScope(scope);
|
|
2665
|
+
if (!Object.values(sharedExternals).some((external) => external.dirty)) continue;
|
|
2666
|
+
let spread = 0;
|
|
2667
|
+
for (const [, members] of buildPools(sharedExternals, useAutoExternalPooling)) {
|
|
2668
|
+
if (!members.some((m) => m.external.dirty)) continue;
|
|
2669
|
+
for (const member of members)
|
|
2670
|
+
if (!member.external.dirty) {
|
|
2671
|
+
member.external.dirty = true;
|
|
2672
|
+
spread++;
|
|
2673
|
+
}
|
|
2674
|
+
}
|
|
2675
|
+
if (spread > 0)
|
|
2676
|
+
config.log.debug(3, `[${scope}] ${spread} pool member(s) marked dirty for re-election.`);
|
|
2677
|
+
}
|
|
2678
|
+
return Promise.resolve();
|
|
2679
|
+
};
|
|
2680
|
+
}
|
|
2681
|
+
|
|
2682
|
+
// node_modules/.pnpm/@softarc+native-federation@4.4.0_typescript@6.0.3/node_modules/@softarc/native-federation/dist/lib/domain/core/build-notification-options.contract.js
|
|
1914
2683
|
var BuildNotificationType = /* @__PURE__ */ ((BuildNotificationType2) => {
|
|
1915
2684
|
BuildNotificationType2["COMPLETED"] = "federation-rebuild-complete";
|
|
1916
2685
|
BuildNotificationType2["ERROR"] = "federation-rebuild-error";
|
|
@@ -1918,7 +2687,7 @@ var BuildNotificationType = /* @__PURE__ */ ((BuildNotificationType2) => {
|
|
|
1918
2687
|
return BuildNotificationType2;
|
|
1919
2688
|
})(BuildNotificationType || {});
|
|
1920
2689
|
|
|
1921
|
-
// node_modules/.pnpm/@softarc+native-federation@4.
|
|
2690
|
+
// node_modules/.pnpm/@softarc+native-federation@4.4.0_typescript@6.0.3/node_modules/@softarc/native-federation/dist/lib/domain/core/chunk.js
|
|
1922
2691
|
var CHUNK_PREFIX = "@nf-internal";
|
|
1923
2692
|
function toChunkImport(fileName) {
|
|
1924
2693
|
if (fileName.startsWith("./")) {
|
|
@@ -1979,9 +2748,10 @@ function createGenerateImportMap(config, ports) {
|
|
|
1979
2748
|
}
|
|
1980
2749
|
function processshareScope(importMap, shareScope, chunkBundles) {
|
|
1981
2750
|
const sharedExternals = ports.sharedExternalsRepo.getFromScope(shareScope);
|
|
2751
|
+
const index = buildServedIndex(sharedExternals);
|
|
1982
2752
|
for (const [externalName, external] of Object.entries(sharedExternals)) {
|
|
1983
2753
|
let override = void 0;
|
|
1984
|
-
|
|
2754
|
+
const cachedBefore = cachedFingerprint(external);
|
|
1985
2755
|
for (const version of external.versions) {
|
|
1986
2756
|
if (version.action === "scope") {
|
|
1987
2757
|
for (const remote of version.remotes) {
|
|
@@ -1997,37 +2767,53 @@ function createGenerateImportMap(config, ports) {
|
|
|
1997
2767
|
continue;
|
|
1998
2768
|
}
|
|
1999
2769
|
version.remotes[0].cached = true;
|
|
2000
|
-
let
|
|
2001
|
-
|
|
2002
|
-
if (version.action === "share") {
|
|
2003
|
-
registerBundleChunks(chunkBundles, source.name, source.bundle);
|
|
2004
|
-
} else if (version.action === "skip") {
|
|
2770
|
+
let serving = version;
|
|
2771
|
+
if (version.action === "skip") {
|
|
2005
2772
|
if (!override) {
|
|
2006
2773
|
override = findOverride(external, shareScope, externalName) ?? "NOT_AVAILABLE";
|
|
2007
2774
|
}
|
|
2008
2775
|
if (override !== "NOT_AVAILABLE") {
|
|
2009
|
-
|
|
2010
|
-
overrideScope = getScope2(shareScope, override.remotes[0].name, externalName);
|
|
2011
|
-
source = override.remotes[0];
|
|
2012
|
-
sourceScope = overrideScope;
|
|
2013
|
-
override.remotes[0].cached = true;
|
|
2776
|
+
serving = override;
|
|
2014
2777
|
version.remotes[0].cached = false;
|
|
2015
2778
|
}
|
|
2016
2779
|
}
|
|
2017
|
-
const
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2780
|
+
const provided = versionEntries(serving);
|
|
2781
|
+
const mappings = [];
|
|
2782
|
+
for (const [packageName, provider] of provided) {
|
|
2783
|
+
const file = provider.entries[packageName];
|
|
2784
|
+
mappings.push({
|
|
2785
|
+
packageName,
|
|
2786
|
+
url: join(getScope2(shareScope, provider.name, externalName), file),
|
|
2787
|
+
name: provider.name,
|
|
2788
|
+
file
|
|
2789
|
+
});
|
|
2790
|
+
provider.cached = true;
|
|
2791
|
+
if (version.action === "share") {
|
|
2792
|
+
registerBundleChunks(chunkBundles, provider.name, provider.bundle);
|
|
2793
|
+
}
|
|
2794
|
+
}
|
|
2023
2795
|
version.remotes.forEach((r) => {
|
|
2024
2796
|
const rScope = getScope2(shareScope, r.name, externalName);
|
|
2797
|
+
if (index && r.servedBy) {
|
|
2798
|
+
const files = index.get(r.servedBy);
|
|
2799
|
+
const servedScope = getScope2(shareScope, r.servedBy, externalName);
|
|
2800
|
+
for (const specifier of Object.keys(r.entries)) {
|
|
2801
|
+
const source = files?.get(specifier);
|
|
2802
|
+
if (!source) continue;
|
|
2803
|
+
const url = join(servedScope, source.file);
|
|
2804
|
+
addToScope(importMap, rScope, { [specifier]: url });
|
|
2805
|
+
addIntegrity(importMap, url, r.servedBy, source.file);
|
|
2806
|
+
registerBundleChunks(chunkBundles, r.servedBy, source.bundle);
|
|
2807
|
+
source.meta.cached = true;
|
|
2808
|
+
}
|
|
2809
|
+
return;
|
|
2810
|
+
}
|
|
2025
2811
|
for (const m of mappings) {
|
|
2026
2812
|
addToScope(importMap, rScope, { [m.packageName]: m.url });
|
|
2027
2813
|
}
|
|
2028
2814
|
for (const [packageName, file] of Object.entries(r.entries)) {
|
|
2029
|
-
if (packageName
|
|
2030
|
-
if (config.strict.strictEntryPointCoverage) {
|
|
2815
|
+
if (provided.has(packageName)) continue;
|
|
2816
|
+
if (config.strict.strictEntryPointCoverage || config.profile.scopeUncoveredEntrypoints) {
|
|
2031
2817
|
warnUncoveredEntrypoint(shareScope, externalName, r.name, packageName);
|
|
2032
2818
|
continue;
|
|
2033
2819
|
}
|
|
@@ -2041,9 +2827,14 @@ function createGenerateImportMap(config, ports) {
|
|
|
2041
2827
|
addIntegrity(importMap, m.url, m.name, m.file);
|
|
2042
2828
|
}
|
|
2043
2829
|
}
|
|
2044
|
-
|
|
2830
|
+
if (cachedFingerprint(external) !== cachedBefore) {
|
|
2831
|
+
ports.sharedExternalsRepo.addOrUpdate(externalName, external, shareScope);
|
|
2832
|
+
}
|
|
2045
2833
|
}
|
|
2046
2834
|
}
|
|
2835
|
+
function cachedFingerprint(external) {
|
|
2836
|
+
return external.versions.map((v) => v.remotes.map((r) => r.cached ? 1 : 0).join("")).join("|");
|
|
2837
|
+
}
|
|
2047
2838
|
function findOverride(external, shareScope, externalName) {
|
|
2048
2839
|
const sharedVersions = external.versions.filter((v) => v.action === "share");
|
|
2049
2840
|
const scopedExternalName = `${shareScope}.${externalName}`;
|
|
@@ -2072,10 +2863,68 @@ function createGenerateImportMap(config, ports) {
|
|
|
2072
2863
|
}
|
|
2073
2864
|
config.log.warn(4, `ShareScope external ${scopedExternalName} has multiple shared versions.`);
|
|
2074
2865
|
}
|
|
2866
|
+
function buildServedIndex(externals) {
|
|
2867
|
+
let anyServed = false;
|
|
2868
|
+
for (const external of Object.values(externals)) {
|
|
2869
|
+
for (const version of external.versions) {
|
|
2870
|
+
if (version.remotes.some((r) => r.servedBy)) anyServed = true;
|
|
2871
|
+
}
|
|
2872
|
+
}
|
|
2873
|
+
if (!anyServed) return void 0;
|
|
2874
|
+
const index = /* @__PURE__ */ new Map();
|
|
2875
|
+
for (const external of Object.values(externals)) {
|
|
2876
|
+
for (const version of external.versions) {
|
|
2877
|
+
for (const remote of version.remotes) {
|
|
2878
|
+
let own = index.get(remote.name);
|
|
2879
|
+
if (!own) index.set(remote.name, own = /* @__PURE__ */ new Map());
|
|
2880
|
+
for (const [specifier, file] of Object.entries(remote.entries)) {
|
|
2881
|
+
if (!own.has(specifier))
|
|
2882
|
+
own.set(specifier, { file, bundle: remote.bundle, meta: remote });
|
|
2883
|
+
}
|
|
2884
|
+
}
|
|
2885
|
+
}
|
|
2886
|
+
}
|
|
2887
|
+
return index;
|
|
2888
|
+
}
|
|
2889
|
+
function collectServed(into, index, ctx, externalName, version) {
|
|
2890
|
+
for (const consumer of version.remotes) {
|
|
2891
|
+
if (!consumer.servedBy) continue;
|
|
2892
|
+
const files = index.get(consumer.servedBy);
|
|
2893
|
+
const scope = getScope2(ctx, consumer.name, externalName);
|
|
2894
|
+
for (const specifier of Object.keys(consumer.entries)) {
|
|
2895
|
+
const source = files?.get(specifier);
|
|
2896
|
+
if (!source) {
|
|
2897
|
+
config.log.warn(
|
|
2898
|
+
4,
|
|
2899
|
+
`[${ctx}][${externalName}][${consumer.name}] '${consumer.servedBy}' does not serve '${specifier}'.`
|
|
2900
|
+
);
|
|
2901
|
+
continue;
|
|
2902
|
+
}
|
|
2903
|
+
into.push({ scope, specifier, source, from: consumer.servedBy });
|
|
2904
|
+
}
|
|
2905
|
+
}
|
|
2906
|
+
}
|
|
2907
|
+
function flushServed(importMap, chunkBundles, served) {
|
|
2908
|
+
for (const { scope, specifier, source, from } of served) {
|
|
2909
|
+
const url = join(getScope2(GLOBAL_SCOPE, from), source.file);
|
|
2910
|
+
if (importMap.imports[specifier] === url) continue;
|
|
2911
|
+
addToScope(importMap, scope, { [specifier]: url });
|
|
2912
|
+
addIntegrity(importMap, url, from, source.file);
|
|
2913
|
+
registerBundleChunks(chunkBundles, from, source.bundle);
|
|
2914
|
+
source.meta.cached = true;
|
|
2915
|
+
}
|
|
2916
|
+
}
|
|
2075
2917
|
function addGlobalSharedExternals(importMap, chunkBundles) {
|
|
2076
2918
|
const sharedExternals = ports.sharedExternalsRepo.getFromScope();
|
|
2919
|
+
const index = buildServedIndex(sharedExternals);
|
|
2920
|
+
const served = [];
|
|
2921
|
+
const cachedBefore = /* @__PURE__ */ new Map();
|
|
2077
2922
|
for (const [externalName, external] of Object.entries(sharedExternals)) {
|
|
2923
|
+
cachedBefore.set(externalName, cachedFingerprint(external));
|
|
2078
2924
|
for (const version of external.versions) {
|
|
2925
|
+
if (index && version.action !== "scope") {
|
|
2926
|
+
collectServed(served, index, GLOBAL_SCOPE, externalName, version);
|
|
2927
|
+
}
|
|
2079
2928
|
if (version.action === "skip") continue;
|
|
2080
2929
|
if (version.action === "scope") {
|
|
2081
2930
|
for (const remote of version.remotes) {
|
|
@@ -2094,39 +2943,64 @@ function createGenerateImportMap(config, ports) {
|
|
|
2094
2943
|
notifyDuplicateGlobalExternal(externalName);
|
|
2095
2944
|
continue;
|
|
2096
2945
|
}
|
|
2097
|
-
|
|
2098
|
-
for (const [packageName, file] of Object.entries(version.remotes[0].entries)) {
|
|
2099
|
-
const url = join(scope, file);
|
|
2100
|
-
addToGlobal(importMap, { [packageName]: url });
|
|
2101
|
-
addIntegrity(importMap, url, version.remotes[0].name, file);
|
|
2102
|
-
}
|
|
2103
|
-
registerBundleChunks(chunkBundles, version.remotes[0].name, version.remotes[0].bundle);
|
|
2104
|
-
version.remotes[0].cached = true;
|
|
2946
|
+
mergeVersionEntries(importMap, chunkBundles, externalName, version);
|
|
2105
2947
|
}
|
|
2106
2948
|
for (const version of external.versions) {
|
|
2107
2949
|
if (version.action !== "skip") continue;
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
const scope = getScope2(GLOBAL_SCOPE, remote.name, externalName);
|
|
2116
|
-
const url = join(scope, file);
|
|
2117
|
-
addToGlobal(importMap, { [packageName]: url });
|
|
2118
|
-
addIntegrity(importMap, url, remote.name, file);
|
|
2119
|
-
registerBundleChunks(chunkBundles, remote.name, remote.bundle);
|
|
2120
|
-
remote.cached = true;
|
|
2121
|
-
}
|
|
2950
|
+
selfFillUncovered(importMap, chunkBundles, externalName, version.remotes);
|
|
2951
|
+
}
|
|
2952
|
+
}
|
|
2953
|
+
flushServed(importMap, chunkBundles, served);
|
|
2954
|
+
for (const [externalName, external] of Object.entries(sharedExternals)) {
|
|
2955
|
+
if (cachedFingerprint(external) !== cachedBefore.get(externalName)) {
|
|
2956
|
+
ports.sharedExternalsRepo.addOrUpdate(externalName, external);
|
|
2122
2957
|
}
|
|
2123
|
-
ports.sharedExternalsRepo.addOrUpdate(externalName, external);
|
|
2124
2958
|
}
|
|
2125
2959
|
return importMap;
|
|
2126
2960
|
}
|
|
2961
|
+
function mergeVersionEntries(importMap, chunkBundles, externalName, version) {
|
|
2962
|
+
const scopes = /* @__PURE__ */ new Map();
|
|
2963
|
+
for (const [packageName, remote] of versionEntries(version)) {
|
|
2964
|
+
if (importMap.imports[packageName]) {
|
|
2965
|
+
config.log.debug(
|
|
2966
|
+
4,
|
|
2967
|
+
`[${GLOBAL_SCOPE}][${externalName}][${remote.name}] Entrypoint '${packageName}' is already mapped by another external, keeping '${importMap.imports[packageName]}'.`
|
|
2968
|
+
);
|
|
2969
|
+
continue;
|
|
2970
|
+
}
|
|
2971
|
+
let scope = scopes.get(remote.name);
|
|
2972
|
+
if (scope === void 0) {
|
|
2973
|
+
scopes.set(remote.name, scope = getScope2(GLOBAL_SCOPE, remote.name, externalName));
|
|
2974
|
+
registerBundleChunks(chunkBundles, remote.name, remote.bundle);
|
|
2975
|
+
remote.cached = true;
|
|
2976
|
+
}
|
|
2977
|
+
const file = remote.entries[packageName];
|
|
2978
|
+
const url = join(scope, file);
|
|
2979
|
+
addToGlobal(importMap, { [packageName]: url });
|
|
2980
|
+
addIntegrity(importMap, url, remote.name, file);
|
|
2981
|
+
}
|
|
2982
|
+
}
|
|
2983
|
+
function selfFillUncovered(importMap, chunkBundles, externalName, remotes) {
|
|
2984
|
+
for (const remote of remotes) {
|
|
2985
|
+
if (remote.servedBy) continue;
|
|
2986
|
+
for (const [packageName, file] of Object.entries(remote.entries)) {
|
|
2987
|
+
if (importMap.imports[packageName]) continue;
|
|
2988
|
+
if (config.strict.strictEntryPointCoverage || config.profile.scopeUncoveredEntrypoints) {
|
|
2989
|
+
warnUncoveredEntrypoint(GLOBAL_SCOPE, externalName, remote.name, packageName);
|
|
2990
|
+
continue;
|
|
2991
|
+
}
|
|
2992
|
+
const scope = getScope2(GLOBAL_SCOPE, remote.name, externalName);
|
|
2993
|
+
const url = join(scope, file);
|
|
2994
|
+
addToGlobal(importMap, { [packageName]: url });
|
|
2995
|
+
addIntegrity(importMap, url, remote.name, file);
|
|
2996
|
+
registerBundleChunks(chunkBundles, remote.name, remote.bundle);
|
|
2997
|
+
remote.cached = true;
|
|
2998
|
+
}
|
|
2999
|
+
}
|
|
3000
|
+
}
|
|
2127
3001
|
function warnUncoveredEntrypoint(scope, externalName, remoteName, packageName) {
|
|
2128
3002
|
const msg = `[${scope}][${externalName}][${remoteName}] Entrypoint '${packageName}' is not covered by the shared version.`;
|
|
2129
|
-
if (config.strict.strictImportMap) {
|
|
3003
|
+
if (config.strict.strictEntryPointCoverage || config.strict.strictImportMap) {
|
|
2130
3004
|
config.log.error(4, msg);
|
|
2131
3005
|
throw new NFError("Could not create ImportMap.");
|
|
2132
3006
|
}
|
|
@@ -2354,18 +3228,24 @@ function createGetRemoteEntry(config, ports) {
|
|
|
2354
3228
|
// src/lib/core/2.app/steps/update-cache.ts
|
|
2355
3229
|
function createUpdateCache(config, ports) {
|
|
2356
3230
|
const storeRemoteEntry = createStoreRemoteEntry(config, ports, 8);
|
|
3231
|
+
const removeCachedRemoteEntries = createRemoveCachedRemoteEntries(ports);
|
|
2357
3232
|
return (remoteEntry) => {
|
|
2358
3233
|
try {
|
|
2359
3234
|
const actions = {};
|
|
3235
|
+
if (remoteEntry?.override) removeCachedRemoteEntries(/* @__PURE__ */ new Set([remoteEntry.name]));
|
|
2360
3236
|
storeRemoteEntry(remoteEntry, (entry, external, ctx) => {
|
|
2361
|
-
const { action,
|
|
3237
|
+
const { action, provided, sameVersion } = resolveSharedExternal(entry, external, ctx);
|
|
2362
3238
|
actions[external.packageName] = { action };
|
|
2363
|
-
if (action === "skip" &&
|
|
2364
|
-
actions[external.packageName].
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
3239
|
+
if (action === "skip" && provided) {
|
|
3240
|
+
actions[external.packageName].covered = Array.from(provided.keys());
|
|
3241
|
+
if (sameVersion) actions[external.packageName].sameVersion = true;
|
|
3242
|
+
if (external.shareScope) {
|
|
3243
|
+
actions[external.packageName].override = resolveOverrideEntries(
|
|
3244
|
+
entry,
|
|
3245
|
+
external,
|
|
3246
|
+
provided
|
|
3247
|
+
);
|
|
3248
|
+
}
|
|
2369
3249
|
}
|
|
2370
3250
|
});
|
|
2371
3251
|
return Promise.resolve({ entry: remoteEntry, actions });
|
|
@@ -2393,78 +3273,188 @@ function createUpdateCache(config, ports) {
|
|
|
2393
3273
|
}
|
|
2394
3274
|
config.log.warn(8, errorMsg);
|
|
2395
3275
|
}
|
|
2396
|
-
|
|
2397
|
-
|
|
3276
|
+
const provided = sharedVersion ? sharedInfo.shareScope ? versionEntries(sharedVersion) : committedEntries(sharedVersion) : void 0;
|
|
3277
|
+
const sameVersion = sharedVersion?.tag === tag;
|
|
3278
|
+
if (action === "skip" && provided && !sameVersion) {
|
|
3279
|
+
const uncovered = uncoveredEntrypoints(remote, provided);
|
|
3280
|
+
if (uncovered.length > 0) {
|
|
3281
|
+
const msg = `[${sharedInfo.shareScope ?? GLOBAL_SCOPE}][${remoteEntry.name}][${sharedInfo.packageName}] Entrypoints not covered by the shared version: ${uncovered.join(", ")}.`;
|
|
3282
|
+
if (config.strict.strictEntryPointCoverage) {
|
|
3283
|
+
config.log.error(8, msg);
|
|
3284
|
+
throw new NFError(`Could not process remote '${remoteEntry.name}'`);
|
|
3285
|
+
}
|
|
3286
|
+
if (config.profile.scopeUncoveredEntrypoints) {
|
|
3287
|
+
config.log.debug(8, msg);
|
|
3288
|
+
action = "scope";
|
|
3289
|
+
}
|
|
3290
|
+
}
|
|
2398
3291
|
}
|
|
2399
|
-
const matchingVersion = cached.versions
|
|
2400
|
-
if (
|
|
3292
|
+
const matchingVersion = findVersionForTag(cached.versions, tag);
|
|
3293
|
+
if (action === "scope") {
|
|
3294
|
+
remote.cached = true;
|
|
3295
|
+
const scoped = cached.versions.find((v) => v.tag === tag && v.action === "scope");
|
|
3296
|
+
if (scoped) scoped.remotes.push(remote);
|
|
3297
|
+
else cached.versions.push({ tag, action, host: false, remotes: [remote] });
|
|
3298
|
+
} else if (matchingVersion) {
|
|
2401
3299
|
assertSameVersionCompatibility(matchingVersion);
|
|
2402
|
-
matchingVersion
|
|
3300
|
+
addRemoteToVersion(matchingVersion, remote);
|
|
2403
3301
|
} else {
|
|
2404
3302
|
if (!sharedVersion) action = "share";
|
|
2405
3303
|
remote.cached = action !== "skip";
|
|
2406
3304
|
cached.versions.push({ tag, action, host: false, remotes: [remote] });
|
|
2407
3305
|
}
|
|
2408
3306
|
commit();
|
|
2409
|
-
return { action,
|
|
2410
|
-
}
|
|
2411
|
-
function resolveOverrideEntries(remoteEntry, external,
|
|
2412
|
-
return
|
|
2413
|
-
(
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
3307
|
+
return { action, provided, sameVersion };
|
|
3308
|
+
}
|
|
3309
|
+
function resolveOverrideEntries(remoteEntry, external, provided) {
|
|
3310
|
+
return Object.fromEntries(
|
|
3311
|
+
Array.from(provided, ([packageName, provider]) => [
|
|
3312
|
+
packageName,
|
|
3313
|
+
join(
|
|
3314
|
+
providerScopeUrl(remoteEntry, external, provider.name),
|
|
3315
|
+
provider.entries[packageName]
|
|
3316
|
+
)
|
|
3317
|
+
])
|
|
3318
|
+
);
|
|
3319
|
+
}
|
|
3320
|
+
function providerScopeUrl(remoteEntry, external, providerName) {
|
|
3321
|
+
return ports.remoteInfoRepo.tryGet(providerName).map((remote) => remote.scopeUrl).orThrow(() => {
|
|
2420
3322
|
config.log.error(
|
|
2421
3323
|
8,
|
|
2422
3324
|
`[${external.shareScope ?? GLOBAL_SCOPE}][${remoteEntry.name}][${external.packageName}@${external.version}][override] Remote name not found in cache.`
|
|
2423
3325
|
);
|
|
2424
|
-
return new NFError(
|
|
2425
|
-
`Could not find override url from remote ${sharedVersion.remotes[0].name}`
|
|
2426
|
-
);
|
|
3326
|
+
return new NFError(`Could not find override url from remote ${providerName}`);
|
|
2427
3327
|
});
|
|
2428
3328
|
}
|
|
2429
3329
|
}
|
|
2430
3330
|
|
|
2431
3331
|
// src/lib/core/2.app/steps/pooling/pool-dynamic-externals.ts
|
|
2432
|
-
function createPoolDynamicExternals(config) {
|
|
3332
|
+
function createPoolDynamicExternals(config, ports) {
|
|
2433
3333
|
return ({ entry, actions }) => {
|
|
2434
3334
|
const { useAutoExternalPooling } = config.feature;
|
|
2435
|
-
|
|
2436
|
-
return Promise.resolve({ entry, actions });
|
|
2437
|
-
}
|
|
2438
|
-
const byScope = /* @__PURE__ */ new Map();
|
|
3335
|
+
const declared = /* @__PURE__ */ new Map();
|
|
2439
3336
|
for (const external of entry.shared ?? []) {
|
|
2440
3337
|
const name = external.packageName;
|
|
2441
3338
|
if (!external.singleton || !actions[name]) continue;
|
|
2442
3339
|
if (external.shareScope === STRICT_SCOPE) continue;
|
|
2443
|
-
const tag = external.pool?.trim();
|
|
2444
3340
|
const shareScope = external.shareScope ?? GLOBAL_SCOPE;
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
scope: autoScope(name, useAutoExternalPooling),
|
|
2449
|
-
tags: tag ? [{ remote: entry.name, tag }] : [],
|
|
2450
|
-
value: name
|
|
2451
|
-
});
|
|
2452
|
-
byScope.set(shareScope, candidates);
|
|
3341
|
+
let names = declared.get(shareScope);
|
|
3342
|
+
if (!names) declared.set(shareScope, names = /* @__PURE__ */ new Set());
|
|
3343
|
+
names.add(name);
|
|
2453
3344
|
}
|
|
3345
|
+
if (declared.size === 0) return Promise.resolve({ entry, actions });
|
|
2454
3346
|
const scope = (name) => {
|
|
2455
3347
|
actions[name].action = "scope";
|
|
2456
3348
|
delete actions[name].override;
|
|
2457
3349
|
};
|
|
2458
|
-
for (const
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
3350
|
+
for (const [shareScope, names] of declared) {
|
|
3351
|
+
if (!useAutoExternalPooling && !ports.sharedExternalsRepo.hasPoolTag(shareScope)) continue;
|
|
3352
|
+
const committed = ports.sharedExternalsRepo.getFromScope(shareScope);
|
|
3353
|
+
for (const pool of buildPools(committed, useAutoExternalPooling).values()) {
|
|
3354
|
+
const mine = pool.filter((member) => names.has(member.name));
|
|
3355
|
+
if (mine.length === 0) continue;
|
|
3356
|
+
if (mine.some((member) => actions[member.name].action === "scope")) {
|
|
3357
|
+
mine.forEach((member) => scope(member.name));
|
|
3358
|
+
continue;
|
|
2463
3359
|
}
|
|
3360
|
+
const asked = gateViews(entry.name, pool);
|
|
3361
|
+
const anchor = anchorFor(entry.name, asked);
|
|
3362
|
+
if (anchor === "witnessed") continue;
|
|
3363
|
+
if (anchor === void 0) {
|
|
3364
|
+
const reason = explainDynamic(entry.name, asked);
|
|
3365
|
+
const closest = reason.closest ? `closest is '${reason.closest}'` : "no committed build serves any of it";
|
|
3366
|
+
config.log.warn(
|
|
3367
|
+
8,
|
|
3368
|
+
`[${shareScope}] '${entry.name}' serves its own family: no committed build offers every entrypoint it imports at a version it accepts \u2014 '${reason.gap}' is the gap, ${closest}. All ${mine.length} members it imports are scoped for it.`
|
|
3369
|
+
);
|
|
3370
|
+
mine.forEach((member) => scope(member.name));
|
|
3371
|
+
continue;
|
|
3372
|
+
}
|
|
3373
|
+
redirect(entry.name, anchor, pool, asked.view, actions, shareScope);
|
|
2464
3374
|
}
|
|
2465
3375
|
}
|
|
2466
3376
|
return Promise.resolve({ entry, actions });
|
|
2467
3377
|
};
|
|
3378
|
+
function gateViews(remote, pool) {
|
|
3379
|
+
return {
|
|
3380
|
+
pool,
|
|
3381
|
+
view: committedView(pool),
|
|
3382
|
+
wants: consumedMembers(pool).get(remote) ?? [],
|
|
3383
|
+
specifiers: consumedSpecifiers(pool).get(remote) ?? /* @__PURE__ */ new Set(),
|
|
3384
|
+
acceptance: lazy(() => acceptanceTable(pool, ports.versionCheck.isCompatible))
|
|
3385
|
+
};
|
|
3386
|
+
}
|
|
3387
|
+
function anchorFor(remote, { pool, view, wants, specifiers, acceptance }) {
|
|
3388
|
+
const shared = /* @__PURE__ */ new Map();
|
|
3389
|
+
for (const [specifier, source] of view.global) shared.set(specifier, source.tag);
|
|
3390
|
+
if (isWitnessed(specifiers, shared, view.builds)) return "witnessed";
|
|
3391
|
+
const basis = basisPerMember(pool);
|
|
3392
|
+
for (const build of candidateOrder(pool, view)) {
|
|
3393
|
+
if (build === remote) continue;
|
|
3394
|
+
const candidate = view.builds.get(build);
|
|
3395
|
+
if (!servesItsOwnFamily(build, pool, basis)) continue;
|
|
3396
|
+
if (!covers(candidate.coverage, specifiers)) continue;
|
|
3397
|
+
if (!acceptsAll(acceptance(), candidate.instance, remote, wants)) continue;
|
|
3398
|
+
return build;
|
|
3399
|
+
}
|
|
3400
|
+
return void 0;
|
|
3401
|
+
}
|
|
3402
|
+
function candidateOrder(pool, view) {
|
|
3403
|
+
const serving = /* @__PURE__ */ new Set();
|
|
3404
|
+
for (const source of view.global.values()) serving.add(source.remote);
|
|
3405
|
+
const hosts = hostRemotes(pool);
|
|
3406
|
+
const rank = (build) => serving.has(build) ? 0 : hosts.has(build) ? 1 : 2;
|
|
3407
|
+
return [...view.builds.keys()].sort((a, b) => rank(a) - rank(b) || a.localeCompare(b));
|
|
3408
|
+
}
|
|
3409
|
+
function servesItsOwnFamily(build, pool, basis) {
|
|
3410
|
+
let ships2 = 0;
|
|
3411
|
+
let winsAll = true;
|
|
3412
|
+
let allScoped = true;
|
|
3413
|
+
for (const member of pool) {
|
|
3414
|
+
for (const version of member.external.versions) {
|
|
3415
|
+
const meta = version.remotes.find((r) => r.name === build);
|
|
3416
|
+
if (!meta) continue;
|
|
3417
|
+
if (meta.servedBy !== void 0) return false;
|
|
3418
|
+
ships2++;
|
|
3419
|
+
if (version.action !== "scope") allScoped = false;
|
|
3420
|
+
if (basis.get(member.name) !== build) winsAll = false;
|
|
3421
|
+
}
|
|
3422
|
+
}
|
|
3423
|
+
return ships2 > 0 && (winsAll || allScoped);
|
|
3424
|
+
}
|
|
3425
|
+
function explainDynamic(remote, { view, wants, specifiers, acceptance }) {
|
|
3426
|
+
return explainSelfServe(remote, wants, specifiers, {
|
|
3427
|
+
builds: view.builds,
|
|
3428
|
+
acceptance: acceptance()
|
|
3429
|
+
});
|
|
3430
|
+
}
|
|
3431
|
+
function redirect(remote, anchor, pool, view, actions, shareScope) {
|
|
3432
|
+
const files = view.builds.get(anchor).coverage;
|
|
3433
|
+
const scopeUrl = ports.remoteInfoRepo.tryGet(anchor).get()?.scopeUrl;
|
|
3434
|
+
if (!scopeUrl) {
|
|
3435
|
+
config.log.warn(
|
|
3436
|
+
8,
|
|
3437
|
+
`[${shareScope}][${remote}] '${anchor}' is not in the cache, so its files cannot be mapped.`
|
|
3438
|
+
);
|
|
3439
|
+
return;
|
|
3440
|
+
}
|
|
3441
|
+
for (const member of pool) {
|
|
3442
|
+
const action = actions[member.name];
|
|
3443
|
+
if (!action || action.action !== "skip") continue;
|
|
3444
|
+
const own = member.external.versions.flatMap((v) => v.remotes).find((r) => r.name === remote);
|
|
3445
|
+
if (!own) continue;
|
|
3446
|
+
const specifiers = Object.keys(own.entries);
|
|
3447
|
+
if (specifiers.length === 0) continue;
|
|
3448
|
+
const override = {};
|
|
3449
|
+
for (const specifier of specifiers) {
|
|
3450
|
+
if (view.global.get(specifier)?.remote === anchor) continue;
|
|
3451
|
+
const file = files.get(specifier);
|
|
3452
|
+
if (file) override[specifier] = join(scopeUrl, file);
|
|
3453
|
+
}
|
|
3454
|
+
action.covered = specifiers;
|
|
3455
|
+
if (Object.keys(override).length > 0) action.override = override;
|
|
3456
|
+
}
|
|
3457
|
+
}
|
|
2468
3458
|
}
|
|
2469
3459
|
|
|
2470
3460
|
// src/lib/core/2.app/steps/convert-to-import-map.ts
|
|
@@ -2502,22 +3492,25 @@ function createConvertToImportMap(config, ports) {
|
|
|
2502
3492
|
return;
|
|
2503
3493
|
}
|
|
2504
3494
|
if (actions[external.packageName].action === "skip") {
|
|
2505
|
-
const override = actions[external.packageName]
|
|
2506
|
-
if (!external.shareScope) return;
|
|
3495
|
+
const { override, covered, sameVersion } = actions[external.packageName];
|
|
2507
3496
|
if (override) {
|
|
2508
3497
|
Object.entries(override).forEach(([packageName, url]) => {
|
|
2509
3498
|
addToScopes(remoteEntryScope, packageName, url, importMap);
|
|
2510
3499
|
});
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
3500
|
+
serveUncovered(
|
|
3501
|
+
remoteEntry,
|
|
3502
|
+
external,
|
|
3503
|
+
covered ?? Object.keys(override),
|
|
3504
|
+
remoteEntryScope,
|
|
3505
|
+
importMap,
|
|
3506
|
+
sameVersion
|
|
3507
|
+
);
|
|
3508
|
+
return;
|
|
3509
|
+
}
|
|
3510
|
+
if (!external.shareScope) {
|
|
3511
|
+
if (covered) {
|
|
3512
|
+
serveUncovered(remoteEntry, external, covered, remoteEntryScope, importMap, sameVersion);
|
|
3513
|
+
}
|
|
2521
3514
|
return;
|
|
2522
3515
|
}
|
|
2523
3516
|
log.error(
|
|
@@ -2584,9 +3577,22 @@ function createConvertToImportMap(config, ports) {
|
|
|
2584
3577
|
});
|
|
2585
3578
|
return importMap;
|
|
2586
3579
|
}
|
|
3580
|
+
function serveUncovered(remoteEntry, external, covered, remoteEntryScope, importMap, sameVersion = false) {
|
|
3581
|
+
const provided = new Set(covered);
|
|
3582
|
+
Object.entries(external.entries).forEach(([packageName, fileName]) => {
|
|
3583
|
+
if (provided.has(packageName)) return;
|
|
3584
|
+
if (!sameVersion && (config.strict.strictEntryPointCoverage || config.profile.scopeUncoveredEntrypoints)) {
|
|
3585
|
+
warnUncoveredEntrypoint(remoteEntry.name, external.packageName, packageName);
|
|
3586
|
+
return;
|
|
3587
|
+
}
|
|
3588
|
+
const url = join(remoteEntryScope, fileName);
|
|
3589
|
+
addToScopes(remoteEntryScope, packageName, url, importMap);
|
|
3590
|
+
addIntegrity(importMap, url, remoteEntry.integrity, fileName);
|
|
3591
|
+
});
|
|
3592
|
+
}
|
|
2587
3593
|
function warnUncoveredEntrypoint(remoteName, externalName, packageName) {
|
|
2588
3594
|
const msg = `[${remoteName}][${externalName}] Entrypoint '${packageName}' is not covered by the override.`;
|
|
2589
|
-
if (config.strict.strictImportMap) {
|
|
3595
|
+
if (config.strict.strictEntryPointCoverage || config.strict.strictImportMap) {
|
|
2590
3596
|
log.error(9, msg);
|
|
2591
3597
|
throw new NFError("Could not create ImportMap.");
|
|
2592
3598
|
}
|
|
@@ -2607,6 +3613,7 @@ var createInitDrivers = ({
|
|
|
2607
3613
|
}) => ({
|
|
2608
3614
|
getRemoteEntries: createGetRemoteEntries(config, adapters),
|
|
2609
3615
|
processRemoteEntries: createProcessRemoteEntries(config, adapters),
|
|
3616
|
+
markPoolsForReelection: createMarkPoolsForReelection(config, adapters),
|
|
2610
3617
|
determineSharedExternals: createDetermineSharedExternals(config, adapters),
|
|
2611
3618
|
poolSharedExternals: createPoolSharedExternals(config, adapters),
|
|
2612
3619
|
generateImportMap: createGenerateImportMap(config, adapters),
|
|
@@ -2614,7 +3621,7 @@ var createInitDrivers = ({
|
|
|
2614
3621
|
exposeModuleLoader: createExposeModuleLoader(config, adapters),
|
|
2615
3622
|
getRemoteEntry: createGetRemoteEntry(config, adapters),
|
|
2616
3623
|
updateCache: createUpdateCache(config, adapters),
|
|
2617
|
-
poolDynamicExternals: createPoolDynamicExternals(config),
|
|
3624
|
+
poolDynamicExternals: createPoolDynamicExternals(config, adapters),
|
|
2618
3625
|
convertToImportMap: createConvertToImportMap(config, adapters)
|
|
2619
3626
|
});
|
|
2620
3627
|
var INIT_FLOW_FACTORY = ({
|
|
@@ -2725,7 +3732,7 @@ var createManifestProvider = () => {
|
|
|
2725
3732
|
};
|
|
2726
3733
|
};
|
|
2727
3734
|
|
|
2728
|
-
// node_modules/.pnpm/@softarc+native-federation@4.
|
|
3735
|
+
// node_modules/.pnpm/@softarc+native-federation@4.4.0_typescript@6.0.3/node_modules/@softarc/native-federation/dist/lib/utils/normalize.js
|
|
2729
3736
|
function inferPackageFromSecondary(secondary) {
|
|
2730
3737
|
const parts = secondary.split("/");
|
|
2731
3738
|
if (secondary.startsWith("@") && parts.length >= 2) {
|
|
@@ -2734,7 +3741,7 @@ function inferPackageFromSecondary(secondary) {
|
|
|
2734
3741
|
return parts[0];
|
|
2735
3742
|
}
|
|
2736
3743
|
|
|
2737
|
-
// node_modules/.pnpm/@softarc+native-federation@4.
|
|
3744
|
+
// node_modules/.pnpm/@softarc+native-federation@4.4.0_typescript@6.0.3/node_modules/@softarc/native-federation/dist/lib/core/output/densify-externals.js
|
|
2738
3745
|
function isDense(entry) {
|
|
2739
3746
|
return "entries" in entry;
|
|
2740
3747
|
}
|
|
@@ -2835,16 +3842,21 @@ var createRemoteInfoRepository = (config) => {
|
|
|
2835
3842
|
const STORAGE = config.storage("remotes", {});
|
|
2836
3843
|
if (config.clearStorage) STORAGE.clear();
|
|
2837
3844
|
const _cache = STORAGE.get() ?? {};
|
|
3845
|
+
let _dirty = false;
|
|
2838
3846
|
return {
|
|
2839
3847
|
contains: function(remoteName) {
|
|
2840
3848
|
return !!_cache[remoteName];
|
|
2841
3849
|
},
|
|
2842
3850
|
remove: function(remoteName) {
|
|
2843
|
-
|
|
3851
|
+
if (remoteName in _cache) {
|
|
3852
|
+
delete _cache[remoteName];
|
|
3853
|
+
_dirty = true;
|
|
3854
|
+
}
|
|
2844
3855
|
return this;
|
|
2845
3856
|
},
|
|
2846
3857
|
addOrUpdate: function(remoteName, remote) {
|
|
2847
3858
|
_cache[remoteName] = remote;
|
|
3859
|
+
_dirty = true;
|
|
2848
3860
|
return this;
|
|
2849
3861
|
},
|
|
2850
3862
|
tryGet: function(remoteName) {
|
|
@@ -2859,7 +3871,9 @@ var createRemoteInfoRepository = (config) => {
|
|
|
2859
3871
|
return _cache;
|
|
2860
3872
|
},
|
|
2861
3873
|
commit: function() {
|
|
3874
|
+
if (!_dirty) return this;
|
|
2862
3875
|
STORAGE.set(_cache);
|
|
3876
|
+
_dirty = false;
|
|
2863
3877
|
return this;
|
|
2864
3878
|
}
|
|
2865
3879
|
};
|
|
@@ -2870,14 +3884,19 @@ var createScopedExternalsRepository = (config) => {
|
|
|
2870
3884
|
const STORAGE = config.storage("scoped-externals", {});
|
|
2871
3885
|
if (config.clearStorage) STORAGE.clear();
|
|
2872
3886
|
const _cache = STORAGE.get() ?? {};
|
|
3887
|
+
let _dirty = false;
|
|
2873
3888
|
return {
|
|
2874
3889
|
addExternal: function(remoteName, external, version) {
|
|
2875
3890
|
if (!_cache[remoteName]) _cache[remoteName] = {};
|
|
2876
3891
|
_cache[remoteName][external] = version;
|
|
3892
|
+
_dirty = true;
|
|
2877
3893
|
return this;
|
|
2878
3894
|
},
|
|
2879
3895
|
remove: function(remoteName) {
|
|
2880
|
-
|
|
3896
|
+
if (remoteName in _cache) {
|
|
3897
|
+
delete _cache[remoteName];
|
|
3898
|
+
_dirty = true;
|
|
3899
|
+
}
|
|
2881
3900
|
return this;
|
|
2882
3901
|
},
|
|
2883
3902
|
getAll: function() {
|
|
@@ -2887,7 +3906,9 @@ var createScopedExternalsRepository = (config) => {
|
|
|
2887
3906
|
return Optional.of(_cache[remoteName]);
|
|
2888
3907
|
},
|
|
2889
3908
|
commit: function() {
|
|
3909
|
+
if (!_dirty) return this;
|
|
2890
3910
|
STORAGE.set(_cache);
|
|
3911
|
+
_dirty = false;
|
|
2891
3912
|
return this;
|
|
2892
3913
|
}
|
|
2893
3914
|
};
|
|
@@ -2900,14 +3921,19 @@ var createSharedExternalsRepository = (config) => {
|
|
|
2900
3921
|
{ [GLOBAL_SCOPE]: {} }
|
|
2901
3922
|
);
|
|
2902
3923
|
if (config.clearStorage) STORAGE.clear();
|
|
2903
|
-
const _cache = STORAGE.get();
|
|
2904
|
-
let
|
|
3924
|
+
const _cache = STORAGE.get() ?? { [GLOBAL_SCOPE]: {} };
|
|
3925
|
+
let _dirty = false;
|
|
2905
3926
|
return {
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
hasPoolTag: function() {
|
|
2910
|
-
|
|
3927
|
+
// Read from the cache rather than remembered from this init's entries: a warm init may not refetch
|
|
3928
|
+
// the tagged remote at all, and pooling has to coordinate its pool anyway. Exits on the first hit.
|
|
3929
|
+
// Per share scope, because a pool never spans one: a tag in another scope is no reason to pool here.
|
|
3930
|
+
hasPoolTag: function(shareScope) {
|
|
3931
|
+
const scope = _cache[shareScope ?? GLOBAL_SCOPE];
|
|
3932
|
+
if (!scope) return false;
|
|
3933
|
+
for (const external of Object.values(scope))
|
|
3934
|
+
for (const version of external.versions)
|
|
3935
|
+
for (const remote of version.remotes) if (remote.pool?.trim()) return true;
|
|
3936
|
+
return false;
|
|
2911
3937
|
},
|
|
2912
3938
|
getFromScope: function(shareScope) {
|
|
2913
3939
|
return { ..._cache[shareScope ?? GLOBAL_SCOPE] };
|
|
@@ -2915,28 +3941,41 @@ var createSharedExternalsRepository = (config) => {
|
|
|
2915
3941
|
addOrUpdate: function(externalName, external, shareScope) {
|
|
2916
3942
|
if (!_cache[shareScope ?? GLOBAL_SCOPE]) _cache[shareScope ?? GLOBAL_SCOPE] = {};
|
|
2917
3943
|
_cache[shareScope ?? GLOBAL_SCOPE][externalName] = external;
|
|
3944
|
+
_dirty = true;
|
|
2918
3945
|
return this;
|
|
2919
3946
|
},
|
|
2920
3947
|
getScopes: function(o = { includeGlobal: true }) {
|
|
2921
3948
|
if (o.includeGlobal) return Object.keys(_cache);
|
|
2922
3949
|
return Object.keys(_cache).filter((s) => s !== GLOBAL_SCOPE);
|
|
2923
3950
|
},
|
|
2924
|
-
|
|
3951
|
+
// Batched: one traversal of the graph per init instead of one per overridden remote.
|
|
3952
|
+
removeFromAllScopes: function(remoteNames) {
|
|
3953
|
+
if (remoteNames.size === 0) return;
|
|
2925
3954
|
Object.values(_cache).forEach((scope) => {
|
|
2926
3955
|
const removeExternals = [];
|
|
2927
3956
|
Object.entries(scope).forEach(([name, external]) => {
|
|
2928
|
-
|
|
2929
|
-
external.versions.
|
|
2930
|
-
const
|
|
2931
|
-
|
|
2932
|
-
|
|
3957
|
+
let removedCopy = false;
|
|
3958
|
+
for (let i = external.versions.length - 1; i >= 0; i--) {
|
|
3959
|
+
const version = external.versions[i];
|
|
3960
|
+
const remotes = version.remotes;
|
|
3961
|
+
const host = version.host ? remotes[0]?.name : void 0;
|
|
3962
|
+
let keep = 0;
|
|
3963
|
+
for (let r = 0; r < remotes.length; r++) {
|
|
3964
|
+
if (remoteNames.has(remotes[r].name)) continue;
|
|
3965
|
+
remotes[keep++] = remotes[r];
|
|
2933
3966
|
}
|
|
2934
|
-
if (
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
3967
|
+
if (keep !== remotes.length) {
|
|
3968
|
+
remotes.length = keep;
|
|
3969
|
+
removedCopy = true;
|
|
3970
|
+
_dirty = true;
|
|
3971
|
+
if (host !== void 0 && remoteNames.has(host)) version.host = false;
|
|
3972
|
+
}
|
|
3973
|
+
if (remotes.length === 0) {
|
|
3974
|
+
external.versions.splice(i, 1);
|
|
3975
|
+
_dirty = true;
|
|
2939
3976
|
}
|
|
3977
|
+
}
|
|
3978
|
+
if (removedCopy) {
|
|
2940
3979
|
external.dirty = true;
|
|
2941
3980
|
if (external.versions.length === 0) removeExternals.push(name);
|
|
2942
3981
|
}
|
|
@@ -2960,7 +3999,9 @@ var createSharedExternalsRepository = (config) => {
|
|
|
2960
3999
|
return Optional.of(_cache[shareScope ?? GLOBAL_SCOPE]?.[external]);
|
|
2961
4000
|
},
|
|
2962
4001
|
commit: function() {
|
|
4002
|
+
if (!_dirty) return this;
|
|
2963
4003
|
STORAGE.set(_cache);
|
|
4004
|
+
_dirty = false;
|
|
2964
4005
|
return this;
|
|
2965
4006
|
}
|
|
2966
4007
|
};
|
|
@@ -2997,17 +4038,29 @@ var createChunkRepository = (config) => {
|
|
|
2997
4038
|
const STORAGE = config.storage("shared-chunks", {});
|
|
2998
4039
|
if (config.clearStorage) STORAGE.clear();
|
|
2999
4040
|
const _cache = STORAGE.get() ?? {};
|
|
4041
|
+
let _dirty = false;
|
|
3000
4042
|
return {
|
|
3001
4043
|
addOrReplace: function(remoteName, bundleName, chunks) {
|
|
3002
4044
|
if (!_cache[remoteName]) _cache[remoteName] = {};
|
|
3003
4045
|
_cache[remoteName][bundleName] = chunks;
|
|
4046
|
+
_dirty = true;
|
|
4047
|
+
return this;
|
|
4048
|
+
},
|
|
4049
|
+
// Whole-remote: a build that stops chunking a bundle omits the key, so `addOrReplace` cannot clear it.
|
|
4050
|
+
remove: function(remoteName) {
|
|
4051
|
+
if (remoteName in _cache) {
|
|
4052
|
+
delete _cache[remoteName];
|
|
4053
|
+
_dirty = true;
|
|
4054
|
+
}
|
|
3004
4055
|
return this;
|
|
3005
4056
|
},
|
|
3006
4057
|
tryGet: function(remoteName, bundleName) {
|
|
3007
4058
|
return Optional.of(_cache[remoteName]?.[bundleName]);
|
|
3008
4059
|
},
|
|
3009
4060
|
commit: function() {
|
|
4061
|
+
if (!_dirty) return this;
|
|
3010
4062
|
STORAGE.set(_cache);
|
|
4063
|
+
_dirty = false;
|
|
3011
4064
|
return this;
|
|
3012
4065
|
}
|
|
3013
4066
|
};
|
|
@@ -3213,10 +4266,9 @@ var globalThisStorageEntry = (namespace) => (key, initialValue) => {
|
|
|
3213
4266
|
globalThis[namespace] = {};
|
|
3214
4267
|
}
|
|
3215
4268
|
const storage = globalThis[namespace];
|
|
3216
|
-
if (!storage[key]) storage[key] = initialValue;
|
|
3217
4269
|
const entry = {
|
|
3218
4270
|
get() {
|
|
3219
|
-
return cloneEntry(key, storage[key]);
|
|
4271
|
+
return cloneEntry(key, key in storage ? storage[key] : initialValue);
|
|
3220
4272
|
},
|
|
3221
4273
|
set(value) {
|
|
3222
4274
|
storage[key] = cloneEntry(key, value);
|
|
@@ -3240,6 +4292,7 @@ var createStorageConfig = (override) => ({
|
|
|
3240
4292
|
var defaultProfile = {
|
|
3241
4293
|
latestSharedExternal: false,
|
|
3242
4294
|
skipInvalidExternalVersions: false,
|
|
4295
|
+
scopeUncoveredEntrypoints: false,
|
|
3243
4296
|
overrideCachedRemotes: "init-only",
|
|
3244
4297
|
overrideCachedRemotesIfURLMatches: false
|
|
3245
4298
|
};
|