@softarc/native-federation-orchestrator 4.4.1 → 4.5.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.
Files changed (25) hide show
  1. package/README.md +2 -2
  2. package/fesm2022/@softarc/native-federation-orchestrator.mjs +517 -76
  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/package.json +1 -1
  9. package/quickstart.mjs +13 -13
  10. package/types/lib/core/1.domain/externals/version.contract.d.ts +3 -2
  11. package/types/lib/core/1.domain/remote-entry/remote-entry.contract.d.ts +6 -4
  12. package/types/lib/core/2.app/config/mode.contract.d.ts +6 -0
  13. package/types/lib/core/2.app/driver-ports/init/drivers.contract.d.ts +4 -0
  14. package/types/lib/core/2.app/driver-ports/init/for-pooling-dynamic-externals.port.d.ts +8 -0
  15. package/types/lib/core/2.app/driver-ports/init/for-pooling-shared-externals.port.d.ts +1 -0
  16. package/types/lib/core/2.app/driver-ports/init/index.d.ts +2 -0
  17. package/types/lib/core/2.app/driving-ports/for-shared-externals-storage.port.d.ts +2 -0
  18. package/types/lib/core/2.app/steps/convert-to-import-map.d.ts +2 -1
  19. package/types/lib/core/2.app/steps/pooling/pool-dynamic-externals.d.ts +3 -0
  20. package/types/lib/core/2.app/steps/pooling/pool-graph.d.ts +25 -0
  21. package/types/lib/core/2.app/steps/pooling/pool-shared-externals.d.ts +5 -0
  22. package/types/lib/core/2.app/steps/pooling/pool.types.d.ts +25 -0
  23. package/types/lib/core/2.app/steps/pooling/pool.util.d.ts +4 -0
  24. package/types/lib/core/2.app/steps/store-remote-entry.d.ts +2 -2
  25. 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 {
@@ -1653,7 +1655,290 @@ function createDetermineSharedExternals(config, ports) {
1653
1655
  }
1654
1656
  }
