@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
package/fesm2022/node.mjs CHANGED
@@ -1560,7 +1560,14 @@ var createSharedExternalsRepository = (config) => {
1560
1560
  );
1561
1561
  if (config.clearStorage) STORAGE.clear();
1562
1562
  const _cache = STORAGE.get();
1563
+ let _sawPoolTag = false;
1563
1564
  return {
1565
+ markPoolTagPresent: function() {
1566
+ _sawPoolTag = true;
1567
+ },
1568
+ hasPoolTag: function() {
1569
+ return _sawPoolTag;
1570
+ },
1564
1571
  getFromScope: function(shareScope) {
1565
1572
  return { ..._cache[shareScope ?? GLOBAL_SCOPE] };
1566
1573
  },
@@ -1871,7 +1878,11 @@ var createModeConfig = (override) => {
1871
1878
  };
1872
1879
  return {
1873
1880
  strict: strictnessConfig,
1874
- profile: { ...defaultProfile, ...override.profile ?? {} }
1881
+ profile: { ...defaultProfile, ...override.profile ?? {} },
1882
+ feature: {
1883
+ convertFlatSharedInfo: override.feature?.convertFlatSharedInfo ?? false,
1884
+ useAutoExternalPooling: override.feature?.useAutoExternalPooling ?? false
1885
+ }
1875
1886
  };
1876
1887
  };
1877
1888
 
