@softarc/native-federation-orchestrator 4.4.1 → 4.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. package/README.md +2 -2
  2. package/fesm2022/@softarc/native-federation-orchestrator.mjs +509 -78
  3. package/fesm2022/@softarc/native-federation-orchestrator.mjs.map +4 -4
  4. package/fesm2022/module-federation.mjs +21 -17
  5. package/fesm2022/module-federation.mjs.map +2 -2
  6. package/fesm2022/node.mjs +439 -72
  7. package/fesm2022/node.mjs.map +4 -4
  8. package/fesm2022/sdk.mjs +63 -1
  9. package/fesm2022/sdk.mjs.map +3 -3
  10. package/package.json +1 -1
  11. package/quickstart.mjs +13 -13
  12. package/types/lib/core/1.domain/externals/version.contract.d.ts +3 -2
  13. package/types/lib/core/1.domain/remote-entry/remote-entry.contract.d.ts +8 -4
  14. package/types/lib/core/2.app/config/mode.contract.d.ts +6 -0
  15. package/types/lib/core/2.app/driver-ports/init/drivers.contract.d.ts +4 -0
  16. package/types/lib/core/2.app/driver-ports/init/for-pooling-dynamic-externals.port.d.ts +8 -0
  17. package/types/lib/core/2.app/driver-ports/init/for-pooling-shared-externals.port.d.ts +1 -0
  18. package/types/lib/core/2.app/driver-ports/init/index.d.ts +2 -0
  19. package/types/lib/core/2.app/driving-ports/for-shared-externals-storage.port.d.ts +2 -0
  20. package/types/lib/core/2.app/steps/convert-to-import-map.d.ts +2 -1
  21. package/types/lib/core/2.app/steps/pooling/pool-dynamic-externals.d.ts +3 -0
  22. package/types/lib/core/2.app/steps/pooling/pool-graph.d.ts +25 -0
  23. package/types/lib/core/2.app/steps/pooling/pool-shared-externals.d.ts +5 -0
  24. package/types/lib/core/2.app/steps/pooling/pool.types.d.ts +25 -0
  25. package/types/lib/core/2.app/steps/pooling/pool.util.d.ts +4 -0
  26. package/types/lib/core/2.app/steps/store-remote-entry.d.ts +2 -2
  27. package/types/lib/core/3.adapters/http/remote-entry-provider.d.ts +2 -1
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
 
