@softarc/native-federation-orchestrator 4.5.3 → 4.6.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.
- package/README.md +2 -2
- package/fesm2022/@softarc/native-federation-orchestrator.mjs +1087 -250
- package/fesm2022/@softarc/native-federation-orchestrator.mjs.map +4 -4
- package/fesm2022/module-federation.mjs +34 -20
- package/fesm2022/module-federation.mjs.map +3 -3
- package/fesm2022/node.mjs +1084 -247
- package/fesm2022/node.mjs.map +4 -4
- package/package.json +1 -1
- package/quickstart.mjs +15 -15
- package/types/lib/core/1.domain/externals/basis.d.ts +17 -2
- package/types/lib/core/1.domain/externals/version.contract.d.ts +1 -0
- package/types/lib/core/1.domain/remote-entry/remote-entry.contract.d.ts +1 -0
- package/types/lib/core/2.app/driver-ports/init/drivers.contract.d.ts +2 -0
- package/types/lib/core/2.app/driver-ports/init/for-determining-shared-externals.port.d.ts +8 -1
- package/types/lib/core/2.app/driver-ports/init/for-marking-pools-for-reelection.port.d.ts +6 -0
- package/types/lib/core/2.app/driver-ports/init/for-pooling-shared-externals.port.d.ts +3 -1
- package/types/lib/core/2.app/driving-ports/for-shared-chunks-storage.port.d.ts +1 -0
- package/types/lib/core/2.app/driving-ports/for-shared-externals-storage.port.d.ts +1 -2
- package/types/lib/core/2.app/steps/apply-winner.d.ts +18 -0
- package/types/lib/core/2.app/steps/pooling/anchoring.d.ts +69 -0
- package/types/lib/core/2.app/steps/pooling/mark-pools-for-reelection.d.ts +5 -0
- package/types/lib/core/2.app/steps/pooling/pool-dynamic-externals.d.ts +3 -1
- package/types/lib/core/2.app/steps/pooling/pool-graph.d.ts +10 -5
- package/types/lib/core/2.app/steps/pooling/pool-shared-externals.d.ts +30 -1
- package/types/lib/core/2.app/steps/pooling/pool-views.d.ts +63 -0
- package/types/lib/core/2.app/steps/pooling/pool.types.d.ts +30 -1
- package/types/lib/core/2.app/steps/pooling/pool.util.d.ts +15 -0
- package/types/lib/core/2.app/steps/store-remote-entry.d.ts +1 -1
- package/types/lib/testing/pooling/no-tear.d.ts +37 -0
package/fesm2022/node.mjs
CHANGED
|
@@ -1574,14 +1574,18 @@ var createSharedExternalsRepository = (config) => {
|
|
|
1574
1574
|
);
|
|
1575
1575
|
if (config.clearStorage) STORAGE.clear();
|
|
1576
1576
|
const _cache = STORAGE.get() ?? { [GLOBAL_SCOPE]: {} };
|
|
1577
|
-
let _sawPoolTag = false;
|
|
1578
1577
|
let _dirty = false;
|
|
1579
1578
|
return {
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
hasPoolTag: function() {
|
|
1584
|
-
|
|
1579
|
+
// Read from the cache rather than remembered from this init's entries: a warm init may not refetch
|
|
1580
|
+
// the tagged remote at all, and pooling has to coordinate its pool anyway. Exits on the first hit.
|
|
1581
|
+
// Per share scope, because a pool never spans one: a tag in another scope is no reason to pool here.
|
|
1582
|
+
hasPoolTag: function(shareScope) {
|
|
1583
|
+
const scope = _cache[shareScope ?? GLOBAL_SCOPE];
|
|
1584
|
+
if (!scope) return false;
|
|
1585
|
+
for (const external of Object.values(scope))
|
|
1586
|
+
for (const version of external.versions)
|
|
1587
|
+
for (const remote of version.remotes) if (remote.pool?.trim()) return true;
|
|
1588
|
+
return false;
|
|
1585
1589
|
},
|
|
1586
1590
|
getFromScope: function(shareScope) {
|
|
1587
1591
|
return { ..._cache[shareScope ?? GLOBAL_SCOPE] };
|
|
@@ -1602,9 +1606,11 @@ var createSharedExternalsRepository = (config) => {
|
|
|
1602
1606
|
Object.values(_cache).forEach((scope) => {
|
|
1603
1607
|
const removeExternals = [];
|
|
1604
1608
|
Object.entries(scope).forEach(([name, external]) => {
|
|
1605
|
-
let
|
|
1609
|
+
let removedCopy = false;
|
|
1606
1610
|
for (let i = external.versions.length - 1; i >= 0; i--) {
|
|
1607
|
-
const
|
|
1611
|
+
const version = external.versions[i];
|
|
1612
|
+
const remotes = version.remotes;
|
|
1613
|
+
const host = version.host ? remotes[0]?.name : void 0;
|
|
1608
1614
|
let keep = 0;
|
|
1609
1615
|
for (let r = 0; r < remotes.length; r++) {
|
|
1610
1616
|
if (remoteNames.has(remotes[r].name)) continue;
|
|
@@ -1612,15 +1618,16 @@ var createSharedExternalsRepository = (config) => {
|
|
|
1612
1618
|
}
|
|
1613
1619
|
if (keep !== remotes.length) {
|
|
1614
1620
|
remotes.length = keep;
|
|
1621
|
+
removedCopy = true;
|
|
1615
1622
|
_dirty = true;
|
|
1623
|
+
if (host !== void 0 && remoteNames.has(host)) version.host = false;
|
|
1616
1624
|
}
|
|
1617
1625
|
if (remotes.length === 0) {
|
|
1618
1626
|
external.versions.splice(i, 1);
|
|
1619
|
-
removedVersion = true;
|
|
1620
1627
|
_dirty = true;
|
|
1621
1628
|
}
|
|
1622
1629
|
}
|
|
1623
|
-
if (
|
|
1630
|
+
if (removedCopy) {
|
|
1624
1631
|
external.dirty = true;
|
|
1625
1632
|
if (external.versions.length === 0) removeExternals.push(name);
|
|
1626
1633
|
}
|
|
@@ -1665,6 +1672,14 @@ var createChunkRepository = (config) => {
|
|
|
1665
1672
|
_dirty = true;
|
|
1666
1673
|
return this;
|
|
1667
1674
|
},
|
|
1675
|
+
// Whole-remote: a build that stops chunking a bundle omits the key, so `addOrReplace` cannot clear it.
|
|
1676
|
+
remove: function(remoteName) {
|
|
1677
|
+
if (remoteName in _cache) {
|
|
1678
|
+
delete _cache[remoteName];
|
|
1679
|
+
_dirty = true;
|
|
1680
|
+
}
|
|
1681
|
+
return this;
|
|
1682
|
+
},
|
|
1668
1683
|
tryGet: function(remoteName, bundleName) {
|
|
1669
1684
|
return Optional.of(_cache[remoteName]?.[bundleName]);
|
|
1670
1685
|
},
|
|
@@ -1934,7 +1949,7 @@ var createInitFlow = ({
|
|
|
1934
1949
|
adapters,
|
|
1935
1950
|
config
|
|
1936
1951
|
}) => {
|
|
1937
|
-
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) => ({
|
|
1952
|
+
return (remotesOrManifestUrl) => flow.getRemoteEntries(remotesOrManifestUrl).then(flow.processRemoteEntries).then(flow.markPoolsForReelection).then(flow.determineSharedExternals).then(flow.poolSharedExternals).then(flow.generateImportMap).then((importMap) => flow.commitChanges(importMap, { override: true })).then(flow.exposeModuleLoader).then((loadRemoteModule) => ({
|
|
1938
1953
|
config,
|
|
1939
1954
|
adapters,
|
|
1940
1955
|
loadRemoteModule
|
|
@@ -2090,13 +2105,33 @@ function findVersionForTag(versions, tag) {
|
|
|
2090
2105
|
}
|
|
2091
2106
|
return scoped;
|
|
2092
2107
|
}
|
|
2093
|
-
function
|
|
2094
|
-
return
|
|
2108
|
+
function versionEntries(version) {
|
|
2109
|
+
return collectEntries(version, void 0);
|
|
2110
|
+
}
|
|
2111
|
+
function committedEntries(version) {
|
|
2112
|
+
return collectEntries(version, (remote) => remote.cached);
|
|
2113
|
+
}
|
|
2114
|
+
function forEachVersionEntry(version, accepts, visit) {
|
|
2115
|
+
for (const remote of version.remotes) {
|
|
2116
|
+
if (remote.servedBy) continue;
|
|
2117
|
+
if (accepts && !accepts(remote)) continue;
|
|
2118
|
+
for (const entrypoint in remote.entries) visit(entrypoint, remote);
|
|
2119
|
+
}
|
|
2120
|
+
}
|
|
2121
|
+
function collectEntries(version, accepts) {
|
|
2122
|
+
const entries = /* @__PURE__ */ new Map();
|
|
2123
|
+
forEachVersionEntry(version, accepts, (entrypoint, remote) => {
|
|
2124
|
+
if (!entries.has(entrypoint)) entries.set(entrypoint, remote);
|
|
2125
|
+
});
|
|
2126
|
+
return entries;
|
|
2127
|
+
}
|
|
2128
|
+
function uncoveredEntrypoints(remote, covered) {
|
|
2129
|
+
return Object.keys(remote.entries).filter((entrypoint) => !covered.has(entrypoint));
|
|
2095
2130
|
}
|
|
2096
|
-
function countUncoveredEntrypoints(remote,
|
|
2131
|
+
function countUncoveredEntrypoints(remote, covered) {
|
|
2097
2132
|
let uncovered = 0;
|
|
2098
2133
|
for (const entrypoint in remote.entries) {
|
|
2099
|
-
if (!(entrypoint
|
|
2134
|
+
if (!covered.has(entrypoint)) uncovered++;
|
|
2100
2135
|
}
|
|
2101
2136
|
return uncovered;
|
|
2102
2137
|
}
|
|
@@ -2108,6 +2143,7 @@ function createRemoveCachedRemoteEntries(ports) {
|
|
|
2108
2143
|
for (const remoteName of remoteNames) {
|
|
2109
2144
|
ports.remoteInfoRepo.remove(remoteName);
|
|
2110
2145
|
ports.scopedExternalsRepo.remove(remoteName);
|
|
2146
|
+
ports.sharedChunksRepo.remove(remoteName);
|
|
2111
2147
|
}
|
|
2112
2148
|
ports.sharedExternalsRepo.removeFromAllScopes(remoteNames);
|
|
2113
2149
|
};
|
|
@@ -2133,7 +2169,6 @@ function createStoreRemoteEntry(config, ports, logStep) {
|
|
|
2133
2169
|
const tag = resolveVersion(remoteEntry, external);
|
|
2134
2170
|
if (tag === null) return;
|
|
2135
2171
|
if (external.singleton) {
|
|
2136
|
-
if (external.pool?.trim()) ports.sharedExternalsRepo.markPoolTagPresent();
|
|
2137
2172
|
onSharedExternal(remoteEntry, external, sharedExternalContext(remoteEntry, external, tag));
|
|
2138
2173
|
} else {
|
|
2139
2174
|
addScopedExternal(remoteEntry, external, tag);
|
|
@@ -2271,8 +2306,129 @@ function createProcessRemoteEntries(config, ports) {
|
|
|
2271
2306
|
}
|
|
2272
2307
|
}
|
|
2273
2308
|
|
|
2309
|
+
// src/lib/core/2.app/steps/apply-winner.ts
|
|
2310
|
+
function versionAcceptance(external, isCompatible) {
|
|
2311
|
+
const demands = new Map(
|
|
2312
|
+
external.versions.map((v) => [v, versionDemands(v)])
|
|
2313
|
+
);
|
|
2314
|
+
return {
|
|
2315
|
+
accepts: (version, tag) => demands.get(version).every((d) => isCompatible(tag, d.requiredVersion)),
|
|
2316
|
+
objector: (version, tag) => demands.get(version).find((d) => d.strictVersion && !isCompatible(tag, d.requiredVersion))
|
|
2317
|
+
};
|
|
2318
|
+
}
|
|
2319
|
+
function createApplyWinner(config) {
|
|
2320
|
+
return function applyWinner(externalName, external, winner, isCompatible, acceptance) {
|
|
2321
|
+
if (external.versions.length > 1) {
|
|
2322
|
+
const { accepts, objector } = acceptance ?? versionAcceptance(external, isCompatible);
|
|
2323
|
+
const rebuilt = [];
|
|
2324
|
+
for (const v of external.versions) {
|
|
2325
|
+
rebuilt.push(v);
|
|
2326
|
+
if (accepts(v, winner.tag)) {
|
|
2327
|
+
v.action = "skip";
|
|
2328
|
+
continue;
|
|
2329
|
+
}
|
|
2330
|
+
const strict = objector(v, winner.tag);
|
|
2331
|
+
if (config.strict.strictExternalCompatibility && strict) {
|
|
2332
|
+
config.log.error(
|
|
2333
|
+
3,
|
|
2334
|
+
`[${strict.name}][${externalName}@${v.tag}] Is not compatible with requiredRange '${strict.requiredVersion}' of shared ${externalName}@${winner.tag}.`
|
|
2335
|
+
);
|
|
2336
|
+
throw new NFError(`External ${externalName}@${v.tag} could not be shared.`);
|
|
2337
|
+
}
|
|
2338
|
+
if (v === winner) continue;
|
|
2339
|
+
if (!strict) {
|
|
2340
|
+
v.action = "skip";
|
|
2341
|
+
continue;
|
|
2342
|
+
}
|
|
2343
|
+
const objecting = new Set(
|
|
2344
|
+
v.remotes.filter((r) => r.strictVersion && !isCompatible(winner.tag, r.requiredVersion))
|
|
2345
|
+
);
|
|
2346
|
+
if (objecting.size === v.remotes.length) {
|
|
2347
|
+
v.action = "scope";
|
|
2348
|
+
continue;
|
|
2349
|
+
}
|
|
2350
|
+
v.remotes = v.remotes.filter((r) => !objecting.has(r));
|
|
2351
|
+
v.action = "skip";
|
|
2352
|
+
rebuilt.push({ tag: v.tag, host: false, action: "scope", remotes: [...objecting] });
|
|
2353
|
+
}
|
|
2354
|
+
const merged = /* @__PURE__ */ new Map();
|
|
2355
|
+
external.versions = rebuilt.filter((v) => {
|
|
2356
|
+
const first = merged.get(`${v.tag}|${v.action}`);
|
|
2357
|
+
if (!first) {
|
|
2358
|
+
merged.set(`${v.tag}|${v.action}`, v);
|
|
2359
|
+
return true;
|
|
2360
|
+
}
|
|
2361
|
+
first.remotes.push(...v.remotes);
|
|
2362
|
+
return false;
|
|
2363
|
+
});
|
|
2364
|
+
}
|
|
2365
|
+
winner.action = "share";
|
|
2366
|
+
applyEntrypointCoveragePolicy(externalName, external);
|
|
2367
|
+
external.dirty = false;
|
|
2368
|
+
return external;
|
|
2369
|
+
};
|
|
2370
|
+
function applyEntrypointCoveragePolicy(externalName, external) {
|
|
2371
|
+
const { strictEntryPointCoverage } = config.strict;
|
|
2372
|
+
if (!strictEntryPointCoverage && !config.profile.scopeUncoveredEntrypoints) return;
|
|
2373
|
+
const tears = findTears(external);
|
|
2374
|
+
if (tears.length === 0) return;
|
|
2375
|
+
if (strictEntryPointCoverage) {
|
|
2376
|
+
const { version, remote, uncovered } = tears[0];
|
|
2377
|
+
config.log.error(
|
|
2378
|
+
3,
|
|
2379
|
+
`[${externalName}@${version.tag}][${remote.name}] Entrypoints not covered by the shared version: ${uncovered.join(", ")}.`
|
|
2380
|
+
);
|
|
2381
|
+
throw new NFError(
|
|
2382
|
+
`External ${externalName} could not be shared without tearing entrypoints.`
|
|
2383
|
+
);
|
|
2384
|
+
}
|
|
2385
|
+
scopeTornRemotes(externalName, external, tears);
|
|
2386
|
+
}
|
|
2387
|
+
function findTears(external) {
|
|
2388
|
+
const shared = external.versions.find((v) => v.action === "share");
|
|
2389
|
+
if (!shared) return [];
|
|
2390
|
+
const basis = versionEntries(shared);
|
|
2391
|
+
const tears = [];
|
|
2392
|
+
for (const version of external.versions) {
|
|
2393
|
+
if (version.action === "scope") continue;
|
|
2394
|
+
if (version === shared) continue;
|
|
2395
|
+
version.remotes.forEach((remote) => {
|
|
2396
|
+
if (remote.servedBy) return;
|
|
2397
|
+
const uncovered = uncoveredEntrypoints(remote, basis);
|
|
2398
|
+
if (uncovered.length > 0) tears.push({ version, remote, uncovered });
|
|
2399
|
+
});
|
|
2400
|
+
}
|
|
2401
|
+
return tears;
|
|
2402
|
+
}
|
|
2403
|
+
function scopeTornRemotes(externalName, external, tears) {
|
|
2404
|
+
const torn = new Set(tears.map((t) => t.remote));
|
|
2405
|
+
const demotedByTag = /* @__PURE__ */ new Map();
|
|
2406
|
+
for (const { version, remote, uncovered } of tears) {
|
|
2407
|
+
const group = demotedByTag.get(version.tag);
|
|
2408
|
+
if (group) group.push(remote);
|
|
2409
|
+
else demotedByTag.set(version.tag, [remote]);
|
|
2410
|
+
config.log.debug(
|
|
2411
|
+
3,
|
|
2412
|
+
`[${externalName}@${version.tag}][${remote.name}] Scoped: entrypoints not covered by the shared version: ${uncovered.join(", ")}.`
|
|
2413
|
+
);
|
|
2414
|
+
}
|
|
2415
|
+
for (const version of external.versions) {
|
|
2416
|
+
if (version.remotes.some((r) => torn.has(r))) {
|
|
2417
|
+
version.remotes = version.remotes.filter((r) => !torn.has(r));
|
|
2418
|
+
}
|
|
2419
|
+
}
|
|
2420
|
+
external.versions = external.versions.filter((v) => v.remotes.length > 0);
|
|
2421
|
+
for (const [tag, remotes] of demotedByTag) {
|
|
2422
|
+
const scoped = external.versions.find((v) => v.tag === tag && v.action === "scope");
|
|
2423
|
+
if (scoped) scoped.remotes.push(...remotes);
|
|
2424
|
+
else external.versions.push({ tag, host: false, action: "scope", remotes });
|
|
2425
|
+
}
|
|
2426
|
+
}
|
|
2427
|
+
}
|
|
2428
|
+
|
|
2274
2429
|
// src/lib/core/2.app/steps/determine-shared-externals.ts
|
|
2275
2430
|
function createDetermineSharedExternals(config, ports) {
|
|
2431
|
+
const applyWinner = createApplyWinner(config);
|
|
2276
2432
|
return () => {
|
|
2277
2433
|
const memo = /* @__PURE__ */ new Map();
|
|
2278
2434
|
const isCompatible = (tag, requiredVersion) => {
|
|
@@ -2284,16 +2440,20 @@ function createDetermineSharedExternals(config, ports) {
|
|
|
2284
2440
|
}
|
|
2285
2441
|
return hit;
|
|
2286
2442
|
};
|
|
2443
|
+
const touched = /* @__PURE__ */ new Map();
|
|
2287
2444
|
for (const shareScope of ports.sharedExternalsRepo.getScopes()) {
|
|
2288
2445
|
const sharedExternals = ports.sharedExternalsRepo.getFromScope(shareScope);
|
|
2289
2446
|
try {
|
|
2290
|
-
|
|
2291
|
-
|
|
2447
|
+
const elected = /* @__PURE__ */ new Set();
|
|
2448
|
+
Object.entries(sharedExternals).filter(([_, e]) => e.dirty).forEach(([name, external]) => {
|
|
2449
|
+
ports.sharedExternalsRepo.addOrUpdate(
|
|
2292
2450
|
name,
|
|
2293
2451
|
setVersionActions(name, external, isCompatible),
|
|
2294
2452
|
shareScope
|
|
2295
|
-
)
|
|
2296
|
-
|
|
2453
|
+
);
|
|
2454
|
+
elected.add(name);
|
|
2455
|
+
});
|
|
2456
|
+
if (elected.size > 0) touched.set(shareScope, elected);
|
|
2297
2457
|
} catch (error) {
|
|
2298
2458
|
config.log.error(
|
|
2299
2459
|
3,
|
|
@@ -2311,28 +2471,25 @@ function createDetermineSharedExternals(config, ports) {
|
|
|
2311
2471
|
);
|
|
2312
2472
|
}
|
|
2313
2473
|
}
|
|
2314
|
-
return Promise.resolve();
|
|
2474
|
+
return Promise.resolve(touched);
|
|
2315
2475
|
};
|
|
2316
2476
|
function uncoveredTears(external, winner, accepts) {
|
|
2317
|
-
const basis = winner
|
|
2477
|
+
const basis = versionEntries(winner);
|
|
2318
2478
|
return external.versions.reduce((sum, v) => {
|
|
2319
2479
|
if (v === winner) return sum;
|
|
2320
2480
|
if (!accepts(v, winner.tag)) return sum;
|
|
2321
|
-
return
|
|
2481
|
+
return v.remotes.reduce(
|
|
2482
|
+
(n, r) => r.servedBy ? n : n + countUncoveredEntrypoints(r, basis),
|
|
2483
|
+
sum
|
|
2484
|
+
);
|
|
2322
2485
|
}, 0);
|
|
2323
2486
|
}
|
|
2324
2487
|
function setVersionActions(externalName, external, isCompatible) {
|
|
2325
2488
|
if (external.versions.length === 1) {
|
|
2326
|
-
external.versions[0]
|
|
2327
|
-
applyEntrypointCoveragePolicy(externalName, external);
|
|
2328
|
-
external.dirty = false;
|
|
2329
|
-
return external;
|
|
2489
|
+
return applyWinner(externalName, external, external.versions[0], isCompatible);
|
|
2330
2490
|
}
|
|
2331
|
-
const
|
|
2332
|
-
|
|
2333
|
-
);
|
|
2334
|
-
const accepts = (version, tag) => demands.get(version).every((d) => isCompatible(tag, d.requiredVersion));
|
|
2335
|
-
const objector = (version, tag) => demands.get(version).find((d) => d.strictVersion && !isCompatible(tag, d.requiredVersion));
|
|
2491
|
+
const acceptance = versionAcceptance(external, isCompatible);
|
|
2492
|
+
const { accepts } = acceptance;
|
|
2336
2493
|
let sharedVersion = external.versions.find((v) => v.host);
|
|
2337
2494
|
if (!sharedVersion && config.profile.latestSharedExternal) {
|
|
2338
2495
|
sharedVersion = external.versions[0];
|
|
@@ -2340,10 +2497,30 @@ function createDetermineSharedExternals(config, ports) {
|
|
|
2340
2497
|
if (!sharedVersion) {
|
|
2341
2498
|
let leastExtraDownloads = Number.MAX_VALUE;
|
|
2342
2499
|
let leastTears = Number.MAX_VALUE;
|
|
2500
|
+
const selfServing = new Map(
|
|
2501
|
+
external.versions.map((v) => {
|
|
2502
|
+
const groups = /* @__PURE__ */ new Map();
|
|
2503
|
+
for (const remote of v.remotes) {
|
|
2504
|
+
if (remote.cached || !remote.strictVersion) continue;
|
|
2505
|
+
const group = groups.get(remote.requiredVersion);
|
|
2506
|
+
if (group) group.copies++;
|
|
2507
|
+
else
|
|
2508
|
+
groups.set(remote.requiredVersion, {
|
|
2509
|
+
requiredVersion: remote.requiredVersion,
|
|
2510
|
+
copies: 1
|
|
2511
|
+
});
|
|
2512
|
+
}
|
|
2513
|
+
return [v, [...groups.values()]];
|
|
2514
|
+
})
|
|
2515
|
+
);
|
|
2516
|
+
const costOf = (version, tag) => selfServing.get(version).reduce((n, g) => isCompatible(tag, g.requiredVersion) ? n : n + g.copies, 0);
|
|
2343
2517
|
external.versions.forEach((vA) => {
|
|
2344
|
-
const extraDownloads = external.versions.
|
|
2345
|
-
|
|
2346
|
-
|
|
2518
|
+
const extraDownloads = external.versions.reduce(
|
|
2519
|
+
// A copy of the winner is never redirected, so it never self-serves however its own range
|
|
2520
|
+
// reads — see `applyWinner`, which does not split the winner either.
|
|
2521
|
+
(sum, vB) => vB === vA ? sum : sum + costOf(vB, vA.tag),
|
|
2522
|
+
0
|
|
2523
|
+
);
|
|
2347
2524
|
if (extraDownloads < leastExtraDownloads) {
|
|
2348
2525
|
leastExtraDownloads = extraDownloads;
|
|
2349
2526
|
leastTears = uncoveredTears(external, vA, accepts);
|
|
@@ -2361,84 +2538,304 @@ function createDetermineSharedExternals(config, ports) {
|
|
|
2361
2538
|
if (!sharedVersion) {
|
|
2362
2539
|
throw new NFError(`[${externalName}] Could not determine shared version!`);
|
|
2363
2540
|
}
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2541
|
+
return applyWinner(externalName, external, sharedVersion, isCompatible, acceptance);
|
|
2542
|
+
}
|
|
2543
|
+
}
|
|
2544
|
+
|
|
2545
|
+
// src/lib/core/2.app/steps/pooling/pool-views.ts
|
|
2546
|
+
function liveBuilds(members, islanded) {
|
|
2547
|
+
return walkBuilds(members, islanded, true);
|
|
2548
|
+
}
|
|
2549
|
+
function committedView(members) {
|
|
2550
|
+
const global = /* @__PURE__ */ new Map();
|
|
2551
|
+
forEachGlobalClaim(members, void 0, (specifier, tag, meta) => {
|
|
2552
|
+
if (!global.has(specifier))
|
|
2553
|
+
global.set(specifier, { tag, remote: meta.name, file: meta.entries[specifier] });
|
|
2554
|
+
});
|
|
2555
|
+
return { builds: walkBuilds(members, void 0, false), global };
|
|
2556
|
+
}
|
|
2557
|
+
function walkBuilds(members, islanded, skipScoped) {
|
|
2558
|
+
const builds = /* @__PURE__ */ new Map();
|
|
2559
|
+
for (const member of members) {
|
|
2560
|
+
const versions = member.external.versions;
|
|
2561
|
+
for (let v = 0; v < versions.length; v++) {
|
|
2562
|
+
const version = versions[v];
|
|
2563
|
+
if (skipScoped && version.action === "scope") continue;
|
|
2564
|
+
const remotes = version.remotes;
|
|
2565
|
+
for (let r = 0; r < remotes.length; r++) {
|
|
2566
|
+
const meta = remotes[r];
|
|
2567
|
+
if (islanded?.has(meta.name)) continue;
|
|
2568
|
+
let own = builds.get(meta.name);
|
|
2569
|
+
if (!own) {
|
|
2570
|
+
builds.set(
|
|
2571
|
+
meta.name,
|
|
2572
|
+
own = { coverage: /* @__PURE__ */ new Map(), tags: /* @__PURE__ */ new Map(), instance: /* @__PURE__ */ new Map() }
|
|
2573
|
+
);
|
|
2574
|
+
}
|
|
2575
|
+
if (!own.instance.has(member.name)) own.instance.set(member.name, version.tag);
|
|
2576
|
+
for (const specifier in meta.entries) {
|
|
2577
|
+
own.coverage.set(specifier, meta.entries[specifier]);
|
|
2578
|
+
if (!own.tags.has(specifier)) own.tags.set(specifier, version.tag);
|
|
2579
|
+
}
|
|
2368
2580
|
}
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2581
|
+
}
|
|
2582
|
+
}
|
|
2583
|
+
return builds;
|
|
2584
|
+
}
|
|
2585
|
+
function ownCopies(members, only) {
|
|
2586
|
+
const own = /* @__PURE__ */ new Map();
|
|
2587
|
+
for (const member of members) {
|
|
2588
|
+
for (const version of member.external.versions) {
|
|
2589
|
+
for (const meta of version.remotes) {
|
|
2590
|
+
if (only && !only.has(meta.name)) continue;
|
|
2591
|
+
let copies = own.get(meta.name);
|
|
2592
|
+
if (!copies) own.set(meta.name, copies = []);
|
|
2593
|
+
if (!copies.some((c) => c.member === member.name))
|
|
2594
|
+
copies.push({ member: member.name, tag: version.tag, entries: meta.entries });
|
|
2376
2595
|
}
|
|
2377
|
-
|
|
2378
|
-
});
|
|
2379
|
-
sharedVersion.action = "share";
|
|
2380
|
-
applyEntrypointCoveragePolicy(externalName, external);
|
|
2381
|
-
external.dirty = false;
|
|
2382
|
-
return external;
|
|
2596
|
+
}
|
|
2383
2597
|
}
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2598
|
+
return own;
|
|
2599
|
+
}
|
|
2600
|
+
function consumedMembers(members) {
|
|
2601
|
+
const consumed = /* @__PURE__ */ new Map();
|
|
2602
|
+
for (const member of members) {
|
|
2603
|
+
const versions = member.external.versions;
|
|
2604
|
+
for (let v = 0; v < versions.length; v++) {
|
|
2605
|
+
const remotes = versions[v].remotes;
|
|
2606
|
+
for (let r = 0; r < remotes.length; r++) {
|
|
2607
|
+
const name = remotes[r].name;
|
|
2608
|
+
const list = consumed.get(name);
|
|
2609
|
+
if (!list) consumed.set(name, [member.name]);
|
|
2610
|
+
else if (list[list.length - 1] !== member.name) list.push(member.name);
|
|
2611
|
+
}
|
|
2398
2612
|
}
|
|
2399
|
-
scopeTornRemotes(externalName, external, tears);
|
|
2400
2613
|
}
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
const
|
|
2411
|
-
|
|
2412
|
-
|
|
2614
|
+
return consumed;
|
|
2615
|
+
}
|
|
2616
|
+
function consumedSpecifiers(members) {
|
|
2617
|
+
const consumed = /* @__PURE__ */ new Map();
|
|
2618
|
+
for (const member of members) {
|
|
2619
|
+
const versions = member.external.versions;
|
|
2620
|
+
for (let v = 0; v < versions.length; v++) {
|
|
2621
|
+
const remotes = versions[v].remotes;
|
|
2622
|
+
for (let r = 0; r < remotes.length; r++) {
|
|
2623
|
+
const meta = remotes[r];
|
|
2624
|
+
let own = consumed.get(meta.name);
|
|
2625
|
+
if (!own) consumed.set(meta.name, own = /* @__PURE__ */ new Set());
|
|
2626
|
+
for (const specifier in meta.entries) own.add(specifier);
|
|
2627
|
+
}
|
|
2413
2628
|
}
|
|
2414
|
-
return tears;
|
|
2415
2629
|
}
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2630
|
+
return consumed;
|
|
2631
|
+
}
|
|
2632
|
+
function coversWholePool(members, build, islanded) {
|
|
2633
|
+
const served = /* @__PURE__ */ new Set();
|
|
2634
|
+
const wanted = /* @__PURE__ */ new Set();
|
|
2635
|
+
for (const member of members) {
|
|
2636
|
+
for (const version of member.external.versions) {
|
|
2637
|
+
for (const meta of version.remotes) {
|
|
2638
|
+
if (islanded.has(meta.name)) continue;
|
|
2639
|
+
const into = meta.name === build ? served : wanted;
|
|
2640
|
+
for (const specifier in meta.entries) into.add(specifier);
|
|
2641
|
+
}
|
|
2427
2642
|
}
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2643
|
+
}
|
|
2644
|
+
for (const specifier of wanted) if (!served.has(specifier)) return false;
|
|
2645
|
+
return true;
|
|
2646
|
+
}
|
|
2647
|
+
function servingBuilds(members, islanded) {
|
|
2648
|
+
const serving = /* @__PURE__ */ new Map();
|
|
2649
|
+
for (const member of members) {
|
|
2650
|
+
const shared = member.external.versions.find((v) => v.action === "share");
|
|
2651
|
+
const basis = shared?.remotes.find((r) => !islanded.has(r.name));
|
|
2652
|
+
if (basis) serving.set(member.name, basis.name);
|
|
2653
|
+
}
|
|
2654
|
+
return serving;
|
|
2655
|
+
}
|
|
2656
|
+
function basisPerMember(members, islanded, dedupsElsewhere) {
|
|
2657
|
+
const basis = /* @__PURE__ */ new Map();
|
|
2658
|
+
for (const member of members) {
|
|
2659
|
+
const winner = member.external.versions.find((v) => v.action === "share");
|
|
2660
|
+
if (!winner) continue;
|
|
2661
|
+
const own = winner.remotes.find(
|
|
2662
|
+
(r) => !islanded?.has(r.name) && !dedupsElsewhere?.(r.name)
|
|
2663
|
+
);
|
|
2664
|
+
if (own) basis.set(member.name, own.name);
|
|
2665
|
+
}
|
|
2666
|
+
return basis;
|
|
2667
|
+
}
|
|
2668
|
+
function hostRemotes(members) {
|
|
2669
|
+
const hosts = /* @__PURE__ */ new Set();
|
|
2670
|
+
for (const member of members) {
|
|
2671
|
+
for (const version of member.external.versions) {
|
|
2672
|
+
if (version.host && version.remotes.length > 0) hosts.add(version.remotes[0].name);
|
|
2673
|
+
}
|
|
2674
|
+
}
|
|
2675
|
+
return hosts;
|
|
2676
|
+
}
|
|
2677
|
+
function arrivalOrder(members) {
|
|
2678
|
+
const arrival = /* @__PURE__ */ new Map();
|
|
2679
|
+
for (const member of members) {
|
|
2680
|
+
for (const version of member.external.versions) {
|
|
2681
|
+
for (const meta of version.remotes) {
|
|
2682
|
+
if (!arrival.has(meta.name)) arrival.set(meta.name, arrival.size);
|
|
2431
2683
|
}
|
|
2432
2684
|
}
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2685
|
+
}
|
|
2686
|
+
return arrival;
|
|
2687
|
+
}
|
|
2688
|
+
function sharedTagPerSpecifier(members, islanded) {
|
|
2689
|
+
const shared = /* @__PURE__ */ new Map();
|
|
2690
|
+
forEachGlobalClaim(members, islanded, (specifier, tag) => {
|
|
2691
|
+
if (!shared.has(specifier)) shared.set(specifier, tag);
|
|
2692
|
+
});
|
|
2693
|
+
return shared;
|
|
2694
|
+
}
|
|
2695
|
+
function forEachGlobalClaim(members, islanded, visit) {
|
|
2696
|
+
const accepts = islanded ? (meta) => !islanded.has(meta.name) : void 0;
|
|
2697
|
+
const claim = (version) => forEachVersionEntry(version, accepts, (specifier, meta) => visit(specifier, version.tag, meta));
|
|
2698
|
+
for (const member of members) {
|
|
2699
|
+
const versions = member.external.versions;
|
|
2700
|
+
const winner = versions.find((v) => v.action === "share");
|
|
2701
|
+
if (winner) claim(winner);
|
|
2702
|
+
for (let v = 0; v < versions.length; v++) {
|
|
2703
|
+
if (versions[v].action === "skip") claim(versions[v]);
|
|
2438
2704
|
}
|
|
2439
2705
|
}
|
|
2440
2706
|
}
|
|
2441
2707
|
|
|
2708
|
+
// src/lib/core/2.app/steps/pooling/anchoring.ts
|
|
2709
|
+
function acceptanceTable(members, isCompatible) {
|
|
2710
|
+
const table = /* @__PURE__ */ new Map();
|
|
2711
|
+
for (const member of members) {
|
|
2712
|
+
const tags = [];
|
|
2713
|
+
for (const version of member.external.versions) tags.push(version.tag);
|
|
2714
|
+
for (const version of member.external.versions) {
|
|
2715
|
+
for (const meta of version.remotes) {
|
|
2716
|
+
let byMember = table.get(meta.name);
|
|
2717
|
+
if (!byMember) table.set(meta.name, byMember = /* @__PURE__ */ new Map());
|
|
2718
|
+
if (byMember.has(member.name)) continue;
|
|
2719
|
+
const accepted = /* @__PURE__ */ new Set();
|
|
2720
|
+
for (let t = 0; t < tags.length; t++) {
|
|
2721
|
+
if (isCompatible(tags[t], meta.requiredVersion)) accepted.add(tags[t]);
|
|
2722
|
+
}
|
|
2723
|
+
byMember.set(member.name, accepted);
|
|
2724
|
+
}
|
|
2725
|
+
}
|
|
2726
|
+
}
|
|
2727
|
+
return table;
|
|
2728
|
+
}
|
|
2729
|
+
function covers(coverage2, consumed) {
|
|
2730
|
+
for (const specifier of consumed) if (!coverage2.has(specifier)) return false;
|
|
2731
|
+
return true;
|
|
2732
|
+
}
|
|
2733
|
+
function acceptsAll(acceptance, build, consumer, consumed) {
|
|
2734
|
+
const byMember = acceptance.get(consumer);
|
|
2735
|
+
if (!byMember) return false;
|
|
2736
|
+
for (let i = 0; i < consumed.length; i++) {
|
|
2737
|
+
const offered = build.get(consumed[i]);
|
|
2738
|
+
if (offered === void 0) return false;
|
|
2739
|
+
if (!byMember.get(consumed[i])?.has(offered)) return false;
|
|
2740
|
+
}
|
|
2741
|
+
return true;
|
|
2742
|
+
}
|
|
2743
|
+
function isWitnessed(specifiers, shared, builds) {
|
|
2744
|
+
const wanted = [...specifiers];
|
|
2745
|
+
for (const specifier of wanted) if (!shared.has(specifier)) return false;
|
|
2746
|
+
for (const [, build] of builds) {
|
|
2747
|
+
let matches2 = true;
|
|
2748
|
+
for (let i = 0; i < wanted.length; i++) {
|
|
2749
|
+
if (build.tags.get(wanted[i]) !== shared.get(wanted[i])) {
|
|
2750
|
+
matches2 = false;
|
|
2751
|
+
break;
|
|
2752
|
+
}
|
|
2753
|
+
}
|
|
2754
|
+
if (matches2) return true;
|
|
2755
|
+
}
|
|
2756
|
+
return false;
|
|
2757
|
+
}
|
|
2758
|
+
function explainSelfServe(remote, wants, specifiers, pool) {
|
|
2759
|
+
let closest;
|
|
2760
|
+
let fewest = Number.MAX_SAFE_INTEGER;
|
|
2761
|
+
let gap = wants[0] ?? "";
|
|
2762
|
+
for (const [build, view] of pool.builds) {
|
|
2763
|
+
if (build === remote) continue;
|
|
2764
|
+
let missing;
|
|
2765
|
+
let count = 0;
|
|
2766
|
+
for (const specifier of specifiers) {
|
|
2767
|
+
if (view.coverage.has(specifier)) continue;
|
|
2768
|
+
count++;
|
|
2769
|
+
missing ??= specifier;
|
|
2770
|
+
}
|
|
2771
|
+
if (count >= fewest) continue;
|
|
2772
|
+
fewest = count;
|
|
2773
|
+
closest = build;
|
|
2774
|
+
gap = missing ?? rejectedMember(view.instance, remote, wants, pool.acceptance) ?? gap;
|
|
2775
|
+
}
|
|
2776
|
+
return { gap, closest };
|
|
2777
|
+
}
|
|
2778
|
+
function rejectedMember(offered, consumer, wants, acceptance) {
|
|
2779
|
+
const accepts = acceptance.get(consumer);
|
|
2780
|
+
if (!accepts) return void 0;
|
|
2781
|
+
for (const member of wants) {
|
|
2782
|
+
const tag = offered.get(member);
|
|
2783
|
+
if (tag === void 0) return member;
|
|
2784
|
+
if (!accepts.get(member)?.has(tag)) return `${member}@${tag}`;
|
|
2785
|
+
}
|
|
2786
|
+
return void 0;
|
|
2787
|
+
}
|
|
2788
|
+
function assignAnchors(input) {
|
|
2789
|
+
const { builds, acceptance, consumedMembers: consumedMembers2, hosts, arrival } = input;
|
|
2790
|
+
const assignment = /* @__PURE__ */ new Map();
|
|
2791
|
+
const byPriority = (a, b) => (arrival.get(a) ?? Number.MAX_SAFE_INTEGER) - (arrival.get(b) ?? Number.MAX_SAFE_INTEGER) || a.localeCompare(b);
|
|
2792
|
+
const candidates = [...builds.keys()].sort(byPriority);
|
|
2793
|
+
const pending = new Set([...consumedMembers2.keys()].sort(byPriority));
|
|
2794
|
+
const anchored = /* @__PURE__ */ new Set();
|
|
2795
|
+
const canServe = (candidate, consumer) => {
|
|
2796
|
+
const build = builds.get(candidate);
|
|
2797
|
+
if (!covers(build.coverage, input.consumedSpecifiers.get(consumer) ?? [])) return false;
|
|
2798
|
+
return acceptsAll(acceptance, build.instance, consumer, consumedMembers2.get(consumer) ?? []);
|
|
2799
|
+
};
|
|
2800
|
+
const electAnchor = (anchor, served) => {
|
|
2801
|
+
pending.delete(anchor);
|
|
2802
|
+
assignment.set(anchor, void 0);
|
|
2803
|
+
for (const consumer of served) {
|
|
2804
|
+
pending.delete(consumer);
|
|
2805
|
+
anchored.add(consumer);
|
|
2806
|
+
assignment.set(consumer, anchor);
|
|
2807
|
+
}
|
|
2808
|
+
};
|
|
2809
|
+
for (const host of [...hosts].sort(byPriority)) {
|
|
2810
|
+
const served = [];
|
|
2811
|
+
if (builds.has(host)) {
|
|
2812
|
+
for (const consumer of pending) {
|
|
2813
|
+
if (consumer !== host && canServe(host, consumer)) served.push(consumer);
|
|
2814
|
+
}
|
|
2815
|
+
}
|
|
2816
|
+
electAnchor(host, served);
|
|
2817
|
+
}
|
|
2818
|
+
for (; ; ) {
|
|
2819
|
+
let best;
|
|
2820
|
+
let bestServed = [];
|
|
2821
|
+
for (const candidate of candidates) {
|
|
2822
|
+
if (anchored.has(candidate)) continue;
|
|
2823
|
+
const served = [];
|
|
2824
|
+
for (const consumer of pending) {
|
|
2825
|
+
if (consumer !== candidate && canServe(candidate, consumer)) served.push(consumer);
|
|
2826
|
+
}
|
|
2827
|
+
if (served.length > bestServed.length) {
|
|
2828
|
+
best = candidate;
|
|
2829
|
+
bestServed = served;
|
|
2830
|
+
}
|
|
2831
|
+
}
|
|
2832
|
+
if (best === void 0) break;
|
|
2833
|
+
electAnchor(best, bestServed);
|
|
2834
|
+
}
|
|
2835
|
+
for (const consumer of pending) assignment.set(consumer, void 0);
|
|
2836
|
+
return assignment;
|
|
2837
|
+
}
|
|
2838
|
+
|
|
2442
2839
|
// src/lib/core/2.app/steps/pooling/pool-graph.ts
|
|
2443
2840
|
var SCOPED_PACKAGE = /^@([^/]+)\//;
|
|
2444
2841
|
function createDSU() {
|
|
@@ -2479,34 +2876,56 @@ function createDSU() {
|
|
|
2479
2876
|
}
|
|
2480
2877
|
var extNode = (name) => `ext\0${name}`;
|
|
2481
2878
|
var tagNode = (remote, tag) => `tag\0${remote}\0${tag}`;
|
|
2482
|
-
var scopeNode = (scope) => `scope\0${scope}`;
|
|
2879
|
+
var scopeNode = (remote, scope) => `scope\0${remote}\0${scope}`;
|
|
2483
2880
|
function autoScope(name, useAutoExternalPooling) {
|
|
2484
2881
|
return useAutoExternalPooling ? SCOPED_PACKAGE.exec(name)?.[1] : void 0;
|
|
2485
2882
|
}
|
|
2883
|
+
function owningPackage(name) {
|
|
2884
|
+
const depth = name.startsWith("@") ? 2 : 1;
|
|
2885
|
+
let cut = -1;
|
|
2886
|
+
for (let seen = 0; seen < depth; seen++) {
|
|
2887
|
+
cut = name.indexOf("/", cut + 1);
|
|
2888
|
+
if (cut === -1) return void 0;
|
|
2889
|
+
}
|
|
2890
|
+
return name.slice(0, cut);
|
|
2891
|
+
}
|
|
2486
2892
|
function groupByMembership(candidates, log) {
|
|
2487
2893
|
const dsu = createDSU();
|
|
2488
2894
|
const tagged = /* @__PURE__ */ new Set();
|
|
2489
|
-
const
|
|
2895
|
+
const joined = /* @__PURE__ */ new Set();
|
|
2896
|
+
const tagRules = /* @__PURE__ */ new Set();
|
|
2897
|
+
for (const candidate of candidates) {
|
|
2898
|
+
if (candidate.scope === void 0) continue;
|
|
2899
|
+
for (const edge of candidate.tags) tagRules.add(`${edge.remote}\0${candidate.scope}`);
|
|
2900
|
+
}
|
|
2490
2901
|
for (const candidate of candidates) {
|
|
2491
|
-
let joined = false;
|
|
2492
|
-
if (candidate.scope !== void 0) {
|
|
2493
|
-
dsu.union(extNode(candidate.name), scopeNode(candidate.scope));
|
|
2494
|
-
joined = true;
|
|
2495
|
-
}
|
|
2496
2902
|
for (const edge of candidate.tags) {
|
|
2497
2903
|
dsu.union(extNode(candidate.name), tagNode(edge.remote, edge.tag));
|
|
2498
2904
|
tagged.add(candidate.name);
|
|
2499
|
-
joined
|
|
2905
|
+
joined.add(candidate.name);
|
|
2906
|
+
}
|
|
2907
|
+
if (candidate.scope === void 0) continue;
|
|
2908
|
+
for (const remote of candidate.remotes) {
|
|
2909
|
+
if (tagRules.has(`${remote}\0${candidate.scope}`)) continue;
|
|
2910
|
+
dsu.union(extNode(candidate.name), scopeNode(remote, candidate.scope));
|
|
2911
|
+
joined.add(candidate.name);
|
|
2500
2912
|
}
|
|
2501
|
-
|
|
2913
|
+
}
|
|
2914
|
+
const declared = new Set(candidates.map((c) => c.name));
|
|
2915
|
+
for (const candidate of candidates) {
|
|
2916
|
+
const owner = owningPackage(candidate.name);
|
|
2917
|
+
if (owner !== void 0 && declared.has(owner))
|
|
2918
|
+
dsu.union(extNode(candidate.name), extNode(owner));
|
|
2502
2919
|
}
|
|
2503
2920
|
const byComponent = /* @__PURE__ */ new Map();
|
|
2504
|
-
for (const
|
|
2505
|
-
const root = dsu.component(extNode(
|
|
2506
|
-
|
|
2921
|
+
for (const candidate of candidates) {
|
|
2922
|
+
const root = dsu.component(extNode(candidate.name));
|
|
2923
|
+
const members = byComponent.get(root) ?? byComponent.set(root, []).get(root);
|
|
2924
|
+
members.push({ name: candidate.name, value: candidate.value });
|
|
2507
2925
|
}
|
|
2508
2926
|
const pools = [];
|
|
2509
2927
|
for (const members of byComponent.values()) {
|
|
2928
|
+
if (!members.some((m) => joined.has(m.name))) continue;
|
|
2510
2929
|
members.sort((a, b) => a.name.localeCompare(b.name));
|
|
2511
2930
|
if (members.length < 2) {
|
|
2512
2931
|
const only = members[0];
|
|
@@ -2534,6 +2953,7 @@ function buildPools(sharedExternals, useAutoExternalPooling, log) {
|
|
|
2534
2953
|
return tag ? [{ remote: r.name, tag }] : [];
|
|
2535
2954
|
})
|
|
2536
2955
|
),
|
|
2956
|
+
remotes: external.versions.flatMap((v) => v.remotes.map((r) => r.name)),
|
|
2537
2957
|
value: { name, external }
|
|
2538
2958
|
})
|
|
2539
2959
|
);
|
|
@@ -2541,28 +2961,131 @@ function buildPools(sharedExternals, useAutoExternalPooling, log) {
|
|
|
2541
2961
|
}
|
|
2542
2962
|
|
|
2543
2963
|
// src/lib/core/2.app/steps/pooling/pool.util.ts
|
|
2964
|
+
function lazy(make) {
|
|
2965
|
+
let value;
|
|
2966
|
+
return () => value ??= make();
|
|
2967
|
+
}
|
|
2544
2968
|
function remotesInPool(members) {
|
|
2545
2969
|
return [
|
|
2546
2970
|
...new Set(members.flatMap((m) => m.external.versions.flatMap((v) => v.remotes.map((r) => r.name))))
|
|
2547
2971
|
];
|
|
2548
2972
|
}
|
|
2973
|
+
function poolableScopes(config, repo) {
|
|
2974
|
+
const { useAutoExternalPooling } = config.feature;
|
|
2975
|
+
return {
|
|
2976
|
+
useAutoExternalPooling,
|
|
2977
|
+
scopes: repo.getScopes().filter(
|
|
2978
|
+
(scope) => repo.scopeType(scope) !== "strict" && (useAutoExternalPooling || repo.hasPoolTag(scope))
|
|
2979
|
+
)
|
|
2980
|
+
};
|
|
2981
|
+
}
|
|
2549
2982
|
|
|
2550
2983
|
// src/lib/core/2.app/steps/pooling/pool-shared-externals.ts
|
|
2551
2984
|
function islandedRemotes(members) {
|
|
2552
|
-
const islanded = /* @__PURE__ */ new
|
|
2985
|
+
const islanded = /* @__PURE__ */ new Map();
|
|
2553
2986
|
for (const member of members)
|
|
2554
2987
|
for (const version of member.external.versions)
|
|
2555
|
-
if (version.action === "scope")
|
|
2988
|
+
if (version.action === "scope") {
|
|
2989
|
+
for (const remote of version.remotes)
|
|
2990
|
+
if (!islanded.has(remote.name))
|
|
2991
|
+
islanded.set(remote.name, {
|
|
2992
|
+
kind: "incompatible",
|
|
2993
|
+
member: member.name,
|
|
2994
|
+
tag: version.tag
|
|
2995
|
+
});
|
|
2996
|
+
}
|
|
2556
2997
|
return islanded;
|
|
2557
2998
|
}
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2999
|
+
var basisFor = (members, islanded, serving) => basisPerMember(members, islanded, (remote) => {
|
|
3000
|
+
const build = serving.get(remote);
|
|
3001
|
+
return build !== void 0 && build !== remote;
|
|
3002
|
+
});
|
|
3003
|
+
function servedPerRemote(consumed, serving, basis) {
|
|
3004
|
+
const served = /* @__PURE__ */ new Map();
|
|
3005
|
+
for (const [remote, build] of serving) {
|
|
3006
|
+
const byMember = /* @__PURE__ */ new Map();
|
|
3007
|
+
for (const name of consumed.get(remote) ?? []) {
|
|
3008
|
+
if (basis.get(name) !== build) byMember.set(name, build);
|
|
3009
|
+
}
|
|
3010
|
+
if (byMember.size > 0) served.set(remote, byMember);
|
|
3011
|
+
}
|
|
3012
|
+
return served;
|
|
3013
|
+
}
|
|
3014
|
+
var anyAnchorStored = (members) => members.some((m) => m.external.versions.some((v) => v.remotes.some((r) => r.servedBy !== void 0)));
|
|
3015
|
+
function findTornRemotes(members, islanded, basis, served, hosts) {
|
|
3016
|
+
const { suspect, selfServed } = movedRemotes(members, islanded, basis, served, hosts);
|
|
3017
|
+
if (suspect.size === 0) return [];
|
|
3018
|
+
const builds = liveBuilds(members, islanded);
|
|
3019
|
+
const shared = sharedTagPerSpecifier(members, islanded);
|
|
3020
|
+
const own = ownCopies(members, suspect);
|
|
3021
|
+
const torn = [];
|
|
3022
|
+
for (const remote of suspect) {
|
|
3023
|
+
const copies = own.get(remote) ?? [];
|
|
3024
|
+
const mine = selfServed.get(remote);
|
|
3025
|
+
const anchors = served.get(remote);
|
|
3026
|
+
const ownTags = /* @__PURE__ */ new Map();
|
|
3027
|
+
const resolved = /* @__PURE__ */ new Map();
|
|
3028
|
+
for (const copy of copies) {
|
|
3029
|
+
const from = mine?.has(copy.member) ? remote : anchors?.get(copy.member);
|
|
3030
|
+
for (const specifier in copy.entries) {
|
|
3031
|
+
if (!ownTags.has(specifier)) ownTags.set(specifier, copy.tag);
|
|
3032
|
+
const tag = from === remote ? copy.tag : from !== void 0 ? builds.get(from)?.tags.get(specifier) : shared.get(specifier);
|
|
3033
|
+
if (tag !== void 0 && !resolved.has(specifier)) resolved.set(specifier, tag);
|
|
3034
|
+
}
|
|
3035
|
+
}
|
|
3036
|
+
if (resolved.size === 0) continue;
|
|
3037
|
+
if (ships(ownTags, resolved)) continue;
|
|
3038
|
+
let witnessed = false;
|
|
3039
|
+
for (const [, build] of builds) {
|
|
3040
|
+
if (ships(build.tags, resolved)) {
|
|
3041
|
+
witnessed = true;
|
|
3042
|
+
break;
|
|
3043
|
+
}
|
|
3044
|
+
}
|
|
3045
|
+
if (!witnessed) {
|
|
3046
|
+
torn.push({
|
|
3047
|
+
remote,
|
|
3048
|
+
combination: [...resolved].map(([specifier, tag]) => `${specifier}@${tag}`).join(", ")
|
|
3049
|
+
});
|
|
3050
|
+
}
|
|
3051
|
+
}
|
|
3052
|
+
return torn;
|
|
3053
|
+
}
|
|
3054
|
+
var ships = (build, resolved) => {
|
|
3055
|
+
if (!build) return false;
|
|
3056
|
+
for (const [specifier, tag] of resolved) if (build.get(specifier) !== tag) return false;
|
|
3057
|
+
return true;
|
|
3058
|
+
};
|
|
3059
|
+
function movedRemotes(members, islanded, basis, served, hosts) {
|
|
3060
|
+
const suspect = /* @__PURE__ */ new Set();
|
|
3061
|
+
const selfServed = /* @__PURE__ */ new Map();
|
|
3062
|
+
const add = (remote) => {
|
|
3063
|
+
if (!islanded.has(remote) && !hosts.has(remote)) suspect.add(remote);
|
|
3064
|
+
};
|
|
3065
|
+
for (const remote of served.keys()) add(remote);
|
|
3066
|
+
for (const member of members) {
|
|
3067
|
+
const unpublished = !basis.has(member.name);
|
|
3068
|
+
for (const version of member.external.versions) {
|
|
3069
|
+
const scoped = version.action === "scope";
|
|
3070
|
+
if (!unpublished && !scoped) continue;
|
|
3071
|
+
for (const meta of version.remotes) {
|
|
3072
|
+
add(meta.name);
|
|
3073
|
+
const servesItself = scoped || unpublished && served.get(meta.name)?.get(member.name) === void 0;
|
|
3074
|
+
if (!servesItself) continue;
|
|
3075
|
+
let mine = selfServed.get(meta.name);
|
|
3076
|
+
if (!mine) selfServed.set(meta.name, mine = /* @__PURE__ */ new Set());
|
|
3077
|
+
mine.add(member.name);
|
|
3078
|
+
}
|
|
2563
3079
|
}
|
|
2564
|
-
|
|
2565
|
-
|
|
3080
|
+
}
|
|
3081
|
+
return { suspect, selfServed };
|
|
3082
|
+
}
|
|
3083
|
+
function createPoolSharedExternals(config, ports) {
|
|
3084
|
+
return (touched) => {
|
|
3085
|
+
const { useAutoExternalPooling, scopes } = poolableScopes(config, ports.sharedExternalsRepo);
|
|
3086
|
+
for (const scope of scopes) {
|
|
3087
|
+
const touchedInScope = touched?.get(scope);
|
|
3088
|
+
if (touched && !touchedInScope) continue;
|
|
2566
3089
|
const sharedExternals = ports.sharedExternalsRepo.getFromScope(scope);
|
|
2567
3090
|
try {
|
|
2568
3091
|
for (const [poolName, members] of buildPools(
|
|
@@ -2570,6 +3093,7 @@ function createPoolSharedExternals(config, ports) {
|
|
|
2570
3093
|
useAutoExternalPooling,
|
|
2571
3094
|
config.log
|
|
2572
3095
|
)) {
|
|
3096
|
+
if (touchedInScope && !members.some((m) => touchedInScope.has(m.name))) continue;
|
|
2573
3097
|
poolFamily(poolName, members, scope);
|
|
2574
3098
|
}
|
|
2575
3099
|
} catch (error) {
|
|
@@ -2590,45 +3114,138 @@ function createPoolSharedExternals(config, ports) {
|
|
|
2590
3114
|
const allRemotes = remotesInPool(members);
|
|
2591
3115
|
if (allRemotes.length < 2) return;
|
|
2592
3116
|
const islanded = islandedRemotes(members);
|
|
3117
|
+
const consumed = consumedMembers(members);
|
|
3118
|
+
const hosts = hostRemotes(members);
|
|
3119
|
+
const views = {
|
|
3120
|
+
consumedSpecifiers: lazy(() => consumedSpecifiers(members)),
|
|
3121
|
+
acceptance: lazy(() => acceptanceTable(members, ports.versionCheck.isCompatible)),
|
|
3122
|
+
arrival: lazy(() => arrivalOrder(members))
|
|
3123
|
+
};
|
|
2593
3124
|
config.log.debug(
|
|
2594
3125
|
3,
|
|
2595
|
-
`[${scope}][pool:${poolName}] ${members.length} members across ${allRemotes.length} remotes,
|
|
3126
|
+
`[${scope}][pool:${poolName}] ${members.length} members across ${allRemotes.length} remotes, incompatible={${[...islanded.keys()].join(", ") || "\u2205"}}
|
|
2596
3127
|
` + members.map((m) => ` - ${m.name}`).join("\n")
|
|
2597
3128
|
);
|
|
2598
|
-
|
|
3129
|
+
const assign = () => {
|
|
3130
|
+
const serving2 = assignServingBuilds(members, islanded, consumed, hosts, views);
|
|
3131
|
+
const basis2 = basisFor(members, islanded, serving2);
|
|
3132
|
+
return { serving: serving2, basis: basis2, served: servedPerRemote(consumed, serving2, basis2) };
|
|
3133
|
+
};
|
|
3134
|
+
let { serving, basis, served } = assign();
|
|
3135
|
+
if (islanded.size === 0 && serving.size === 0 && !anyAnchorStored(members)) return;
|
|
3136
|
+
for (; ; ) {
|
|
3137
|
+
const torn = findTornRemotes(members, islanded, basis, served, hosts);
|
|
3138
|
+
if (torn.length === 0) break;
|
|
3139
|
+
for (const { remote, combination } of torn)
|
|
3140
|
+
islanded.set(remote, { kind: "torn", combination });
|
|
3141
|
+
({ serving, basis, served } = assign());
|
|
3142
|
+
}
|
|
3143
|
+
const incompatible = [...islanded].filter(([, cause]) => cause.kind === "incompatible").map(([remote]) => remote);
|
|
3144
|
+
if (config.strict.strictExternalCompatibility && incompatible.length > 0) {
|
|
2599
3145
|
config.log.error(
|
|
2600
3146
|
3,
|
|
2601
|
-
`[${scope}][pool:${poolName}] version-incompatible remotes cannot be pooled: {${
|
|
3147
|
+
`[${scope}][pool:${poolName}] version-incompatible remotes cannot be pooled: {${incompatible.join(", ")}}.`
|
|
2602
3148
|
);
|
|
2603
3149
|
throw new NFError(`Could not pool '${poolName}' in scope ${scope}.`);
|
|
2604
3150
|
}
|
|
3151
|
+
for (const [remote, cause] of islanded) {
|
|
3152
|
+
const scoped = consumed.get(remote)?.length ?? 0;
|
|
3153
|
+
config.log.warn(3, `[${scope}][pool:${poolName}] ${islandWarning(remote, cause, scoped)}`);
|
|
3154
|
+
}
|
|
2605
3155
|
for (const member of members) {
|
|
2606
|
-
const rebuilt = rebuildMember(member, islanded);
|
|
2607
|
-
warnIfScopedOnly(poolName, member
|
|
3156
|
+
const rebuilt = rebuildMember(member, islanded, served, basis.get(member.name));
|
|
3157
|
+
warnIfScopedOnly(poolName, member, rebuilt, islanded, scope);
|
|
2608
3158
|
ports.sharedExternalsRepo.addOrUpdate(member.name, rebuilt, scope);
|
|
2609
3159
|
}
|
|
2610
3160
|
}
|
|
2611
|
-
function
|
|
3161
|
+
function assignServingBuilds(members, islanded, consumed, hosts, views) {
|
|
3162
|
+
for (; ; ) {
|
|
3163
|
+
const bases = servingBuilds(members, islanded);
|
|
3164
|
+
const distinct = new Set(bases.values());
|
|
3165
|
+
if (distinct.size === 1 && bases.size === members.length) {
|
|
3166
|
+
const sole = [...distinct][0];
|
|
3167
|
+
if (coversWholePool(members, sole, islanded)) return /* @__PURE__ */ new Map();
|
|
3168
|
+
}
|
|
3169
|
+
const builds = liveBuilds(members, islanded);
|
|
3170
|
+
const shared = sharedTagPerSpecifier(members, islanded);
|
|
3171
|
+
const consumedSpec = views.consumedSpecifiers();
|
|
3172
|
+
const needAnchor = /* @__PURE__ */ new Map();
|
|
3173
|
+
const needSpecifiers = /* @__PURE__ */ new Map();
|
|
3174
|
+
for (const [remote, wants] of consumed) {
|
|
3175
|
+
if (islanded.has(remote)) continue;
|
|
3176
|
+
if (isWitnessed(consumedSpec.get(remote) ?? [], shared, builds)) continue;
|
|
3177
|
+
needAnchor.set(remote, wants);
|
|
3178
|
+
needSpecifiers.set(remote, consumedSpec.get(remote) ?? /* @__PURE__ */ new Set());
|
|
3179
|
+
}
|
|
3180
|
+
if (needAnchor.size === 0) return /* @__PURE__ */ new Map();
|
|
3181
|
+
const acceptance = views.acceptance();
|
|
3182
|
+
const assignment = assignAnchors({
|
|
3183
|
+
builds,
|
|
3184
|
+
acceptance,
|
|
3185
|
+
consumedSpecifiers: needSpecifiers,
|
|
3186
|
+
consumedMembers: needAnchor,
|
|
3187
|
+
hosts,
|
|
3188
|
+
arrival: views.arrival()
|
|
3189
|
+
});
|
|
3190
|
+
const anchors = /* @__PURE__ */ new Set();
|
|
3191
|
+
for (const build of assignment.values()) if (build !== void 0) anchors.add(build);
|
|
3192
|
+
const serving = /* @__PURE__ */ new Map();
|
|
3193
|
+
let scoped = false;
|
|
3194
|
+
for (const [remote, wants] of needAnchor) {
|
|
3195
|
+
const assigned = assignment.get(remote);
|
|
3196
|
+
const build = assigned ?? (anchors.has(remote) || hosts.has(remote) ? remote : void 0);
|
|
3197
|
+
if (build === void 0) {
|
|
3198
|
+
islanded.set(remote, {
|
|
3199
|
+
kind: "uncovered",
|
|
3200
|
+
...explainSelfServe(remote, wants, needSpecifiers.get(remote), { builds, acceptance })
|
|
3201
|
+
});
|
|
3202
|
+
scoped = true;
|
|
3203
|
+
continue;
|
|
3204
|
+
}
|
|
3205
|
+
serving.set(remote, build);
|
|
3206
|
+
}
|
|
3207
|
+
if (!scoped) return serving;
|
|
3208
|
+
}
|
|
3209
|
+
}
|
|
3210
|
+
function warnIfScopedOnly(poolName, member, rebuilt, islanded, scope) {
|
|
2612
3211
|
if (rebuilt.versions.some((v) => v.action === "share")) return;
|
|
2613
|
-
const consumers = new Set(
|
|
3212
|
+
const consumers = new Set(
|
|
3213
|
+
rebuilt.versions.filter((v) => v.action === "scope").flatMap((v) => v.remotes.map((r) => r.name))
|
|
3214
|
+
).size;
|
|
2614
3215
|
if (consumers < 2) return;
|
|
3216
|
+
const provider = member.external.versions.find((v) => v.action === "share");
|
|
3217
|
+
if (provider?.remotes.some((r) => islanded.has(r.name))) return;
|
|
2615
3218
|
config.log.warn(
|
|
2616
3219
|
3,
|
|
2617
|
-
`[${scope}][pool:${poolName}] '${
|
|
3220
|
+
`[${scope}][pool:${poolName}] '${member.name}' is scoped-only \u2014 no coherent shared build provides it; ${consumers} remotes download their own copy.`
|
|
2618
3221
|
);
|
|
2619
3222
|
}
|
|
2620
|
-
function rebuildMember(member, islanded) {
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
)
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
3223
|
+
function rebuildMember(member, islanded, served, basis) {
|
|
3224
|
+
let scoped = [];
|
|
3225
|
+
let clean = [];
|
|
3226
|
+
for (const version of member.external.versions) {
|
|
3227
|
+
for (const meta of version.remotes) {
|
|
3228
|
+
const entry = {
|
|
3229
|
+
remote: meta.name,
|
|
3230
|
+
tag: version.tag,
|
|
3231
|
+
host: version.host,
|
|
3232
|
+
action: version.action,
|
|
3233
|
+
meta
|
|
3234
|
+
};
|
|
3235
|
+
(islanded.has(entry.remote) || entry.action === "scope" ? scoped : clean).push(entry);
|
|
3236
|
+
}
|
|
3237
|
+
}
|
|
3238
|
+
for (const entry of scoped) delete entry.meta.servedBy;
|
|
3239
|
+
for (const entry of clean) {
|
|
3240
|
+
const build = served.get(entry.remote)?.get(member.name);
|
|
3241
|
+
if (build !== void 0) entry.meta.servedBy = build;
|
|
3242
|
+
else delete entry.meta.servedBy;
|
|
3243
|
+
}
|
|
3244
|
+
if (basis === void 0) {
|
|
3245
|
+
scoped = [...scoped, ...clean.filter((e) => e.meta.servedBy === void 0)];
|
|
3246
|
+
clean = clean.filter((e) => e.meta.servedBy !== void 0).map((e) => ({ ...e, action: "skip" }));
|
|
3247
|
+
}
|
|
3248
|
+
const shareEntries = clean.filter((e) => e.action === "share").sort((a, b) => Number(b.remote === basis) - Number(a.remote === basis));
|
|
2632
3249
|
const shareVersion = shareEntries.length > 0 ? [
|
|
2633
3250
|
{
|
|
2634
3251
|
tag: shareEntries[0].tag,
|
|
@@ -2637,37 +3254,70 @@ function createPoolSharedExternals(config, ports) {
|
|
|
2637
3254
|
remotes: shareEntries.map((e) => e.meta)
|
|
2638
3255
|
}
|
|
2639
3256
|
] : [];
|
|
2640
|
-
const
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
const version = scopeByTag.get(e.tag) ?? {
|
|
2655
|
-
tag: e.tag,
|
|
2656
|
-
host: false,
|
|
2657
|
-
action: "scope",
|
|
2658
|
-
remotes: []
|
|
2659
|
-
};
|
|
2660
|
-
version.remotes.push(e.meta);
|
|
2661
|
-
scopeByTag.set(e.tag, version);
|
|
2662
|
-
}
|
|
3257
|
+
const byTag = (entries, action) => {
|
|
3258
|
+
const versions = /* @__PURE__ */ new Map();
|
|
3259
|
+
for (const entry of entries) {
|
|
3260
|
+
const version = versions.get(entry.tag) ?? {
|
|
3261
|
+
tag: entry.tag,
|
|
3262
|
+
host: entry.host,
|
|
3263
|
+
action,
|
|
3264
|
+
remotes: []
|
|
3265
|
+
};
|
|
3266
|
+
version.remotes.push(entry.meta);
|
|
3267
|
+
versions.set(entry.tag, version);
|
|
3268
|
+
}
|
|
3269
|
+
return [...versions.values()];
|
|
3270
|
+
};
|
|
2663
3271
|
return {
|
|
2664
3272
|
dirty: false,
|
|
2665
|
-
versions: [
|
|
3273
|
+
versions: [
|
|
3274
|
+
...shareVersion,
|
|
3275
|
+
...byTag(
|
|
3276
|
+
clean.filter((e) => e.action === "skip"),
|
|
3277
|
+
"skip"
|
|
3278
|
+
),
|
|
3279
|
+
...byTag(scoped, "scope")
|
|
3280
|
+
].sort((a, b) => ports.versionCheck.compare(b.tag, a.tag))
|
|
2666
3281
|
};
|
|
2667
3282
|
}
|
|
2668
3283
|
}
|
|
3284
|
+
function islandWarning(remote, cause, scoped) {
|
|
3285
|
+
switch (cause.kind) {
|
|
3286
|
+
case "incompatible":
|
|
3287
|
+
return `'${remote}' is islanded: the resolver scoped its '${cause.member}@${cause.tag}', so all ${scoped} members it imports are scoped for it.`;
|
|
3288
|
+
case "torn":
|
|
3289
|
+
return `'${remote}' serves its own family: the mapping would have handed it ${cause.combination}, which no build shipped together, so all ${scoped} members it imports are scoped for it.`;
|
|
3290
|
+
default: {
|
|
3291
|
+
const closest = cause.closest ? `closest is '${cause.closest}'` : "no other build in the pool serves any of it";
|
|
3292
|
+
return `'${remote}' serves its own family: no shared build offers every entrypoint it imports at a version it accepts \u2014 '${cause.gap}' is the gap, ${closest}. All ${scoped} members it imports are scoped for it.`;
|
|
3293
|
+
}
|
|
3294
|
+
}
|
|
3295
|
+
}
|
|
2669
3296
|
|
|
2670
|
-
//
|
|
3297
|
+
// src/lib/core/2.app/steps/pooling/mark-pools-for-reelection.ts
|
|
3298
|
+
function createMarkPoolsForReelection(config, ports) {
|
|
3299
|
+
return () => {
|
|
3300
|
+
const { useAutoExternalPooling, scopes } = poolableScopes(config, ports.sharedExternalsRepo);
|
|
3301
|
+
for (const scope of scopes) {
|
|
3302
|
+
const sharedExternals = ports.sharedExternalsRepo.getFromScope(scope);
|
|
3303
|
+
if (!Object.values(sharedExternals).some((external) => external.dirty)) continue;
|
|
3304
|
+
let spread = 0;
|
|
3305
|
+
for (const [, members] of buildPools(sharedExternals, useAutoExternalPooling)) {
|
|
3306
|
+
if (!members.some((m) => m.external.dirty)) continue;
|
|
3307
|
+
for (const member of members)
|
|
3308
|
+
if (!member.external.dirty) {
|
|
3309
|
+
member.external.dirty = true;
|
|
3310
|
+
spread++;
|
|
3311
|
+
}
|
|
3312
|
+
}
|
|
3313
|
+
if (spread > 0)
|
|
3314
|
+
config.log.debug(3, `[${scope}] ${spread} pool member(s) marked dirty for re-election.`);
|
|
3315
|
+
}
|
|
3316
|
+
return Promise.resolve();
|
|
3317
|
+
};
|
|
3318
|
+
}
|
|
3319
|
+
|
|
3320
|
+
// node_modules/.pnpm/@softarc+native-federation@4.4.0_typescript@6.0.3/node_modules/@softarc/native-federation/dist/lib/domain/core/chunk.js
|
|
2671
3321
|
var CHUNK_PREFIX = "@nf-internal";
|
|
2672
3322
|
function toChunkImport(fileName) {
|
|
2673
3323
|
if (fileName.startsWith("./")) {
|
|
@@ -2728,9 +3378,9 @@ function createGenerateImportMap(config, ports) {
|
|
|
2728
3378
|
}
|
|
2729
3379
|
function processshareScope(importMap, shareScope, chunkBundles) {
|
|
2730
3380
|
const sharedExternals = ports.sharedExternalsRepo.getFromScope(shareScope);
|
|
3381
|
+
const index = buildServedIndex(sharedExternals);
|
|
2731
3382
|
for (const [externalName, external] of Object.entries(sharedExternals)) {
|
|
2732
3383
|
let override = void 0;
|
|
2733
|
-
let overrideScope = void 0;
|
|
2734
3384
|
const cachedBefore = cachedFingerprint(external);
|
|
2735
3385
|
for (const version of external.versions) {
|
|
2736
3386
|
if (version.action === "scope") {
|
|
@@ -2747,36 +3397,52 @@ function createGenerateImportMap(config, ports) {
|
|
|
2747
3397
|
continue;
|
|
2748
3398
|
}
|
|
2749
3399
|
version.remotes[0].cached = true;
|
|
2750
|
-
let
|
|
2751
|
-
|
|
2752
|
-
if (version.action === "share") {
|
|
2753
|
-
registerBundleChunks(chunkBundles, source.name, source.bundle);
|
|
2754
|
-
} else if (version.action === "skip") {
|
|
3400
|
+
let serving = version;
|
|
3401
|
+
if (version.action === "skip") {
|
|
2755
3402
|
if (!override) {
|
|
2756
3403
|
override = findOverride(external, shareScope, externalName) ?? "NOT_AVAILABLE";
|
|
2757
3404
|
}
|
|
2758
3405
|
if (override !== "NOT_AVAILABLE") {
|
|
2759
|
-
|
|
2760
|
-
overrideScope = getScope2(shareScope, override.remotes[0].name, externalName);
|
|
2761
|
-
source = override.remotes[0];
|
|
2762
|
-
sourceScope = overrideScope;
|
|
2763
|
-
override.remotes[0].cached = true;
|
|
3406
|
+
serving = override;
|
|
2764
3407
|
version.remotes[0].cached = false;
|
|
2765
3408
|
}
|
|
2766
3409
|
}
|
|
2767
|
-
const
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
3410
|
+
const provided = versionEntries(serving);
|
|
3411
|
+
const mappings = [];
|
|
3412
|
+
for (const [packageName, provider] of provided) {
|
|
3413
|
+
const file = provider.entries[packageName];
|
|
3414
|
+
mappings.push({
|
|
3415
|
+
packageName,
|
|
3416
|
+
url: join(getScope2(shareScope, provider.name, externalName), file),
|
|
3417
|
+
name: provider.name,
|
|
3418
|
+
file
|
|
3419
|
+
});
|
|
3420
|
+
provider.cached = true;
|
|
3421
|
+
if (version.action === "share") {
|
|
3422
|
+
registerBundleChunks(chunkBundles, provider.name, provider.bundle);
|
|
3423
|
+
}
|
|
3424
|
+
}
|
|
2773
3425
|
version.remotes.forEach((r) => {
|
|
2774
3426
|
const rScope = getScope2(shareScope, r.name, externalName);
|
|
3427
|
+
if (index && r.servedBy) {
|
|
3428
|
+
const files = index.get(r.servedBy);
|
|
3429
|
+
const servedScope = getScope2(shareScope, r.servedBy, externalName);
|
|
3430
|
+
for (const specifier of Object.keys(r.entries)) {
|
|
3431
|
+
const source = files?.get(specifier);
|
|
3432
|
+
if (!source) continue;
|
|
3433
|
+
const url = join(servedScope, source.file);
|
|
3434
|
+
addToScope(importMap, rScope, { [specifier]: url });
|
|
3435
|
+
addIntegrity(importMap, url, r.servedBy, source.file);
|
|
3436
|
+
registerBundleChunks(chunkBundles, r.servedBy, source.bundle);
|
|
3437
|
+
source.meta.cached = true;
|
|
3438
|
+
}
|
|
3439
|
+
return;
|
|
3440
|
+
}
|
|
2775
3441
|
for (const m of mappings) {
|
|
2776
3442
|
addToScope(importMap, rScope, { [m.packageName]: m.url });
|
|
2777
3443
|
}
|
|
2778
3444
|
for (const [packageName, file] of Object.entries(r.entries)) {
|
|
2779
|
-
if (packageName
|
|
3445
|
+
if (provided.has(packageName)) continue;
|
|
2780
3446
|
if (config.strict.strictEntryPointCoverage || config.profile.scopeUncoveredEntrypoints) {
|
|
2781
3447
|
warnUncoveredEntrypoint(shareScope, externalName, r.name, packageName);
|
|
2782
3448
|
continue;
|
|
@@ -2827,11 +3493,68 @@ function createGenerateImportMap(config, ports) {
|
|
|
2827
3493
|
}
|
|
2828
3494
|
config.log.warn(4, `ShareScope external ${scopedExternalName} has multiple shared versions.`);
|
|
2829
3495
|
}
|
|
3496
|
+
function buildServedIndex(externals) {
|
|
3497
|
+
let anyServed = false;
|
|
3498
|
+
for (const external of Object.values(externals)) {
|
|
3499
|
+
for (const version of external.versions) {
|
|
3500
|
+
if (version.remotes.some((r) => r.servedBy)) anyServed = true;
|
|
3501
|
+
}
|
|
3502
|
+
}
|
|
3503
|
+
if (!anyServed) return void 0;
|
|
3504
|
+
const index = /* @__PURE__ */ new Map();
|
|
3505
|
+
for (const external of Object.values(externals)) {
|
|
3506
|
+
for (const version of external.versions) {
|
|
3507
|
+
for (const remote of version.remotes) {
|
|
3508
|
+
let own = index.get(remote.name);
|
|
3509
|
+
if (!own) index.set(remote.name, own = /* @__PURE__ */ new Map());
|
|
3510
|
+
for (const [specifier, file] of Object.entries(remote.entries)) {
|
|
3511
|
+
if (!own.has(specifier))
|
|
3512
|
+
own.set(specifier, { file, bundle: remote.bundle, meta: remote });
|
|
3513
|
+
}
|
|
3514
|
+
}
|
|
3515
|
+
}
|
|
3516
|
+
}
|
|
3517
|
+
return index;
|
|
3518
|
+
}
|
|
3519
|
+
function collectServed(into, index, ctx, externalName, version) {
|
|
3520
|
+
for (const consumer of version.remotes) {
|
|
3521
|
+
if (!consumer.servedBy) continue;
|
|
3522
|
+
const files = index.get(consumer.servedBy);
|
|
3523
|
+
const scope = getScope2(ctx, consumer.name, externalName);
|
|
3524
|
+
for (const specifier of Object.keys(consumer.entries)) {
|
|
3525
|
+
const source = files?.get(specifier);
|
|
3526
|
+
if (!source) {
|
|
3527
|
+
config.log.warn(
|
|
3528
|
+
4,
|
|
3529
|
+
`[${ctx}][${externalName}][${consumer.name}] '${consumer.servedBy}' does not serve '${specifier}'.`
|
|
3530
|
+
);
|
|
3531
|
+
continue;
|
|
3532
|
+
}
|
|
3533
|
+
into.push({ scope, specifier, source, from: consumer.servedBy });
|
|
3534
|
+
}
|
|
3535
|
+
}
|
|
3536
|
+
}
|
|
3537
|
+
function flushServed(importMap, chunkBundles, served) {
|
|
3538
|
+
for (const { scope, specifier, source, from } of served) {
|
|
3539
|
+
const url = join(getScope2(GLOBAL_SCOPE, from), source.file);
|
|
3540
|
+
if (importMap.imports[specifier] === url) continue;
|
|
3541
|
+
addToScope(importMap, scope, { [specifier]: url });
|
|
3542
|
+
addIntegrity(importMap, url, from, source.file);
|
|
3543
|
+
registerBundleChunks(chunkBundles, from, source.bundle);
|
|
3544
|
+
source.meta.cached = true;
|
|
3545
|
+
}
|
|
3546
|
+
}
|
|
2830
3547
|
function addGlobalSharedExternals(importMap, chunkBundles) {
|
|
2831
3548
|
const sharedExternals = ports.sharedExternalsRepo.getFromScope();
|
|
3549
|
+
const index = buildServedIndex(sharedExternals);
|
|
3550
|
+
const served = [];
|
|
3551
|
+
const cachedBefore = /* @__PURE__ */ new Map();
|
|
2832
3552
|
for (const [externalName, external] of Object.entries(sharedExternals)) {
|
|
2833
|
-
|
|
3553
|
+
cachedBefore.set(externalName, cachedFingerprint(external));
|
|
2834
3554
|
for (const version of external.versions) {
|
|
3555
|
+
if (index && version.action !== "scope") {
|
|
3556
|
+
collectServed(served, index, GLOBAL_SCOPE, externalName, version);
|
|
3557
|
+
}
|
|
2835
3558
|
if (version.action === "skip") continue;
|
|
2836
3559
|
if (version.action === "scope") {
|
|
2837
3560
|
for (const remote of version.remotes) {
|
|
@@ -2850,29 +3573,46 @@ function createGenerateImportMap(config, ports) {
|
|
|
2850
3573
|
notifyDuplicateGlobalExternal(externalName);
|
|
2851
3574
|
continue;
|
|
2852
3575
|
}
|
|
2853
|
-
|
|
2854
|
-
for (const [packageName, file] of Object.entries(version.remotes[0].entries)) {
|
|
2855
|
-
const url = join(scope, file);
|
|
2856
|
-
addToGlobal(importMap, { [packageName]: url });
|
|
2857
|
-
addIntegrity(importMap, url, version.remotes[0].name, file);
|
|
2858
|
-
}
|
|
2859
|
-
registerBundleChunks(chunkBundles, version.remotes[0].name, version.remotes[0].bundle);
|
|
2860
|
-
version.remotes[0].cached = true;
|
|
2861
|
-
selfFillUncovered(importMap, chunkBundles, externalName, version.remotes, 1);
|
|
3576
|
+
mergeVersionEntries(importMap, chunkBundles, externalName, version);
|
|
2862
3577
|
}
|
|
2863
3578
|
for (const version of external.versions) {
|
|
2864
3579
|
if (version.action !== "skip") continue;
|
|
2865
3580
|
selfFillUncovered(importMap, chunkBundles, externalName, version.remotes);
|
|
2866
3581
|
}
|
|
2867
|
-
|
|
3582
|
+
}
|
|
3583
|
+
flushServed(importMap, chunkBundles, served);
|
|
3584
|
+
for (const [externalName, external] of Object.entries(sharedExternals)) {
|
|
3585
|
+
if (cachedFingerprint(external) !== cachedBefore.get(externalName)) {
|
|
2868
3586
|
ports.sharedExternalsRepo.addOrUpdate(externalName, external);
|
|
2869
3587
|
}
|
|
2870
3588
|
}
|
|
2871
3589
|
return importMap;
|
|
2872
3590
|
}
|
|
2873
|
-
function
|
|
2874
|
-
|
|
2875
|
-
|
|
3591
|
+
function mergeVersionEntries(importMap, chunkBundles, externalName, version) {
|
|
3592
|
+
const scopes = /* @__PURE__ */ new Map();
|
|
3593
|
+
for (const [packageName, remote] of versionEntries(version)) {
|
|
3594
|
+
if (importMap.imports[packageName]) {
|
|
3595
|
+
config.log.debug(
|
|
3596
|
+
4,
|
|
3597
|
+
`[${GLOBAL_SCOPE}][${externalName}][${remote.name}] Entrypoint '${packageName}' is already mapped by another external, keeping '${importMap.imports[packageName]}'.`
|
|
3598
|
+
);
|
|
3599
|
+
continue;
|
|
3600
|
+
}
|
|
3601
|
+
let scope = scopes.get(remote.name);
|
|
3602
|
+
if (scope === void 0) {
|
|
3603
|
+
scopes.set(remote.name, scope = getScope2(GLOBAL_SCOPE, remote.name, externalName));
|
|
3604
|
+
registerBundleChunks(chunkBundles, remote.name, remote.bundle);
|
|
3605
|
+
remote.cached = true;
|
|
3606
|
+
}
|
|
3607
|
+
const file = remote.entries[packageName];
|
|
3608
|
+
const url = join(scope, file);
|
|
3609
|
+
addToGlobal(importMap, { [packageName]: url });
|
|
3610
|
+
addIntegrity(importMap, url, remote.name, file);
|
|
3611
|
+
}
|
|
3612
|
+
}
|
|
3613
|
+
function selfFillUncovered(importMap, chunkBundles, externalName, remotes) {
|
|
3614
|
+
for (const remote of remotes) {
|
|
3615
|
+
if (remote.servedBy) continue;
|
|
2876
3616
|
for (const [packageName, file] of Object.entries(remote.entries)) {
|
|
2877
3617
|
if (importMap.imports[packageName]) continue;
|
|
2878
3618
|
if (config.strict.strictEntryPointCoverage || config.profile.scopeUncoveredEntrypoints) {
|
|
@@ -3085,15 +3825,16 @@ function createUpdateCache(config, ports) {
|
|
|
3085
3825
|
const actions = {};
|
|
3086
3826
|
if (remoteEntry?.override) removeCachedRemoteEntries(/* @__PURE__ */ new Set([remoteEntry.name]));
|
|
3087
3827
|
storeRemoteEntry(remoteEntry, (entry, external, ctx) => {
|
|
3088
|
-
const { action,
|
|
3828
|
+
const { action, provided, sameVersion } = resolveSharedExternal(entry, external, ctx);
|
|
3089
3829
|
actions[external.packageName] = { action };
|
|
3090
|
-
if (action === "skip" &&
|
|
3091
|
-
actions[external.packageName].covered =
|
|
3830
|
+
if (action === "skip" && provided) {
|
|
3831
|
+
actions[external.packageName].covered = Array.from(provided.keys());
|
|
3832
|
+
if (sameVersion) actions[external.packageName].sameVersion = true;
|
|
3092
3833
|
if (external.shareScope) {
|
|
3093
3834
|
actions[external.packageName].override = resolveOverrideEntries(
|
|
3094
3835
|
entry,
|
|
3095
3836
|
external,
|
|
3096
|
-
|
|
3837
|
+
provided
|
|
3097
3838
|
);
|
|
3098
3839
|
}
|
|
3099
3840
|
}
|
|
@@ -3123,8 +3864,10 @@ function createUpdateCache(config, ports) {
|
|
|
3123
3864
|
}
|
|
3124
3865
|
config.log.warn(8, errorMsg);
|
|
3125
3866
|
}
|
|
3126
|
-
|
|
3127
|
-
|
|
3867
|
+
const provided = sharedVersion ? sharedInfo.shareScope ? versionEntries(sharedVersion) : committedEntries(sharedVersion) : void 0;
|
|
3868
|
+
const sameVersion = sharedVersion?.tag === tag;
|
|
3869
|
+
if (action === "skip" && provided && !sameVersion) {
|
|
3870
|
+
const uncovered = uncoveredEntrypoints(remote, provided);
|
|
3128
3871
|
if (uncovered.length > 0) {
|
|
3129
3872
|
const msg = `[${sharedInfo.shareScope ?? GLOBAL_SCOPE}][${remoteEntry.name}][${sharedInfo.packageName}] Entrypoints not covered by the shared version: ${uncovered.join(", ")}.`;
|
|
3130
3873
|
if (config.strict.strictEntryPointCoverage) {
|
|
@@ -3152,65 +3895,157 @@ function createUpdateCache(config, ports) {
|
|
|
3152
3895
|
cached2.versions.push({ tag, action, host: false, remotes: [remote] });
|
|
3153
3896
|
}
|
|
3154
3897
|
commit();
|
|
3155
|
-
return { action,
|
|
3156
|
-
}
|
|
3157
|
-
function resolveOverrideEntries(remoteEntry, external,
|
|
3158
|
-
return
|
|
3159
|
-
(
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3898
|
+
return { action, provided, sameVersion };
|
|
3899
|
+
}
|
|
3900
|
+
function resolveOverrideEntries(remoteEntry, external, provided) {
|
|
3901
|
+
return Object.fromEntries(
|
|
3902
|
+
Array.from(provided, ([packageName, provider]) => [
|
|
3903
|
+
packageName,
|
|
3904
|
+
join(
|
|
3905
|
+
providerScopeUrl(remoteEntry, external, provider.name),
|
|
3906
|
+
provider.entries[packageName]
|
|
3907
|
+
)
|
|
3908
|
+
])
|
|
3909
|
+
);
|
|
3910
|
+
}
|
|
3911
|
+
function providerScopeUrl(remoteEntry, external, providerName) {
|
|
3912
|
+
return ports.remoteInfoRepo.tryGet(providerName).map((remote) => remote.scopeUrl).orThrow(() => {
|
|
3166
3913
|
config.log.error(
|
|
3167
3914
|
8,
|
|
3168
3915
|
`[${external.shareScope ?? GLOBAL_SCOPE}][${remoteEntry.name}][${external.packageName}@${external.version}][override] Remote name not found in cache.`
|
|
3169
3916
|
);
|
|
3170
|
-
return new NFError(
|
|
3171
|
-
`Could not find override url from remote ${sharedVersion.remotes[0].name}`
|
|
3172
|
-
);
|
|
3917
|
+
return new NFError(`Could not find override url from remote ${providerName}`);
|
|
3173
3918
|
});
|
|
3174
3919
|
}
|
|
3175
3920
|
}
|
|
3176
3921
|
|
|
3177
3922
|
// src/lib/core/2.app/steps/pooling/pool-dynamic-externals.ts
|
|
3178
|
-
function createPoolDynamicExternals(config) {
|
|
3923
|
+
function createPoolDynamicExternals(config, ports) {
|
|
3179
3924
|
return ({ entry, actions }) => {
|
|
3180
3925
|
const { useAutoExternalPooling } = config.feature;
|
|
3181
|
-
|
|
3182
|
-
return Promise.resolve({ entry, actions });
|
|
3183
|
-
}
|
|
3184
|
-
const byScope = /* @__PURE__ */ new Map();
|
|
3926
|
+
const declared = /* @__PURE__ */ new Map();
|
|
3185
3927
|
for (const external of entry.shared ?? []) {
|
|
3186
3928
|
const name = external.packageName;
|
|
3187
3929
|
if (!external.singleton || !actions[name]) continue;
|
|
3188
3930
|
if (external.shareScope === STRICT_SCOPE) continue;
|
|
3189
|
-
const tag = external.pool?.trim();
|
|
3190
3931
|
const shareScope = external.shareScope ?? GLOBAL_SCOPE;
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
scope: autoScope(name, useAutoExternalPooling),
|
|
3195
|
-
tags: tag ? [{ remote: entry.name, tag }] : [],
|
|
3196
|
-
value: name
|
|
3197
|
-
});
|
|
3198
|
-
byScope.set(shareScope, candidates);
|
|
3932
|
+
let names = declared.get(shareScope);
|
|
3933
|
+
if (!names) declared.set(shareScope, names = /* @__PURE__ */ new Set());
|
|
3934
|
+
names.add(name);
|
|
3199
3935
|
}
|
|
3936
|
+
if (declared.size === 0) return Promise.resolve({ entry, actions });
|
|
3200
3937
|
const scope = (name) => {
|
|
3201
3938
|
actions[name].action = "scope";
|
|
3202
3939
|
delete actions[name].override;
|
|
3203
3940
|
};
|
|
3204
|
-
for (const
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
|
|
3208
|
-
|
|
3941
|
+
for (const [shareScope, names] of declared) {
|
|
3942
|
+
if (!useAutoExternalPooling && !ports.sharedExternalsRepo.hasPoolTag(shareScope)) continue;
|
|
3943
|
+
const committed = ports.sharedExternalsRepo.getFromScope(shareScope);
|
|
3944
|
+
for (const pool of buildPools(committed, useAutoExternalPooling).values()) {
|
|
3945
|
+
const mine = pool.filter((member) => names.has(member.name));
|
|
3946
|
+
if (mine.length === 0) continue;
|
|
3947
|
+
if (mine.some((member) => actions[member.name].action === "scope")) {
|
|
3948
|
+
mine.forEach((member) => scope(member.name));
|
|
3949
|
+
continue;
|
|
3950
|
+
}
|
|
3951
|
+
const asked = gateViews(entry.name, pool);
|
|
3952
|
+
const anchor = anchorFor(entry.name, asked);
|
|
3953
|
+
if (anchor === "witnessed") continue;
|
|
3954
|
+
if (anchor === void 0) {
|
|
3955
|
+
const reason = explainDynamic(entry.name, asked);
|
|
3956
|
+
const closest = reason.closest ? `closest is '${reason.closest}'` : "no committed build serves any of it";
|
|
3957
|
+
config.log.warn(
|
|
3958
|
+
8,
|
|
3959
|
+
`[${shareScope}] '${entry.name}' serves its own family: no committed build offers every entrypoint it imports at a version it accepts \u2014 '${reason.gap}' is the gap, ${closest}. All ${mine.length} members it imports are scoped for it.`
|
|
3960
|
+
);
|
|
3961
|
+
mine.forEach((member) => scope(member.name));
|
|
3962
|
+
continue;
|
|
3209
3963
|
}
|
|
3964
|
+
redirect(entry.name, anchor, pool, asked.view, actions, shareScope);
|
|
3210
3965
|
}
|
|
3211
3966
|
}
|
|
3212
3967
|
return Promise.resolve({ entry, actions });
|
|
3213
3968
|
};
|
|
3969
|
+
function gateViews(remote, pool) {
|
|
3970
|
+
return {
|
|
3971
|
+
pool,
|
|
3972
|
+
view: committedView(pool),
|
|
3973
|
+
wants: consumedMembers(pool).get(remote) ?? [],
|
|
3974
|
+
specifiers: consumedSpecifiers(pool).get(remote) ?? /* @__PURE__ */ new Set(),
|
|
3975
|
+
acceptance: lazy(() => acceptanceTable(pool, ports.versionCheck.isCompatible))
|
|
3976
|
+
};
|
|
3977
|
+
}
|
|
3978
|
+
function anchorFor(remote, { pool, view, wants, specifiers, acceptance }) {
|
|
3979
|
+
const shared = /* @__PURE__ */ new Map();
|
|
3980
|
+
for (const [specifier, source] of view.global) shared.set(specifier, source.tag);
|
|
3981
|
+
if (isWitnessed(specifiers, shared, view.builds)) return "witnessed";
|
|
3982
|
+
const basis = basisPerMember(pool);
|
|
3983
|
+
for (const build of candidateOrder(pool, view)) {
|
|
3984
|
+
if (build === remote) continue;
|
|
3985
|
+
const candidate = view.builds.get(build);
|
|
3986
|
+
if (!servesItsOwnFamily(build, pool, basis)) continue;
|
|
3987
|
+
if (!covers(candidate.coverage, specifiers)) continue;
|
|
3988
|
+
if (!acceptsAll(acceptance(), candidate.instance, remote, wants)) continue;
|
|
3989
|
+
return build;
|
|
3990
|
+
}
|
|
3991
|
+
return void 0;
|
|
3992
|
+
}
|
|
3993
|
+
function candidateOrder(pool, view) {
|
|
3994
|
+
const serving = /* @__PURE__ */ new Set();
|
|
3995
|
+
for (const source of view.global.values()) serving.add(source.remote);
|
|
3996
|
+
const hosts = hostRemotes(pool);
|
|
3997
|
+
const rank = (build) => serving.has(build) ? 0 : hosts.has(build) ? 1 : 2;
|
|
3998
|
+
return [...view.builds.keys()].sort((a, b) => rank(a) - rank(b) || a.localeCompare(b));
|
|
3999
|
+
}
|
|
4000
|
+
function servesItsOwnFamily(build, pool, basis) {
|
|
4001
|
+
let ships2 = 0;
|
|
4002
|
+
let winsAll = true;
|
|
4003
|
+
let allScoped = true;
|
|
4004
|
+
for (const member of pool) {
|
|
4005
|
+
for (const version of member.external.versions) {
|
|
4006
|
+
const meta = version.remotes.find((r) => r.name === build);
|
|
4007
|
+
if (!meta) continue;
|
|
4008
|
+
if (meta.servedBy !== void 0) return false;
|
|
4009
|
+
ships2++;
|
|
4010
|
+
if (version.action !== "scope") allScoped = false;
|
|
4011
|
+
if (basis.get(member.name) !== build) winsAll = false;
|
|
4012
|
+
}
|
|
4013
|
+
}
|
|
4014
|
+
return ships2 > 0 && (winsAll || allScoped);
|
|
4015
|
+
}
|
|
4016
|
+
function explainDynamic(remote, { view, wants, specifiers, acceptance }) {
|
|
4017
|
+
return explainSelfServe(remote, wants, specifiers, {
|
|
4018
|
+
builds: view.builds,
|
|
4019
|
+
acceptance: acceptance()
|
|
4020
|
+
});
|
|
4021
|
+
}
|
|
4022
|
+
function redirect(remote, anchor, pool, view, actions, shareScope) {
|
|
4023
|
+
const files = view.builds.get(anchor).coverage;
|
|
4024
|
+
const scopeUrl = ports.remoteInfoRepo.tryGet(anchor).get()?.scopeUrl;
|
|
4025
|
+
if (!scopeUrl) {
|
|
4026
|
+
config.log.warn(
|
|
4027
|
+
8,
|
|
4028
|
+
`[${shareScope}][${remote}] '${anchor}' is not in the cache, so its files cannot be mapped.`
|
|
4029
|
+
);
|
|
4030
|
+
return;
|
|
4031
|
+
}
|
|
4032
|
+
for (const member of pool) {
|
|
4033
|
+
const action = actions[member.name];
|
|
4034
|
+
if (!action || action.action !== "skip") continue;
|
|
4035
|
+
const own = member.external.versions.flatMap((v) => v.remotes).find((r) => r.name === remote);
|
|
4036
|
+
if (!own) continue;
|
|
4037
|
+
const specifiers = Object.keys(own.entries);
|
|
4038
|
+
if (specifiers.length === 0) continue;
|
|
4039
|
+
const override = {};
|
|
4040
|
+
for (const specifier of specifiers) {
|
|
4041
|
+
if (view.global.get(specifier)?.remote === anchor) continue;
|
|
4042
|
+
const file = files.get(specifier);
|
|
4043
|
+
if (file) override[specifier] = join(scopeUrl, file);
|
|
4044
|
+
}
|
|
4045
|
+
action.covered = specifiers;
|
|
4046
|
+
if (Object.keys(override).length > 0) action.override = override;
|
|
4047
|
+
}
|
|
4048
|
+
}
|
|
3214
4049
|
}
|
|
3215
4050
|
|
|
3216
4051
|
// src/lib/core/2.app/steps/convert-to-import-map.ts
|
|
@@ -3248,13 +4083,7 @@ function createConvertToImportMap(config, ports) {
|
|
|
3248
4083
|
return;
|
|
3249
4084
|
}
|
|
3250
4085
|
if (actions[external.packageName].action === "skip") {
|
|
3251
|
-
const { override, covered } = actions[external.packageName];
|
|
3252
|
-
if (!external.shareScope) {
|
|
3253
|
-
if (covered) {
|
|
3254
|
-
serveUncovered(remoteEntry, external, covered, remoteEntryScope, importMap);
|
|
3255
|
-
}
|
|
3256
|
-
return;
|
|
3257
|
-
}
|
|
4086
|
+
const { override, covered, sameVersion } = actions[external.packageName];
|
|
3258
4087
|
if (override) {
|
|
3259
4088
|
Object.entries(override).forEach(([packageName, url]) => {
|
|
3260
4089
|
addToScopes(remoteEntryScope, packageName, url, importMap);
|
|
@@ -3264,10 +4093,17 @@ function createConvertToImportMap(config, ports) {
|
|
|
3264
4093
|
external,
|
|
3265
4094
|
covered ?? Object.keys(override),
|
|
3266
4095
|
remoteEntryScope,
|
|
3267
|
-
importMap
|
|
4096
|
+
importMap,
|
|
4097
|
+
sameVersion
|
|
3268
4098
|
);
|
|
3269
4099
|
return;
|
|
3270
4100
|
}
|
|
4101
|
+
if (!external.shareScope) {
|
|
4102
|
+
if (covered) {
|
|
4103
|
+
serveUncovered(remoteEntry, external, covered, remoteEntryScope, importMap, sameVersion);
|
|
4104
|
+
}
|
|
4105
|
+
return;
|
|
4106
|
+
}
|
|
3271
4107
|
log.error(
|
|
3272
4108
|
9,
|
|
3273
4109
|
`[${remoteEntry.name}][${external.packageName}] shareScope skip has no override.`
|
|
@@ -3332,11 +4168,11 @@ function createConvertToImportMap(config, ports) {
|
|
|
3332
4168
|
});
|
|
3333
4169
|
return importMap;
|
|
3334
4170
|
}
|
|
3335
|
-
function serveUncovered(remoteEntry, external, covered, remoteEntryScope, importMap) {
|
|
4171
|
+
function serveUncovered(remoteEntry, external, covered, remoteEntryScope, importMap, sameVersion = false) {
|
|
3336
4172
|
const provided = new Set(covered);
|
|
3337
4173
|
Object.entries(external.entries).forEach(([packageName, fileName]) => {
|
|
3338
4174
|
if (provided.has(packageName)) return;
|
|
3339
|
-
if (config.strict.strictEntryPointCoverage || config.profile.scopeUncoveredEntrypoints) {
|
|
4175
|
+
if (!sameVersion && (config.strict.strictEntryPointCoverage || config.profile.scopeUncoveredEntrypoints)) {
|
|
3340
4176
|
warnUncoveredEntrypoint(remoteEntry.name, external.packageName, packageName);
|
|
3341
4177
|
return;
|
|
3342
4178
|
}
|
|
@@ -3368,6 +4204,7 @@ var createInitDrivers = ({
|
|
|
3368
4204
|
}) => ({
|
|
3369
4205
|
getRemoteEntries: createGetRemoteEntries(config, adapters),
|
|
3370
4206
|
processRemoteEntries: createProcessRemoteEntries(config, adapters),
|
|
4207
|
+
markPoolsForReelection: createMarkPoolsForReelection(config, adapters),
|
|
3371
4208
|
determineSharedExternals: createDetermineSharedExternals(config, adapters),
|
|
3372
4209
|
poolSharedExternals: createPoolSharedExternals(config, adapters),
|
|
3373
4210
|
generateImportMap: createGenerateImportMap(config, adapters),
|
|
@@ -3375,7 +4212,7 @@ var createInitDrivers = ({
|
|
|
3375
4212
|
exposeModuleLoader: createExposeModuleLoader(config, adapters),
|
|
3376
4213
|
getRemoteEntry: createGetRemoteEntry(config, adapters),
|
|
3377
4214
|
updateCache: createUpdateCache(config, adapters),
|
|
3378
|
-
poolDynamicExternals: createPoolDynamicExternals(config),
|
|
4215
|
+
poolDynamicExternals: createPoolDynamicExternals(config, adapters),
|
|
3379
4216
|
convertToImportMap: createConvertToImportMap(config, adapters)
|
|
3380
4217
|
});
|
|
3381
4218
|
var INIT_FLOW_FACTORY = ({
|