1655
1657
 
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
1658
+ // src/lib/core/2.app/steps/pooling/pool-graph.ts
1659
+ var SCOPED_PACKAGE = /^@([^/]+)\//;
1660
+ function createDSU() {
1661
+ const ids = /* @__PURE__ */ new Map();
1662
+ const parent = [];
1663
+ const size = [];
1664
+ const intern = (key) => {
1665
+ let id = ids.get(key);
1666
+ if (id === void 0) {
1667
+ id = parent.length;
1668
+ ids.set(key, id);
1669
+ parent.push(id);
1670
+ size.push(1);
1671
+ }
1672
+ return id;
1673
+ };
1674
+ const findRoot = (x) => {
1675
+ while (parent[x] !== x) {
1676
+ parent[x] = parent[parent[x]];
1677
+ x = parent[x];
1678
+ }
1679
+ return x;
1680
+ };
1681
+ return {
1682
+ union(a, b) {
1683
+ let rootA = findRoot(intern(a));
1684
+ let rootB = findRoot(intern(b));
1685
+ if (rootA === rootB) return;
1686
+ if (size[rootA] < size[rootB]) [rootA, rootB] = [rootB, rootA];
1687
+ parent[rootB] = rootA;
1688
+ size[rootA] = size[rootA] + size[rootB];
1689
+ },
1690
+ // Interns the key if unseen — an isolated key is its own component.
1691
+ component(key) {
1692
+ return findRoot(intern(key));
1693
+ }
1694
+ };
1695
+ }
1696
+ var extNode = (name) => `ext\0${name}`;
1697
+ var tagNode = (remote, tag) => `tag\0${remote}\0${tag}`;
1698
+ var scopeNode = (scope) => `scope\0${scope}`;
1699
+ function autoScope(name, useAutoExternalPooling) {
1700
+ return useAutoExternalPooling ? SCOPED_PACKAGE.exec(name)?.[1] : void 0;
1701
+ }
1702
+ function groupByMembership(candidates, log) {
1703
+ const dsu = createDSU();
1704
+ const tagged = /* @__PURE__ */ new Set();
1705
+ const poolable = [];
1706
+ for (const candidate of candidates) {
1707
+ let joined = false;
1708
+ if (candidate.scope !== void 0) {
1709
+ dsu.union(extNode(candidate.name), scopeNode(candidate.scope));
1710
+ joined = true;
1711
+ }
1712
+ for (const edge of candidate.tags) {
1713
+ dsu.union(extNode(candidate.name), tagNode(edge.remote, edge.tag));
1714
+ tagged.add(candidate.name);
1715
+ joined = true;
1716
+ }
1717
+ if (joined) poolable.push({ name: candidate.name, value: candidate.value });
1718
+ }
1719
+ const byComponent = /* @__PURE__ */ new Map();
1720
+ for (const entry of poolable) {
1721
+ const root = dsu.component(extNode(entry.name));
1722
+ (byComponent.get(root) ?? byComponent.set(root, []).get(root)).push(entry);
1723
+ }
1724
+ const pools = [];
1725
+ for (const members of byComponent.values()) {
1726
+ members.sort((a, b) => a.name.localeCompare(b.name));
1727
+ if (members.length < 2) {
1728
+ const only = members[0];
1729
+ if (tagged.has(only.name)) {
1730
+ log?.warn(
1731
+ 3,
1732
+ `[${only.name}] declares a 'pool' tag but no other external joined its pool; likely a typo or a missing sibling.`
1733
+ );
1734
+ }
1735
+ continue;
1736
+ }
1737
+ pools.push(members);
1738
+ }
1739
+ pools.sort((a, b) => a[0].name.localeCompare(b[0].name));
1740
+ return new Map(pools.map((members) => [members[0].name, members.map((m) => m.value)]));
1741
+ }
1742
+ function buildPools(sharedExternals, useAutoExternalPooling, log) {
1743
+ const candidates = Object.entries(sharedExternals).map(
1744
+ ([name, external]) => ({
1745
+ name,
1746
+ scope: autoScope(name, useAutoExternalPooling),
1747
+ tags: external.versions.flatMap(
1748
+ (v) => v.remotes.flatMap((r) => {
1749
+ const tag = r.pool?.trim();
1750
+ return tag ? [{ remote: r.name, tag }] : [];
1751
+ })
1752
+ ),
1753
+ value: { name, external }
1754
+ })
1755
+ );
1756
+ return groupByMembership(candidates, log);
1757
+ }
1758
+
1759
+ // src/lib/core/2.app/steps/pooling/pool.util.ts
1760
+ function versionForRemote(member, remote) {
1761
+ return member.external.versions.find((v) => v.remotes.some((r) => r.name === remote));
1762
+ }
1763
+ function remotesInPool(members) {
1764
+ return [
1765
+ ...new Set(members.flatMap((m) => m.external.versions.flatMap((v) => v.remotes.map((r) => r.name))))
1766
+ ];
1767
+ }
1768
+
1769
+ // src/lib/core/2.app/steps/pooling/pool-shared-externals.ts
1770
+ function poolWinners(members) {
1771
+ return members.map((member) => {
1772
+ const share = member.external.versions.find((v) => v.action === "share");
1773
+ return {
1774
+ member,
1775
+ tag: share?.tag ?? "",
1776
+ providers: new Set((share?.remotes ?? []).map((r) => r.name))
1777
+ };
1778
+ });
1779
+ }
1780
+ function selectCoverageAnchor(winners) {
1781
+ const coverage = /* @__PURE__ */ new Map();
1782
+ for (const winner of winners)
1783
+ for (const remote of winner.providers) coverage.set(remote, (coverage.get(remote) ?? 0) + 1);
1784
+ if (coverage.size === 0) return void 0;
1785
+ const anchorRemote = [...coverage.keys()].sort(
1786
+ (a, b) => coverage.get(b) - coverage.get(a) || a.localeCompare(b)
1787
+ )[0];
1788
+ return {
1789
+ anchorRemote,
1790
+ tagPerMember: Object.fromEntries(
1791
+ winners.filter((w) => w.providers.has(anchorRemote)).map((w) => [w.member.name, w.tag])
1792
+ )
1793
+ };
1794
+ }
1795
+ function classifyPoolConservative(members, anchor) {
1796
+ const classification = /* @__PURE__ */ new Map();
1797
+ for (const remote of remotesInPool(members)) {
1798
+ let coverageForced = false;
1799
+ let verdict = "follow";
1800
+ for (const member of members) {
1801
+ const version = versionForRemote(member, remote);
1802
+ if (!version) continue;
1803
+ const anchorTag = anchor.tagPerMember[member.name];
1804
+ if (!anchorTag) {
1805
+ coverageForced = true;
1806
+ continue;
1807
+ }
1808
+ if (version.tag === anchorTag) continue;
1809
+ if (version.action === "scope") {
1810
+ verdict = "scope-incompat";
1811
+ break;
1812
+ }
1813
+ }
1814
+ classification.set(
1815
+ remote,
1816
+ verdict === "scope-incompat" ? "scope-incompat" : coverageForced ? "scope-coverage" : "follow"
1817
+ );
1818
+ }
1819
+ classification.set(anchor.anchorRemote, "follow");
1820
+ return classification;
1821
+ }
1822
+ function createPoolSharedExternals(config, ports) {
1823
+ return () => {
1824
+ const { useAutoExternalPooling } = config.feature;
1825
+ if (!useAutoExternalPooling && !ports.sharedExternalsRepo.hasPoolTag()) {
1826
+ return Promise.resolve();
1827
+ }
1828
+ for (const scope of ports.sharedExternalsRepo.getScopes()) {
1829
+ if (ports.sharedExternalsRepo.scopeType(scope) === "strict") continue;
1830
+ const sharedExternals = ports.sharedExternalsRepo.getFromScope(scope);
1831
+ try {
1832
+ for (const [poolName, members] of buildPools(
1833
+ sharedExternals,
1834
+ useAutoExternalPooling,
1835
+ config.log
1836
+ )) {
1837
+ poolFamily(poolName, members, scope);
1838
+ }
1839
+ } catch (error) {
1840
+ if (error instanceof NFError) return Promise.reject(error);
1841
+ config.log.error(3, `[${scope}] failed to pool shared externals.`, {
1842
+ sharedExternals,
1843
+ error
1844
+ });
1845
+ return Promise.reject(
1846
+ new NFError(`Could not pool shared externals in scope ${scope}.`, error)
1847
+ );
1848
+ }
1849
+ }
1850
+ return Promise.resolve();
1851
+ };
1852
+ function poolFamily(poolName, members, scope) {
1853
+ if (members.length < 2) return;
1854
+ const allRemotes = remotesInPool(members);
1855
+ if (allRemotes.length < 2) return;
1856
+ const anchor = selectCoverageAnchor(poolWinners(members));
1857
+ if (!anchor) return;
1858
+ const classification = classifyPoolConservative(members, anchor);
1859
+ config.log.debug(
1860
+ 3,
1861
+ `[${scope}][pool:${poolName}] members=[${members.map((m) => m.name).join(", ")}], anchor=${anchor.anchorRemote}, remotes={${allRemotes.map((r) => `${r}:${classification.get(r)}`).join(", ")}}`
1862
+ );
1863
+ const incompatForced = allRemotes.some((r) => classification.get(r) === "scope-incompat");
1864
+ if (incompatForced && config.strict.strictExternalCompatibility) {
1865
+ config.log.error(
1866
+ 3,
1867
+ `[${scope}][pool:${poolName}] A remote is version-incompatible with anchor '${anchor.anchorRemote}'.`
1868
+ );
1869
+ throw new NFError(`Could not pool '${poolName}' in scope ${scope}.`);
1870
+ }
1871
+ for (const member of members) {
1872
+ const rebuilt = rebuildMember(member, anchor, classification);
1873
+ warnIfScopedOnly(poolName, member.name, rebuilt, scope);
1874
+ ports.sharedExternalsRepo.addOrUpdate(member.name, rebuilt, scope);
1875
+ }
1876
+ }
1877
+ function warnIfScopedOnly(poolName, memberName, rebuilt, scope) {
1878
+ if (rebuilt.versions.some((v) => v.action === "share")) return;
1879
+ const copies = rebuilt.versions.reduce((n, v) => n + v.remotes.length, 0);
1880
+ config.log.warn(
1881
+ 3,
1882
+ `[${scope}][pool:${poolName}] '${memberName}' is scoped-only \u2014 no coherent shared build provides it; ${copies} remotes download their own copy.`
1883
+ );
1884
+ }
1885
+ function rebuildMember(member, anchor, classification) {
1886
+ const anchorTag = anchor.tagPerMember[member.name];
1887
+ const entries = member.external.versions.flatMap(
1888
+ (v) => v.remotes.map((meta) => ({ remote: meta.name, tag: v.tag, host: v.host, meta }))
1889
+ );
1890
+ if (!anchorTag) {
1891
+ return {
1892
+ dirty: false,
1893
+ versions: entries.length > 0 ? [
1894
+ {
1895
+ tag: entries[0].tag,
1896
+ host: false,
1897
+ action: "scope",
1898
+ remotes: entries.map((e) => e.meta)
1899
+ }
1900
+ ] : []
1901
+ };
1902
+ }
1903
+ const anchorMeta = entries.find(
1904
+ (e) => e.remote === anchor.anchorRemote && e.tag === anchorTag
1905
+ ).meta;
1906
+ const anchorHost = member.external.versions.find((v) => v.tag === anchorTag)?.host ?? false;
1907
+ const scopes = (e) => {
1908
+ const cls = classification.get(e.remote);
1909
+ return cls === "scope-incompat" || cls === "scope-coverage" && e.tag !== anchorTag;
1910
+ };
1911
+ const shareOnAnchor = entries.filter(
1912
+ (e) => e.remote !== anchor.anchorRemote && e.tag === anchorTag && !scopes(e)
1913
+ );
1914
+ const shareVersion = {
1915
+ tag: anchorTag,
1916
+ host: anchorHost,
1917
+ action: "share",
1918
+ remotes: [anchorMeta, ...shareOnAnchor.map((e) => e.meta)]
1919
+ };
1920
+ const skipByTag = /* @__PURE__ */ new Map();
1921
+ for (const e of entries) {
1922
+ if (e.tag === anchorTag || scopes(e)) continue;
1923
+ const version = skipByTag.get(e.tag) ?? {
1924
+ tag: e.tag,
1925
+ host: e.host,
1926
+ action: "skip",
1927
+ remotes: []
1928
+ };
1929
+ version.remotes.push(e.meta);
1930
+ skipByTag.set(e.tag, version);
1931
+ }
1932
+ const scoped = entries.filter((e) => scopes(e));
1933
+ const scopeVersion = scoped.length > 0 ? [{ tag: anchorTag, host: false, action: "scope", remotes: scoped.map((e) => e.meta) }] : [];
1934
+ return {
1935
+ dirty: false,
1936
+ versions: [shareVersion, ...skipByTag.values(), ...scopeVersion]
1937
+ };
1938
+ }
1939
+ }
1940
+
1941
+ // node_modules/.pnpm/@softarc+native-federation@4.3.1_typescript@6.0.3/node_modules/@softarc/native-federation/dist/lib/domain/core/build-notification-options.contract.js
1657
1942
  var BuildNotificationType = /* @__PURE__ */ ((BuildNotificationType2) => {
1658
1943
  BuildNotificationType2["COMPLETED"] = "federation-rebuild-complete";
1659
1944
  BuildNotificationType2["ERROR"] = "federation-rebuild-error";
@@ -1661,7 +1946,7 @@ var BuildNotificationType = /* @__PURE__ */ ((BuildNotificationType2) => {
1661
1946
  return BuildNotificationType2;
1662
1947
  })(BuildNotificationType || {});
1663
1948
 
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
1949
+ // node_modules/.pnpm/@softarc+native-federation@4.3.1_typescript@6.0.3/node_modules/@softarc/native-federation/dist/lib/domain/core/chunk.js
1665
1950
  var CHUNK_PREFIX = "@nf-internal";
1666
1951
  function toChunkImport(fileName) {
1667
1952
  if (fileName.startsWith("./")) {
@@ -1696,12 +1981,9 @@ function createGenerateImportMap(config, ports) {
1696
1981
  });
1697
1982
  addToScope(importMap, remote.scopeUrl, createScopeModules(externals, remote.scopeUrl));
1698
1983
  for (const version of Object.values(externals)) {
1699
- addIntegrity(
1700
- importMap,
1701
- join(remote.scopeUrl, version.file),
1702
- remoteName,
1703
- version.file
1704
- );
1984
+ for (const file of Object.values(version.entries)) {
1985
+ addIntegrity(importMap, join(remote.scopeUrl, file), remoteName, file);
1986
+ }
1705
1987
  }
1706
1988
  Object.values(externals).filter((e) => !!e.bundle).forEach((e) => registerBundleChunks(chunkBundles, remoteName, e.bundle));
1707
1989
  }
@@ -1709,8 +1991,10 @@ function createGenerateImportMap(config, ports) {
1709
1991
  }
1710
1992
  function createScopeModules(externals, scope) {
1711
1993
  const modules = {};
1712
- for (const [external, version] of Object.entries(externals)) {
1713
- modules[external] = join(scope, version.file);
1994
+ for (const version of Object.values(externals)) {
1995
+ for (const [packageName, file] of Object.entries(version.entries)) {
1996
+ modules[packageName] = join(scope, file);
1997
+ }
1714
1998
  }
1715
1999
  return modules;
1716
2000
  }
@@ -1730,47 +2014,49 @@ function createGenerateImportMap(config, ports) {
1730
2014
  if (version.action === "scope") {
1731
2015
  for (const remote of version.remotes) {
1732
2016
  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);
2017
+ for (const [packageName, file] of Object.entries(remote.entries)) {
2018
+ const url = join(remoteScope, file);
2019
+ addToScope(importMap, remoteScope, { [packageName]: url });
2020
+ addIntegrity(importMap, url, remote.name, file);
2021
+ }
1738
2022
  registerBundleChunks(chunkBundles, remote.name, remote.bundle);
1739
2023
  remote.cached = true;
1740
2024
  }
1741
2025
  continue;
1742
2026
  }
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
2027
  version.remotes[0].cached = true;
2028
+ let source = version.remotes[0];
2029
+ let sourceScope = getScope2(shareScope, source.name, externalName);
1750
2030
  if (version.action === "share") {
1751
- registerBundleChunks(chunkBundles, version.remotes[0].name, version.remotes[0].bundle);
1752
- }
1753
- if (version.action === "skip") {
2031
+ registerBundleChunks(chunkBundles, source.name, source.bundle);
2032
+ } else if (version.action === "skip") {
1754
2033
  if (!override) {
1755
2034
  override = findOverride(external, shareScope, externalName) ?? "NOT_AVAILABLE";
1756
2035
  }
1757
2036
  if (override !== "NOT_AVAILABLE") {
1758
2037
  if (!overrideScope)
1759
2038
  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
- };
2039
+ source = override.remotes[0];
2040
+ sourceScope = overrideScope;
1765
2041
  override.remotes[0].cached = true;
1766
2042
  version.remotes[0].cached = false;
1767
2043
  }
1768
2044
  }
2045
+ const mappings = Object.entries(source.entries).map(([packageName, file]) => ({
2046
+ packageName,
2047
+ url: join(sourceScope, file),
2048
+ name: source.name,
2049
+ file
2050
+ }));
1769
2051
  version.remotes.forEach((r) => {
1770
- const scope2 = getScope2(shareScope, r.name, externalName);
1771
- addToScope(importMap, scope2, { [externalName]: targetFileUrl });
2052
+ const rScope = getScope2(shareScope, r.name, externalName);
2053
+ for (const m of mappings) {
2054
+ addToScope(importMap, rScope, { [m.packageName]: m.url });
2055
+ }
1772
2056
  });
1773
- addIntegrity(importMap, targetFileUrl, targetFileSource.name, targetFileSource.file);
2057
+ for (const m of mappings) {
2058
+ addIntegrity(importMap, m.url, m.name, m.file);
2059
+ }
1774
2060
  }
1775
2061
  ports.sharedExternalsRepo.addOrUpdate(externalName, external, shareScope);
1776
2062
  }
