@softarc/native-federation-orchestrator 4.4.1 → 4.5.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.
Files changed (27) hide show
  1. package/README.md +2 -2
  2. package/fesm2022/@softarc/native-federation-orchestrator.mjs +509 -78
  3. package/fesm2022/@softarc/native-federation-orchestrator.mjs.map +4 -4
  4. package/fesm2022/module-federation.mjs +21 -17
  5. package/fesm2022/module-federation.mjs.map +2 -2
  6. package/fesm2022/node.mjs +439 -72
  7. package/fesm2022/node.mjs.map +4 -4
  8. package/fesm2022/sdk.mjs +63 -1
  9. package/fesm2022/sdk.mjs.map +3 -3
  10. package/package.json +1 -1
  11. package/quickstart.mjs +13 -13
  12. package/types/lib/core/1.domain/externals/version.contract.d.ts +3 -2
  13. package/types/lib/core/1.domain/remote-entry/remote-entry.contract.d.ts +8 -4
  14. package/types/lib/core/2.app/config/mode.contract.d.ts +6 -0
  15. package/types/lib/core/2.app/driver-ports/init/drivers.contract.d.ts +4 -0
  16. package/types/lib/core/2.app/driver-ports/init/for-pooling-dynamic-externals.port.d.ts +8 -0
  17. package/types/lib/core/2.app/driver-ports/init/for-pooling-shared-externals.port.d.ts +1 -0
  18. package/types/lib/core/2.app/driver-ports/init/index.d.ts +2 -0
  19. package/types/lib/core/2.app/driving-ports/for-shared-externals-storage.port.d.ts +2 -0
  20. package/types/lib/core/2.app/steps/convert-to-import-map.d.ts +2 -1
  21. package/types/lib/core/2.app/steps/pooling/pool-dynamic-externals.d.ts +3 -0
  22. package/types/lib/core/2.app/steps/pooling/pool-graph.d.ts +25 -0
  23. package/types/lib/core/2.app/steps/pooling/pool-shared-externals.d.ts +5 -0
  24. package/types/lib/core/2.app/steps/pooling/pool.types.d.ts +25 -0
  25. package/types/lib/core/2.app/steps/pooling/pool.util.d.ts +4 -0
  26. package/types/lib/core/2.app/steps/store-remote-entry.d.ts +2 -2
  27. package/types/lib/core/3.adapters/http/remote-entry-provider.d.ts +2 -1
