@softarc/native-federation-orchestrator 4.4.0 → 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 +523 -82
  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 +453 -76
  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 +7 -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
 
@@ -1335,16 +1335,25 @@ function createGetRemoteEntries(config, ports) {
1335
1335
  };
1336
1336
  function addHostRemoteEntry(manifest) {
1337
1337
  if (!config.hostRemoteEntry) return manifest;
1338
- const { name, url, cacheTag, integrity } = config.hostRemoteEntry;
1339
- const urlWithCache = cacheTag ? `${url}?cacheTag=${cacheTag}` : url;
1338
+ const { name, url, integrity } = config.hostRemoteEntry;
1340
1339
  return {
1341
1340
  ...manifest,
1342
- [name]: integrity ? { url: urlWithCache, integrity } : urlWithCache
1341
+ [name]: integrity ? { url, integrity } : url
1343
1342
  };
1344
1343
  }
1345
1344
  function normalizeEntry(descriptor) {
1346
1345
  return typeof descriptor === "string" ? { url: descriptor } : descriptor;
1347
1346
  }
1347
+ function withCacheTag(url, cacheTag) {
1348
+ if (!cacheTag) return url;
1349
+ return `${url}${url.includes("?") ? "&" : "?"}cacheTag=${cacheTag}`;
1350
+ }
1351
+ function resolveCacheTag(remoteName) {
1352
+ if (config.hostRemoteEntry && remoteName === config.hostRemoteEntry.name && config.hostRemoteEntry.cacheTag) {
1353
+ return config.hostRemoteEntry.cacheTag;
1354
+ }
1355
+ return config.profile.cacheTag;
1356
+ }
1348
1357
  async function fetchRemoteEntries(manifest) {
1349
1358
  const fetchPromises = Object.entries(manifest).map(
1350
1359
  ([remoteName, descriptor]) => fetchRemoteEntry(remoteName, descriptor)
@@ -1365,8 +1374,9 @@ function createGetRemoteEntries(config, ports) {
1365
1374
  }
1366
1375
  });
1367
1376
  if (skip) return false;