@@ -1890,7 +1901,7 @@ var createInitFlow = ({
1890
1901
  adapters,
1891
1902
  config
1892
1903
  }) => {
1893
- 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) => ({
1904
+ 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) => ({
1894
1905
  config,
1895
1906
  adapters,
1896
1907
  loadRemoteModule
@@ -1901,7 +1912,7 @@ var createInitFlow = ({
1901
1912
  var createInitRemoteEntryFlow = ({
1902
1913
  flow
1903
1914
  }) => {
1904
- const processDynamicRemoteEntry = (remoteEntry) => flow.updateCache(remoteEntry).then(flow.convertToImportMap).then(flow.commitChanges);
1915
+ const processDynamicRemoteEntry = (remoteEntry) => flow.updateCache(remoteEntry).then(flow.poolDynamicExternals).then(flow.convertToImportMap).then(flow.commitChanges);
1905
1916
  return (remoteEntryUrl, remote) => flow.getRemoteEntry(remoteEntryUrl, remote).then((entry) => entry.map(processDynamicRemoteEntry).orElse(Promise.resolve())).then(() => void 0);
1906
1917
  };
1907
1918
 
@@ -1918,16 +1929,25 @@ function createGetRemoteEntries(config, ports) {
1918
1929
  };
1919
1930
  function addHostRemoteEntry(manifest) {
1920
1931
  if (!config.hostRemoteEntry) return manifest;
1921
- const { name, url, cacheTag, integrity } = config.hostRemoteEntry;
1922
- const urlWithCache = cacheTag ? `${url}?cacheTag=${cacheTag}` : url;
1932
+ const { name, url, integrity } = config.hostRemoteEntry;
1923
1933
  return {
1924
1934
  ...manifest,
1925
- [name]: integrity ? { url: urlWithCache, integrity } : urlWithCache
1935
+ [name]: integrity ? { url, integrity } : url
1926
1936
  };
1927
1937
  }
1928
1938
  function normalizeEntry(descriptor) {
1929
1939
  return typeof descriptor === "string" ? { url: descriptor } : descriptor;
1930
1940
  }
1941
+ function withCacheTag(url, cacheTag) {
1942
+ if (!cacheTag) return url;
1943
+ return `${url}${url.includes("?") ? "&" : "?"}cacheTag=${cacheTag}`;
1944
+ }
1945
+ function resolveCacheTag(remoteName) {
1946
+ if (config.hostRemoteEntry && remoteName === config.hostRemoteEntry.name && config.hostRemoteEntry.cacheTag) {
1947
+ return config.hostRemoteEntry.cacheTag;
1948
+ }
1949
+ return config.profile.cacheTag;
1950
+ }
1931
1951
  async function fetchRemoteEntries(manifest) {
1932
1952
  const fetchPromises = Object.entries(manifest).map(
1933
1953
  ([remoteName, descriptor]) => fetchRemoteEntry(remoteName, descriptor)
@@ -1948,8 +1968,9 @@ function createGetRemoteEntries(config, ports) {
1948
1968
  }
1949
1969
  });
1950
1970
  if (skip) return false;
1971
+ const fetchUrl = withCacheTag(remoteEntryUrl, resolveCacheTag(remoteName));
1951
1972
  try {
1952
- const remoteEntry = integrity ? await ports.remoteEntryProvider.provide(remoteEntryUrl, { integrity }) : await ports.remoteEntryProvider.provide(remoteEntryUrl);
1973
+ const remoteEntry = integrity ? await ports.remoteEntryProvider.provide(fetchUrl, { integrity }) : await ports.remoteEntryProvider.provide(fetchUrl);
1953
1974
  config.log.debug(
1954
1975
  1,
1955
1976
  `Fetched '${remoteEntry.name}' from '${remoteEntry.url}', exposing: ${JSON.stringify(remoteEntry.exposes)}`
@@ -2027,6 +2048,7 @@ function createStoreRemoteEntry(config, ports, logStep) {
2027
2048
  const tag = resolveVersion(remoteEntry, external);
2028
2049
  if (tag === null) return;
2029
2050
  if (external.singleton) {
2051
+ if (external.pool?.trim()) ports.sharedExternalsRepo.markPoolTagPresent();
2030
2052
  onSharedExternal(remoteEntry, external, sharedExternalContext(remoteEntry, external, tag));
2031
2053
  } else {
2032
2054
  addScopedExternal(remoteEntry, external, tag);
@@ -2046,8 +2068,8 @@ function createStoreRemoteEntry(config, ports, logStep) {
2046
2068
  function addScopedExternal(remoteEntry, sharedInfo, tag) {
2047
2069
  ports.scopedExternalsRepo.addExternal(remoteEntry.name, sharedInfo.packageName, {
2048
2070
  tag,
2049
- file: sharedInfo.outFileName,
2050
- bundle: sharedInfo.bundle
2071
+ bundle: sharedInfo.bundle,
2072
+ entries: sharedInfo.entries
2051
2073
  });
2052
2074
  }
2053
2075
  function resolveVersion(remoteEntry, external) {
@@ -2069,12 +2091,13 @@ function createStoreRemoteEntry(config, ports, logStep) {
2069
2091
  function sharedExternalContext(remoteEntry, sharedInfo, tag) {
2070
2092
  const scopeType = ports.sharedExternalsRepo.scopeType(sharedInfo.shareScope);
2071
2093
  const remote = {
2072
- file: sharedInfo.outFileName,
2073
2094
  name: remoteEntry.name,
2074
2095
  bundle: sharedInfo.bundle,
2075
2096
  strictVersion: sharedInfo.strictVersion,
2076
2097
  cached: false,
2077
- requiredVersion: scopeType === "strict" ? tag : sharedInfo.requiredVersion || tag
2098
+ requiredVersion: scopeType === "strict" ? tag : sharedInfo.requiredVersion || tag,
2099
+ ...sharedInfo.pool?.trim() ? { pool: sharedInfo.pool } : {},
2100
+ entries: sharedInfo.entries
2078
2101
  };
2079
2102
  const cached2 = ports.sharedExternalsRepo.tryGet(sharedInfo.packageName, sharedInfo.shareScope).orElse({ dirty: false, versions: [] });
2080
2103
  return {
@@ -2222,7 +2245,290 @@ function createDetermineSharedExternals(config, ports) {
2222
2245
  }
2223
2246
  }
2224
2247
 
2225
- // node_modules/.pnpm/@softarc+native-federation@4.2.1_typescript@6.0.3/node_modules/@softarc/native-federation/dist/lib/domain/core/chunk.js
2248
+ // src/lib/core/2.app/steps/pooling/pool-graph.ts
2249
+ var SCOPED_PACKAGE = /^@([^/]+)\//;
2250
+ function createDSU() {
2251
+ const ids = /* @__PURE__ */ new Map();
2252
+ const parent = [];
2253
+ const size = [];
2254
+ const intern = (key) => {
2255
+ let id = ids.get(key);
2256
+ if (id === void 0) {
2257
+ id = parent.length;
2258
+ ids.set(key, id);
2259
+ parent.push(id);
2260
+ size.push(1);
2261
+ }
2262
+ return id;
2263
+ };
2264
+ const findRoot = (x) => {
2265
+ while (parent[x] !== x) {
2266
+ parent[x] = parent[parent[x]];
2267
+ x = parent[x];
2268
+ }
2269
+ return x;
2270
+ };
2271
+ return {
2272
+ union(a, b) {
2273
+ let rootA = findRoot(intern(a));
2274
+ let rootB = findRoot(intern(b));
2275
+ if (rootA === rootB) return;
2276
+ if (size[rootA] < size[rootB]) [rootA, rootB] = [rootB, rootA];
2277
+ parent[rootB] = rootA;
2278
+ size[rootA] = size[rootA] + size[rootB];
2279
+ },
2280
+ // Interns the key if unseen — an isolated key is its own component.
2281
+ component(key) {
2282
+ return findRoot(intern(key));
2283
+ }
2284
+ };
2285
+ }
2286
+ var extNode = (name) => `ext\0${name}`;
2287
+ var tagNode = (remote, tag) => `tag\0${remote}\0${tag}`;
2288
+ var scopeNode = (scope) => `scope\0${scope}`;
2289
+ function autoScope(name, useAutoExternalPooling) {
2290
+ return useAutoExternalPooling ? SCOPED_PACKAGE.exec(name)?.[1] : void 0;
2291
+ }
2292
+ function groupByMembership(candidates, log) {
2293
+ const dsu = createDSU();
2294
+ const tagged = /* @__PURE__ */ new Set();
2295
+ const poolable = [];
2296
+ for (const candidate of candidates) {
2297
+ let joined = false;
2298
+ if (candidate.scope !== void 0) {
2299
+ dsu.union(extNode(candidate.name), scopeNode(candidate.scope));
2300
+ joined = true;
2301
+ }
2302
+ for (const edge of candidate.tags) {
2303
+ dsu.union(extNode(candidate.name), tagNode(edge.remote, edge.tag));
2304
+ tagged.add(candidate.name);
2305
+ joined = true;
2306
+ }
2307
+ if (joined) poolable.push({ name: candidate.name, value: candidate.value });
2308
+ }
2309
+ const byComponent = /* @__PURE__ */ new Map();
2310
+ for (const entry of poolable) {
2311
+ const root = dsu.component(extNode(entry.name));
2312
+ (byComponent.get(root) ?? byComponent.set(root, []).get(root)).push(entry);
2313
+ }
2314
+ const pools = [];
2315
+ for (const members of byComponent.values()) {
2316
+ members.sort((a, b) => a.name.localeCompare(b.name));
2317
+ if (members.length < 2) {
2318
+ const only = members[0];
2319
+ if (tagged.has(only.name)) {
2320
+ log?.warn(
2321
+ 3,
2322
+ `[${only.name}] declares a 'pool' tag but no other external joined its pool; likely a typo or a missing sibling.`
2323
+ );
2324
+ }
2325
+ continue;
2326
+ }
2327
+ pools.push(members);
2328
+ }
2329
+ pools.sort((a, b) => a[0].name.localeCompare(b[0].name));
2330
+ return new Map(pools.map((members) => [members[0].name, members.map((m) => m.value)]));
2331
+ }
2332
+ function buildPools(sharedExternals, useAutoExternalPooling, log) {
2333
+ const candidates = Object.entries(sharedExternals).map(
2334
+ ([name, external]) => ({
2335
+ name,
2336
+ scope: autoScope(name, useAutoExternalPooling),
2337
+ tags: external.versions.flatMap(
2338
+ (v) => v.remotes.flatMap((r) => {
2339
+ const tag = r.pool?.trim();
2340
+ return tag ? [{ remote: r.name, tag }] : [];
2341
+ })
2342
+ ),
2343
+ value: { name, external }
2344
+ })
2345
+ );
2346
+ return groupByMembership(candidates, log);
2347
+ }
2348
+
2349
+ // src/lib/core/2.app/steps/pooling/pool.util.ts
2350
+ function versionForRemote(member, remote) {
2351
+ return member.external.versions.find((v) => v.remotes.some((r) => r.name === remote));
2352
+ }
2353
+ function remotesInPool(members) {
2354
+ return [
2355
+ ...new Set(members.flatMap((m) => m.external.versions.flatMap((v) => v.remotes.map((r) => r.name))))
2356
+ ];
2357
+ }
2358
+
2359
+ // src/lib/core/2.app/steps/pooling/pool-shared-externals.ts
2360
+ function poolWinners(members) {
2361
+ return members.map((member) => {
2362
+ const share = member.external.versions.find((v) => v.action === "share");
2363
+ return {
2364
+ member,
2365
+ tag: share?.tag ?? "",
2366
+ providers: new Set((share?.remotes ?? []).map((r) => r.name))
2367
+ };
2368
+ });
2369
+ }
2370
+ function selectCoverageAnchor(winners) {
2371
+ const coverage = /* @__PURE__ */ new Map();
2372
+ for (const winner of winners)
2373
+ for (const remote of winner.providers) coverage.set(remote, (coverage.get(remote) ?? 0) + 1);
2374
+ if (coverage.size === 0) return void 0;
2375
+ const anchorRemote = [...coverage.keys()].sort(
2376
+ (a, b) => coverage.get(b) - coverage.get(a) || a.localeCompare(b)
2377
+ )[0];
2378
+ return {
2379
+ anchorRemote,
2380
+ tagPerMember: Object.fromEntries(
2381
+ winners.filter((w) => w.providers.has(anchorRemote)).map((w) => [w.member.name, w.tag])
2382
+ )
2383
+ };
2384
+ }
2385
+ function classifyPoolConservative(members, anchor) {
2386
+ const classification = /* @__PURE__ */ new Map();
2387
+ for (const remote of remotesInPool(members)) {
2388
+ let coverageForced = false;
2389
+ let verdict = "follow";
2390
+ for (const member of members) {
2391
+ const version = versionForRemote(member, remote);
2392
+ if (!version) continue;
2393
+ const anchorTag = anchor.tagPerMember[member.name];
2394
+ if (!anchorTag) {
2395
+ coverageForced = true;
2396
+ continue;
2397
+ }
2398
+ if (version.tag === anchorTag) continue;
2399
+ if (version.action === "scope") {
2400
+ verdict = "scope-incompat";
2401
+ break;
2402
+ }
2403
+ }
2404
+ classification.set(
2405
+ remote,
2406
+ verdict === "scope-incompat" ? "scope-incompat" : coverageForced ? "scope-coverage" : "follow"
2407
+ );
2408
+ }
2409
+ classification.set(anchor.anchorRemote, "follow");
2410
+ return classification;
2411
+ }
2412
+ function createPoolSharedExternals(config, ports) {
2413
+ return () => {
2414
+ const { useAutoExternalPooling } = config.feature;
2415
+ if (!useAutoExternalPooling && !ports.sharedExternalsRepo.hasPoolTag()) {
2416
+ return Promise.resolve();
2417
+ }
2418
+ for (const scope of ports.sharedExternalsRepo.getScopes()) {
2419
+ if (ports.sharedExternalsRepo.scopeType(scope) === "strict") continue;
2420
+ const sharedExternals = ports.sharedExternalsRepo.getFromScope(scope);
2421
+ try {
2422
+ for (const [poolName, members] of buildPools(
2423
+ sharedExternals,
2424
+ useAutoExternalPooling,
2425
+ config.log
2426
+ )) {
2427
+ poolFamily(poolName, members, scope);
2428
+ }
2429
+ } catch (error) {
2430
+ if (error instanceof NFError) return Promise.reject(error);
2431
+ config.log.error(3, `[${scope}] failed to pool shared externals.`, {
2432
+ sharedExternals,
2433
+ error
2434
+ });
2435
+ return Promise.reject(
2436
+ new NFError(`Could not pool shared externals in scope ${scope}.`, error)
2437
+ );
2438
+ }
2439
+ }
2440
+ return Promise.resolve();
2441
+ };
2442
+ function poolFamily(poolName, members, scope) {
2443
+ if (members.length < 2) return;
2444
+ const allRemotes = remotesInPool(members);
2445
+ if (allRemotes.length < 2) return;
2446
+ const anchor = selectCoverageAnchor(poolWinners(members));
2447
+ if (!anchor) return;
2448
+ const classification = classifyPoolConservative(members, anchor);
2449
+ config.log.debug(
2450
+ 3,
2451
+ `[${scope}][pool:${poolName}] members=[${members.map((m) => m.name).join(", ")}], anchor=${anchor.anchorRemote}, remotes={${allRemotes.map((r) => `${r}:${classification.get(r)}`).join(", ")}}`
2452
+ );
2453
+ const incompatForced = allRemotes.some((r) => classification.get(r) === "scope-incompat");
2454
+ if (incompatForced && config.strict.strictExternalCompatibility) {
2455
+ config.log.error(
2456
+ 3,
2457
+ `[${scope}][pool:${poolName}] A remote is version-incompatible with anchor '${anchor.anchorRemote}'.`
2458
+ );
2459
+ throw new NFError(`Could not pool '${poolName}' in scope ${scope}.`);
2460
+ }
2461
+ for (const member of members) {
2462
+ const rebuilt = rebuildMember(member, anchor, classification);
2463
+ warnIfScopedOnly(poolName, member.name, rebuilt, scope);
2464
+ ports.sharedExternalsRepo.addOrUpdate(member.name, rebuilt, scope);
2465
+ }
2466
+ }
2467
+ function warnIfScopedOnly(poolName, memberName, rebuilt, scope) {
2468
+ if (rebuilt.versions.some((v) => v.action === "share")) return;
2469
+ const copies = rebuilt.versions.reduce((n, v) => n + v.remotes.length, 0);
2470
+ config.log.warn(
2471
+ 3,
2472
+ `[${scope}][pool:${poolName}] '${memberName}' is scoped-only \u2014 no coherent shared build provides it; ${copies} remotes download their own copy.`
2473
+ );
2474
+ }
2475
+ function rebuildMember(member, anchor, classification) {
2476
+ const anchorTag = anchor.tagPerMember[member.name];
2477
+ const entries = member.external.versions.flatMap(
2478
+ (v) => v.remotes.map((meta) => ({ remote: meta.name, tag: v.tag, host: v.host, meta }))
2479
+ );
2480
+ if (!anchorTag) {
2481
+ return {
2482
+ dirty: false,
2483
+ versions: entries.length > 0 ? [
2484
+ {
2485
+ tag: entries[0].tag,
2486
+ host: false,
2487
+ action: "scope",
2488
+ remotes: entries.map((e) => e.meta)
2489
+ }
2490
+ ] : []
2491
+ };
2492
+ }
2493
+ const anchorMeta = entries.find(
2494
+ (e) => e.remote === anchor.anchorRemote && e.tag === anchorTag
2495
+ ).meta;
2496
+ const anchorHost = member.external.versions.find((v) => v.tag === anchorTag)?.host ?? false;
2497
+ const scopes = (e) => {
2498
+ const cls = classification.get(e.remote);
2499
+ return cls === "scope-incompat" || cls === "scope-coverage" && e.tag !== anchorTag;
2500
+ };
2501
+ const shareOnAnchor = entries.filter(
2502
+ (e) => e.remote !== anchor.anchorRemote && e.tag === anchorTag && !scopes(e)
2503
+ );
2504
+ const shareVersion = {
2505
+ tag: anchorTag,
2506
+ host: anchorHost,
2507
+ action: "share",
2508
+ remotes: [anchorMeta, ...shareOnAnchor.map((e) => e.meta)]
2509
+ };
2510
+ const skipByTag = /* @__PURE__ */ new Map();
2511
+ for (const e of entries) {
2512
+ if (e.tag === anchorTag || scopes(e)) continue;
2513
+ const version = skipByTag.get(e.tag) ?? {
2514
+ tag: e.tag,
2515
+ host: e.host,
2516
+ action: "skip",
2517
+ remotes: []
2518
+ };
2519
+ version.remotes.push(e.meta);
2520
+ skipByTag.set(e.tag, version);
2521
+ }
2522
+ const scoped = entries.filter((e) => scopes(e));
2523
+ const scopeVersion = scoped.length > 0 ? [{ tag: anchorTag, host: false, action: "scope", remotes: scoped.map((e) => e.meta) }] : [];
2524
+ return {
2525
+ dirty: false,
2526
+ versions: [shareVersion, ...skipByTag.values(), ...scopeVersion]
2527
+ };
2528
+ }
2529
+ }
2530
+
2531
+ // node_modules/.pnpm/@softarc+native-federation@4.3.0_typescript@6.0.3/node_modules/@softarc/native-federation/dist/lib/domain/core/chunk.js
2226
2532
  var CHUNK_PREFIX = "@nf-internal";
2227
2533
  function toChunkImport(fileName) {
2228
2534
  if (fileName.startsWith("./")) {
@@ -2257,12 +2563,9 @@ function createGenerateImportMap(config, ports) {
2257
2563
  });
2258
2564
  addToScope(importMap, remote.scopeUrl, createScopeModules(externals, remote.scopeUrl));
2259
2565
  for (const version of Object.values(externals)) {
2260
- addIntegrity(
2261
- importMap,
2262
- join(remote.scopeUrl, version.file),
2263
- remoteName,
2264
- version.file
2265
- );
2566
+ for (const file of Object.values(version.entries)) {
2567
+ addIntegrity(importMap, join(remote.scopeUrl, file), remoteName, file);
2568
+ }
2266
2569
  }
2267
2570
  Object.values(externals).filter((e) => !!e.bundle).forEach((e) => registerBundleChunks(chunkBundles, remoteName, e.bundle));
2268
2571
  }
@@ -2270,8 +2573,10 @@ function createGenerateImportMap(config, ports) {
2270
2573
  }
2271
2574
  function createScopeModules(externals, scope) {
2272
2575
  const modules = {};
2273
- for (const [external, version] of Object.entries(externals)) {
2274
- modules[external] = join(scope, version.file);
2576
+ for (const version of Object.values(externals)) {
2577
+ for (const [packageName, file] of Object.entries(version.entries)) {
2578
+ modules[packageName] = join(scope, file);
2579
+ }
2275
2580
  }
2276
2581
  return modules;
2277
2582
  }
@@ -2291,47 +2596,49 @@ function createGenerateImportMap(config, ports) {
2291
2596
  if (version.action === "scope") {
2292
2597
  for (const remote of version.remotes) {
2293
2598
  const remoteScope = getScope2(shareScope, remote.name, externalName);
2294
- const url = join(remoteScope, remote.file);
2295
- addToScope(importMap, remoteScope, {
2296
- [externalName]: url
2297
- });
2298
- addIntegrity(importMap, url, remote.name, remote.file);
2599
+ for (const [packageName, file] of Object.entries(remote.entries)) {
2600
+ const url = join(remoteScope, file);
2601
+ addToScope(importMap, remoteScope, { [packageName]: url });
2602
+ addIntegrity(importMap, url, remote.name, file);
2603
+ }
2299
2604
  registerBundleChunks(chunkBundles, remote.name, remote.bundle);
2300
2605
  remote.cached = true;
2301
2606
  }
2302
2607
  continue;
2303
2608
  }
2304
- const scope = getScope2(shareScope, version.remotes[0].name, externalName);
2305
- let targetFileUrl = join(scope, version.remotes[0].file);
2306
- let targetFileSource = {
2307
- name: version.remotes[0].name,
2308
- file: version.remotes[0].file
2309
- };
2310
2609
  version.remotes[0].cached = true;
2610
+ let source = version.remotes[0];
2611
+ let sourceScope = getScope2(shareScope, source.name, externalName);
2311
2612
  if (version.action === "share") {
2312
- registerBundleChunks(chunkBundles, version.remotes[0].name, version.remotes[0].bundle);
2313
- }
2314
- if (version.action === "skip") {
2613
+ registerBundleChunks(chunkBundles, source.name, source.bundle);
2614
+ } else if (version.action === "skip") {
2315
2615
  if (!override) {
2316
2616
  override = findOverride(external, shareScope, externalName) ?? "NOT_AVAILABLE";
2317
2617
  }
2318
2618
  if (override !== "NOT_AVAILABLE") {
2319
2619
  if (!overrideScope)
2320
2620
  overrideScope = getScope2(shareScope, override.remotes[0].name, externalName);
2321
- targetFileUrl = join(overrideScope, override.remotes[0].file);
2322
- targetFileSource = {
2323
- name: override.remotes[0].name,
2324
- file: override.remotes[0].file
2325
- };
2621
+ source = override.remotes[0];
2622
+ sourceScope = overrideScope;
2326
2623
  override.remotes[0].cached = true;
2327
2624
  version.remotes[0].cached = false;
2328
2625
  }
2329
2626
  }
2627
+ const mappings = Object.entries(source.entries).map(([packageName, file]) => ({
2628
+ packageName,
2629
+ url: join(sourceScope, file),
2630
+ name: source.name,
2631
+ file
2632
+ }));
2330
2633
  version.remotes.forEach((r) => {
2331
- const scope2 = getScope2(shareScope, r.name, externalName);
2332
- addToScope(importMap, scope2, { [externalName]: targetFileUrl });
2634
+ const rScope = getScope2(shareScope, r.name, externalName);
2635
+ for (const m of mappings) {
2636
+ addToScope(importMap, rScope, { [m.packageName]: m.url });
2637
+ }
2333
2638
  });
2334
- addIntegrity(importMap, targetFileUrl, targetFileSource.name, targetFileSource.file);
2639
+ for (const m of mappings) {
2640
+ addIntegrity(importMap, m.url, m.name, m.file);
2641
+ }
2335
2642
  }
2336
2643
  ports.sharedExternalsRepo.addOrUpdate(externalName, external, shareScope);
2337
2644
  }
@@ -2372,11 +2679,11 @@ function createGenerateImportMap(config, ports) {
2372
2679
  if (version.action === "scope") {
2373
2680
  for (const remote of version.remotes) {
2374
2681
  const remoteScope = getScope2(GLOBAL_SCOPE, remote.name, externalName);
2375
- const url2 = join(remoteScope, remote.file);
2376
- addToScope(importMap, remoteScope, {
2377
- [externalName]: url2
2378
- });
2379
- addIntegrity(importMap, url2, remote.name, remote.file);
2682
+ for (const [packageName, file] of Object.entries(remote.entries)) {
2683
+ const url = join(remoteScope, file);
2684
+ addToScope(importMap, remoteScope, { [packageName]: url });
2685
+ addIntegrity(importMap, url, remote.name, file);
2686
+ }
2380
2687
  remote.cached = true;
2381
2688
  registerBundleChunks(chunkBundles, remote.name, remote.bundle);
2382
2689
  }
@@ -2387,9 +2694,11 @@ function createGenerateImportMap(config, ports) {
2387
2694
  continue;
2388
2695
  }
2389
2696
  const scope = getScope2(GLOBAL_SCOPE, version.remotes[0].name, externalName);
2390
- const url = join(scope, version.remotes[0].file);
2391
- addToGlobal(importMap, { [externalName]: url });
2392
- addIntegrity(importMap, url, version.remotes[0].name, version.remotes[0].file);
2697
+ for (const [packageName, file] of Object.entries(version.remotes[0].entries)) {
2698
+ const url = join(scope, file);
2699
+ addToGlobal(importMap, { [packageName]: url });
2700
+ addIntegrity(importMap, url, version.remotes[0].name, file);
2701
+ }
2393
2702
  registerBundleChunks(chunkBundles, version.remotes[0].name, version.remotes[0].bundle);
2394
2703
  version.remotes[0].cached = true;
2395
2704
  }
@@ -2586,8 +2895,8 @@ function createUpdateCache(config, ports) {
2586
2895
  storeRemoteEntry(remoteEntry, (entry, external, ctx) => {
2587
2896
  const { action, sharedVersion } = resolveSharedExternal(entry, external, ctx);
2588
2897
  actions[external.packageName] = { action };
2589
- if (action === "skip" && external.shareScope && sharedVersion?.remotes[0]?.file) {
2590
- actions[external.packageName].override = resolveOverrideUrl(
2898
+ if (action === "skip" && external.shareScope && sharedVersion?.remotes[0]?.entries) {
2899
+ actions[external.packageName].override = resolveOverrideEntries(
2591
2900
  entry,
2592
2901
  external,
2593
2902
  sharedVersion
@@ -2631,8 +2940,15 @@ function createUpdateCache(config, ports) {
2631
2940
  commit();
2632
2941
  return { action, sharedVersion };
2633
2942
  }
2634
- function resolveOverrideUrl(remoteEntry, external, sharedVersion) {
2635
- return ports.remoteInfoRepo.tryGet(sharedVersion.remotes[0].name).map((remote) => join(remote.scopeUrl, sharedVersion.remotes[0].file)).orThrow(() => {
2943
+ function resolveOverrideEntries(remoteEntry, external, sharedVersion) {
2944
+ return ports.remoteInfoRepo.tryGet(sharedVersion.remotes[0].name).map(
2945
+ (remote) => Object.fromEntries(
2946
+ Object.entries(sharedVersion.remotes[0].entries).map(([packageName, file]) => [
2947
+ packageName,
2948
+ join(remote.scopeUrl, file)
2949
+ ])
2950
+ )
2951
+ ).orThrow(() => {
2636
2952
  config.log.error(
2637
2953
  8,
2638
2954
  `[${external.shareScope ?? GLOBAL_SCOPE}][${remoteEntry.name}][${external.packageName}@${external.version}][override] Remote name not found in cache.`
@@ -2644,14 +2960,56 @@ function createUpdateCache(config, ports) {
2644
2960
  }
2645
2961
  }
2646
2962
 
2647
- // src/lib/core/2.app/steps/convert-to-import-map.ts
2648
- function createConvertToImportMap({ log }, ports) {
2963
+ // src/lib/core/2.app/steps/pooling/pool-dynamic-externals.ts
2964
+ function createPoolDynamicExternals(config) {
2649
2965
  return ({ entry, actions }) => {
2966
+ const { useAutoExternalPooling } = config.feature;
2967
+ if (!useAutoExternalPooling && !(entry.shared ?? []).some((e) => e.pool?.trim())) {
2968
+ return Promise.resolve({ entry, actions });
2969
+ }
2970
+ const byScope = /* @__PURE__ */ new Map();
2971
+ for (const external of entry.shared ?? []) {
2972
+ const name = external.packageName;
2973
+ if (!external.singleton || !actions[name]) continue;
2974
+ if (external.shareScope === STRICT_SCOPE) continue;
2975
+ const tag = external.pool?.trim();
2976
+ const shareScope = external.shareScope ?? GLOBAL_SCOPE;
2977
+ const candidates = byScope.get(shareScope) ?? [];
2978
+ candidates.push({
2979
+ name,
2980
+ scope: autoScope(name, useAutoExternalPooling),
2981
+ tags: tag ? [{ remote: entry.name, tag }] : [],
2982
+ value: name
2983
+ });
2984
+ byScope.set(shareScope, candidates);
2985
+ }
2986
+ const scope = (name) => {
2987
+ actions[name].action = "scope";
2988
+ delete actions[name].override;
2989
+ };
2990
+ for (const candidates of byScope.values()) {
2991
+ for (const members of groupByMembership(candidates).values()) {
2992
+ const memberActions = members.map((name) => actions[name].action);
2993
+ if (memberActions.includes("scope")) {
2994
+ members.forEach(scope);
2995
+ } else if (memberActions.includes("share") && memberActions.includes("skip")) {
2996
+ members.filter((name) => actions[name].action === "share").forEach(scope);
2997
+ }
2998
+ }
2999
+ }
3000
+ return Promise.resolve({ entry, actions });
3001
+ };
3002
+ }
3003
+
3004
+ // src/lib/core/2.app/steps/convert-to-import-map.ts
3005
+ function createConvertToImportMap(config, ports) {
3006
+ const { log } = config;
3007
+ return async ({ entry, actions }) => {
2650
3008
  const importMap = { imports: {} };
2651
3009
  addExternals(entry, actions, importMap);
2652
3010
  addRemoteInfos(entry, importMap);
2653
3011
  log.debug(9, `[${entry.name}] Processed actions:`, actions);
2654
- return Promise.resolve(importMap);
3012
+ return importMap;
2655
3013
  };
2656
3014
  function addExternals(remoteEntry, actions, importMap) {
2657
3015
  if (!remoteEntry.shared) {
@@ -2662,9 +3020,11 @@ function createConvertToImportMap({ log }, ports) {
2662
3020
  const chunkBundles = /* @__PURE__ */ new Set(["mapping-or-exposed"]);
2663
3021
  remoteEntry.shared.forEach((external) => {
2664
3022
  if (!external.singleton) {
2665
- const url2 = join(remoteEntryScope, external.outFileName);
2666
- addToScopes(remoteEntryScope, external.packageName, url2, importMap);
2667
- addIntegrity(importMap, url2, integrityMap, external.outFileName);
3023
+ Object.entries(external.entries).forEach(([packageName, fileName]) => {
3024
+ const url = join(remoteEntryScope, fileName);
3025
+ addToScopes(remoteEntryScope, packageName, url, importMap);
3026
+ addIntegrity(importMap, url, integrityMap, fileName);
3027
+ });
2668
3028
  if (external?.bundle) chunkBundles.add(external?.bundle);
2669
3029
  return;
2670
3030
  }
@@ -2676,33 +3036,48 @@ function createConvertToImportMap({ log }, ports) {
2676
3036
  return;
2677
3037
  }
2678
3038
  if (actions[external.packageName].action === "skip") {
3039
+ const override = actions[external.packageName].override;
2679
3040
  if (!external.shareScope) return;
2680
- if (actions[external.packageName].override) {
2681
- addToScopes(
2682
- remoteEntryScope,
2683
- external.packageName,
2684
- actions[external.packageName].override,
2685
- importMap
2686
- );
3041
+ if (override) {
3042
+ Object.entries(override).forEach(([packageName, url]) => {
3043
+ addToScopes(remoteEntryScope, packageName, url, importMap);
3044
+ });
2687
3045
  return;
2688
3046
  }
3047
+ log.error(
3048
+ 9,
3049
+ `[${remoteEntry.name}][${external.packageName}] shareScope skip has no override.`
3050
+ );
3051
+ if (config.strict.strictImportMap) throw new NFError("Could not create ImportMap.");
3052
+ Object.entries(external.entries).forEach(([packageName, fileName]) => {
3053
+ const url = join(remoteEntryScope, fileName);
3054
+ addToScopes(remoteEntryScope, packageName, url, importMap);
3055
+ addIntegrity(importMap, url, integrityMap, fileName);
3056
+ });
3057
+ return;
2689
3058
  }
2690
3059
  if (external?.bundle) chunkBundles.add(external?.bundle);
2691
3060
  if (actions[external.packageName].action === "scope") {
2692
- const url2 = join(remoteEntryScope, external.outFileName);
2693
- addToScopes(remoteEntryScope, external.packageName, url2, importMap);
2694
- addIntegrity(importMap, url2, integrityMap, external.outFileName);
3061
+ Object.entries(external.entries).forEach(([packageName, fileName]) => {
3062
+ const url = join(remoteEntryScope, fileName);
3063
+ addToScopes(remoteEntryScope, packageName, url, importMap);
3064
+ addIntegrity(importMap, url, integrityMap, fileName);
3065
+ });
2695
3066
  return;
2696
3067
  }
2697
3068
  if (external.shareScope) {
2698
- const url2 = join(remoteEntryScope, external.outFileName);
2699
- addToScopes(remoteEntryScope, external.packageName, url2, importMap);
2700
- addIntegrity(importMap, url2, integrityMap, external.outFileName);
3069
+ Object.entries(external.entries).forEach(([packageName, fileName]) => {
3070
+ const url = join(remoteEntryScope, fileName);
3071
+ addToScopes(remoteEntryScope, packageName, url, importMap);
3072
+ addIntegrity(importMap, url, integrityMap, fileName);
3073
+ });
2701
3074
  return;
2702
3075
  }
2703
- const url = join(remoteEntryScope, external.outFileName);
2704
- importMap.imports[external.packageName] = url;
2705
- addIntegrity(importMap, url, integrityMap, external.outFileName);
3076
+ Object.entries(external.entries).forEach(([packageName, fileName]) => {
3077
+ const url = join(remoteEntryScope, fileName);
3078
+ importMap.imports[packageName] = url;
3079
+ addIntegrity(importMap, url, integrityMap, fileName);
3080
+ });
2706
3081
  });
2707
3082
  addChunkImports(importMap, remoteEntry, remoteEntryScope, chunkBundles);
2708
3083
  }
@@ -2749,11 +3124,13 @@ var createInitDrivers = ({
2749
3124
  getRemoteEntries: createGetRemoteEntries(config, adapters),
2750
3125
  processRemoteEntries: createProcessRemoteEntries(config, adapters),
2751
3126
  determineSharedExternals: createDetermineSharedExternals(config, adapters),
3127
+ poolSharedExternals: createPoolSharedExternals(config, adapters),
2752
3128
  generateImportMap: createGenerateImportMap(config, adapters),
2753
3129
  commitChanges: createCommitChanges(config, adapters),
2754
3130
  exposeModuleLoader: createExposeModuleLoader(config, adapters),
2755
3131
  getRemoteEntry: createGetRemoteEntry(config, adapters),
2756
3132
  updateCache: createUpdateCache(config, adapters),
3133
+ poolDynamicExternals: createPoolDynamicExternals(config),
2757
3134
  convertToImportMap: createConvertToImportMap(config, adapters)
2758
3135
  });
2759
3136
  var INIT_FLOW_FACTORY = ({