@@ -1811,11 +2097,11 @@ function createGenerateImportMap(config, ports) {
1811
2097
  if (version.action === "scope") {
1812
2098
  for (const remote of version.remotes) {
1813
2099
  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);
2100
+ for (const [packageName, file] of Object.entries(remote.entries)) {
2101
+ const url = join(remoteScope, file);
2102
+ addToScope(importMap, remoteScope, { [packageName]: url });
2103
+ addIntegrity(importMap, url, remote.name, file);
2104
+ }
1819
2105
  remote.cached = true;
1820
2106
  registerBundleChunks(chunkBundles, remote.name, remote.bundle);
1821
2107
  }
@@ -1826,9 +2112,11 @@ function createGenerateImportMap(config, ports) {
1826
2112
  continue;
1827
2113
  }
1828
2114
  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);
2115
+ for (const [packageName, file] of Object.entries(version.remotes[0].entries)) {
2116
+ const url = join(scope, file);
2117
+ addToGlobal(importMap, { [packageName]: url });
2118
+ addIntegrity(importMap, url, version.remotes[0].name, file);
2119
+ }
1832
2120
  registerBundleChunks(chunkBundles, version.remotes[0].name, version.remotes[0].bundle);
1833
2121
  version.remotes[0].cached = true;
1834
2122
  }