@@ -2037,6 +2048,7 @@ function createStoreRemoteEntry(config, ports, logStep) {
2037
2048
  const tag = resolveVersion(remoteEntry, external);
2038
2049
  if (tag === null) return;
2039
2050
  if (external.singleton) {
2051
+ if (external.pool?.trim()) ports.sharedExternalsRepo.markPoolTagPresent();
2040
2052
  onSharedExternal(remoteEntry, external, sharedExternalContext(remoteEntry, external, tag));
2041
2053
  } else {
2042
2054
  addScopedExternal(remoteEntry, external, tag);
@@ -2056,8 +2068,8 @@ function createStoreRemoteEntry(config, ports, logStep) {
2056
2068
  function addScopedExternal(remoteEntry, sharedInfo, tag) {
2057
2069
  ports.scopedExternalsRepo.addExternal(remoteEntry.name, sharedInfo.packageName, {
2058
2070
  tag,
2059
- file: sharedInfo.outFileName,
2060
- bundle: sharedInfo.bundle
2071
+ bundle: sharedInfo.bundle,
2072
+ entries: sharedInfo.entries
2061
2073
  });
2062
2074
  }
2063
2075
  function resolveVersion(remoteEntry, external) {
@@ -2079,12 +2091,13 @@ function createStoreRemoteEntry(config, ports, logStep) {
2079
2091
  function sharedExternalContext(remoteEntry, sharedInfo, tag) {
2080
2092
  const scopeType = ports.sharedExternalsRepo.scopeType(sharedInfo.shareScope);
2081
2093
  const remote = {
2082
- file: sharedInfo.outFileName,
2083
2094
  name: remoteEntry.name,
2084
2095
  bundle: sharedInfo.bundle,
2085
2096
  strictVersion: sharedInfo.strictVersion,
2086
2097
  cached: false,
2087
- 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
2088
2101
  };
2089
2102
  const cached2 = ports.sharedExternalsRepo.tryGet(sharedInfo.packageName, sharedInfo.shareScope).orElse({ dirty: false, versions: [] });
2090
2103
  return {
@@ -2232,7 +2245,290 @@ function createDetermineSharedExternals(config, ports) {
2232
2245
  }
2233
2246
  }
2234
2247
 
2235
- // 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
2236
2532
  var CHUNK_PREFIX = "@nf-internal";
2237
2533
  function toChunkImport(fileName) {
2238
2534
  if (fileName.startsWith("./")) {
@@ -2267,12 +2563,9 @@ function createGenerateImportMap(config, ports) {
2267
2563
  });
2268
2564
  addToScope(importMap, remote.scopeUrl, createScopeModules(externals, remote.scopeUrl));
2269
2565
  for (const version of Object.values(externals)) {
2270
- addIntegrity(
2271
- importMap,
2272
- join(remote.scopeUrl, version.file),
2273
- remoteName,
2274
- version.file
2275
- );
2566
+ for (const file of Object.values(version.entries)) {
2567
+ addIntegrity(importMap, join(remote.scopeUrl, file), remoteName, file);
2568
+ }
2276
2569
  }
2277
2570
  Object.values(externals).filter((e) => !!e.bundle).forEach((e) => registerBundleChunks(chunkBundles, remoteName, e.bundle));
2278
2571
  }
@@ -2280,8 +2573,10 @@ function createGenerateImportMap(config, ports) {
2280
2573
  }
2281
2574
  function createScopeModules(externals, scope) {
2282
2575
  const modules = {};
2283
- for (const [external, version] of Object.entries(externals)) {
2284
- 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
+ }
2285
2580
  }
2286
2581
  return modules;
2287
2582
  }
@@ -2301,47 +2596,49 @@ function createGenerateImportMap(config, ports) {
2301
2596
  if (version.action === "scope") {
2302
2597
  for (const remote of version.remotes) {
2303
2598
  const remoteScope = getScope2(shareScope, remote.name, externalName);
2304
- const url = join(remoteScope, remote.file);
2305
- addToScope(importMap, remoteScope, {
2306
- [externalName]: url
2307
- });
2308
- 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
+ }
2309
2604
  registerBundleChunks(chunkBundles, remote.name, remote.bundle);
2310
2605
  remote.cached = true;
2311
2606
  }
2312
2607
  continue;
2313
2608
  }
2314
- const scope = getScope2(shareScope, version.remotes[0].name, externalName);
2315
- let targetFileUrl = join(scope, version.remotes[0].file);
2316
- let targetFileSource = {
2317
- name: version.remotes[0].name,
2318
- file: version.remotes[0].file
2319
- };
2320
2609
  version.remotes[0].cached = true;
2610
+ let source = version.remotes[0];
2611
+ let sourceScope = getScope2(shareScope, source.name, externalName);
2321
2612
  if (version.action === "share") {
2322
- registerBundleChunks(chunkBundles, version.remotes[0].name, version.remotes[0].bundle);
2323
- }
2324
- if (version.action === "skip") {
2613
+ registerBundleChunks(chunkBundles, source.name, source.bundle);
2614
+ } else if (version.action === "skip") {
2325
2615
  if (!override) {
2326
2616
  override = findOverride(external, shareScope, externalName) ?? "NOT_AVAILABLE";
2327
2617
  }
2328
2618
  if (override !== "NOT_AVAILABLE") {
2329
2619
  if (!overrideScope)
2330
2620
  overrideScope = getScope2(shareScope, override.remotes[0].name, externalName);
2331
- targetFileUrl = join(overrideScope, override.remotes[0].file);
2332
- targetFileSource = {
2333
- name: override.remotes[0].name,
2334
- file: override.remotes[0].file
2335
- };
2621
+ source = override.remotes[0];
2622
+ sourceScope = overrideScope;
2336
2623
  override.remotes[0].cached = true;
2337
2624
  version.remotes[0].cached = false;
2338
2625
  }
2339
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
+ }));
2340
2633
  version.remotes.forEach((r) => {
2341
- const scope2 = getScope2(shareScope, r.name, externalName);
2342
- 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
+ }
2343
2638
  });
2344
- addIntegrity(importMap, targetFileUrl, targetFileSource.name, targetFileSource.file);
2639
+ for (const m of mappings) {
2640
+ addIntegrity(importMap, m.url, m.name, m.file);
2641
+ }
2345
2642
  }
2346
2643
  ports.sharedExternalsRepo.addOrUpdate(externalName, external, shareScope);
2347
2644
  }
