@softarc/native-federation-orchestrator 4.5.3 → 4.6.1
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 +1184 -266
- package/fesm2022/@softarc/native-federation-orchestrator.mjs.map +4 -4
- package/fesm2022/module-federation.mjs +34 -20
- package/fesm2022/module-federation.mjs.map +3 -3
- package/fesm2022/node.mjs +1084 -247
- package/fesm2022/node.mjs.map +4 -4
- package/node-loader/loader.mjs +19 -2
- package/node-loader/loader.mjs.map +2 -2
- package/package.json +1 -1
- package/quickstart.mjs +15 -15
- package/types/lib/core/1.domain/externals/basis.d.ts +17 -2
- 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 +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 +1 -2
- 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 +1 -1
- package/types/lib/core/3.adapters/browser/sse-handler.d.ts +0 -8
- package/types/lib/testing/pooling/no-tear.d.ts +37 -0
- package/types/scripts/node-loader.d.ts +1 -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
|
|
@@ -1463,13 +1463,33 @@ function findVersionForTag(versions, tag) {
|
|
|
1463
1463
|
}
|
|
1464
1464
|
return scoped;
|
|
1465
1465
|
}
|
|
1466
|
-
function
|
|
1467
|
-
return
|
|
1466
|
+
function versionEntries(version) {
|
|
1467
|
+
return collectEntries(version, void 0);
|
|
1468
1468
|
}
|
|
1469
|
-
function
|
|
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) {
|
|
1470
1490
|
let uncovered = 0;
|
|
1471
1491
|
for (const entrypoint in remote.entries) {
|
|
1472
|
-
if (!(entrypoint
|
|
1492
|
+
if (!covered.has(entrypoint)) uncovered++;
|
|
1473
1493
|
}
|
|
1474
1494
|
return uncovered;
|
|
1475
1495
|
}
|
|
@@ -1481,6 +1501,7 @@ function createRemoveCachedRemoteEntries(ports) {
|
|
|
1481
1501
|
for (const remoteName of remoteNames) {
|
|
1482
1502
|
ports.remoteInfoRepo.remove(remoteName);
|
|
1483
1503
|
ports.scopedExternalsRepo.remove(remoteName);
|
|
1504
|
+
ports.sharedChunksRepo.remove(remoteName);
|
|
1484
1505
|
}
|
|
1485
1506
|
ports.sharedExternalsRepo.removeFromAllScopes(remoteNames);
|
|
1486
1507
|
};
|
|
@@ -1506,7 +1527,6 @@ function createStoreRemoteEntry(config, ports, logStep) {
|
|
|
1506
1527
|
const tag = resolveVersion(remoteEntry, external);
|
|
1507
1528
|
if (tag === null) return;
|
|
1508
1529
|
if (external.singleton) {
|
|
1509
|
-
if (external.pool?.trim()) ports.sharedExternalsRepo.markPoolTagPresent();
|
|
1510
1530
|
onSharedExternal(remoteEntry, external, sharedExternalContext(remoteEntry, external, tag));
|
|
1511
1531
|
} else {
|
|
1512
1532
|
addScopedExternal(remoteEntry, external, tag);
|
|
@@ -1648,8 +1668,129 @@ function createProcessRemoteEntries(config, ports) {
|
|
|
1648
1668
|
var GLOBAL_SCOPE = "__GLOBAL__";
|
|
1649
1669
|
var STRICT_SCOPE = "strict";
|
|
1650
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
|
+
|
|
1651
1791
|
// src/lib/core/2.app/steps/determine-shared-externals.ts
|
|
1652
1792
|
function createDetermineSharedExternals(config, ports) {
|
|
1793
|
+
const applyWinner = createApplyWinner(config);
|
|
1653
1794
|
return () => {
|
|
1654
1795
|
const memo = /* @__PURE__ */ new Map();
|
|
1655
1796
|
const isCompatible = (tag, requiredVersion) => {
|
|
@@ -1661,16 +1802,20 @@ function createDetermineSharedExternals(config, ports) {
|
|
|
1661
1802
|
}
|
|
1662
1803
|
return hit;
|
|
1663
1804
|
};
|
|
1805
|
+
const touched = /* @__PURE__ */ new Map();
|
|
1664
1806
|
for (const shareScope of ports.sharedExternalsRepo.getScopes()) {
|
|
1665
1807
|
const sharedExternals = ports.sharedExternalsRepo.getFromScope(shareScope);
|
|
1666
1808
|
try {
|
|
1667
|
-
|
|
1668
|
-
|
|
1809
|
+
const elected = /* @__PURE__ */ new Set();
|
|
1810
|
+
Object.entries(sharedExternals).filter(([_, e]) => e.dirty).forEach(([name, external]) => {
|
|
1811
|
+
ports.sharedExternalsRepo.addOrUpdate(
|
|
1669
1812
|
name,
|
|
1670
1813
|
setVersionActions(name, external, isCompatible),
|
|
1671
1814
|
shareScope
|
|
1672
|
-
)
|
|
1673
|
-
|
|
1815
|
+
);
|
|
1816
|
+
elected.add(name);
|
|
1817
|
+
});
|
|
1818
|
+
if (elected.size > 0) touched.set(shareScope, elected);
|
|
1674
1819
|
} catch (error) {
|
|
1675
1820
|
config.log.error(
|
|
1676
1821
|
3,
|
|
@@ -1688,28 +1833,25 @@ function createDetermineSharedExternals(config, ports) {
|
|
|
1688
1833
|
);
|
|
1689
1834
|
}
|
|
1690
1835
|
}
|
|
1691
|
-
return Promise.resolve();
|
|
1836
|
+
return Promise.resolve(touched);
|
|
1692
1837
|
};
|
|
1693
1838
|
function uncoveredTears(external, winner, accepts) {
|
|
1694
|
-
const basis = winner
|
|
1839
|
+
const basis = versionEntries(winner);
|
|
1695
1840
|
return external.versions.reduce((sum, v) => {
|
|
1696
1841
|
if (v === winner) return sum;
|
|
1697
1842
|
if (!accepts(v, winner.tag)) return sum;
|
|
1698
|
-
return
|
|
1843
|
+
return v.remotes.reduce(
|
|
1844
|
+
(n, r) => r.servedBy ? n : n + countUncoveredEntrypoints(r, basis),
|
|
1845
|
+
sum
|
|
1846
|
+
);
|
|
1699
1847
|
}, 0);
|
|
1700
1848
|
}
|
|
1701
1849
|
function setVersionActions(externalName, external, isCompatible) {
|
|
1702
1850
|
if (external.versions.length === 1) {
|
|
1703
|
-
external.versions[0]
|
|
1704
|
-
applyEntrypointCoveragePolicy(externalName, external);
|
|
1705
|
-
external.dirty = false;
|
|
1706
|
-
return external;
|
|
1851
|
+
return applyWinner(externalName, external, external.versions[0], isCompatible);
|
|
1707
1852
|
}
|
|
1708
|
-
const
|
|
1709
|
-
|
|
1710
|
-
);
|
|
1711
|
-
const accepts = (version, tag) => demands.get(version).every((d) => isCompatible(tag, d.requiredVersion));
|
|
1712
|
-
const objector = (version, tag) => demands.get(version).find((d) => d.strictVersion && !isCompatible(tag, d.requiredVersion));
|
|
1853
|
+
const acceptance = versionAcceptance(external, isCompatible);
|
|
1854
|
+
const { accepts } = acceptance;
|
|
1713
1855
|
let sharedVersion = external.versions.find((v) => v.host);
|
|
1714
1856
|
if (!sharedVersion && config.profile.latestSharedExternal) {
|
|
1715
1857
|
sharedVersion = external.versions[0];
|
|
@@ -1717,10 +1859,30 @@ function createDetermineSharedExternals(config, ports) {
|
|
|
1717
1859
|
if (!sharedVersion) {
|
|
1718
1860
|
let leastExtraDownloads = Number.MAX_VALUE;
|
|
1719
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);
|
|
1720
1879
|
external.versions.forEach((vA) => {
|
|
1721
|
-
const extraDownloads = external.versions.
|
|
1722
|
-
|
|
1723
|
-
|
|
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
|
+
);
|
|
1724
1886
|
if (extraDownloads < leastExtraDownloads) {
|
|
1725
1887
|
leastExtraDownloads = extraDownloads;
|
|
1726
1888
|
leastTears = uncoveredTears(external, vA, accepts);
|
|
@@ -1738,84 +1900,304 @@ function createDetermineSharedExternals(config, ports) {
|
|
|
1738
1900
|
if (!sharedVersion) {
|
|
1739
1901
|
throw new NFError(`[${externalName}] Could not determine shared version!`);
|
|
1740
1902
|
}
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
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
|
+
}
|
|
1745
1942
|
}
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
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 });
|
|
1753
1957
|
}
|
|
1754
|
-
|
|
1755
|
-
});
|
|
1756
|
-
sharedVersion.action = "share";
|
|
1757
|
-
applyEntrypointCoveragePolicy(externalName, external);
|
|
1758
|
-
external.dirty = false;
|
|
1759
|
-
return external;
|
|
1958
|
+
}
|
|
1760
1959
|
}
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
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
|
+
}
|
|
1775
1974
|
}
|
|
1776
|
-
scopeTornRemotes(externalName, external, tears);
|
|
1777
1975
|
}
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
const
|
|
1788
|
-
|
|
1789
|
-
|
|
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
|
+
}
|
|
1790
1990
|
}
|
|
1791
|
-
return tears;
|
|
1792
1991
|
}
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
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
|
+
}
|
|
1804
2004
|
}
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
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);
|
|
1808
2045
|
}
|
|
1809
2046
|
}
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
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]);
|
|
1815
2066
|
}
|
|
1816
2067
|
}
|
|
1817
2068
|
}
|
|
1818
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]);
|
|
2084
|
+
}
|
|
2085
|
+
byMember.set(member.name, accepted);
|
|
2086
|
+
}
|
|
2087
|
+
}
|
|
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;
|
|
2199
|
+
}
|
|
2200
|
+
|
|
1819
2201
|
// src/lib/core/2.app/steps/pooling/pool-graph.ts
|
|
1820
2202
|
var SCOPED_PACKAGE = /^@([^/]+)\//;
|
|
1821
2203
|
function createDSU() {
|
|
@@ -1856,34 +2238,56 @@ function createDSU() {
|
|
|
1856
2238
|
}
|
|
1857
2239
|
var extNode = (name) => `ext\0${name}`;
|
|
1858
2240
|
var tagNode = (remote, tag) => `tag\0${remote}\0${tag}`;
|
|
1859
|
-
var scopeNode = (scope) => `scope\0${scope}`;
|
|
2241
|
+
var scopeNode = (remote, scope) => `scope\0${remote}\0${scope}`;
|
|
1860
2242
|
function autoScope(name, useAutoExternalPooling) {
|
|
1861
2243
|
return useAutoExternalPooling ? SCOPED_PACKAGE.exec(name)?.[1] : void 0;
|
|
1862
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
|
+
}
|
|
1863
2254
|
function groupByMembership(candidates, log) {
|
|
1864
2255
|
const dsu = createDSU();
|
|
1865
2256
|
const tagged = /* @__PURE__ */ new Set();
|
|
1866
|
-
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
|
+
}
|
|
1867
2263
|
for (const candidate of candidates) {
|
|
1868
|
-
let joined = false;
|
|
1869
|
-
if (candidate.scope !== void 0) {
|
|
1870
|
-
dsu.union(extNode(candidate.name), scopeNode(candidate.scope));
|
|
1871
|
-
joined = true;
|
|
1872
|
-
}
|
|
1873
2264
|
for (const edge of candidate.tags) {
|
|
1874
2265
|
dsu.union(extNode(candidate.name), tagNode(edge.remote, edge.tag));
|
|
1875
2266
|
tagged.add(candidate.name);
|
|
1876
|
-
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);
|
|
1877
2274
|
}
|
|
1878
|
-
|
|
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));
|
|
1879
2281
|
}
|
|
1880
2282
|
const byComponent = /* @__PURE__ */ new Map();
|
|
1881
|
-
for (const
|
|
1882
|
-
const root = dsu.component(extNode(
|
|
1883
|
-
|
|
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 });
|
|
1884
2287
|
}
|
|
1885
2288
|
const pools = [];
|
|
1886
2289
|
for (const members of byComponent.values()) {
|
|
2290
|
+
if (!members.some((m) => joined.has(m.name))) continue;
|
|
1887
2291
|
members.sort((a, b) => a.name.localeCompare(b.name));
|
|
1888
2292
|
if (members.length < 2) {
|
|
1889
2293
|
const only = members[0];
|
|
@@ -1911,6 +2315,7 @@ function buildPools(sharedExternals, useAutoExternalPooling, log) {
|
|
|
1911
2315
|
return tag ? [{ remote: r.name, tag }] : [];
|
|
1912
2316
|
})
|
|
1913
2317
|
),
|
|
2318
|
+
remotes: external.versions.flatMap((v) => v.remotes.map((r) => r.name)),
|
|
1914
2319
|
value: { name, external }
|
|
1915
2320
|
})
|
|
1916
2321
|
);
|
|
@@ -1918,28 +2323,131 @@ function buildPools(sharedExternals, useAutoExternalPooling, log) {
|
|
|
1918
2323
|
}
|
|
1919
2324
|
|
|
1920
2325
|
// src/lib/core/2.app/steps/pooling/pool.util.ts
|
|
2326
|
+
function lazy(make) {
|
|
2327
|
+
let value;
|
|
2328
|
+
return () => value ??= make();
|
|
2329
|
+
}
|
|
1921
2330
|
function remotesInPool(members) {
|
|
1922
2331
|
return [
|
|
1923
2332
|
...new Set(members.flatMap((m) => m.external.versions.flatMap((v) => v.remotes.map((r) => r.name))))
|
|
1924
2333
|
];
|
|
1925
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
|
+
}
|
|
1926
2344
|
|
|
1927
2345
|
// src/lib/core/2.app/steps/pooling/pool-shared-externals.ts
|
|
1928
2346
|
function islandedRemotes(members) {
|
|
1929
|
-
const islanded = /* @__PURE__ */ new
|
|
2347
|
+
const islanded = /* @__PURE__ */ new Map();
|
|
1930
2348
|
for (const member of members)
|
|
1931
2349
|
for (const version of member.external.versions)
|
|
1932
|
-
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
|
+
}
|
|
1933
2359
|
return islanded;
|
|
1934
2360
|
}
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
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
|
+
}
|
|
2406
|
+
}
|
|
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
|
+
}
|
|
1940
2441
|
}
|
|
1941
|
-
|
|
1942
|
-
|
|
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;
|
|
1943
2451
|
const sharedExternals = ports.sharedExternalsRepo.getFromScope(scope);
|
|
1944
2452
|
try {
|
|
1945
2453
|
for (const [poolName, members] of buildPools(
|
|
@@ -1947,6 +2455,7 @@ function createPoolSharedExternals(config, ports) {
|
|
|
1947
2455
|
useAutoExternalPooling,
|
|
1948
2456
|
config.log
|
|
1949
2457
|
)) {
|
|
2458
|
+
if (touchedInScope && !members.some((m) => touchedInScope.has(m.name))) continue;
|
|
1950
2459
|
poolFamily(poolName, members, scope);
|
|
1951
2460
|
}
|
|
1952
2461
|
} catch (error) {
|
|
@@ -1967,45 +2476,138 @@ function createPoolSharedExternals(config, ports) {
|
|
|
1967
2476
|
const allRemotes = remotesInPool(members);
|
|
1968
2477
|
if (allRemotes.length < 2) return;
|
|
1969
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
|
+
};
|
|
1970
2486
|
config.log.debug(
|
|
1971
2487
|
3,
|
|
1972
|
-
`[${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"}}
|
|
1973
2489
|
` + members.map((m) => ` - ${m.name}`).join("\n")
|
|
1974
2490
|
);
|
|
1975
|
-
|
|
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) {
|
|
1976
2507
|
config.log.error(
|
|
1977
2508
|
3,
|
|
1978
|
-
`[${scope}][pool:${poolName}] version-incompatible remotes cannot be pooled: {${
|
|
2509
|
+
`[${scope}][pool:${poolName}] version-incompatible remotes cannot be pooled: {${incompatible.join(", ")}}.`
|
|
1979
2510
|
);
|
|
1980
2511
|
throw new NFError(`Could not pool '${poolName}' in scope ${scope}.`);
|
|
1981
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
|
+
}
|
|
1982
2517
|
for (const member of members) {
|
|
1983
|
-
const rebuilt = rebuildMember(member, islanded);
|
|
1984
|
-
warnIfScopedOnly(poolName, member
|
|
2518
|
+
const rebuilt = rebuildMember(member, islanded, served, basis.get(member.name));
|
|
2519
|
+
warnIfScopedOnly(poolName, member, rebuilt, islanded, scope);
|
|
1985
2520
|
ports.sharedExternalsRepo.addOrUpdate(member.name, rebuilt, scope);
|
|
1986
2521
|
}
|
|
1987
2522
|
}
|
|
1988
|
-
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) {
|
|
1989
2573
|
if (rebuilt.versions.some((v) => v.action === "share")) return;
|
|
1990
|
-
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;
|
|
1991
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;
|
|
1992
2580
|
config.log.warn(
|
|
1993
2581
|
3,
|
|
1994
|
-
`[${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.`
|
|
1995
2583
|
);
|
|
1996
2584
|
}
|
|
1997
|
-
function rebuildMember(member, islanded) {
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
)
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
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));
|
|
2009
2611
|
const shareVersion = shareEntries.length > 0 ? [
|
|
2010
2612
|
{
|
|
2011
2613
|
tag: shareEntries[0].tag,
|
|
@@ -2014,37 +2616,70 @@ function createPoolSharedExternals(config, ports) {
|
|
|
2014
2616
|
remotes: shareEntries.map((e) => e.meta)
|
|
2015
2617
|
}
|
|
2016
2618
|
] : [];
|
|
2017
|
-
const
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
const version = scopeByTag.get(e.tag) ?? {
|
|
2032
|
-
tag: e.tag,
|
|
2033
|
-
host: false,
|
|
2034
|
-
action: "scope",
|
|
2035
|
-
remotes: []
|
|
2036
|
-
};
|
|
2037
|
-
version.remotes.push(e.meta);
|
|
2038
|
-
scopeByTag.set(e.tag, version);
|
|
2039
|
-
}
|
|
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
|
+
};
|
|
2040
2633
|
return {
|
|
2041
2634
|
dirty: false,
|
|
2042
|
-
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))
|
|
2043
2643
|
};
|
|
2044
2644
|
}
|
|
2045
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
|
+
}
|
|
2658
|
+
|
|
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
|
+
}
|
|
2046
2681
|
|
|
2047
|
-
// node_modules/.pnpm/@softarc+native-federation@4.
|
|
2682
|
+
// node_modules/.pnpm/@softarc+native-federation@4.5.0_typescript@6.0.3/node_modules/@softarc/native-federation/dist/lib/domain/core/build-notification-options.contract.js
|
|
2048
2683
|
var BuildNotificationType = /* @__PURE__ */ ((BuildNotificationType2) => {
|
|
2049
2684
|
BuildNotificationType2["COMPLETED"] = "federation-rebuild-complete";
|
|
2050
2685
|
BuildNotificationType2["ERROR"] = "federation-rebuild-error";
|
|
@@ -2052,7 +2687,7 @@ var BuildNotificationType = /* @__PURE__ */ ((BuildNotificationType2) => {
|
|
|
2052
2687
|
return BuildNotificationType2;
|
|
2053
2688
|
})(BuildNotificationType || {});
|
|
2054
2689
|
|
|
2055
|
-
// node_modules/.pnpm/@softarc+native-federation@4.
|
|
2690
|
+
// node_modules/.pnpm/@softarc+native-federation@4.5.0_typescript@6.0.3/node_modules/@softarc/native-federation/dist/lib/domain/core/chunk.js
|
|
2056
2691
|
var CHUNK_PREFIX = "@nf-internal";
|
|
2057
2692
|
function toChunkImport(fileName) {
|
|
2058
2693
|
if (fileName.startsWith("./")) {
|
|
@@ -2113,9 +2748,9 @@ function createGenerateImportMap(config, ports) {
|
|
|
2113
2748
|
}
|
|
2114
2749
|
function processshareScope(importMap, shareScope, chunkBundles) {
|
|
2115
2750
|
const sharedExternals = ports.sharedExternalsRepo.getFromScope(shareScope);
|
|
2751
|
+
const index = buildServedIndex(sharedExternals);
|
|
2116
2752
|
for (const [externalName, external] of Object.entries(sharedExternals)) {
|
|
2117
2753
|
let override = void 0;
|
|
2118
|
-
let overrideScope = void 0;
|
|
2119
2754
|
const cachedBefore = cachedFingerprint(external);
|
|
2120
2755
|
for (const version of external.versions) {
|
|
2121
2756
|
if (version.action === "scope") {
|
|
@@ -2132,36 +2767,52 @@ function createGenerateImportMap(config, ports) {
|
|
|
2132
2767
|
continue;
|
|
2133
2768
|
}
|
|
2134
2769
|
version.remotes[0].cached = true;
|
|
2135
|
-
let
|
|
2136
|
-
|
|
2137
|
-
if (version.action === "share") {
|
|
2138
|
-
registerBundleChunks(chunkBundles, source.name, source.bundle);
|
|
2139
|
-
} else if (version.action === "skip") {
|
|
2770
|
+
let serving = version;
|
|
2771
|
+
if (version.action === "skip") {
|
|
2140
2772
|
if (!override) {
|
|
2141
2773
|
override = findOverride(external, shareScope, externalName) ?? "NOT_AVAILABLE";
|
|
2142
2774
|
}
|
|
2143
2775
|
if (override !== "NOT_AVAILABLE") {
|
|
2144
|
-
|
|
2145
|
-
overrideScope = getScope2(shareScope, override.remotes[0].name, externalName);
|
|
2146
|
-
source = override.remotes[0];
|
|
2147
|
-
sourceScope = overrideScope;
|
|
2148
|
-
override.remotes[0].cached = true;
|
|
2776
|
+
serving = override;
|
|
2149
2777
|
version.remotes[0].cached = false;
|
|
2150
2778
|
}
|
|
2151
2779
|
}
|
|
2152
|
-
const
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
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
|
+
}
|
|
2158
2795
|
version.remotes.forEach((r) => {
|
|
2159
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
|
+
}
|
|
2160
2811
|
for (const m of mappings) {
|
|
2161
2812
|
addToScope(importMap, rScope, { [m.packageName]: m.url });
|
|
2162
2813
|
}
|
|
2163
2814
|
for (const [packageName, file] of Object.entries(r.entries)) {
|
|
2164
|
-
if (packageName
|
|
2815
|
+
if (provided.has(packageName)) continue;
|
|
2165
2816
|
if (config.strict.strictEntryPointCoverage || config.profile.scopeUncoveredEntrypoints) {
|
|
2166
2817
|
warnUncoveredEntrypoint(shareScope, externalName, r.name, packageName);
|
|
2167
2818
|
continue;
|
|
@@ -2212,11 +2863,68 @@ function createGenerateImportMap(config, ports) {
|
|
|
2212
2863
|
}
|
|
2213
2864
|
config.log.warn(4, `ShareScope external ${scopedExternalName} has multiple shared versions.`);
|
|
2214
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
|
+
}
|
|
2215
2917
|
function addGlobalSharedExternals(importMap, chunkBundles) {
|
|
2216
2918
|
const sharedExternals = ports.sharedExternalsRepo.getFromScope();
|
|
2919
|
+
const index = buildServedIndex(sharedExternals);
|
|
2920
|
+
const served = [];
|
|
2921
|
+
const cachedBefore = /* @__PURE__ */ new Map();
|
|
2217
2922
|
for (const [externalName, external] of Object.entries(sharedExternals)) {
|
|
2218
|
-
|
|
2923
|
+
cachedBefore.set(externalName, cachedFingerprint(external));
|
|
2219
2924
|
for (const version of external.versions) {
|
|
2925
|
+
if (index && version.action !== "scope") {
|
|
2926
|
+
collectServed(served, index, GLOBAL_SCOPE, externalName, version);
|
|
2927
|
+
}
|
|
2220
2928
|
if (version.action === "skip") continue;
|
|
2221
2929
|
if (version.action === "scope") {
|
|
2222
2930
|
for (const remote of version.remotes) {
|
|
@@ -2235,29 +2943,46 @@ function createGenerateImportMap(config, ports) {
|
|
|
2235
2943
|
notifyDuplicateGlobalExternal(externalName);
|
|
2236
2944
|
continue;
|
|
2237
2945
|
}
|
|
2238
|
-
|
|
2239
|
-
for (const [packageName, file] of Object.entries(version.remotes[0].entries)) {
|
|
2240
|
-
const url = join(scope, file);
|
|
2241
|
-
addToGlobal(importMap, { [packageName]: url });
|
|
2242
|
-
addIntegrity(importMap, url, version.remotes[0].name, file);
|
|
2243
|
-
}
|
|
2244
|
-
registerBundleChunks(chunkBundles, version.remotes[0].name, version.remotes[0].bundle);
|
|
2245
|
-
version.remotes[0].cached = true;
|
|
2246
|
-
selfFillUncovered(importMap, chunkBundles, externalName, version.remotes, 1);
|
|
2946
|
+
mergeVersionEntries(importMap, chunkBundles, externalName, version);
|
|
2247
2947
|
}
|
|
2248
2948
|
for (const version of external.versions) {
|
|
2249
2949
|
if (version.action !== "skip") continue;
|
|
2250
2950
|
selfFillUncovered(importMap, chunkBundles, externalName, version.remotes);
|
|
2251
2951
|
}
|
|
2252
|
-
|
|
2952
|
+
}
|
|
2953
|
+
flushServed(importMap, chunkBundles, served);
|
|
2954
|
+
for (const [externalName, external] of Object.entries(sharedExternals)) {
|
|
2955
|
+
if (cachedFingerprint(external) !== cachedBefore.get(externalName)) {
|
|
2253
2956
|
ports.sharedExternalsRepo.addOrUpdate(externalName, external);
|
|
2254
2957
|
}
|
|
2255
2958
|
}
|
|
2256
2959
|
return importMap;
|
|
2257
2960
|
}
|
|
2258
|
-
function
|
|
2259
|
-
|
|
2260
|
-
|
|
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;
|
|
2261
2986
|
for (const [packageName, file] of Object.entries(remote.entries)) {
|
|
2262
2987
|
if (importMap.imports[packageName]) continue;
|
|
2263
2988
|
if (config.strict.strictEntryPointCoverage || config.profile.scopeUncoveredEntrypoints) {
|
|
@@ -2509,15 +3234,16 @@ function createUpdateCache(config, ports) {
|
|
|
2509
3234
|
const actions = {};
|
|
2510
3235
|
if (remoteEntry?.override) removeCachedRemoteEntries(/* @__PURE__ */ new Set([remoteEntry.name]));
|
|
2511
3236
|
storeRemoteEntry(remoteEntry, (entry, external, ctx) => {
|
|
2512
|
-
const { action,
|
|
3237
|
+
const { action, provided, sameVersion } = resolveSharedExternal(entry, external, ctx);
|
|
2513
3238
|
actions[external.packageName] = { action };
|
|
2514
|
-
if (action === "skip" &&
|
|
2515
|
-
actions[external.packageName].covered =
|
|
3239
|
+
if (action === "skip" && provided) {
|
|
3240
|
+
actions[external.packageName].covered = Array.from(provided.keys());
|
|
3241
|
+
if (sameVersion) actions[external.packageName].sameVersion = true;
|
|
2516
3242
|
if (external.shareScope) {
|
|
2517
3243
|
actions[external.packageName].override = resolveOverrideEntries(
|
|
2518
3244
|
entry,
|
|
2519
3245
|
external,
|
|
2520
|
-
|
|
3246
|
+
provided
|
|
2521
3247
|
);
|
|
2522
3248
|
}
|
|
2523
3249
|
}
|
|
@@ -2547,8 +3273,10 @@ function createUpdateCache(config, ports) {
|
|
|
2547
3273
|
}
|
|
2548
3274
|
config.log.warn(8, errorMsg);
|
|
2549
3275
|
}
|
|
2550
|
-
|
|
2551
|
-
|
|
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);
|
|
2552
3280
|
if (uncovered.length > 0) {
|
|
2553
3281
|
const msg = `[${sharedInfo.shareScope ?? GLOBAL_SCOPE}][${remoteEntry.name}][${sharedInfo.packageName}] Entrypoints not covered by the shared version: ${uncovered.join(", ")}.`;
|
|
2554
3282
|
if (config.strict.strictEntryPointCoverage) {
|
|
@@ -2576,65 +3304,157 @@ function createUpdateCache(config, ports) {
|
|
|
2576
3304
|
cached.versions.push({ tag, action, host: false, remotes: [remote] });
|
|
2577
3305
|
}
|
|
2578
3306
|
commit();
|
|
2579
|
-
return { action,
|
|
2580
|
-
}
|
|
2581
|
-
function resolveOverrideEntries(remoteEntry, external,
|
|
2582
|
-
return
|
|
2583
|
-
(
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
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(() => {
|
|
2590
3322
|
config.log.error(
|
|
2591
3323
|
8,
|
|
2592
3324
|
`[${external.shareScope ?? GLOBAL_SCOPE}][${remoteEntry.name}][${external.packageName}@${external.version}][override] Remote name not found in cache.`
|
|
2593
3325
|
);
|
|
2594
|
-
return new NFError(
|
|
2595
|
-
`Could not find override url from remote ${sharedVersion.remotes[0].name}`
|
|
2596
|
-
);
|
|
3326
|
+
return new NFError(`Could not find override url from remote ${providerName}`);
|
|
2597
3327
|
});
|
|
2598
3328
|
}
|
|
2599
3329
|
}
|
|
2600
3330
|
|
|
2601
3331
|
// src/lib/core/2.app/steps/pooling/pool-dynamic-externals.ts
|
|
2602
|
-
function createPoolDynamicExternals(config) {
|
|
3332
|
+
function createPoolDynamicExternals(config, ports) {
|
|
2603
3333
|
return ({ entry, actions }) => {
|
|
2604
3334
|
const { useAutoExternalPooling } = config.feature;
|
|
2605
|
-
|
|
2606
|
-
return Promise.resolve({ entry, actions });
|
|
2607
|
-
}
|
|
2608
|
-
const byScope = /* @__PURE__ */ new Map();
|
|
3335
|
+
const declared = /* @__PURE__ */ new Map();
|
|
2609
3336
|
for (const external of entry.shared ?? []) {
|
|
2610
3337
|
const name = external.packageName;
|
|
2611
3338
|
if (!external.singleton || !actions[name]) continue;
|
|
2612
3339
|
if (external.shareScope === STRICT_SCOPE) continue;
|
|
2613
|
-
const tag = external.pool?.trim();
|
|
2614
3340
|
const shareScope = external.shareScope ?? GLOBAL_SCOPE;
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
scope: autoScope(name, useAutoExternalPooling),
|
|
2619
|
-
tags: tag ? [{ remote: entry.name, tag }] : [],
|
|
2620
|
-
value: name
|
|
2621
|
-
});
|
|
2622
|
-
byScope.set(shareScope, candidates);
|
|
3341
|
+
let names = declared.get(shareScope);
|
|
3342
|
+
if (!names) declared.set(shareScope, names = /* @__PURE__ */ new Set());
|
|
3343
|
+
names.add(name);
|
|
2623
3344
|
}
|
|
3345
|
+
if (declared.size === 0) return Promise.resolve({ entry, actions });
|
|
2624
3346
|
const scope = (name) => {
|
|
2625
3347
|
actions[name].action = "scope";
|
|
2626
3348
|
delete actions[name].override;
|
|
2627
3349
|
};
|
|
2628
|
-
for (const
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
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;
|
|
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;
|
|
2633
3372
|
}
|
|
3373
|
+
redirect(entry.name, anchor, pool, asked.view, actions, shareScope);
|
|
2634
3374
|
}
|
|
2635
3375
|
}
|
|
2636
3376
|
return Promise.resolve({ entry, actions });
|
|
2637
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
|
+
}
|
|
2638
3458
|
}
|
|
2639
3459
|
|
|
2640
3460
|
// src/lib/core/2.app/steps/convert-to-import-map.ts
|
|
@@ -2672,13 +3492,7 @@ function createConvertToImportMap(config, ports) {
|
|
|
2672
3492
|
return;
|
|
2673
3493
|
}
|
|
2674
3494
|
if (actions[external.packageName].action === "skip") {
|
|
2675
|
-
const { override, covered } = actions[external.packageName];
|
|
2676
|
-
if (!external.shareScope) {
|
|
2677
|
-
if (covered) {
|
|
2678
|
-
serveUncovered(remoteEntry, external, covered, remoteEntryScope, importMap);
|
|
2679
|
-
}
|
|
2680
|
-
return;
|
|
2681
|
-
}
|
|
3495
|
+
const { override, covered, sameVersion } = actions[external.packageName];
|
|
2682
3496
|
if (override) {
|
|
2683
3497
|
Object.entries(override).forEach(([packageName, url]) => {
|
|
2684
3498
|
addToScopes(remoteEntryScope, packageName, url, importMap);
|
|
@@ -2688,10 +3502,17 @@ function createConvertToImportMap(config, ports) {
|
|
|
2688
3502
|
external,
|
|
2689
3503
|
covered ?? Object.keys(override),
|
|
2690
3504
|
remoteEntryScope,
|
|
2691
|
-
importMap
|
|
3505
|
+
importMap,
|
|
3506
|
+
sameVersion
|
|
2692
3507
|
);
|
|
2693
3508
|
return;
|
|
2694
3509
|
}
|
|
3510
|
+
if (!external.shareScope) {
|
|
3511
|
+
if (covered) {
|
|
3512
|
+
serveUncovered(remoteEntry, external, covered, remoteEntryScope, importMap, sameVersion);
|
|
3513
|
+
}
|
|
3514
|
+
return;
|
|
3515
|
+
}
|
|
2695
3516
|
log.error(
|
|
2696
3517
|
9,
|
|
2697
3518
|
`[${remoteEntry.name}][${external.packageName}] shareScope skip has no override.`
|
|
@@ -2756,11 +3577,11 @@ function createConvertToImportMap(config, ports) {
|
|
|
2756
3577
|
});
|
|
2757
3578
|
return importMap;
|
|
2758
3579
|
}
|
|
2759
|
-
function serveUncovered(remoteEntry, external, covered, remoteEntryScope, importMap) {
|
|
3580
|
+
function serveUncovered(remoteEntry, external, covered, remoteEntryScope, importMap, sameVersion = false) {
|
|
2760
3581
|
const provided = new Set(covered);
|
|
2761
3582
|
Object.entries(external.entries).forEach(([packageName, fileName]) => {
|
|
2762
3583
|
if (provided.has(packageName)) return;
|
|
2763
|
-
if (config.strict.strictEntryPointCoverage || config.profile.scopeUncoveredEntrypoints) {
|
|
3584
|
+
if (!sameVersion && (config.strict.strictEntryPointCoverage || config.profile.scopeUncoveredEntrypoints)) {
|
|
2764
3585
|
warnUncoveredEntrypoint(remoteEntry.name, external.packageName, packageName);
|
|
2765
3586
|
return;
|
|
2766
3587
|
}
|
|
@@ -2792,6 +3613,7 @@ var createInitDrivers = ({
|
|
|
2792
3613
|
}) => ({
|
|
2793
3614
|
getRemoteEntries: createGetRemoteEntries(config, adapters),
|
|
2794
3615
|
processRemoteEntries: createProcessRemoteEntries(config, adapters),
|
|
3616
|
+
markPoolsForReelection: createMarkPoolsForReelection(config, adapters),
|
|
2795
3617
|
determineSharedExternals: createDetermineSharedExternals(config, adapters),
|
|
2796
3618
|
poolSharedExternals: createPoolSharedExternals(config, adapters),
|
|
2797
3619
|
generateImportMap: createGenerateImportMap(config, adapters),
|
|
@@ -2799,7 +3621,7 @@ var createInitDrivers = ({
|
|
|
2799
3621
|
exposeModuleLoader: createExposeModuleLoader(config, adapters),
|
|
2800
3622
|
getRemoteEntry: createGetRemoteEntry(config, adapters),
|
|
2801
3623
|
updateCache: createUpdateCache(config, adapters),
|
|
2802
|
-
poolDynamicExternals: createPoolDynamicExternals(config),
|
|
3624
|
+
poolDynamicExternals: createPoolDynamicExternals(config, adapters),
|
|
2803
3625
|
convertToImportMap: createConvertToImportMap(config, adapters)
|
|
2804
3626
|
});
|
|
2805
3627
|
var INIT_FLOW_FACTORY = ({
|
|
@@ -2910,7 +3732,7 @@ var createManifestProvider = () => {
|
|
|
2910
3732
|
};
|
|
2911
3733
|
};
|
|
2912
3734
|
|
|
2913
|
-
// node_modules/.pnpm/@softarc+native-federation@4.
|
|
3735
|
+
// node_modules/.pnpm/@softarc+native-federation@4.5.0_typescript@6.0.3/node_modules/@softarc/native-federation/dist/lib/utils/normalize.js
|
|
2914
3736
|
function inferPackageFromSecondary(secondary) {
|
|
2915
3737
|
const parts = secondary.split("/");
|
|
2916
3738
|
if (secondary.startsWith("@") && parts.length >= 2) {
|
|
@@ -2919,7 +3741,7 @@ function inferPackageFromSecondary(secondary) {
|
|
|
2919
3741
|
return parts[0];
|
|
2920
3742
|
}
|
|
2921
3743
|
|
|
2922
|
-
// node_modules/.pnpm/@softarc+native-federation@4.
|
|
3744
|
+
// node_modules/.pnpm/@softarc+native-federation@4.5.0_typescript@6.0.3/node_modules/@softarc/native-federation/dist/lib/core/output/densify-externals.js
|
|
2923
3745
|
function isDense(entry) {
|
|
2924
3746
|
return "entries" in entry;
|
|
2925
3747
|
}
|
|
@@ -3100,14 +3922,18 @@ var createSharedExternalsRepository = (config) => {
|
|
|
3100
3922
|
);
|
|
3101
3923
|
if (config.clearStorage) STORAGE.clear();
|
|
3102
3924
|
const _cache = STORAGE.get() ?? { [GLOBAL_SCOPE]: {} };
|
|
3103
|
-
let _sawPoolTag = false;
|
|
3104
3925
|
let _dirty = false;
|
|
3105
3926
|
return {
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
hasPoolTag: function() {
|
|
3110
|
-
|
|
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;
|
|
3111
3937
|
},
|
|
3112
3938
|
getFromScope: function(shareScope) {
|
|
3113
3939
|
return { ..._cache[shareScope ?? GLOBAL_SCOPE] };
|
|
@@ -3128,9 +3954,11 @@ var createSharedExternalsRepository = (config) => {
|
|
|
3128
3954
|
Object.values(_cache).forEach((scope) => {
|
|
3129
3955
|
const removeExternals = [];
|
|
3130
3956
|
Object.entries(scope).forEach(([name, external]) => {
|
|
3131
|
-
let
|
|
3957
|
+
let removedCopy = false;
|
|
3132
3958
|
for (let i = external.versions.length - 1; i >= 0; i--) {
|
|
3133
|
-
const
|
|
3959
|
+
const version = external.versions[i];
|
|
3960
|
+
const remotes = version.remotes;
|
|
3961
|
+
const host = version.host ? remotes[0]?.name : void 0;
|
|
3134
3962
|
let keep = 0;
|
|
3135
3963
|
for (let r = 0; r < remotes.length; r++) {
|
|
3136
3964
|
if (remoteNames.has(remotes[r].name)) continue;
|
|
@@ -3138,15 +3966,16 @@ var createSharedExternalsRepository = (config) => {
|
|
|
3138
3966
|
}
|
|
3139
3967
|
if (keep !== remotes.length) {
|
|
3140
3968
|
remotes.length = keep;
|
|
3969
|
+
removedCopy = true;
|
|
3141
3970
|
_dirty = true;
|
|
3971
|
+
if (host !== void 0 && remoteNames.has(host)) version.host = false;
|
|
3142
3972
|
}
|
|
3143
3973
|
if (remotes.length === 0) {
|
|
3144
3974
|
external.versions.splice(i, 1);
|
|
3145
|
-
removedVersion = true;
|
|
3146
3975
|
_dirty = true;
|
|
3147
3976
|
}
|
|
3148
3977
|
}
|
|
3149
|
-
if (
|
|
3978
|
+
if (removedCopy) {
|
|
3150
3979
|
external.dirty = true;
|
|
3151
3980
|
if (external.versions.length === 0) removeExternals.push(name);
|
|
3152
3981
|
}
|
|
@@ -3179,27 +4008,108 @@ var createSharedExternalsRepository = (config) => {
|
|
|
3179
4008
|
};
|
|
3180
4009
|
|
|
3181
4010
|
// src/lib/core/3.adapters/browser/sse-handler.ts
|
|
4011
|
+
var RELAY_CHANNEL = "native-federation-sse";
|
|
4012
|
+
var LOCK_PREFIX = "native-federation-sse:";
|
|
3182
4013
|
var createSSEHandler = (config) => {
|
|
3183
|
-
const
|
|
4014
|
+
const subscriptions = /* @__PURE__ */ new Map();
|
|
4015
|
+
const locks = typeof navigator !== "undefined" ? navigator.locks : void 0;
|
|
4016
|
+
const canRelay = !!locks && typeof BroadcastChannel !== "undefined";
|
|
4017
|
+
let relay;
|
|
4018
|
+
let bound = false;
|
|
4019
|
+
function reload(reason) {
|
|
4020
|
+
config.log.debug(0, `[SSE] ${reason}, reloading...`);
|
|
4021
|
+
config.reloadBrowserFn();
|
|
4022
|
+
}
|
|
4023
|
+
function openEventSource(endpoint, subscription) {
|
|
4024
|
+
const eventSource = new EventSource(endpoint);
|
|
4025
|
+
eventSource.onmessage = function(event) {
|
|
4026
|
+
const data = JSON.parse(event.data);
|
|
4027
|
+
if (data.type === BuildNotificationType.COMPLETED) {
|
|
4028
|
+
relay?.postMessage({ endpoint });
|
|
4029
|
+
reload("Rebuild completed");
|
|
4030
|
+
}
|
|
4031
|
+
};
|
|
4032
|
+
eventSource.onerror = function(event) {
|
|
4033
|
+
config.log.error(0, "[SSE] Connection error:", event);
|
|
4034
|
+
if (eventSource.readyState !== eventSource.CLOSED) return;
|
|
4035
|
+
config.log.debug(0, `[SSE] Stream for '${endpoint}' will not reopen, releasing the lock`);
|
|
4036
|
+
disconnect(subscription);
|
|
4037
|
+
};
|
|
4038
|
+
return eventSource;
|
|
4039
|
+
}
|
|
4040
|
+
function connect(endpoint, subscription) {
|
|
4041
|
+
if (!locks) {
|
|
4042
|
+
subscription.source = openEventSource(endpoint, subscription);
|
|
4043
|
+
return;
|
|
4044
|
+
}
|
|
4045
|
+
const abort = new AbortController();
|
|
4046
|
+
const held = new Promise((resolve) => {
|
|
4047
|
+
subscription.releaseLock = resolve;
|
|
4048
|
+
});
|
|
4049
|
+
subscription.abort = abort;
|
|
4050
|
+
locks.request(`${LOCK_PREFIX}${endpoint}`, { signal: abort.signal }, () => {
|
|
4051
|
+
if (abort.signal.aborted) return;
|
|
4052
|
+
config.log.debug(0, `[SSE] Holding the connection for '${endpoint}'`);
|
|
4053
|
+
subscription.source = openEventSource(endpoint, subscription);
|
|
4054
|
+
return held;
|
|
4055
|
+
}).catch(() => {
|
|
4056
|
+
config.log.debug(0, `[SSE] Released the connection for '${endpoint}'`);
|
|
4057
|
+
});
|
|
4058
|
+
}
|
|
4059
|
+
function disconnect(subscription) {
|
|
4060
|
+
subscription.source?.close();
|
|
4061
|
+
subscription.abort?.abort();
|
|
4062
|
+
subscription.releaseLock?.();
|
|
4063
|
+
subscription.source = void 0;
|
|
4064
|
+
subscription.abort = void 0;
|
|
4065
|
+
subscription.releaseLock = void 0;
|
|
4066
|
+
}
|
|
4067
|
+
function onPageHide(event) {
|
|
4068
|
+
if (event.persisted) subscriptions.forEach(disconnect);
|
|
4069
|
+
}
|
|
4070
|
+
function onPageShow(event) {
|
|
4071
|
+
if (event.persisted)
|
|
4072
|
+
subscriptions.forEach((subscription, endpoint) => connect(endpoint, subscription));
|
|
4073
|
+
}
|
|
4074
|
+
function bind() {
|
|
4075
|
+
if (bound) return;
|
|
4076
|
+
bound = true;
|
|
4077
|
+
if (canRelay) {
|
|
4078
|
+
relay = new BroadcastChannel(RELAY_CHANNEL);
|
|
4079
|
+
relay.onmessage = (event) => {
|
|
4080
|
+
if (subscriptions.has(event.data?.endpoint)) reload("Rebuild completed in another tab");
|
|
4081
|
+
};
|
|
4082
|
+
}
|
|
4083
|
+
if (typeof window !== "undefined") {
|
|
4084
|
+
window.addEventListener("pagehide", onPageHide);
|
|
4085
|
+
window.addEventListener("pageshow", onPageShow);
|
|
4086
|
+
}
|
|
4087
|
+
}
|
|
4088
|
+
function unbind() {
|
|
4089
|
+
if (!bound) return;
|
|
4090
|
+
bound = false;
|
|
4091
|
+
relay?.close();
|
|
4092
|
+
relay = void 0;
|
|
4093
|
+
if (typeof window !== "undefined") {
|
|
4094
|
+
window.removeEventListener("pagehide", onPageHide);
|
|
4095
|
+
window.removeEventListener("pageshow", onPageShow);
|
|
4096
|
+
}
|
|
4097
|
+
}
|
|
3184
4098
|
return {
|
|
3185
4099
|
watchRemoteBuilds: function(endpoint) {
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
};
|
|
3195
|
-
eventSource.onerror = function(event) {
|
|
3196
|
-
config.log.error(0, "[SSE] Connection error:", event);
|
|
3197
|
-
};
|
|
3198
|
-
subscribers.push(eventSource);
|
|
4100
|
+
if (subscriptions.has(endpoint)) {
|
|
4101
|
+
config.log.debug(0, `[SSE] Already watching '${endpoint}'`);
|
|
4102
|
+
return;
|
|
4103
|
+
}
|
|
4104
|
+
bind();
|
|
4105
|
+
const subscription = {};
|
|
4106
|
+
subscriptions.set(endpoint, subscription);
|
|
4107
|
+
connect(endpoint, subscription);
|
|
3199
4108
|
},
|
|
3200
4109
|
closeAll: function() {
|
|
3201
|
-
|
|
3202
|
-
|
|
4110
|
+
subscriptions.forEach(disconnect);
|
|
4111
|
+
subscriptions.clear();
|
|
4112
|
+
unbind();
|
|
3203
4113
|
}
|
|
3204
4114
|
};
|
|
3205
4115
|
};
|
|
@@ -3217,6 +4127,14 @@ var createChunkRepository = (config) => {
|
|
|
3217
4127
|
_dirty = true;
|
|
3218
4128
|
return this;
|
|
3219
4129
|
},
|
|
4130
|
+
// Whole-remote: a build that stops chunking a bundle omits the key, so `addOrReplace` cannot clear it.
|
|
4131
|
+
remove: function(remoteName) {
|
|
4132
|
+
if (remoteName in _cache) {
|
|
4133
|
+
delete _cache[remoteName];
|
|
4134
|
+
_dirty = true;
|
|
4135
|
+
}
|
|
4136
|
+
return this;
|
|
4137
|
+
},
|
|
3220
4138
|
tryGet: function(remoteName, bundleName) {
|
|
3221
4139
|
return Optional.of(_cache[remoteName]?.[bundleName]);
|
|
3222
4140
|
},
|