@@ -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.generateImportMap).then((importMap) => flow.commitChanges(importMap, { override: true })).then(flow.exposeModuleLoader).then((loadRemoteModule) => ({
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) => ({
1287
1287
  config,
1288
1288
  adapters,
1289
1289
  loadRemoteModule
@@ -1294,7 +1294,7 @@ var createInitFlow = ({
1294
1294
  var createInitRemoteEntryFlow = ({
1295
1295
  flow
1296
1296
  }) => {
1297
- const processDynamicRemoteEntry = (remoteEntry) => flow.updateCache(remoteEntry).then(flow.convertToImportMap).then(flow.commitChanges);
1297
+ const processDynamicRemoteEntry = (remoteEntry) => flow.updateCache(remoteEntry).then(flow.poolDynamicExternals).then(flow.convertToImportMap).then(flow.commitChanges);
1298
1298
  return (remoteEntryUrl, remote) => flow.getRemoteEntry(remoteEntryUrl, remote).then((entry) => entry.map(processDynamicRemoteEntry).orElse(Promise.resolve())).then(() => void 0);
1299
1299
  };
1300
1300
 
@@ -1454,6 +1454,7 @@ function createStoreRemoteEntry(config, ports, logStep) {
1454
1454
  const tag = resolveVersion(remoteEntry, external);
1455
1455
  if (tag === null) return;
1456
1456
  if (external.singleton) {
1457
+ if (external.pool?.trim()) ports.sharedExternalsRepo.markPoolTagPresent();
1457
1458
  onSharedExternal(remoteEntry, external, sharedExternalContext(remoteEntry, external, tag));
1458
1459
  } else {
1459
1460
  addScopedExternal(remoteEntry, external, tag);
@@ -1473,8 +1474,8 @@ function createStoreRemoteEntry(config, ports, logStep) {
1473
1474
  function addScopedExternal(remoteEntry, sharedInfo, tag) {
1474
1475
  ports.scopedExternalsRepo.addExternal(remoteEntry.name, sharedInfo.packageName, {
1475
1476
  tag,
1476
- file: sharedInfo.outFileName,
1477
- bundle: sharedInfo.bundle
1477
+ bundle: sharedInfo.bundle,
1478
+ entries: sharedInfo.entries
1478
1479
  });
1479
1480
  }
1480
1481
  function resolveVersion(remoteEntry, external) {
@@ -1496,12 +1497,13 @@ function createStoreRemoteEntry(config, ports, logStep) {
1496
1497
  function sharedExternalContext(remoteEntry, sharedInfo, tag) {
1497
1498
  const scopeType = ports.sharedExternalsRepo.scopeType(sharedInfo.shareScope);
1498
1499
  const remote = {
1499
- file: sharedInfo.outFileName,
1500
1500
  name: remoteEntry.name,
1501
1501
  bundle: sharedInfo.bundle,
1502
1502
  strictVersion: sharedInfo.strictVersion,
1503
1503
  cached: false,
1504
- requiredVersion: scopeType === "strict" ? tag : sharedInfo.requiredVersion || tag
1504
+ requiredVersion: scopeType === "strict" ? tag : sharedInfo.requiredVersion || tag,
1505
+ ...sharedInfo.pool?.trim() ? { pool: sharedInfo.pool } : {},
1506
+ entries: sharedInfo.entries
1505
1507
  };
1506
1508
  const cached = ports.sharedExternalsRepo.tryGet(sharedInfo.packageName, sharedInfo.shareScope).orElse({ dirty: false, versions: [] });
1507
1509
  return {
@@ -1576,6 +1578,66 @@ function createProcessRemoteEntries(config, ports) {
1576
1578
  var GLOBAL_SCOPE = "__GLOBAL__";
1577
1579
  var STRICT_SCOPE = "strict";
1578
1580
 
1581
+ // src/lib/core/1.domain/remote-entry/remote-entry.contract.ts
1582
+ var CHUNK_PREFIX = "@nf-internal";
1583
+ function toDenseSharedInfoFormat(shared) {
1584
+ return shared.map((external) => {
1585
+ if ("entries" in external) return external;
1586
+ const { outFileName, ...baseSharedInfoProps } = external;
1587
+ return {
1588
+ ...baseSharedInfoProps,
1589
+ entries: { [external.packageName]: outFileName }
1590
+ };
1591
+ });
1592
+ }
1593
+ function inferPackageFromSecondary(secondary) {
1594
+ const parts = secondary.split("/");
1595
+ if (secondary.startsWith("@") && parts.length >= 2) {
1596
+ return parts[0] + "/" + parts[1];
1597
+ }
1598
+ return parts[0] ?? secondary;
1599
+ }
1600
+ function densifyExternals(shared) {
1601
+ const result = [];
1602
+ const groupIndex = /* @__PURE__ */ new Map();
1603
+ for (const entry of shared) {
1604
+ const isDense = "entries" in entry;
1605
+ const isChunk = entry.packageName.startsWith(CHUNK_PREFIX + "/");
1606
+ if (isDense || isChunk) {
1607
+ result.push(entry);
1608
+ continue;
1609
+ }
1610
+ const parent = inferPackageFromSecondary(entry.packageName);
1611
+ const sig = JSON.stringify({
1612
+ singleton: entry.singleton,
1613
+ strictVersion: entry.strictVersion,
1614
+ requiredVersion: entry.requiredVersion,
1615
+ version: entry.version,
1616
+ shareScope: entry.shareScope
1617
+ });
1618
+ const key = parent + " " + sig;
1619
+ const existing = groupIndex.get(key);
1620
+ if (existing === void 0) {
1621
+ const dense = {
1622
+ singleton: entry.singleton,
1623
+ strictVersion: entry.strictVersion,
1624
+ requiredVersion: entry.requiredVersion,
1625
+ packageName: parent,
1626
+ entries: { [entry.packageName]: entry.outFileName }
1627
+ };
1628
+ if (entry.version !== void 0) dense.version = entry.version;
1629
+ if (entry.shareScope !== void 0) dense.shareScope = entry.shareScope;
1630
+ if (entry.bundle !== void 0) dense.bundle = entry.bundle;
1631
+ if (entry.dev !== void 0) dense.dev = entry.dev;
1632
+ groupIndex.set(key, result.length);
1633
+ result.push(dense);
1634
+ } else {
1635
+ result[existing].entries[entry.packageName] = entry.outFileName;
1636
+ }
1637
+ }
1638
+ return result;
1639
+ }
1640
+
1579
1641
  // src/lib/core/2.app/steps/determine-shared-externals.ts
1580
1642
  function createDetermineSharedExternals(config, ports) {
1581
1643
  return () => {
@@ -1653,7 +1715,290 @@ function createDetermineSharedExternals(config, ports) {
1653
1715
  }
1654
1716
  }
1655
1717
 
1656
- // node_modules/.pnpm/@softarc+native-federation@4.2.1_typescript@6.0.3/node_modules/@softarc/native-federation/dist/lib/domain/core/build-notification-options.contract.js
1718
+ // src/lib/core/2.app/steps/pooling/pool-graph.ts
1719
+ var SCOPED_PACKAGE = /^@([^/]+)\//;
1720
+ function createDSU() {
1721
+ const ids = /* @__PURE__ */ new Map();
1722
+ const parent = [];
1723
+ const size = [];
1724
+ const intern = (key) => {
1725
+ let id = ids.get(key);
1726
+ if (id === void 0) {
1727
+ id = parent.length;
1728
+ ids.set(key, id);
1729
+ parent.push(id);
1730
+ size.push(1);
1731
+ }
1732
+ return id;
1733
+ };
1734
+ const findRoot = (x) => {
1735
+ while (parent[x] !== x) {
1736
+ parent[x] = parent[parent[x]];
1737
+ x = parent[x];
1738
+ }
1739
+ return x;
1740
+ };
1741
+ return {
1742
+ union(a, b) {
1743
+ let rootA = findRoot(intern(a));
1744
+ let rootB = findRoot(intern(b));
1745
+ if (rootA === rootB) return;
1746
+ if (size[rootA] < size[rootB]) [rootA, rootB] = [rootB, rootA];
1747
+ parent[rootB] = rootA;
1748
+ size[rootA] = size[rootA] + size[rootB];
1749
+ },
1750
+ // Interns the key if unseen — an isolated key is its own component.
1751
+ component(key) {
1752
+ return findRoot(intern(key));
1753
+ }
1754
+ };
1755
+ }
1756
+ var extNode = (name) => `ext\0${name}`;
1757
+ var tagNode = (remote, tag) => `tag\0${remote}\0${tag}`;
1758
+ var scopeNode = (scope) => `scope\0${scope}`;
1759
+ function autoScope(name, useAutoExternalPooling) {
1760
+ return useAutoExternalPooling ? SCOPED_PACKAGE.exec(name)?.[1] : void 0;
1761
+ }
1762
+ function groupByMembership(candidates, log) {
1763
+ const dsu = createDSU();
1764
+ const tagged = /* @__PURE__ */ new Set();
1765
+ const poolable = [];
1766
+ for (const candidate of candidates) {
1767
+ let joined = false;
1768
+ if (candidate.scope !== void 0) {
1769
+ dsu.union(extNode(candidate.name), scopeNode(candidate.scope));
1770
+ joined = true;
1771
+ }
1772
+ for (const edge of candidate.tags) {
1773
+ dsu.union(extNode(candidate.name), tagNode(edge.remote, edge.tag));
1774
+ tagged.add(candidate.name);
1775
+ joined = true;
1776
+ }
1777
+ if (joined) poolable.push({ name: candidate.name, value: candidate.value });
1778
+ }
1779
+ const byComponent = /* @__PURE__ */ new Map();
1780
+ for (const entry of poolable) {
1781
+ const root = dsu.component(extNode(entry.name));
1782
+ (byComponent.get(root) ?? byComponent.set(root, []).get(root)).push(entry);
1783
+ }
1784
+ const pools = [];
1785
+ for (const members of byComponent.values()) {
1786
+ members.sort((a, b) => a.name.localeCompare(b.name));
1787
+ if (members.length < 2) {
1788
+ const only = members[0];
1789
+ if (tagged.has(only.name)) {
1790
+ log?.warn(
1791
+ 3,
1792
+ `[${only.name}] declares a 'pool' tag but no other external joined its pool; likely a typo or a missing sibling.`
1793
+ );
1794
+ }
1795
+ continue;
1796
+ }
1797
+ pools.push(members);
1798
+ }
1799
+ pools.sort((a, b) => a[0].name.localeCompare(b[0].name));
1800
+ return new Map(pools.map((members) => [members[0].name, members.map((m) => m.value)]));
1801
+ }
1802
+ function buildPools(sharedExternals, useAutoExternalPooling, log) {
1803
+ const candidates = Object.entries(sharedExternals).map(
1804
+ ([name, external]) => ({
1805
+ name,
1806
+ scope: autoScope(name, useAutoExternalPooling),
1807
+ tags: external.versions.flatMap(
1808
+ (v) => v.remotes.flatMap((r) => {
1809
+ const tag = r.pool?.trim();
1810
+ return tag ? [{ remote: r.name, tag }] : [];
1811
+ })
1812
+ ),
1813
+ value: { name, external }
1814
+ })
1815
+ );
1816
+ return groupByMembership(candidates, log);
1817
+ }
1818
+
1819
+ // src/lib/core/2.app/steps/pooling/pool.util.ts
1820
+ function versionForRemote(member, remote) {
1821
+ return member.external.versions.find((v) => v.remotes.some((r) => r.name === remote));
1822
+ }
1823
+ function remotesInPool(members) {
1824
+ return [
1825
+ ...new Set(members.flatMap((m) => m.external.versions.flatMap((v) => v.remotes.map((r) => r.name))))
1826
+ ];
1827
+ }
1828
+
1829
+ // src/lib/core/2.app/steps/pooling/pool-shared-externals.ts
1830
+ function poolWinners(members) {
1831
+ return members.map((member) => {
1832
+ const share = member.external.versions.find((v) => v.action === "share");
1833
+ return {
1834
+ member,
1835
+ tag: share?.tag ?? "",
1836
+ providers: new Set((share?.remotes ?? []).map((r) => r.name))
1837
+ };
1838
+ });
1839
+ }
1840
+ function selectCoverageAnchor(winners) {
1841
+ const coverage = /* @__PURE__ */ new Map();
1842
+ for (const winner of winners)
1843
+ for (const remote of winner.providers) coverage.set(remote, (coverage.get(remote) ?? 0) + 1);
1844
+ if (coverage.size === 0) return void 0;
1845
+ const anchorRemote = [...coverage.keys()].sort(
1846
+ (a, b) => coverage.get(b) - coverage.get(a) || a.localeCompare(b)
1847
+ )[0];
1848
+ return {
1849
+ anchorRemote,
1850
+ tagPerMember: Object.fromEntries(
1851
+ winners.filter((w) => w.providers.has(anchorRemote)).map((w) => [w.member.name, w.tag])
1852
+ )
1853
+ };
1854
+ }
1855
+ function classifyPoolConservative(members, anchor) {
1856
+ const classification = /* @__PURE__ */ new Map();
1857
+ for (const remote of remotesInPool(members)) {
1858
+ let coverageForced = false;
1859
+ let verdict = "follow";
1860
+ for (const member of members) {
1861
+ const version = versionForRemote(member, remote);
1862
+ if (!version) continue;
1863
+ const anchorTag = anchor.tagPerMember[member.name];
1864
+ if (!anchorTag) {
1865
+ coverageForced = true;
1866
+ continue;
1867
+ }
1868
+ if (version.tag === anchorTag) continue;
1869
+ if (version.action === "scope") {
1870
+ verdict = "scope-incompat";
1871
+ break;
1872
+ }
1873
+ }
1874
+ classification.set(
1875
+ remote,
1876
+ verdict === "scope-incompat" ? "scope-incompat" : coverageForced ? "scope-coverage" : "follow"
1877
+ );
1878
+ }
1879
+ classification.set(anchor.anchorRemote, "follow");
1880
+ return classification;
1881
+ }
1882
+ function createPoolSharedExternals(config, ports) {
1883
+ return () => {
1884
+ const { useAutoExternalPooling } = config.feature;
1885
+ if (!useAutoExternalPooling && !ports.sharedExternalsRepo.hasPoolTag()) {
1886
+ return Promise.resolve();
1887
+ }
1888
+ for (const scope of ports.sharedExternalsRepo.getScopes()) {
1889
+ if (ports.sharedExternalsRepo.scopeType(scope) === "strict") continue;
1890
+ const sharedExternals = ports.sharedExternalsRepo.getFromScope(scope);
1891
+ try {
1892
+ for (const [poolName, members] of buildPools(
1893
+ sharedExternals,
1894
+ useAutoExternalPooling,
1895
+ config.log
1896
+ )) {
1897
+ poolFamily(poolName, members, scope);
1898
+ }
1899
+ } catch (error) {
1900
+ if (error instanceof NFError) return Promise.reject(error);
1901
+ config.log.error(3, `[${scope}] failed to pool shared externals.`, {
1902
+ sharedExternals,
1903
+ error
1904
+ });
1905
+ return Promise.reject(
1906
+ new NFError(`Could not pool shared externals in scope ${scope}.`, error)
1907
+ );
1908
+ }
1909
+ }
1910
+ return Promise.resolve();
1911
+ };
1912
+ function poolFamily(poolName, members, scope) {
1913
+ if (members.length < 2) return;
1914
+ const allRemotes = remotesInPool(members);
1915
+ if (allRemotes.length < 2) return;
1916
+ const anchor = selectCoverageAnchor(poolWinners(members));
1917
+ if (!anchor) return;
1918
+ const classification = classifyPoolConservative(members, anchor);
1919
+ config.log.debug(
1920
+ 3,
1921
+ `[${scope}][pool:${poolName}] members=[${members.map((m) => m.name).join(", ")}], anchor=${anchor.anchorRemote}, remotes={${allRemotes.map((r) => `${r}:${classification.get(r)}`).join(", ")}}`
1922
+ );
1923
+ const incompatForced = allRemotes.some((r) => classification.get(r) === "scope-incompat");
1924
+ if (incompatForced && config.strict.strictExternalCompatibility) {
1925
+ config.log.error(
1926
+ 3,
1927
+ `[${scope}][pool:${poolName}] A remote is version-incompatible with anchor '${anchor.anchorRemote}'.`
1928
+ );
1929
+ throw new NFError(`Could not pool '${poolName}' in scope ${scope}.`);
1930
+ }
1931
+ for (const member of members) {
1932
+ const rebuilt = rebuildMember(member, anchor, classification);
1933
+ warnIfScopedOnly(poolName, member.name, rebuilt, scope);
1934
+ ports.sharedExternalsRepo.addOrUpdate(member.name, rebuilt, scope);
1935
+ }
1936
+ }
1937
+ function warnIfScopedOnly(poolName, memberName, rebuilt, scope) {
1938
+ if (rebuilt.versions.some((v) => v.action === "share")) return;
1939
+ const copies = rebuilt.versions.reduce((n, v) => n + v.remotes.length, 0);
1940
+ config.log.warn(
1941
+ 3,
1942
+ `[${scope}][pool:${poolName}] '${memberName}' is scoped-only \u2014 no coherent shared build provides it; ${copies} remotes download their own copy.`
1943
+ );
1944
+ }
1945
+ function rebuildMember(member, anchor, classification) {
1946
+ const anchorTag = anchor.tagPerMember[member.name];
1947
+ const entries = member.external.versions.flatMap(
1948
+ (v) => v.remotes.map((meta) => ({ remote: meta.name, tag: v.tag, host: v.host, meta }))
1949
+ );
1950
+ if (!anchorTag) {
1951
+ return {
1952
+ dirty: false,
1953
+ versions: entries.length > 0 ? [
1954
+ {
1955
+ tag: entries[0].tag,
1956
+ host: false,
1957
+ action: "scope",
1958
+ remotes: entries.map((e) => e.meta)
1959
+ }
1960
+ ] : []
1961
+ };
1962
+ }
1963
+ const anchorMeta = entries.find(
1964
+ (e) => e.remote === anchor.anchorRemote && e.tag === anchorTag
1965
+ ).meta;
1966
+ const anchorHost = member.external.versions.find((v) => v.tag === anchorTag)?.host ?? false;
1967
+ const scopes = (e) => {
1968
+ const cls = classification.get(e.remote);
1969
+ return cls === "scope-incompat" || cls === "scope-coverage" && e.tag !== anchorTag;
1970
+ };
1971
+ const shareOnAnchor = entries.filter(
1972
+ (e) => e.remote !== anchor.anchorRemote && e.tag === anchorTag && !scopes(e)
1973
+ );
1974
+ const shareVersion = {
1975
+ tag: anchorTag,
1976
+ host: anchorHost,
1977
+ action: "share",
1978
+ remotes: [anchorMeta, ...shareOnAnchor.map((e) => e.meta)]
1979
+ };
1980
+ const skipByTag = /* @__PURE__ */ new Map();
1981
+ for (const e of entries) {
1982
+ if (e.tag === anchorTag || scopes(e)) continue;
1983
+ const version = skipByTag.get(e.tag) ?? {
1984
+ tag: e.tag,
1985
+ host: e.host,
1986
+ action: "skip",
1987
+ remotes: []
1988
+ };
1989
+ version.remotes.push(e.meta);
1990
+ skipByTag.set(e.tag, version);
1991
+ }
1992
+ const scoped = entries.filter((e) => scopes(e));
1993
+ const scopeVersion = scoped.length > 0 ? [{ tag: anchorTag, host: false, action: "scope", remotes: scoped.map((e) => e.meta) }] : [];
1994
+ return {
1995
+ dirty: false,
1996
+ versions: [shareVersion, ...skipByTag.values(), ...scopeVersion]
1997
+ };
1998
+ }
1999
+ }
2000
+
2001
+ // node_modules/.pnpm/@softarc+native-federation@4.3.0_typescript@6.0.3/node_modules/@softarc/native-federation/dist/lib/domain/core/build-notification-options.contract.js
1657
2002
  var BuildNotificationType = /* @__PURE__ */ ((BuildNotificationType2) => {
1658
2003
  BuildNotificationType2["COMPLETED"] = "federation-rebuild-complete";
1659
2004
  BuildNotificationType2["ERROR"] = "federation-rebuild-error";
@@ -1661,14 +2006,14 @@ var BuildNotificationType = /* @__PURE__ */ ((BuildNotificationType2) => {
1661
2006
  return BuildNotificationType2;
1662
2007
  })(BuildNotificationType || {});
1663
2008
 
1664
- // node_modules/.pnpm/@softarc+native-federation@4.2.1_typescript@6.0.3/node_modules/@softarc/native-federation/dist/lib/domain/core/chunk.js
1665
- var CHUNK_PREFIX = "@nf-internal";
2009
+ // node_modules/.pnpm/@softarc+native-federation@4.3.0_typescript@6.0.3/node_modules/@softarc/native-federation/dist/lib/domain/core/chunk.js
2010
+ var CHUNK_PREFIX2 = "@nf-internal";
1666
2011
  function toChunkImport(fileName) {
1667
2012
  if (fileName.startsWith("./")) {
1668
2013
  fileName = fileName.slice(2);
1669
2014
  }
1670
2015
  const packageName = fileName.replace(/.(m|c)?js$/, "");
1671
- return CHUNK_PREFIX + "/" + packageName;
2016
+ return CHUNK_PREFIX2 + "/" + packageName;
1672
2017
  }
1673
2018
 
1674
2019
  // src/lib/core/2.app/steps/generate-import-map.ts
@@ -1696,12 +2041,9 @@ function createGenerateImportMap(config, ports) {
1696
2041
  });
1697
2042
  addToScope(importMap, remote.scopeUrl, createScopeModules(externals, remote.scopeUrl));
1698
2043
  for (const version of Object.values(externals)) {
1699
- addIntegrity(
1700
- importMap,
1701
- join(remote.scopeUrl, version.file),
1702
- remoteName,
1703
- version.file
1704
- );
2044
+ for (const file of Object.values(version.entries)) {
2045
+ addIntegrity(importMap, join(remote.scopeUrl, file), remoteName, file);
2046
+ }
1705
2047
  }
1706
2048
  Object.values(externals).filter((e) => !!e.bundle).forEach((e) => registerBundleChunks(chunkBundles, remoteName, e.bundle));
1707
2049
  }
@@ -1709,8 +2051,10 @@ function createGenerateImportMap(config, ports) {
1709
2051
  }
1710
2052
  function createScopeModules(externals, scope) {
1711
2053
  const modules = {};
1712
- for (const [external, version] of Object.entries(externals)) {
1713
- modules[external] = join(scope, version.file);
2054
+ for (const version of Object.values(externals)) {
2055
+ for (const [packageName, file] of Object.entries(version.entries)) {
2056
+ modules[packageName] = join(scope, file);
2057
+ }
1714
2058
  }
1715
2059
  return modules;
1716
2060
  }
@@ -1730,47 +2074,49 @@ function createGenerateImportMap(config, ports) {
1730
2074
  if (version.action === "scope") {
1731
2075
  for (const remote of version.remotes) {
1732
2076
  const remoteScope = getScope2(shareScope, remote.name, externalName);
1733
- const url = join(remoteScope, remote.file);
1734
- addToScope(importMap, remoteScope, {
1735
- [externalName]: url
1736
- });
1737
- addIntegrity(importMap, url, remote.name, remote.file);
2077
+ for (const [packageName, file] of Object.entries(remote.entries)) {
2078
+ const url = join(remoteScope, file);
2079
+ addToScope(importMap, remoteScope, { [packageName]: url });
2080
+ addIntegrity(importMap, url, remote.name, file);
2081
+ }
1738
2082
  registerBundleChunks(chunkBundles, remote.name, remote.bundle);
1739
2083
  remote.cached = true;
1740
2084
  }
1741
2085
  continue;
1742
2086
  }
1743
- const scope = getScope2(shareScope, version.remotes[0].name, externalName);
1744
- let targetFileUrl = join(scope, version.remotes[0].file);
1745
- let targetFileSource = {
1746
- name: version.remotes[0].name,
1747
- file: version.remotes[0].file
1748
- };
1749
2087
  version.remotes[0].cached = true;
2088
+ let source = version.remotes[0];
2089
+ let sourceScope = getScope2(shareScope, source.name, externalName);
1750
2090
  if (version.action === "share") {
1751
- registerBundleChunks(chunkBundles, version.remotes[0].name, version.remotes[0].bundle);
1752
- }
1753
- if (version.action === "skip") {
2091
+ registerBundleChunks(chunkBundles, source.name, source.bundle);
2092
+ } else if (version.action === "skip") {
1754
2093
  if (!override) {
1755
2094
  override = findOverride(external, shareScope, externalName) ?? "NOT_AVAILABLE";
1756
2095
  }
1757
2096
  if (override !== "NOT_AVAILABLE") {
1758
2097
  if (!overrideScope)
1759
2098
  overrideScope = getScope2(shareScope, override.remotes[0].name, externalName);
1760
- targetFileUrl = join(overrideScope, override.remotes[0].file);
1761
- targetFileSource = {
1762
- name: override.remotes[0].name,
1763
- file: override.remotes[0].file
1764
- };
2099
+ source = override.remotes[0];
2100
+ sourceScope = overrideScope;
1765
2101
  override.remotes[0].cached = true;
1766
2102
  version.remotes[0].cached = false;
1767
2103
  }
1768
2104
  }
2105
+ const mappings = Object.entries(source.entries).map(([packageName, file]) => ({
2106
+ packageName,
2107
+ url: join(sourceScope, file),
2108
+ name: source.name,
2109
+ file
2110
+ }));
1769
2111
  version.remotes.forEach((r) => {
1770
- const scope2 = getScope2(shareScope, r.name, externalName);
1771
- addToScope(importMap, scope2, { [externalName]: targetFileUrl });
2112
+ const rScope = getScope2(shareScope, r.name, externalName);
2113
+ for (const m of mappings) {
2114
+ addToScope(importMap, rScope, { [m.packageName]: m.url });
2115
+ }
1772
2116
  });
1773
- addIntegrity(importMap, targetFileUrl, targetFileSource.name, targetFileSource.file);
2117
+ for (const m of mappings) {
2118
+ addIntegrity(importMap, m.url, m.name, m.file);
2119
+ }
1774
2120
  }
1775
2121
  ports.sharedExternalsRepo.addOrUpdate(externalName, external, shareScope);
1776
2122
  }
@@ -1811,11 +2157,11 @@ function createGenerateImportMap(config, ports) {
1811
2157
  if (version.action === "scope") {
1812
2158
  for (const remote of version.remotes) {
1813
2159
  const remoteScope = getScope2(GLOBAL_SCOPE, remote.name, externalName);
1814
- const url2 = join(remoteScope, remote.file);
1815
- addToScope(importMap, remoteScope, {
1816
- [externalName]: url2
1817
- });
1818
- addIntegrity(importMap, url2, remote.name, remote.file);
2160
+ for (const [packageName, file] of Object.entries(remote.entries)) {
2161
+ const url = join(remoteScope, file);
2162
+ addToScope(importMap, remoteScope, { [packageName]: url });
2163
+ addIntegrity(importMap, url, remote.name, file);
2164
+ }
1819
2165
  remote.cached = true;
1820
2166
  registerBundleChunks(chunkBundles, remote.name, remote.bundle);
1821
2167
  }
@@ -1826,9 +2172,11 @@ function createGenerateImportMap(config, ports) {
1826
2172
  continue;
1827
2173
  }
1828
2174
  const scope = getScope2(GLOBAL_SCOPE, version.remotes[0].name, externalName);
1829
- const url = join(scope, version.remotes[0].file);
1830
- addToGlobal(importMap, { [externalName]: url });
1831
- addIntegrity(importMap, url, version.remotes[0].name, version.remotes[0].file);
2175
+ for (const [packageName, file] of Object.entries(version.remotes[0].entries)) {
2176
+ const url = join(scope, file);
2177
+ addToGlobal(importMap, { [packageName]: url });
2178
+ addIntegrity(importMap, url, version.remotes[0].name, file);
2179
+ }
1832
2180
  registerBundleChunks(chunkBundles, version.remotes[0].name, version.remotes[0].bundle);
1833
2181
  version.remotes[0].cached = true;
1834
2182
  }
@@ -2064,8 +2412,8 @@ function createUpdateCache(config, ports) {
2064
2412
  storeRemoteEntry(remoteEntry, (entry, external, ctx) => {
2065
2413
  const { action, sharedVersion } = resolveSharedExternal(entry, external, ctx);
2066
2414
  actions[external.packageName] = { action };
2067
- if (action === "skip" && external.shareScope && sharedVersion?.remotes[0]?.file) {
2068
- actions[external.packageName].override = resolveOverrideUrl(
2415
+ if (action === "skip" && external.shareScope && sharedVersion?.remotes[0]?.entries) {
2416
+ actions[external.packageName].override = resolveOverrideEntries(
2069
2417
  entry,
2070
2418
  external,
2071
2419
  sharedVersion
@@ -2109,8 +2457,15 @@ function createUpdateCache(config, ports) {
2109
2457
  commit();
2110
2458
  return { action, sharedVersion };
2111
2459
  }
2112
- function resolveOverrideUrl(remoteEntry, external, sharedVersion) {
2113
- return ports.remoteInfoRepo.tryGet(sharedVersion.remotes[0].name).map((remote) => join(remote.scopeUrl, sharedVersion.remotes[0].file)).orThrow(() => {
2460
+ function resolveOverrideEntries(remoteEntry, external, sharedVersion) {
2461
+ return ports.remoteInfoRepo.tryGet(sharedVersion.remotes[0].name).map(
2462
+ (remote) => Object.fromEntries(
2463
+ Object.entries(sharedVersion.remotes[0].entries).map(([packageName, file]) => [
2464
+ packageName,
2465
+ join(remote.scopeUrl, file)
2466
+ ])
2467
+ )
2468
+ ).orThrow(() => {
2114
2469
  config.log.error(
2115
2470
  8,
2116
2471
  `[${external.shareScope ?? GLOBAL_SCOPE}][${remoteEntry.name}][${external.packageName}@${external.version}][override] Remote name not found in cache.`
@@ -2122,14 +2477,56 @@ function createUpdateCache(config, ports) {
2122
2477
  }
2123
2478
  }
2124
2479
 
2125
- // src/lib/core/2.app/steps/convert-to-import-map.ts
2126
- function createConvertToImportMap({ log }, ports) {
2480
+ // src/lib/core/2.app/steps/pooling/pool-dynamic-externals.ts
2481
+ function createPoolDynamicExternals(config) {
2127
2482
  return ({ entry, actions }) => {
2483
+ const { useAutoExternalPooling } = config.feature;
2484
+ if (!useAutoExternalPooling && !(entry.shared ?? []).some((e) => e.pool?.trim())) {
2485
+ return Promise.resolve({ entry, actions });
2486
+ }
2487
+ const byScope = /* @__PURE__ */ new Map();
2488
+ for (const external of entry.shared ?? []) {
2489
+ const name = external.packageName;
2490
+ if (!external.singleton || !actions[name]) continue;
2491
+ if (external.shareScope === STRICT_SCOPE) continue;
2492
+ const tag = external.pool?.trim();
2493
+ const shareScope = external.shareScope ?? GLOBAL_SCOPE;
2494
+ const candidates = byScope.get(shareScope) ?? [];
2495
+ candidates.push({
2496
+ name,
2497
+ scope: autoScope(name, useAutoExternalPooling),
2498
+ tags: tag ? [{ remote: entry.name, tag }] : [],
2499
+ value: name
2500
+ });
2501
+ byScope.set(shareScope, candidates);
2502
+ }
2503
+ const scope = (name) => {
2504
+ actions[name].action = "scope";
2505
+ delete actions[name].override;
2506
+ };
2507
+ for (const candidates of byScope.values()) {
2508
+ for (const members of groupByMembership(candidates).values()) {
2509
+ const memberActions = members.map((name) => actions[name].action);
2510
+ if (memberActions.includes("scope")) {
2511
+ members.forEach(scope);
2512
+ } else if (memberActions.includes("share") && memberActions.includes("skip")) {
2513
+ members.filter((name) => actions[name].action === "share").forEach(scope);
2514
+ }
2515
+ }
2516
+ }
2517
+ return Promise.resolve({ entry, actions });
2518
+ };
2519
+ }
2520
+
2521
+ // src/lib/core/2.app/steps/convert-to-import-map.ts
2522
+ function createConvertToImportMap(config, ports) {
2523
+ const { log } = config;
2524
+ return async ({ entry, actions }) => {
2128
2525
  const importMap = { imports: {} };
2129
2526
  addExternals(entry, actions, importMap);
2130
2527
  addRemoteInfos(entry, importMap);
2131
2528
  log.debug(9, `[${entry.name}] Processed actions:`, actions);
2132
- return Promise.resolve(importMap);
2529
+ return importMap;
2133
2530
  };
2134
2531
  function addExternals(remoteEntry, actions, importMap) {
2135
2532
  if (!remoteEntry.shared) {
@@ -2140,9 +2537,11 @@ function createConvertToImportMap({ log }, ports) {
2140
2537
  const chunkBundles = /* @__PURE__ */ new Set(["mapping-or-exposed"]);
2141
2538
  remoteEntry.shared.forEach((external) => {
2142
2539
  if (!external.singleton) {
2143
- const url2 = join(remoteEntryScope, external.outFileName);
2144
- addToScopes(remoteEntryScope, external.packageName, url2, importMap);
2145
- addIntegrity(importMap, url2, integrityMap, external.outFileName);
2540
+ Object.entries(external.entries).forEach(([packageName, fileName]) => {
2541
+ const url = join(remoteEntryScope, fileName);
2542
+ addToScopes(remoteEntryScope, packageName, url, importMap);
2543
+ addIntegrity(importMap, url, integrityMap, fileName);
2544
+ });
2146
2545
  if (external?.bundle) chunkBundles.add(external?.bundle);
2147
2546
  return;
2148
2547
  }
@@ -2154,33 +2553,48 @@ function createConvertToImportMap({ log }, ports) {
2154
2553
  return;
2155
2554
  }
2156
2555
  if (actions[external.packageName].action === "skip") {
2556
+ const override = actions[external.packageName].override;
2157
2557
  if (!external.shareScope) return;
2158
- if (actions[external.packageName].override) {
2159
- addToScopes(
2160
- remoteEntryScope,
2161
- external.packageName,
2162
- actions[external.packageName].override,
2163
- importMap
2164
- );
2558
+ if (override) {
2559
+ Object.entries(override).forEach(([packageName, url]) => {
2560
+ addToScopes(remoteEntryScope, packageName, url, importMap);
2561
+ });
2165
2562
  return;
2166
2563
  }
2564
+ log.error(
2565
+ 9,
2566
+ `[${remoteEntry.name}][${external.packageName}] shareScope skip has no override.`
2567
+ );
2568
+ if (config.strict.strictImportMap) throw new NFError("Could not create ImportMap.");
2569
+ Object.entries(external.entries).forEach(([packageName, fileName]) => {
2570
+ const url = join(remoteEntryScope, fileName);
2571
+ addToScopes(remoteEntryScope, packageName, url, importMap);
2572
+ addIntegrity(importMap, url, integrityMap, fileName);
2573
+ });
2574
+ return;
2167
2575
  }
2168
2576
  if (external?.bundle) chunkBundles.add(external?.bundle);
2169
2577
  if (actions[external.packageName].action === "scope") {
2170
- const url2 = join(remoteEntryScope, external.outFileName);
2171
- addToScopes(remoteEntryScope, external.packageName, url2, importMap);
2172
- addIntegrity(importMap, url2, integrityMap, external.outFileName);
2578
+ Object.entries(external.entries).forEach(([packageName, fileName]) => {
2579
+ const url = join(remoteEntryScope, fileName);
2580
+ addToScopes(remoteEntryScope, packageName, url, importMap);
2581
+ addIntegrity(importMap, url, integrityMap, fileName);
2582
+ });
2173
2583
  return;
2174
2584
  }
2175
2585
  if (external.shareScope) {
2176
- const url2 = join(remoteEntryScope, external.outFileName);
2177
- addToScopes(remoteEntryScope, external.packageName, url2, importMap);
2178
- addIntegrity(importMap, url2, integrityMap, external.outFileName);
2586
+ Object.entries(external.entries).forEach(([packageName, fileName]) => {
2587
+ const url = join(remoteEntryScope, fileName);
2588
+ addToScopes(remoteEntryScope, packageName, url, importMap);
2589
+ addIntegrity(importMap, url, integrityMap, fileName);
2590
+ });
2179
2591
  return;
2180
2592
  }
2181
- const url = join(remoteEntryScope, external.outFileName);
2182
- importMap.imports[external.packageName] = url;
2183
- addIntegrity(importMap, url, integrityMap, external.outFileName);
2593
+ Object.entries(external.entries).forEach(([packageName, fileName]) => {
2594
+ const url = join(remoteEntryScope, fileName);
2595
+ importMap.imports[packageName] = url;
2596
+ addIntegrity(importMap, url, integrityMap, fileName);
2597
+ });
2184
2598
  });
2185
2599
  addChunkImports(importMap, remoteEntry, remoteEntryScope, chunkBundles);
2186
2600
  }
@@ -2227,11 +2641,13 @@ var createInitDrivers = ({
2227
2641
  getRemoteEntries: createGetRemoteEntries(config, adapters),
2228
2642
  processRemoteEntries: createProcessRemoteEntries(config, adapters),
2229
2643
  determineSharedExternals: createDetermineSharedExternals(config, adapters),
2644
+ poolSharedExternals: createPoolSharedExternals(config, adapters),
2230
2645
  generateImportMap: createGenerateImportMap(config, adapters),
2231
2646
  commitChanges: createCommitChanges(config, adapters),
2232
2647
  exposeModuleLoader: createExposeModuleLoader(config, adapters),
2233
2648
  getRemoteEntry: createGetRemoteEntry(config, adapters),
2234
2649
  updateCache: createUpdateCache(config, adapters),
2650
+ poolDynamicExternals: createPoolDynamicExternals(config),
2235
2651
  convertToImportMap: createConvertToImportMap(config, adapters)
2236
2652
  });
2237
2653
  var INIT_FLOW_FACTORY = ({
@@ -2343,7 +2759,7 @@ var createManifestProvider = () => {
2343
2759
  };
2344
2760
 
2345
2761
  // src/lib/core/3.adapters/http/remote-entry-provider.ts
2346
- var createRemoteEntryProvider = () => {
2762
+ var createRemoteEntryProvider = (config) => {
2347
2763
  const ensureOk = (response) => {
2348
2764
  if (!response.ok)
2349
2765
  return Promise.reject(new Error(`${response.status} - ${response.statusText}`));
@@ -2355,6 +2771,10 @@ var createRemoteEntryProvider = () => {
2355
2771
  if (!remoteEntry.url) remoteEntry.url = remoteEntryUrl;
2356
2772
  return remoteEntry;
2357
2773
  };
2774
+ const normalizeRemoteEntry = (raw) => {
2775
+ const shared = config.feature.convertFlatSharedInfo ? densifyExternals(raw.shared ?? []) : toDenseSharedInfoFormat(raw.shared ?? []);
2776
+ return { ...raw, shared };
2777
+ };
2358
2778
  const formatError = (remoteEntryUrl) => (err) => {
2359
2779
  const msg = err instanceof Error ? err.message : String(err);
2360
2780
  throw new NFError(`Fetch of '${remoteEntryUrl}' returned ${msg}`);
@@ -2367,7 +2787,7 @@ var createRemoteEntryProvider = () => {
2367
2787
  await verifyIntegrity(bytes, opts.integrity);
2368
2788
  return JSON.parse(new TextDecoder().decode(bytes));
2369
2789
  };
2370
- return fetch(remoteEntryUrl).then(ensureOk).then(parse).then(fillEmptyFields(remoteEntryUrl)).catch(formatError(remoteEntryUrl));
2790
+ return fetch(remoteEntryUrl).then(ensureOk).then(parse).then(normalizeRemoteEntry).then(fillEmptyFields(remoteEntryUrl)).catch(formatError(remoteEntryUrl));
2371
2791
  }
2372
2792
  };
2373
2793
  };
@@ -2443,7 +2863,14 @@ var createSharedExternalsRepository = (config) => {
2443
2863
  );
2444
2864
  if (config.clearStorage) STORAGE.clear();
2445
2865
  const _cache = STORAGE.get();
2866
+ let _sawPoolTag = false;
2446
2867
  return {
2868
+ markPoolTagPresent: function() {
2869
+ _sawPoolTag = true;
2870
+ },
2871
+ hasPoolTag: function() {
2872
+ return _sawPoolTag;
2873
+ },
2447
2874
  getFromScope: function(shareScope) {
2448
2875
  return { ..._cache[shareScope ?? GLOBAL_SCOPE] };
2449
2876
  },
@@ -2553,7 +2980,7 @@ var createDriving = (config) => {
2553
2980
  const adapters = {
2554
2981
  versionCheck: createVersionCheck(),
2555
2982
  manifestProvider: createManifestProvider(),
2556
- remoteEntryProvider: createRemoteEntryProvider(),
2983
+ remoteEntryProvider: createRemoteEntryProvider(config),
2557
2984
  remoteInfoRepo: createRemoteInfoRepository(config),
2558
2985
  scopedExternalsRepo: createScopedExternalsRepository(config),
2559
2986
  sharedExternalsRepo: createSharedExternalsRepository(config),
@@ -2796,7 +3223,11 @@ var createModeConfig = (override) => {
2796
3223
  };
2797
3224
  return {
2798
3225
  strict: strictnessConfig,
2799
- profile: { ...defaultProfile, ...override.profile ?? {} }
3226
+ profile: { ...defaultProfile, ...override.profile ?? {} },
3227
+ feature: {
3228
+ convertFlatSharedInfo: override.feature?.convertFlatSharedInfo ?? false,
3229
+ useAutoExternalPooling: override.feature?.useAutoExternalPooling ?? false
3230
+ }
2800
3231
  };
2801
3232
  };
2802
3233