@@ -2382,11 +2679,11 @@ function createGenerateImportMap(config, ports) {
2382
2679
  if (version.action === "scope") {
2383
2680
  for (const remote of version.remotes) {
2384
2681
  const remoteScope = getScope2(GLOBAL_SCOPE, remote.name, externalName);
2385
- const url2 = join(remoteScope, remote.file);
2386
- addToScope(importMap, remoteScope, {
2387
- [externalName]: url2
2388
- });
2389
- 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
+ }
2390
2687
  remote.cached = true;
2391
2688
  registerBundleChunks(chunkBundles, remote.name, remote.bundle);
2392
2689
  }
@@ -2397,9 +2694,11 @@ function createGenerateImportMap(config, ports) {
2397
2694
  continue;
2398
2695
  }
2399
2696
  const scope = getScope2(GLOBAL_SCOPE, version.remotes[0].name, externalName);
2400
- const url = join(scope, version.remotes[0].file);
2401
- addToGlobal(importMap, { [externalName]: url });
2402
- 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
+ }
2403
2702
  registerBundleChunks(chunkBundles, version.remotes[0].name, version.remotes[0].bundle);
2404
2703
  version.remotes[0].cached = true;
2405
2704
  }
@@ -2596,8 +2895,8 @@ function createUpdateCache(config, ports) {
2596
2895
  storeRemoteEntry(remoteEntry, (entry, external, ctx) => {
2597
2896
  const { action, sharedVersion } = resolveSharedExternal(entry, external, ctx);
2598
2897
  actions[external.packageName] = { action };
2599
- if (action === "skip" && external.shareScope && sharedVersion?.remotes[0]?.file) {
2600
- actions[external.packageName].override = resolveOverrideUrl(
2898
+ if (action === "skip" && external.shareScope && sharedVersion?.remotes[0]?.entries) {
2899
+ actions[external.packageName].override = resolveOverrideEntries(
2601
2900
  entry,
2602
2901
  external,
2603
2902
  sharedVersion
@@ -2641,8 +2940,15 @@ function createUpdateCache(config, ports) {
2641
2940
  commit();
2642
2941
  return { action, sharedVersion };
2643
2942
  }
2644
- function resolveOverrideUrl(remoteEntry, external, sharedVersion) {
2645
- 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(() => {
2646
2952
  config.log.error(
2647
2953
  8,
2648
2954
  `[${external.shareScope ?? GLOBAL_SCOPE}][${remoteEntry.name}][${external.packageName}@${external.version}][override] Remote name not found in cache.`
@@ -2654,14 +2960,56 @@ function createUpdateCache(config, ports) {
2654
2960
  }
2655
2961
  }
2656
2962
 
2657
- // src/lib/core/2.app/steps/convert-to-import-map.ts
2658
- function createConvertToImportMap({ log }, ports) {
2963
+ // src/lib/core/2.app/steps/pooling/pool-dynamic-externals.ts
2964
+ function createPoolDynamicExternals(config) {
2659
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 }) => {
2660
3008
  const importMap = { imports: {} };
2661
3009
  addExternals(entry, actions, importMap);
2662
3010
  addRemoteInfos(entry, importMap);
2663
3011
  log.debug(9, `[${entry.name}] Processed actions:`, actions);
2664
- return Promise.resolve(importMap);
3012
+ return importMap;
2665
3013
  };
2666
3014
  function addExternals(remoteEntry, actions, importMap) {
2667
3015
  if (!remoteEntry.shared) {
@@ -2672,9 +3020,11 @@ function createConvertToImportMap({ log }, ports) {
2672
3020
  const chunkBundles = /* @__PURE__ */ new Set(["mapping-or-exposed"]);
2673
3021
  remoteEntry.shared.forEach((external) => {
2674
3022
  if (!external.singleton) {
2675
- const url2 = join(remoteEntryScope, external.outFileName);
2676
- addToScopes(remoteEntryScope, external.packageName, url2, importMap);
2677
- 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
+ });
2678
3028
  if (external?.bundle) chunkBundles.add(external?.bundle);
2679
3029
  return;
2680
3030
  }
@@ -2686,33 +3036,48 @@ function createConvertToImportMap({ log }, ports) {
2686
3036
  return;
2687
3037
  }
2688
3038
  if (actions[external.packageName].action === "skip") {
3039
+ const override = actions[external.packageName].override;
2689
3040
  if (!external.shareScope) return;
2690
- if (actions[external.packageName].override) {
2691
- addToScopes(
2692
- remoteEntryScope,
2693
- external.packageName,
2694
- actions[external.packageName].override,
2695
- importMap
2696
- );
3041
+ if (override) {
3042
+ Object.entries(override).forEach(([packageName, url]) => {
3043
+ addToScopes(remoteEntryScope, packageName, url, importMap);
3044
+ });
2697
3045
  return;
2698
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;
2699
3058
  }
2700
3059
  if (external?.bundle) chunkBundles.add(external?.bundle);
2701
3060
  if (actions[external.packageName].action === "scope") {
2702
- const url2 = join(remoteEntryScope, external.outFileName);
2703
- addToScopes(remoteEntryScope, external.packageName, url2, importMap);
2704
- 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
+ });
2705
3066
  return;
2706
3067
  }
2707
3068
  if (external.shareScope) {
2708
- const url2 = join(remoteEntryScope, external.outFileName);
2709
- addToScopes(remoteEntryScope, external.packageName, url2, importMap);
2710
- 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
+ });
2711
3074
  return;
2712
3075
  }
2713
- const url = join(remoteEntryScope, external.outFileName);
2714
- importMap.imports[external.packageName] = url;
2715
- 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
+ });
2716
3081
  });
2717
3082
  addChunkImports(importMap, remoteEntry, remoteEntryScope, chunkBundles);
2718
3083
  }
@@ -2759,11 +3124,13 @@ var createInitDrivers = ({
2759
3124
  getRemoteEntries: createGetRemoteEntries(config, adapters),
2760
3125
  processRemoteEntries: createProcessRemoteEntries(config, adapters),
2761
3126
  determineSharedExternals: createDetermineSharedExternals(config, adapters),
3127
+ poolSharedExternals: createPoolSharedExternals(config, adapters),
2762
3128
  generateImportMap: createGenerateImportMap(config, adapters),
2763
3129
  commitChanges: createCommitChanges(config, adapters),
2764
3130
  exposeModuleLoader: createExposeModuleLoader(config, adapters),
2765
3131
  getRemoteEntry: createGetRemoteEntry(config, adapters),
2766
3132
  updateCache: createUpdateCache(config, adapters),
3133
+ poolDynamicExternals: createPoolDynamicExternals(config),
2767
3134
  convertToImportMap: createConvertToImportMap(config, adapters)
2768
3135
  });
2769
3136
  var INIT_FLOW_FACTORY = ({