1377
+ const fetchUrl = withCacheTag(remoteEntryUrl, resolveCacheTag(remoteName));
1368
1378
  try {
1369
- const remoteEntry = integrity ? await ports.remoteEntryProvider.provide(remoteEntryUrl, { integrity }) : await ports.remoteEntryProvider.provide(remoteEntryUrl);
1379
+ const remoteEntry = integrity ? await ports.remoteEntryProvider.provide(fetchUrl, { integrity }) : await ports.remoteEntryProvider.provide(fetchUrl);
1370
1380
  config.log.debug(
1371
1381
  1,
1372
1382
  `Fetched '${remoteEntry.name}' from '${remoteEntry.url}', exposing: ${JSON.stringify(remoteEntry.exposes)}`
@@ -1444,6 +1454,7 @@ function createStoreRemoteEntry(config, ports, logStep) {
1444
1454
  const tag = resolveVersion(remoteEntry, external);
1445
1455
  if (tag === null) return;
1446
1456
  if (external.singleton) {
1457
+ if (external.pool?.trim()) ports.sharedExternalsRepo.markPoolTagPresent();
1447
1458
  onSharedExternal(remoteEntry, external, sharedExternalContext(remoteEntry, external, tag));
1448
1459
  } else {
1449
1460
  addScopedExternal(remoteEntry, external, tag);
@@ -1463,8 +1474,8 @@ function createStoreRemoteEntry(config, ports, logStep) {
1463
1474
  function addScopedExternal(remoteEntry, sharedInfo, tag) {
1464
1475
  ports.scopedExternalsRepo.addExternal(remoteEntry.name, sharedInfo.packageName, {
1465
1476
  tag,
1466
- file: sharedInfo.outFileName,
1467
- bundle: sharedInfo.bundle
1477
+ bundle: sharedInfo.bundle,
1478
+ entries: sharedInfo.entries
1468
1479
  });
1469
1480
  }
1470
1481
  function resolveVersion(remoteEntry, external) {
@@ -1486,12 +1497,13 @@ function createStoreRemoteEntry(config, ports, logStep) {
1486
1497
  function sharedExternalContext(remoteEntry, sharedInfo, tag) {
1487
1498
  const scopeType = ports.sharedExternalsRepo.scopeType(sharedInfo.shareScope);
1488
1499
  const remote = {
1489
- file: sharedInfo.outFileName,
1490
1500
  name: remoteEntry.name,
1491
1501
  bundle: sharedInfo.bundle,
1492
1502
  strictVersion: sharedInfo.strictVersion,
1493
1503
  cached: false,
1494
- 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
1495
1507
  };
1496
1508
  const cached = ports.sharedExternalsRepo.tryGet(sharedInfo.packageName, sharedInfo.shareScope).orElse({ dirty: false, versions: [] });
1497
1509
  return {
@@ -1566,6 +1578,66 @@ function createProcessRemoteEntries(config, ports) {
1566
1578
  var GLOBAL_SCOPE = "__GLOBAL__";
1567
1579
  var STRICT_SCOPE = "strict";
1568
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
+
1569
1641
  // src/lib/core/2.app/steps/determine-shared-externals.ts
1570
1642
  function createDetermineSharedExternals(config, ports) {
1571
1643
  return () => {
@@ -1643,7 +1715,290 @@ function createDetermineSharedExternals(config, ports) {
1643
1715
  }
1644
1716
  }
1645
1717
 
1646
- // 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
1647
2002
  var BuildNotificationType = /* @__PURE__ */ ((BuildNotificationType2) => {
1648
2003
  BuildNotificationType2["COMPLETED"] = "federation-rebuild-complete";
1649
2004
  BuildNotificationType2["ERROR"] = "federation-rebuild-error";
@@ -1651,14 +2006,14 @@ var BuildNotificationType = /* @__PURE__ */ ((BuildNotificationType2) => {
1651
2006
  return BuildNotificationType2;
1652
2007
  })(BuildNotificationType || {});
1653
2008
 
1654
- // node_modules/.pnpm/@softarc+native-federation@4.2.1_typescript@6.0.3/node_modules/@softarc/native-federation/dist/lib/domain/core/chunk.js
1655
- 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";
1656
2011
  function toChunkImport(fileName) {
1657
2012
  if (fileName.startsWith("./")) {
1658
2013
  fileName = fileName.slice(2);
1659
2014
  }
1660
2015
  const packageName = fileName.replace(/.(m|c)?js$/, "");
1661
- return CHUNK_PREFIX + "/" + packageName;
2016
+ return CHUNK_PREFIX2 + "/" + packageName;
1662
2017
  }
1663
2018
 
1664
2019
  // src/lib/core/2.app/steps/generate-import-map.ts
@@ -1686,12 +2041,9 @@ function createGenerateImportMap(config, ports) {
1686
2041
  });
1687
2042
  addToScope(importMap, remote.scopeUrl, createScopeModules(externals, remote.scopeUrl));
1688
2043
  for (const version of Object.values(externals)) {
1689
- addIntegrity(
1690
- importMap,
1691
- join(remote.scopeUrl, version.file),
1692
- remoteName,
1693
- version.file
1694
- );
2044
+ for (const file of Object.values(version.entries)) {
2045
+ addIntegrity(importMap, join(remote.scopeUrl, file), remoteName, file);
2046
+ }
1695
2047
  }
1696
2048
  Object.values(externals).filter((e) => !!e.bundle).forEach((e) => registerBundleChunks(chunkBundles, remoteName, e.bundle));
1697
2049
  }
@@ -1699,8 +2051,10 @@ function createGenerateImportMap(config, ports) {
1699
2051
  }
1700
2052
  function createScopeModules(externals, scope) {
1701
2053
  const modules = {};
1702
- for (const [external, version] of Object.entries(externals)) {
1703
- 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
+ }
1704
2058
  }
1705
2059
  return modules;
1706
2060
  }
@@ -1720,47 +2074,49 @@ function createGenerateImportMap(config, ports) {
1720
2074
  if (version.action === "scope") {
1721
2075
  for (const remote of version.remotes) {
1722
2076
  const remoteScope = getScope2(shareScope, remote.name, externalName);
1723
- const url = join(remoteScope, remote.file);
1724
- addToScope(importMap, remoteScope, {
1725
- [externalName]: url
1726
- });
1727
- 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
+ }
1728
2082
  registerBundleChunks(chunkBundles, remote.name, remote.bundle);
1729
2083
  remote.cached = true;
1730
2084
  }
1731
2085
  continue;
1732
2086
  }
1733
- const scope = getScope2(shareScope, version.remotes[0].name, externalName);
1734
- let targetFileUrl = join(scope, version.remotes[0].file);
1735
- let targetFileSource = {
1736
- name: version.remotes[0].name,
1737
- file: version.remotes[0].file
1738
- };
1739
2087
  version.remotes[0].cached = true;
2088
+ let source = version.remotes[0];
2089
+ let sourceScope = getScope2(shareScope, source.name, externalName);
1740
2090
  if (version.action === "share") {
1741
- registerBundleChunks(chunkBundles, version.remotes[0].name, version.remotes[0].bundle);
1742
- }
1743
- if (version.action === "skip") {
2091
+ registerBundleChunks(chunkBundles, source.name, source.bundle);
2092
+ } else if (version.action === "skip") {
1744
2093
  if (!override) {
1745
2094
  override = findOverride(external, shareScope, externalName) ?? "NOT_AVAILABLE";
1746
2095
  }
1747
2096
  if (override !== "NOT_AVAILABLE") {
1748
2097
  if (!overrideScope)
1749
2098
  overrideScope = getScope2(shareScope, override.remotes[0].name, externalName);
1750
- targetFileUrl = join(overrideScope, override.remotes[0].file);
1751
- targetFileSource = {
1752
- name: override.remotes[0].name,
1753
- file: override.remotes[0].file
1754
- };
2099
+ source = override.remotes[0];
2100
+ sourceScope = overrideScope;
1755
2101
  override.remotes[0].cached = true;
1756
2102
  version.remotes[0].cached = false;
1757
2103
  }
1758
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
+ }));
1759
2111
  version.remotes.forEach((r) => {
1760
- const scope2 = getScope2(shareScope, r.name, externalName);
1761
- 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
+ }
1762
2116
  });
1763
- addIntegrity(importMap, targetFileUrl, targetFileSource.name, targetFileSource.file);
2117
+ for (const m of mappings) {
2118
+ addIntegrity(importMap, m.url, m.name, m.file);
2119
+ }
1764
2120
  }
1765
2121
  ports.sharedExternalsRepo.addOrUpdate(externalName, external, shareScope);
1766
2122
  }
@@ -1801,11 +2157,11 @@ function createGenerateImportMap(config, ports) {
1801
2157
  if (version.action === "scope") {
1802
2158
  for (const remote of version.remotes) {
1803
2159
  const remoteScope = getScope2(GLOBAL_SCOPE, remote.name, externalName);
1804
- const url2 = join(remoteScope, remote.file);
1805
- addToScope(importMap, remoteScope, {
1806
- [externalName]: url2
1807
- });
1808
- 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
+ }
1809
2165
  remote.cached = true;
1810
2166
  registerBundleChunks(chunkBundles, remote.name, remote.bundle);
1811
2167
  }
@@ -1816,9 +2172,11 @@ function createGenerateImportMap(config, ports) {
1816
2172
  continue;
1817
2173
  }
1818
2174
  const scope = getScope2(GLOBAL_SCOPE, version.remotes[0].name, externalName);
1819
- const url = join(scope, version.remotes[0].file);
1820
- addToGlobal(importMap, { [externalName]: url });
1821
- 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
+ }
1822
2180
  registerBundleChunks(chunkBundles, version.remotes[0].name, version.remotes[0].bundle);
1823
2181
  version.remotes[0].cached = true;
1824
2182
  }
@@ -2054,8 +2412,8 @@ function createUpdateCache(config, ports) {
2054
2412
  storeRemoteEntry(remoteEntry, (entry, external, ctx) => {
2055
2413
  const { action, sharedVersion } = resolveSharedExternal(entry, external, ctx);
2056
2414
  actions[external.packageName] = { action };
2057
- if (action === "skip" && external.shareScope && sharedVersion?.remotes[0]?.file) {
2058
- actions[external.packageName].override = resolveOverrideUrl(
2415
+ if (action === "skip" && external.shareScope && sharedVersion?.remotes[0]?.entries) {
2416
+ actions[external.packageName].override = resolveOverrideEntries(
2059
2417
  entry,
2060
2418
  external,
2061
2419
  sharedVersion
@@ -2099,8 +2457,15 @@ function createUpdateCache(config, ports) {
2099
2457
  commit();
2100
2458
  return { action, sharedVersion };
2101
2459
  }
2102
- function resolveOverrideUrl(remoteEntry, external, sharedVersion) {
2103
- 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(() => {
2104
2469
  config.log.error(
2105
2470
  8,
2106
2471
  `[${external.shareScope ?? GLOBAL_SCOPE}][${remoteEntry.name}][${external.packageName}@${external.version}][override] Remote name not found in cache.`
@@ -2112,14 +2477,56 @@ function createUpdateCache(config, ports) {
2112
2477
  }
2113
2478
  }
2114
2479
 
2115
- // src/lib/core/2.app/steps/convert-to-import-map.ts
2116
- function createConvertToImportMap({ log }, ports) {
2480
+ // src/lib/core/2.app/steps/pooling/pool-dynamic-externals.ts
2481
+ function createPoolDynamicExternals(config) {
2117
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 }) => {
2118
2525
  const importMap = { imports: {} };
2119
2526
  addExternals(entry, actions, importMap);
2120
2527
  addRemoteInfos(entry, importMap);
2121
2528
  log.debug(9, `[${entry.name}] Processed actions:`, actions);
2122
- return Promise.resolve(importMap);
2529
+ return importMap;
2123
2530
  };
2124
2531
  function addExternals(remoteEntry, actions, importMap) {
2125
2532
  if (!remoteEntry.shared) {
@@ -2130,9 +2537,11 @@ function createConvertToImportMap({ log }, ports) {
2130
2537
  const chunkBundles = /* @__PURE__ */ new Set(["mapping-or-exposed"]);
2131
2538
  remoteEntry.shared.forEach((external) => {
2132
2539
  if (!external.singleton) {
2133
- const url2 = join(remoteEntryScope, external.outFileName);
2134
- addToScopes(remoteEntryScope, external.packageName, url2, importMap);
2135
- 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
+ });
2136
2545
  if (external?.bundle) chunkBundles.add(external?.bundle);
2137
2546
  return;
2138
2547
  }
@@ -2144,33 +2553,48 @@ function createConvertToImportMap({ log }, ports) {
2144
2553
  return;
2145
2554
  }
2146
2555
  if (actions[external.packageName].action === "skip") {
2556
+ const override = actions[external.packageName].override;
2147
2557
  if (!external.shareScope) return;
2148
- if (actions[external.packageName].override) {
2149
- addToScopes(
2150
- remoteEntryScope,
2151
- external.packageName,
2152
- actions[external.packageName].override,
2153
- importMap
2154
- );
2558
+ if (override) {
2559
+ Object.entries(override).forEach(([packageName, url]) => {
2560
+ addToScopes(remoteEntryScope, packageName, url, importMap);
2561
+ });
2155
2562
  return;
2156
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;
2157
2575
  }
2158
2576
  if (external?.bundle) chunkBundles.add(external?.bundle);
2159
2577
  if (actions[external.packageName].action === "scope") {
2160
- const url2 = join(remoteEntryScope, external.outFileName);
2161
- addToScopes(remoteEntryScope, external.packageName, url2, importMap);
2162
- 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
+ });
2163
2583
  return;
2164
2584
  }
2165
2585
  if (external.shareScope) {
2166
- const url2 = join(remoteEntryScope, external.outFileName);
2167
- addToScopes(remoteEntryScope, external.packageName, url2, importMap);
2168
- 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
+ });
2169
2591
  return;
2170
2592
  }
2171
- const url = join(remoteEntryScope, external.outFileName);
2172
- importMap.imports[external.packageName] = url;
2173
- 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
+ });
2174
2598
  });
2175
2599
  addChunkImports(importMap, remoteEntry, remoteEntryScope, chunkBundles);
2176
2600
  }
@@ -2217,11 +2641,13 @@ var createInitDrivers = ({
2217
2641
  getRemoteEntries: createGetRemoteEntries(config, adapters),
2218
2642
  processRemoteEntries: createProcessRemoteEntries(config, adapters),
2219
2643
  determineSharedExternals: createDetermineSharedExternals(config, adapters),
2644
+ poolSharedExternals: createPoolSharedExternals(config, adapters),
2220
2645
  generateImportMap: createGenerateImportMap(config, adapters),
2221
2646
  commitChanges: createCommitChanges(config, adapters),
2222
2647
  exposeModuleLoader: createExposeModuleLoader(config, adapters),
2223
2648
  getRemoteEntry: createGetRemoteEntry(config, adapters),
2224
2649
  updateCache: createUpdateCache(config, adapters),
2650
+ poolDynamicExternals: createPoolDynamicExternals(config),
2225
2651
  convertToImportMap: createConvertToImportMap(config, adapters)
2226
2652
  });
2227
2653
  var INIT_FLOW_FACTORY = ({
@@ -2333,7 +2759,7 @@ var createManifestProvider = () => {
2333
2759
  };
2334
2760
 
2335
2761
  // src/lib/core/3.adapters/http/remote-entry-provider.ts
2336
- var createRemoteEntryProvider = () => {
2762
+ var createRemoteEntryProvider = (config) => {
2337
2763
  const ensureOk = (response) => {
2338
2764
  if (!response.ok)
2339
2765
  return Promise.reject(new Error(`${response.status} - ${response.statusText}`));
@@ -2345,6 +2771,10 @@ var createRemoteEntryProvider = () => {
2345
2771
  if (!remoteEntry.url) remoteEntry.url = remoteEntryUrl;
2346
2772
  return remoteEntry;
2347
2773
  };
2774
+ const normalizeRemoteEntry = (raw) => {
2775
+ const shared = config.feature.convertFlatSharedInfo ? densifyExternals(raw.shared ?? []) : toDenseSharedInfoFormat(raw.shared ?? []);
2776
+ return { ...raw, shared };
2777
+ };
2348
2778
  const formatError = (remoteEntryUrl) => (err) => {
2349
2779
  const msg = err instanceof Error ? err.message : String(err);
2350
2780
  throw new NFError(`Fetch of '${remoteEntryUrl}' returned ${msg}`);
@@ -2357,7 +2787,7 @@ var createRemoteEntryProvider = () => {
2357
2787
  await verifyIntegrity(bytes, opts.integrity);
2358
2788
  return JSON.parse(new TextDecoder().decode(bytes));
2359
2789
  };
2360
- 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));
2361
2791
  }
2362
2792
  };
2363
2793
  };
@@ -2433,7 +2863,14 @@ var createSharedExternalsRepository = (config) => {
2433
2863
  );
2434
2864
  if (config.clearStorage) STORAGE.clear();
2435
2865
  const _cache = STORAGE.get();
2866
+ let _sawPoolTag = false;
2436
2867
  return {
2868
+ markPoolTagPresent: function() {
2869
+ _sawPoolTag = true;
2870
+ },
2871
+ hasPoolTag: function() {
2872
+ return _sawPoolTag;
2873
+ },
2437
2874
  getFromScope: function(shareScope) {
2438
2875
  return { ..._cache[shareScope ?? GLOBAL_SCOPE] };
2439
2876
  },
@@ -2543,7 +2980,7 @@ var createDriving = (config) => {
2543
2980
  const adapters = {
2544
2981
  versionCheck: createVersionCheck(),
2545
2982
  manifestProvider: createManifestProvider(),
2546
- remoteEntryProvider: createRemoteEntryProvider(),
2983
+ remoteEntryProvider: createRemoteEntryProvider(config),
2547
2984
  remoteInfoRepo: createRemoteInfoRepository(config),
2548
2985
  scopedExternalsRepo: createScopedExternalsRepository(config),
2549
2986
  sharedExternalsRepo: createSharedExternalsRepository(config),
@@ -2786,7 +3223,11 @@ var createModeConfig = (override) => {
2786
3223
  };
2787
3224
  return {
2788
3225
  strict: strictnessConfig,
2789
- profile: { ...defaultProfile, ...override.profile ?? {} }
3226
+ profile: { ...defaultProfile, ...override.profile ?? {} },
3227
+ feature: {
3228
+ convertFlatSharedInfo: override.feature?.convertFlatSharedInfo ?? false,
3229
+ useAutoExternalPooling: override.feature?.useAutoExternalPooling ?? false
3230
+ }
2790
3231
  };
2791
3232
  };
2792
3233