@@ -2064,8 +2352,8 @@ function createUpdateCache(config, ports) {
2064
2352
  storeRemoteEntry(remoteEntry, (entry, external, ctx) => {
2065
2353
  const { action, sharedVersion } = resolveSharedExternal(entry, external, ctx);
2066
2354
  actions[external.packageName] = { action };
2067
- if (action === "skip" && external.shareScope && sharedVersion?.remotes[0]?.file) {
2068
- actions[external.packageName].override = resolveOverrideUrl(
2355
+ if (action === "skip" && external.shareScope && sharedVersion?.remotes[0]?.entries) {
2356
+ actions[external.packageName].override = resolveOverrideEntries(
2069
2357
  entry,
2070
2358
  external,
2071
2359
  sharedVersion
@@ -2109,8 +2397,15 @@ function createUpdateCache(config, ports) {
2109
2397
  commit();
2110
2398
  return { action, sharedVersion };
2111
2399
  }
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(() => {
2400
+ function resolveOverrideEntries(remoteEntry, external, sharedVersion) {
2401
+ return ports.remoteInfoRepo.tryGet(sharedVersion.remotes[0].name).map(
2402
+ (remote) => Object.fromEntries(
2403
+ Object.entries(sharedVersion.remotes[0].entries).map(([packageName, file]) => [
2404
+ packageName,
2405
+ join(remote.scopeUrl, file)
2406
+ ])
2407
+ )
2408
+ ).orThrow(() => {
2114
2409
  config.log.error(
2115
2410
  8,
2116
2411
  `[${external.shareScope ?? GLOBAL_SCOPE}][${remoteEntry.name}][${external.packageName}@${external.version}][override] Remote name not found in cache.`
@@ -2122,14 +2417,56 @@ function createUpdateCache(config, ports) {
2122
2417
  }
2123
2418
  }
2124
2419
 
2125
- // src/lib/core/2.app/steps/convert-to-import-map.ts
2126
- function createConvertToImportMap({ log }, ports) {
2420
+ // src/lib/core/2.app/steps/pooling/pool-dynamic-externals.ts
2421
+ function createPoolDynamicExternals(config) {
2127
2422
  return ({ entry, actions }) => {
2423
+ const { useAutoExternalPooling } = config.feature;
2424
+ if (!useAutoExternalPooling && !(entry.shared ?? []).some((e) => e.pool?.trim())) {
2425
+ return Promise.resolve({ entry, actions });
2426
+ }
2427
+ const byScope = /* @__PURE__ */ new Map();
2428
+ for (const external of entry.shared ?? []) {
2429
+ const name = external.packageName;
2430
+ if (!external.singleton || !actions[name]) continue;
2431
+ if (external.shareScope === STRICT_SCOPE) continue;
2432
+ const tag = external.pool?.trim();
2433
+ const shareScope = external.shareScope ?? GLOBAL_SCOPE;
2434
+ const candidates = byScope.get(shareScope) ?? [];
2435
+ candidates.push({
2436
+ name,
2437
+ scope: autoScope(name, useAutoExternalPooling),
2438
+ tags: tag ? [{ remote: entry.name, tag }] : [],
2439
+ value: name
2440
+ });
2441
+ byScope.set(shareScope, candidates);
2442
+ }
2443
+ const scope = (name) => {
2444
+ actions[name].action = "scope";
2445
+ delete actions[name].override;
2446
+ };
2447
+ for (const candidates of byScope.values()) {
2448
+ for (const members of groupByMembership(candidates).values()) {
2449
+ const memberActions = members.map((name) => actions[name].action);
2450
+ if (memberActions.includes("scope")) {
2451
+ members.forEach(scope);
2452
+ } else if (memberActions.includes("share") && memberActions.includes("skip")) {
2453
+ members.filter((name) => actions[name].action === "share").forEach(scope);
2454
+ }
2455
+ }
2456
+ }
2457
+ return Promise.resolve({ entry, actions });
2458
+ };
2459
+ }
2460
+
2461
+ // src/lib/core/2.app/steps/convert-to-import-map.ts
2462
+ function createConvertToImportMap(config, ports) {
2463
+ const { log } = config;
2464
+ return async ({ entry, actions }) => {
2128
2465
  const importMap = { imports: {} };
2129
2466
  addExternals(entry, actions, importMap);
2130
2467
  addRemoteInfos(entry, importMap);
2131
2468
  log.debug(9, `[${entry.name}] Processed actions:`, actions);
2132
- return Promise.resolve(importMap);
2469
+ return importMap;
2133
2470
  };
2134
2471
  function addExternals(remoteEntry, actions, importMap) {
2135
2472
  if (!remoteEntry.shared) {
@@ -2140,9 +2477,11 @@ function createConvertToImportMap({ log }, ports) {
2140
2477
  const chunkBundles = /* @__PURE__ */ new Set(["mapping-or-exposed"]);
2141
2478
  remoteEntry.shared.forEach((external) => {
2142
2479
  if (!external.singleton) {
2143
- const url2 = join(remoteEntryScope, external.outFileName);
2144
- addToScopes(remoteEntryScope, external.packageName, url2, importMap);
2145
- addIntegrity(importMap, url2, integrityMap, external.outFileName);
2480
+ Object.entries(external.entries).forEach(([packageName, fileName]) => {
2481
+ const url = join(remoteEntryScope, fileName);
2482
+ addToScopes(remoteEntryScope, packageName, url, importMap);
2483
+ addIntegrity(importMap, url, integrityMap, fileName);
2484
+ });
2146
2485
  if (external?.bundle) chunkBundles.add(external?.bundle);
2147
2486
  return;
2148
2487
  }
@@ -2154,33 +2493,48 @@ function createConvertToImportMap({ log }, ports) {
2154
2493
  return;
2155
2494
  }
2156
2495
  if (actions[external.packageName].action === "skip") {
2496
+ const override = actions[external.packageName].override;
2157
2497
  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
- );
2498
+ if (override) {
2499
+ Object.entries(override).forEach(([packageName, url]) => {
2500
+ addToScopes(remoteEntryScope, packageName, url, importMap);
2501
+ });
2165
2502
  return;
2166
2503
  }
2504
+ log.error(
2505
+ 9,
2506
+ `[${remoteEntry.name}][${external.packageName}] shareScope skip has no override.`
2507
+ );
2508
+ if (config.strict.strictImportMap) throw new NFError("Could not create ImportMap.");
2509
+ Object.entries(external.entries).forEach(([packageName, fileName]) => {
2510
+ const url = join(remoteEntryScope, fileName);
2511
+ addToScopes(remoteEntryScope, packageName, url, importMap);
2512
+ addIntegrity(importMap, url, integrityMap, fileName);
2513
+ });
2514
+ return;
2167
2515
  }
2168
2516
  if (external?.bundle) chunkBundles.add(external?.bundle);
2169
2517
  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);
2518
+ Object.entries(external.entries).forEach(([packageName, fileName]) => {
2519
+ const url = join(remoteEntryScope, fileName);
2520
+ addToScopes(remoteEntryScope, packageName, url, importMap);
2521
+ addIntegrity(importMap, url, integrityMap, fileName);
2522
+ });
2173
2523
  return;
2174
2524
  }
2175
2525
  if (external.shareScope) {
2176
- const url2 = join(remoteEntryScope, external.outFileName);
2177
- addToScopes(remoteEntryScope, external.packageName, url2, importMap);
2178
- addIntegrity(importMap, url2, integrityMap, external.outFileName);
2526
+ Object.entries(external.entries).forEach(([packageName, fileName]) => {
2527
+ const url = join(remoteEntryScope, fileName);
2528
+ addToScopes(remoteEntryScope, packageName, url, importMap);
2529
+ addIntegrity(importMap, url, integrityMap, fileName);
2530
+ });
2179
2531
  return;
2180
2532
  }
2181
- const url = join(remoteEntryScope, external.outFileName);
2182
- importMap.imports[external.packageName] = url;
2183
- addIntegrity(importMap, url, integrityMap, external.outFileName);
2533
+ Object.entries(external.entries).forEach(([packageName, fileName]) => {
2534
+ const url = join(remoteEntryScope, fileName);
2535
+ importMap.imports[packageName] = url;
2536
+ addIntegrity(importMap, url, integrityMap, fileName);
2537
+ });
2184
2538
  });
2185
2539
  addChunkImports(importMap, remoteEntry, remoteEntryScope, chunkBundles);
2186
2540
  }
@@ -2227,11 +2581,13 @@ var createInitDrivers = ({
2227
2581
  getRemoteEntries: createGetRemoteEntries(config, adapters),
2228
2582
  processRemoteEntries: createProcessRemoteEntries(config, adapters),
2229
2583
  determineSharedExternals: createDetermineSharedExternals(config, adapters),
2584
+ poolSharedExternals: createPoolSharedExternals(config, adapters),
2230
2585
  generateImportMap: createGenerateImportMap(config, adapters),
2231
2586
  commitChanges: createCommitChanges(config, adapters),
2232
2587
  exposeModuleLoader: createExposeModuleLoader(config, adapters),
2233
2588
  getRemoteEntry: createGetRemoteEntry(config, adapters),
2234
2589
  updateCache: createUpdateCache(config, adapters),
2590
+ poolDynamicExternals: createPoolDynamicExternals(config),
2235
2591
  convertToImportMap: createConvertToImportMap(config, adapters)
2236
2592
  });
2237
2593
  var INIT_FLOW_FACTORY = ({
@@ -2342,8 +2698,78 @@ var createManifestProvider = () => {
2342
2698
  };
2343
2699
  };
2344
2700
 
2701
+ // node_modules/.pnpm/@softarc+native-federation@4.3.1_typescript@6.0.3/node_modules/@softarc/native-federation/dist/lib/utils/normalize.js
2702
+ function inferPackageFromSecondary(secondary) {
2703
+ const parts = secondary.split("/");
2704
+ if (secondary.startsWith("@") && parts.length >= 2) {
2705
+ return parts[0] + "/" + parts[1];
2706
+ }
2707
+ return parts[0];
2708
+ }
2709
+
2710
+ // node_modules/.pnpm/@softarc+native-federation@4.3.1_typescript@6.0.3/node_modules/@softarc/native-federation/dist/lib/core/output/densify-externals.js
2711
+ function isDense(entry) {
2712
+ return "entries" in entry;
2713
+ }
2714
+ function isFlatChunk(entry) {
2715
+ return entry.packageName.startsWith(CHUNK_PREFIX);
2716
+ }
2717
+ function densifyExternals(shared) {
2718
+ const result = [];
2719
+ const groupIndex = /* @__PURE__ */ new Map();
2720
+ for (const entry of shared) {
2721
+ if (isDense(entry)) {
2722
+ result.push(entry);
2723
+ continue;
2724
+ }
2725
+ if (isFlatChunk(entry)) {
2726
+ const { outFileName, ...rest } = entry;
2727
+ result.push({ ...rest, entries: { [entry.packageName]: outFileName } });
2728
+ continue;
2729
+ }
2730
+ const parent = inferPackageFromSecondary(entry.packageName);
2731
+ const sig = JSON.stringify({
2732
+ singleton: entry.singleton,
2733
+ strictVersion: entry.strictVersion,
2734
+ requiredVersion: entry.requiredVersion,
2735
+ version: entry.version,
2736
+ shareScope: entry.shareScope
2737
+ });
2738
+ const key = parent + " " + sig;
2739
+ const existing = groupIndex.get(key);
2740
+ if (existing === void 0) {
2741
+ const dense = {
2742
+ singleton: entry.singleton,
2743
+ strictVersion: entry.strictVersion,
2744
+ requiredVersion: entry.requiredVersion,
2745
+ packageName: parent,
2746
+ entries: { [entry.packageName]: entry.outFileName }
2747
+ };
2748
+ if (entry.version !== void 0) dense.version = entry.version;
2749
+ if (entry.shareScope !== void 0) dense.shareScope = entry.shareScope;
2750
+ if (entry.bundle !== void 0) dense.bundle = entry.bundle;
2751
+ if (entry.dev !== void 0) dense.dev = entry.dev;
2752
+ groupIndex.set(key, result.length);
2753
+ result.push(dense);
2754
+ } else {
2755
+ result[existing].entries[entry.packageName] = entry.outFileName;
2756
+ }
2757
+ }
2758
+ return result;
2759
+ }
2760
+ function toDenseSharedInfoFormat(shared) {
2761
+ return shared.map((external) => {
2762
+ if ("entries" in external) return external;
2763
+ const { outFileName, ...baseSharedInfoProps } = external;
2764
+ return {
2765
+ ...baseSharedInfoProps,
2766
+ entries: { [external.packageName]: outFileName }
2767
+ };
2768
+ });
2769
+ }
2770
+
2345
2771
  // src/lib/core/3.adapters/http/remote-entry-provider.ts
2346
- var createRemoteEntryProvider = () => {
2772
+ var createRemoteEntryProvider = (config) => {
2347
2773
  const ensureOk = (response) => {
2348
2774
  if (!response.ok)
2349
2775
  return Promise.reject(new Error(`${response.status} - ${response.statusText}`));
@@ -2355,6 +2781,10 @@ var createRemoteEntryProvider = () => {
2355
2781
  if (!remoteEntry.url) remoteEntry.url = remoteEntryUrl;
2356
2782
  return remoteEntry;
2357
2783
  };
2784
+ const normalizeRemoteEntry = (raw) => {
2785
+ const shared = config.feature.convertFlatSharedInfo ? densifyExternals(raw.shared ?? []) : toDenseSharedInfoFormat(raw.shared ?? []);
2786
+ return { ...raw, shared };
2787
+ };
2358
2788
  const formatError = (remoteEntryUrl) => (err) => {
2359
2789
  const msg = err instanceof Error ? err.message : String(err);
2360
2790
  throw new NFError(`Fetch of '${remoteEntryUrl}' returned ${msg}`);
@@ -2367,7 +2797,7 @@ var createRemoteEntryProvider = () => {
2367
2797
  await verifyIntegrity(bytes, opts.integrity);
2368
2798
  return JSON.parse(new TextDecoder().decode(bytes));
2369
2799
  };
2370
- return fetch(remoteEntryUrl).then(ensureOk).then(parse).then(fillEmptyFields(remoteEntryUrl)).catch(formatError(remoteEntryUrl));
2800
+ return fetch(remoteEntryUrl).then(ensureOk).then(parse).then(normalizeRemoteEntry).then(fillEmptyFields(remoteEntryUrl)).catch(formatError(remoteEntryUrl));
2371
2801
  }
2372
2802
  };
2373
2803
  };
@@ -2443,7 +2873,14 @@ var createSharedExternalsRepository = (config) => {
2443
2873
  );
2444
2874
  if (config.clearStorage) STORAGE.clear();
2445
2875
  const _cache = STORAGE.get();
2876
+ let _sawPoolTag = false;
2446
2877
  return {
2878
+ markPoolTagPresent: function() {
2879
+ _sawPoolTag = true;
2880
+ },
2881
+ hasPoolTag: function() {
2882
+ return _sawPoolTag;
2883
+ },
2447
2884
  getFromScope: function(shareScope) {
2448
2885
  return { ..._cache[shareScope ?? GLOBAL_SCOPE] };
2449
2886
  },
@@ -2553,7 +2990,7 @@ var createDriving = (config) => {
2553
2990
  const adapters = {
2554
2991
  versionCheck: createVersionCheck(),
2555
2992
  manifestProvider: createManifestProvider(),
2556
- remoteEntryProvider: createRemoteEntryProvider(),
2993
+ remoteEntryProvider: createRemoteEntryProvider(config),
2557
2994
  remoteInfoRepo: createRemoteInfoRepository(config),
2558
2995
  scopedExternalsRepo: createScopedExternalsRepository(config),
2559
2996
  sharedExternalsRepo: createSharedExternalsRepository(config),
@@ -2796,7 +3233,11 @@ var createModeConfig = (override) => {
2796
3233
  };
2797
3234
  return {
2798
3235
  strict: strictnessConfig,
2799
- profile: { ...defaultProfile, ...override.profile ?? {} }
3236
+ profile: { ...defaultProfile, ...override.profile ?? {} },
3237
+ feature: {
3238
+ convertFlatSharedInfo: override.feature?.convertFlatSharedInfo ?? false,
3239
+ useAutoExternalPooling: override.feature?.useAutoExternalPooling ?? false
3240
+ }
2800
3241
  };
2801
3242
  };
2802
3243