@softarc/native-federation-orchestrator 4.7.0 → 4.8.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/fesm2022/@softarc/native-federation-orchestrator.mjs +1810 -1359
- package/fesm2022/@softarc/native-federation-orchestrator.mjs.map +4 -4
- package/fesm2022/audit.mjs +1 -1
- package/fesm2022/audit.mjs.map +2 -2
- package/fesm2022/module-federation.mjs +199 -32
- package/fesm2022/module-federation.mjs.map +3 -3
- package/fesm2022/node.mjs +1804 -1353
- package/fesm2022/node.mjs.map +4 -4
- package/fesm2022/options.mjs +1 -3
- package/fesm2022/options.mjs.map +2 -2
- package/fesm2022/registry.mjs +1 -1
- package/fesm2022/registry.mjs.map +2 -2
- package/fesm2022/sdk.mjs +1 -1
- package/fesm2022/sdk.mjs.map +2 -2
- package/init-registry.mjs +1 -1
- package/package.json +9 -1
- package/quickstart.mjs +14 -15
- package/types/lib/core/1.domain/externals/basis.d.ts +2 -15
- package/types/lib/core/1.domain/externals/claims.d.ts +22 -0
- package/types/lib/core/1.domain/externals/compatibility.d.ts +9 -0
- package/types/lib/core/1.domain/externals/external.contract.d.ts +3 -0
- package/types/lib/core/1.domain/externals/rows.d.ts +9 -0
- package/types/lib/core/1.domain/externals/serving.d.ts +52 -0
- package/types/lib/core/1.domain/externals/specifier.d.ts +13 -0
- package/types/lib/core/1.domain/externals/version.contract.d.ts +1 -1
- package/types/lib/core/1.domain/pooling/builds.d.ts +20 -0
- package/types/lib/core/1.domain/pooling/election.d.ts +40 -0
- package/types/lib/core/1.domain/pooling/gate.d.ts +62 -0
- package/types/lib/core/1.domain/pooling/membership.d.ts +17 -0
- package/types/lib/core/1.domain/pooling/plan.d.ts +9 -0
- package/types/lib/core/1.domain/pooling/pool-state.d.ts +4 -0
- package/types/lib/core/1.domain/pooling/records.d.ts +16 -0
- package/types/lib/core/1.domain/pooling/rules.d.ts +6 -0
- package/types/lib/core/1.domain/remote-entry/load-actions.contract.d.ts +4 -0
- package/types/lib/core/1.domain/remote-entry/remote-entry.contract.d.ts +0 -2
- package/types/lib/core/2.app/config/mode.contract.d.ts +8 -4
- package/types/lib/core/2.app/config/storage.contract.d.ts +6 -1
- package/types/lib/core/2.app/driver-ports/init/drivers.contract.d.ts +4 -2
- package/types/lib/core/2.app/driver-ports/init/for-committing-changes.port.d.ts +1 -0
- package/types/lib/core/2.app/driver-ports/init/for-deciding-serving-builds.port.d.ts +1 -0
- package/types/lib/core/2.app/driver-ports/init/for-determining-shared-externals.port.d.ts +1 -8
- package/types/lib/core/2.app/driver-ports/init/for-pooling-dynamic-externals.port.d.ts +2 -1
- package/types/lib/core/2.app/driver-ports/init/for-pooling-shared-externals.port.d.ts +1 -3
- package/types/lib/core/2.app/driver-ports/init/for-rolling-back-changes.port.d.ts +1 -0
- package/types/lib/core/2.app/driver-ports/init/for-updating-cache.d.ts +3 -2
- package/types/lib/core/2.app/driver-ports/init/index.d.ts +2 -0
- package/types/lib/core/2.app/driving-ports/for-remote-info-storage.port.d.ts +1 -0
- package/types/lib/core/2.app/driving-ports/for-scoped-externals-storage.port.d.ts +1 -0
- 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 +5 -1
- package/types/lib/core/2.app/steps/apply-winner.d.ts +6 -13
- package/types/lib/core/2.app/steps/decide-serving-builds.d.ts +5 -0
- package/types/lib/core/2.app/steps/pooling/pool-shared-externals.d.ts +0 -29
- package/types/lib/core/2.app/steps/rollback-changes.d.ts +5 -0
- package/types/lib/core/2.app/steps/store-remote-entry.d.ts +2 -2
- package/types/lib/core/3.adapters/storage/cache-state.d.ts +5 -0
- package/types/lib/core/4.config/orchestrator-version.d.ts +1 -0
- package/types/lib/core/4.config/storage/storage.config.d.ts +1 -1
- package/types/lib/core/5.di/federation-result.factory.d.ts +1 -1
- package/types/lib/sdk.index.d.ts +4 -0
- package/types/lib/utils/compare-strings.d.ts +2 -0
- package/types/lib/utils/deep-readonly.d.ts +3 -0
- package/types/lib/core/2.app/driver-ports/init/for-marking-pools-for-reelection.port.d.ts +0 -6
- package/types/lib/core/2.app/steps/pooling/anchoring.d.ts +0 -69
- package/types/lib/core/2.app/steps/pooling/mark-pools-for-reelection.d.ts +0 -5
- package/types/lib/core/2.app/steps/pooling/pool-graph.d.ts +0 -25
- package/types/lib/core/2.app/steps/pooling/pool-views.d.ts +0 -63
- package/types/lib/core/2.app/steps/pooling/pool.types.d.ts +0 -35
- package/types/lib/core/2.app/steps/pooling/pool.util.d.ts +0 -19
- package/types/lib/testing/handlers/log.handler.d.ts +0 -3
- package/types/lib/testing/pooling/no-tear.d.ts +0 -37
- package/types/lib/testing/pooling/tag-by-npm-scope.d.ts +0 -9
package/fesm2022/node.mjs
CHANGED
|
@@ -223,7 +223,7 @@ var require_semver = __commonJS({
|
|
|
223
223
|
}
|
|
224
224
|
return true;
|
|
225
225
|
};
|
|
226
|
-
var
|
|
226
|
+
var SemVer2 = class _SemVer {
|
|
227
227
|
constructor(version, options) {
|
|
228
228
|
options = parseOptions(options);
|
|
229
229
|
if (version instanceof _SemVer) {
|
|
@@ -490,7 +490,7 @@ var require_semver = __commonJS({
|
|
|
490
490
|
return this;
|
|
491
491
|
}
|
|
492
492
|
};
|
|
493
|
-
module.exports =
|
|
493
|
+
module.exports = SemVer2;
|
|
494
494
|
}
|
|
495
495
|
});
|
|
496
496
|
|
|
@@ -498,13 +498,13 @@ var require_semver = __commonJS({
|
|
|
498
498
|
var require_parse = __commonJS({
|
|
499
499
|
"node_modules/.pnpm/semver@7.8.5/node_modules/semver/functions/parse.js"(exports, module) {
|
|
500
500
|
"use strict";
|
|
501
|
-
var
|
|
501
|
+
var SemVer2 = require_semver();
|
|
502
502
|
var parse = (version, options, throwErrors = false) => {
|
|
503
|
-
if (version instanceof
|
|
503
|
+
if (version instanceof SemVer2) {
|
|
504
504
|
return version;
|
|
505
505
|
}
|
|
506
506
|
try {
|
|
507
|
-
return new
|
|
507
|
+
return new SemVer2(version, options);
|
|
508
508
|
} catch (er) {
|
|
509
509
|
if (!throwErrors) {
|
|
510
510
|
return null;
|
|
@@ -571,8 +571,8 @@ var require_lrucache = __commonJS({
|
|
|
571
571
|
var require_compare = __commonJS({
|
|
572
572
|
"node_modules/.pnpm/semver@7.8.5/node_modules/semver/functions/compare.js"(exports, module) {
|
|
573
573
|
"use strict";
|
|
574
|
-
var
|
|
575
|
-
var compare = (a, b, loose) => new
|
|
574
|
+
var SemVer2 = require_semver();
|
|
575
|
+
var compare = (a, b, loose) => new SemVer2(a, loose).compare(new SemVer2(b, loose));
|
|
576
576
|
module.exports = compare;
|
|
577
577
|
}
|
|
578
578
|
});
|
|
@@ -730,7 +730,7 @@ var require_comparator = __commonJS({
|
|
|
730
730
|
if (!m[2]) {
|
|
731
731
|
this.semver = ANY;
|
|
732
732
|
} else {
|
|
733
|
-
this.semver = new
|
|
733
|
+
this.semver = new SemVer2(m[2], this.options.loose);
|
|
734
734
|
}
|
|
735
735
|
}
|
|
736
736
|
toString() {
|
|
@@ -743,7 +743,7 @@ var require_comparator = __commonJS({
|
|
|
743
743
|
}
|
|
744
744
|
if (typeof version === "string") {
|
|
745
745
|
try {
|
|
746
|
-
version = new
|
|
746
|
+
version = new SemVer2(version, this.options);
|
|
747
747
|
} catch (er) {
|
|
748
748
|
return false;
|
|
749
749
|
}
|
|
@@ -795,7 +795,7 @@ var require_comparator = __commonJS({
|
|
|
795
795
|
var { safeRe: re, t } = require_re();
|
|
796
796
|
var cmp = require_cmp();
|
|
797
797
|
var debug = require_debug();
|
|
798
|
-
var
|
|
798
|
+
var SemVer2 = require_semver();
|
|
799
799
|
var Range = require_range();
|
|
800
800
|
}
|
|
801
801
|
});
|
|
@@ -931,7 +931,7 @@ var require_range = __commonJS({
|
|
|
931
931
|
}
|
|
932
932
|
if (typeof version === "string") {
|
|
933
933
|
try {
|
|
934
|
-
version = new
|
|
934
|
+
version = new SemVer2(version, this.options);
|
|
935
935
|
} catch (er) {
|
|
936
936
|
return false;
|
|
937
937
|
}
|
|
@@ -950,7 +950,7 @@ var require_range = __commonJS({
|
|
|
950
950
|
var parseOptions = require_parse_options();
|
|
951
951
|
var Comparator = require_comparator();
|
|
952
952
|
var debug = require_debug();
|
|
953
|
-
var
|
|
953
|
+
var SemVer2 = require_semver();
|
|
954
954
|
var {
|
|
955
955
|
safeRe: re,
|
|
956
956
|
src,
|
|
@@ -1206,16 +1206,16 @@ var require_satisfies = __commonJS({
|
|
|
1206
1206
|
var require_min_version = __commonJS({
|
|
1207
1207
|
"node_modules/.pnpm/semver@7.8.5/node_modules/semver/ranges/min-version.js"(exports, module) {
|
|
1208
1208
|
"use strict";
|
|
1209
|
-
var
|
|
1209
|
+
var SemVer2 = require_semver();
|
|
1210
1210
|
var Range = require_range();
|
|
1211
1211
|
var gt = require_gt();
|
|
1212
1212
|
var minVersion = (range, loose) => {
|
|
1213
1213
|
range = new Range(range, loose);
|
|
1214
|
-
let minver = new
|
|
1214
|
+
let minver = new SemVer2("0.0.0");
|
|
1215
1215
|
if (range.test(minver)) {
|
|
1216
1216
|
return minver;
|
|
1217
1217
|
}
|
|
1218
|
-
minver = new
|
|
1218
|
+
minver = new SemVer2("0.0.0-0");
|
|
1219
1219
|
if (range.test(minver)) {
|
|
1220
1220
|
return minver;
|
|
1221
1221
|
}
|
|
@@ -1224,7 +1224,7 @@ var require_min_version = __commonJS({
|
|
|
1224
1224
|
const comparators = range.set[i];
|
|
1225
1225
|
let setMin = null;
|
|
1226
1226
|
comparators.forEach((comparator) => {
|
|
1227
|
-
const compver = new
|
|
1227
|
+
const compver = new SemVer2(comparator.semver.version);
|
|
1228
1228
|
switch (comparator.operator) {
|
|
1229
1229
|
case ">":
|
|
1230
1230
|
if (compver.prerelease.length === 0) {
|
|
@@ -1288,19 +1288,30 @@ var createBrowser = (config) => {
|
|
|
1288
1288
|
// src/lib/core/3.adapters/checks/version.check.ts
|
|
1289
1289
|
var import_valid = __toESM(require_valid());
|
|
1290
1290
|
var import_satisfies = __toESM(require_satisfies());
|
|
1291
|
-
var
|
|
1291
|
+
var import_semver = __toESM(require_semver());
|
|
1292
1292
|
var import_min_version = __toESM(require_min_version());
|
|
1293
1293
|
var import_valid2 = __toESM(require_valid2());
|
|
1294
1294
|
var createVersionCheck = () => {
|
|
1295
|
+
const parsed = /* @__PURE__ */ new Map();
|
|
1296
|
+
const parse = (v) => {
|
|
1297
|
+
let hit = parsed.get(v);
|
|
1298
|
+
if (!hit) parsed.set(v, hit = new import_semver.default(v, true));
|
|
1299
|
+
return hit;
|
|
1300
|
+
};
|
|
1301
|
+
const satisfied = /* @__PURE__ */ new Map();
|
|
1295
1302
|
return {
|
|
1296
1303
|
isValidSemver: function(version) {
|
|
1297
1304
|
return (0, import_valid.default)(version) !== null;
|
|
1298
1305
|
},
|
|
1299
1306
|
isCompatible: function(version, range) {
|
|
1300
|
-
|
|
1307
|
+
let byVersion = satisfied.get(range);
|
|
1308
|
+
if (!byVersion) satisfied.set(range, byVersion = /* @__PURE__ */ new Map());
|
|
1309
|
+
let hit = byVersion.get(version);
|
|
1310
|
+
if (hit === void 0) byVersion.set(version, hit = (0, import_satisfies.default)(version, range));
|
|
1311
|
+
return hit;
|
|
1301
1312
|
},
|
|
1302
1313
|
compare: function(versionA, versionB) {
|
|
1303
|
-
return (
|
|
1314
|
+
return parse(versionA).compare(parse(versionB));
|
|
1304
1315
|
},
|
|
1305
1316
|
smallestVersion: function(versionRange) {
|
|
1306
1317
|
if (!(0, import_valid2.default)(versionRange)) return "0.0.0";
|
|
@@ -1313,7 +1324,7 @@ var createVersionCheck = () => {
|
|
|
1313
1324
|
// src/lib/core/native-federation.error.ts
|
|
1314
1325
|
var NFError = class extends Error {
|
|
1315
1326
|
constructor(message, cause) {
|
|
1316
|
-
super(message, cause);
|
|
1327
|
+
super(message, cause ? { cause } : void 0);
|
|
1317
1328
|
this.name = "NFError";
|
|
1318
1329
|
}
|
|
1319
1330
|
};
|
|
@@ -1435,7 +1446,8 @@ var STORAGE_KEYS = {
|
|
|
1435
1446
|
remotes: "remotes",
|
|
1436
1447
|
sharedExternals: "shared-externals",
|
|
1437
1448
|
scopedExternals: "scoped-externals",
|
|
1438
|
-
sharedChunks: "shared-chunks"
|
|
1449
|
+
sharedChunks: "shared-chunks",
|
|
1450
|
+
cacheState: "cache-state"
|
|
1439
1451
|
};
|
|
1440
1452
|
|
|
1441
1453
|
// src/lib/utils/path.ts
|
|
@@ -1497,7 +1509,7 @@ var Optional = class _Optional {
|
|
|
1497
1509
|
var createRemoteInfoRepository = (config) => {
|
|
1498
1510
|
const STORAGE = config.storage(STORAGE_KEYS.remotes, {});
|
|
1499
1511
|
if (config.clearStorage) STORAGE.clear();
|
|
1500
|
-
|
|
1512
|
+
let _cache = STORAGE.get() ?? {};
|
|
1501
1513
|
let _dirty = false;
|
|
1502
1514
|
return {
|
|
1503
1515
|
contains: function(remoteName) {
|
|
@@ -1531,6 +1543,11 @@ var createRemoteInfoRepository = (config) => {
|
|
|
1531
1543
|
STORAGE.set(_cache);
|
|
1532
1544
|
_dirty = false;
|
|
1533
1545
|
return this;
|
|
1546
|
+
},
|
|
1547
|
+
rollback: function() {
|
|
1548
|
+
_cache = STORAGE.get() ?? {};
|
|
1549
|
+
_dirty = false;
|
|
1550
|
+
return this;
|
|
1534
1551
|
}
|
|
1535
1552
|
};
|
|
1536
1553
|
};
|
|
@@ -1539,7 +1556,7 @@ var createRemoteInfoRepository = (config) => {
|
|
|
1539
1556
|
var createScopedExternalsRepository = (config) => {
|
|
1540
1557
|
const STORAGE = config.storage(STORAGE_KEYS.scopedExternals, {});
|
|
1541
1558
|
if (config.clearStorage) STORAGE.clear();
|
|
1542
|
-
|
|
1559
|
+
let _cache = STORAGE.get() ?? {};
|
|
1543
1560
|
let _dirty = false;
|
|
1544
1561
|
return {
|
|
1545
1562
|
addExternal: function(remoteName, external, version) {
|
|
@@ -1566,6 +1583,11 @@ var createScopedExternalsRepository = (config) => {
|
|
|
1566
1583
|
STORAGE.set(_cache);
|
|
1567
1584
|
_dirty = false;
|
|
1568
1585
|
return this;
|
|
1586
|
+
},
|
|
1587
|
+
rollback: function() {
|
|
1588
|
+
_cache = STORAGE.get() ?? {};
|
|
1589
|
+
_dirty = false;
|
|
1590
|
+
return this;
|
|
1569
1591
|
}
|
|
1570
1592
|
};
|
|
1571
1593
|
};
|
|
@@ -1574,39 +1596,48 @@ var createScopedExternalsRepository = (config) => {
|
|
|
1574
1596
|
var GLOBAL_SCOPE = "__GLOBAL__";
|
|
1575
1597
|
var STRICT_SCOPE = "strict";
|
|
1576
1598
|
|
|
1599
|
+
// src/lib/core/3.adapters/storage/cache-state.ts
|
|
1600
|
+
var createCacheState = (storage) => {
|
|
1601
|
+
const entry = storage(STORAGE_KEYS.cacheState, null);
|
|
1602
|
+
const read = () => {
|
|
1603
|
+
const stored = entry.get();
|
|
1604
|
+
if (typeof stored !== "object" || stored === null) return {};
|
|
1605
|
+
const { version, undecidedScopes } = stored;
|
|
1606
|
+
return {
|
|
1607
|
+
...typeof version === "string" ? { version } : {},
|
|
1608
|
+
...Array.isArray(undecidedScopes) ? { undecidedScopes: undecidedScopes.filter((s) => typeof s === "string") } : {}
|
|
1609
|
+
};
|
|
1610
|
+
};
|
|
1611
|
+
return {
|
|
1612
|
+
read,
|
|
1613
|
+
update: (fields) => void entry.set({ ...read(), ...fields })
|
|
1614
|
+
};
|
|
1615
|
+
};
|
|
1616
|
+
|
|
1577
1617
|
// src/lib/core/3.adapters/storage/shared-externals.repository.ts
|
|
1578
1618
|
var createSharedExternalsRepository = (config) => {
|
|
1579
1619
|
const STORAGE = config.storage(
|
|
1580
1620
|
STORAGE_KEYS.sharedExternals,
|
|
1581
1621
|
{ [GLOBAL_SCOPE]: {} }
|
|
1582
1622
|
);
|
|
1583
|
-
|
|
1584
|
-
const
|
|
1585
|
-
|
|
1623
|
+
const cacheState = createCacheState(config.storage);
|
|
1624
|
+
const storedUndecided = () => new Set(cacheState.read().undecidedScopes);
|
|
1625
|
+
if (config.clearStorage) {
|
|
1626
|
+
STORAGE.clear();
|
|
1627
|
+
cacheState.update({ undecidedScopes: [] });
|
|
1628
|
+
}
|
|
1629
|
+
let _cache = STORAGE.get() ?? { [GLOBAL_SCOPE]: {} };
|
|
1630
|
+
const _uncommitted = /* @__PURE__ */ new Set();
|
|
1631
|
+
let _undecided = storedUndecided();
|
|
1632
|
+
let _undecidedChanged = false;
|
|
1586
1633
|
return {
|
|
1587
|
-
// Read from the cache rather than remembered from this init's entries: a warm init may not refetch
|
|
1588
|
-
// the tagged remote at all, and pooling has to coordinate its pool anyway. Exits on the first hit.
|
|
1589
|
-
// Per share scope, because a pool never spans one: a tag in another scope is no reason to pool here.
|
|
1590
|
-
// Stored pool results count too, since a scope whose last tag left still has pool state to clear.
|
|
1591
|
-
hasPoolState: function(shareScope) {
|
|
1592
|
-
const scope = _cache[shareScope ?? GLOBAL_SCOPE];
|
|
1593
|
-
if (!scope) return false;
|
|
1594
|
-
for (const external of Object.values(scope)) {
|
|
1595
|
-
if (external.poolName !== void 0) return true;
|
|
1596
|
-
for (const version of external.versions)
|
|
1597
|
-
for (const remote of version.remotes)
|
|
1598
|
-
if (remote.pool?.trim() || remote.servedBy !== void 0 || remote.poolCause !== void 0)
|
|
1599
|
-
return true;
|
|
1600
|
-
}
|
|
1601
|
-
return false;
|
|
1602
|
-
},
|
|
1603
1634
|
getFromScope: function(shareScope) {
|
|
1604
1635
|
return { ..._cache[shareScope ?? GLOBAL_SCOPE] };
|
|
1605
1636
|
},
|
|
1606
1637
|
addOrUpdate: function(externalName, external, shareScope) {
|
|
1607
1638
|
if (!_cache[shareScope ?? GLOBAL_SCOPE]) _cache[shareScope ?? GLOBAL_SCOPE] = {};
|
|
1608
1639
|
_cache[shareScope ?? GLOBAL_SCOPE][externalName] = external;
|
|
1609
|
-
|
|
1640
|
+
_uncommitted.add(shareScope ?? GLOBAL_SCOPE);
|
|
1610
1641
|
return this;
|
|
1611
1642
|
},
|
|
1612
1643
|
getScopes: function(o = { includeGlobal: true }) {
|
|
@@ -1616,7 +1647,7 @@ var createSharedExternalsRepository = (config) => {
|
|
|
1616
1647
|
// Batched: one traversal of the graph per init instead of one per overridden remote.
|
|
1617
1648
|
removeFromAllScopes: function(remoteNames) {
|
|
1618
1649
|
if (remoteNames.size === 0) return;
|
|
1619
|
-
Object.
|
|
1650
|
+
Object.entries(_cache).forEach(([scopeName, scope]) => {
|
|
1620
1651
|
const removeExternals = [];
|
|
1621
1652
|
Object.entries(scope).forEach(([name, external]) => {
|
|
1622
1653
|
let removedCopy = false;
|
|
@@ -1632,12 +1663,12 @@ var createSharedExternalsRepository = (config) => {
|
|
|
1632
1663
|
if (keep !== remotes.length) {
|
|
1633
1664
|
remotes.length = keep;
|
|
1634
1665
|
removedCopy = true;
|
|
1635
|
-
|
|
1666
|
+
_uncommitted.add(scopeName);
|
|
1636
1667
|
if (host !== void 0 && remoteNames.has(host)) version.host = false;
|
|
1637
1668
|
}
|
|
1638
1669
|
if (remotes.length === 0) {
|
|
1639
1670
|
external.versions.splice(i, 1);
|
|
1640
|
-
|
|
1671
|
+
_uncommitted.add(scopeName);
|
|
1641
1672
|
}
|
|
1642
1673
|
}
|
|
1643
1674
|
if (removedCopy) {
|
|
@@ -1645,7 +1676,17 @@ var createSharedExternalsRepository = (config) => {
|
|
|
1645
1676
|
if (external.versions.length === 0) removeExternals.push(name);
|
|
1646
1677
|
}
|
|
1647
1678
|
});
|
|
1648
|
-
|
|
1679
|
+
const lostPools = /* @__PURE__ */ new Set();
|
|
1680
|
+
for (const name of removeExternals) {
|
|
1681
|
+
const poolName = scope[name].poolName;
|
|
1682
|
+
if (poolName !== void 0) lostPools.add(poolName);
|
|
1683
|
+
delete scope[name];
|
|
1684
|
+
}
|
|
1685
|
+
if (lostPools.size > 0) {
|
|
1686
|
+
for (const external of Object.values(scope))
|
|
1687
|
+
if (external.poolName !== void 0 && lostPools.has(external.poolName))
|
|
1688
|
+
external.dirty = true;
|
|
1689
|
+
}
|
|
1649
1690
|
});
|
|
1650
1691
|
},
|
|
1651
1692
|
scopeType: function(shareScope) {
|
|
@@ -1663,10 +1704,36 @@ var createSharedExternalsRepository = (config) => {
|
|
|
1663
1704
|
tryGet: function(external, shareScope) {
|
|
1664
1705
|
return Optional.of(_cache[shareScope ?? GLOBAL_SCOPE]?.[external]);
|
|
1665
1706
|
},
|
|
1707
|
+
uncommittedScopes: function() {
|
|
1708
|
+
return [..._uncommitted];
|
|
1709
|
+
},
|
|
1710
|
+
markUndecided: function(shareScope) {
|
|
1711
|
+
if (_undecided.has(shareScope)) return;
|
|
1712
|
+
_undecided.add(shareScope);
|
|
1713
|
+
_undecidedChanged = true;
|
|
1714
|
+
},
|
|
1715
|
+
isUndecided: function(shareScope) {
|
|
1716
|
+
return _undecided.has(shareScope);
|
|
1717
|
+
},
|
|
1718
|
+
markDecided: function(shareScope) {
|
|
1719
|
+
if (_undecided.delete(shareScope)) _undecidedChanged = true;
|
|
1720
|
+
},
|
|
1721
|
+
// Records before marks: should their write throw, the stored marks still send the next init to decide them. A
|
|
1722
|
+
// failed cache-state write after the records loses only this load's marks.
|
|
1666
1723
|
commit: function() {
|
|
1667
|
-
if (
|
|
1668
|
-
|
|
1669
|
-
|
|
1724
|
+
if (_uncommitted.size > 0) {
|
|
1725
|
+
STORAGE.set(_cache);
|
|
1726
|
+
_uncommitted.clear();
|
|
1727
|
+
}
|
|
1728
|
+
if (_undecidedChanged) cacheState.update({ undecidedScopes: [..._undecided] });
|
|
1729
|
+
_undecidedChanged = false;
|
|
1730
|
+
return this;
|
|
1731
|
+
},
|
|
1732
|
+
rollback: function() {
|
|
1733
|
+
_cache = STORAGE.get() ?? { [GLOBAL_SCOPE]: {} };
|
|
1734
|
+
_uncommitted.clear();
|
|
1735
|
+
_undecided = storedUndecided();
|
|
1736
|
+
_undecidedChanged = false;
|
|
1670
1737
|
return this;
|
|
1671
1738
|
}
|
|
1672
1739
|
};
|
|
@@ -1676,7 +1743,7 @@ var createSharedExternalsRepository = (config) => {
|
|
|
1676
1743
|
var createChunkRepository = (config) => {
|
|
1677
1744
|
const STORAGE = config.storage(STORAGE_KEYS.sharedChunks, {});
|
|
1678
1745
|
if (config.clearStorage) STORAGE.clear();
|
|
1679
|
-
|
|
1746
|
+
let _cache = STORAGE.get() ?? {};
|
|
1680
1747
|
let _dirty = false;
|
|
1681
1748
|
return {
|
|
1682
1749
|
addOrReplace: function(remoteName, bundleName, chunks) {
|
|
@@ -1701,6 +1768,11 @@ var createChunkRepository = (config) => {
|
|
|
1701
1768
|
STORAGE.set(_cache);
|
|
1702
1769
|
_dirty = false;
|
|
1703
1770
|
return this;
|
|
1771
|
+
},
|
|
1772
|
+
rollback: function() {
|
|
1773
|
+
_cache = STORAGE.get() ?? {};
|
|
1774
|
+
_dirty = false;
|
|
1775
|
+
return this;
|
|
1704
1776
|
}
|
|
1705
1777
|
};
|
|
1706
1778
|
};
|
|
@@ -1907,8 +1979,10 @@ var globalThisStorageEntry = (namespace) => (key, initialValue) => {
|
|
|
1907
1979
|
globalThisStorageEntry.type = "globalThis";
|
|
1908
1980
|
Object.freeze(globalThisStorageEntry);
|
|
1909
1981
|
|
|
1982
|
+
// src/lib/core/4.config/orchestrator-version.ts
|
|
1983
|
+
var ORCHESTRATOR_VERSION = true ? "4.8.0" : "dev";
|
|
1984
|
+
|
|
1910
1985
|
// src/lib/core/4.config/storage/publish-storage-info.ts
|
|
1911
|
-
var VERSION = true ? "4.7.0" : "dev";
|
|
1912
1986
|
var KEYS = Object.freeze(Object.values(STORAGE_KEYS));
|
|
1913
1987
|
var isStorageKey = (key) => KEYS.includes(key);
|
|
1914
1988
|
var createStorageInfo = ({
|
|
@@ -1917,7 +1991,12 @@ var createStorageInfo = ({
|
|
|
1917
1991
|
storage,
|
|
1918
1992
|
exposeGetter
|
|
1919
1993
|
}) => {
|
|
1920
|
-
const info = {
|
|
1994
|
+
const info = {
|
|
1995
|
+
version: ORCHESTRATOR_VERSION,
|
|
1996
|
+
type,
|
|
1997
|
+
namespace,
|
|
1998
|
+
keys: KEYS
|
|
1999
|
+
};
|
|
1921
2000
|
if (!exposeGetter) return Object.freeze(info);
|
|
1922
2001
|
return Object.freeze({
|
|
1923
2002
|
...info,
|
|
@@ -1938,6 +2017,12 @@ var publishStorageInfo = (options) => {
|
|
|
1938
2017
|
};
|
|
1939
2018
|
|
|
1940
2019
|
// src/lib/core/4.config/storage/storage.config.ts
|
|
2020
|
+
var isOutdatedCache = (storage) => {
|
|
2021
|
+
const cacheState = createCacheState(storage);
|
|
2022
|
+
if (cacheState.read().version === ORCHESTRATOR_VERSION) return false;
|
|
2023
|
+
cacheState.update({ version: ORCHESTRATOR_VERSION, undecidedScopes: [] });
|
|
2024
|
+
return Object.values(STORAGE_KEYS).filter((key) => key !== STORAGE_KEYS.cacheState).some((key) => storage(key, null).get() !== null);
|
|
2025
|
+
};
|
|
1941
2026
|
var createStorageConfig = (override) => {
|
|
1942
2027
|
const creator = override.storage ?? globalThisStorageEntry;
|
|
1943
2028
|
const namespace = override.storageNamespace ?? "__NATIVE_FEDERATION__";
|
|
@@ -1948,9 +2033,10 @@ var createStorageConfig = (override) => {
|
|
|
1948
2033
|
storage,
|
|
1949
2034
|
exposeGetter: override.exposeStorageGetter ?? true
|
|
1950
2035
|
});
|
|
2036
|
+
const outdated = isOutdatedCache(storage);
|
|
1951
2037
|
return {
|
|
1952
2038
|
storage,
|
|
1953
|
-
clearStorage: override.clearStorage ?? false
|
|
2039
|
+
clearStorage: (override.clearStorage ?? false) || outdated
|
|
1954
2040
|
};
|
|
1955
2041
|
};
|
|
1956
2042
|
|
|
@@ -1958,7 +2044,6 @@ var createStorageConfig = (override) => {
|
|
|
1958
2044
|
var defaultProfile = {
|
|
1959
2045
|
latestSharedExternal: false,
|
|
1960
2046
|
skipInvalidExternalVersions: false,
|
|
1961
|
-
scopeUncoveredEntrypoints: false,
|
|
1962
2047
|
overrideCachedRemotes: "init-only",
|
|
1963
2048
|
overrideCachedRemotesIfURLMatches: false
|
|
1964
2049
|
};
|
|
@@ -1970,15 +2055,13 @@ var createModeConfig = (override) => {
|
|
|
1970
2055
|
strictExternalCompatibility: override.strict,
|
|
1971
2056
|
strictExternalSameVersionCompatibility: override.strict,
|
|
1972
2057
|
strictExternalVersion: override.strict,
|
|
1973
|
-
strictImportMap: override.strict
|
|
1974
|
-
strictEntryPointCoverage: override.strict
|
|
2058
|
+
strictImportMap: override.strict
|
|
1975
2059
|
} : {
|
|
1976
2060
|
strictRemoteEntry: override.strict?.strictRemoteEntry ?? false,
|
|
1977
2061
|
strictExternalCompatibility: override.strict?.strictExternalCompatibility ?? false,
|
|
1978
2062
|
strictExternalSameVersionCompatibility: override.strict?.strictExternalSameVersionCompatibility ?? false,
|
|
1979
2063
|
strictExternalVersion: override.strict?.strictExternalVersion ?? false,
|
|
1980
|
-
strictImportMap: override.strict?.strictImportMap ?? false
|
|
1981
|
-
strictEntryPointCoverage: override.strict?.strictEntryPointCoverage ?? false
|
|
2064
|
+
strictImportMap: override.strict?.strictImportMap ?? false
|
|
1982
2065
|
};
|
|
1983
2066
|
return {
|
|
1984
2067
|
strict: strictnessConfig,
|
|
@@ -2004,7 +2087,7 @@ var createInitFlow = ({
|
|
|
2004
2087
|
adapters,
|
|
2005
2088
|
config
|
|
2006
2089
|
}) => {
|
|
2007
|
-
return (remotesOrManifestUrl) => flow.getRemoteEntries(remotesOrManifestUrl).then(flow.processRemoteEntries).then(flow.
|
|
2090
|
+
return (remotesOrManifestUrl) => flow.getRemoteEntries(remotesOrManifestUrl).then(flow.processRemoteEntries).then(flow.poolSharedExternals).then(flow.determineSharedExternals).then(flow.decideServingBuilds).then(flow.generateImportMap).then((importMap) => flow.commitChanges(importMap, { override: true })).then(flow.exposeModuleLoader).then((loadRemoteModule) => ({
|
|
2008
2091
|
config,
|
|
2009
2092
|
adapters,
|
|
2010
2093
|
loadRemoteModule
|
|
@@ -2015,8 +2098,11 @@ var createInitFlow = ({
|
|
|
2015
2098
|
var createInitRemoteEntryFlow = ({
|
|
2016
2099
|
flow
|
|
2017
2100
|
}) => {
|
|
2018
|
-
const processDynamicRemoteEntry = (remoteEntry) => flow.updateCache(remoteEntry).then(flow.poolDynamicExternals).then(flow.convertToImportMap).then(flow.commitChanges);
|
|
2019
|
-
return (remoteEntryUrl, remote) => flow.getRemoteEntry(remoteEntryUrl, remote).then((entry) => entry.map(processDynamicRemoteEntry).orElse(Promise.resolve())).then(() => void 0)
|
|
2101
|
+
const processDynamicRemoteEntry = (remoteEntry) => flow.updateCache(remoteEntry).then(flow.poolDynamicExternals).then(flow.convertToImportMap).then((importMap) => flow.commitChanges(importMap, { markUndecided: true }));
|
|
2102
|
+
return (remoteEntryUrl, remote) => flow.getRemoteEntry(remoteEntryUrl, remote).then((entry) => entry.map(processDynamicRemoteEntry).orElse(Promise.resolve())).then(() => void 0).catch((error) => {
|
|
2103
|
+
const remoteName = typeof remote === "string" ? remote : remote?.name;
|
|
2104
|
+
return flow.rollbackChanges(`[dynamic-init][${remoteName ?? remoteEntryUrl}]`).then(() => Promise.reject(error));
|
|
2105
|
+
});
|
|
2020
2106
|
};
|
|
2021
2107
|
|
|
2022
2108
|
// src/lib/core/2.app/steps/get-remote-entries.ts
|
|
@@ -2123,11 +2209,17 @@ function createGetRemoteEntries(config, ports) {
|
|
|
2123
2209
|
}
|
|
2124
2210
|
}
|
|
2125
2211
|
|
|
2212
|
+
// src/lib/utils/compare-strings.ts
|
|
2213
|
+
function compareStrings(a, b) {
|
|
2214
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
2215
|
+
}
|
|
2216
|
+
|
|
2126
2217
|
// src/lib/core/1.domain/externals/basis.ts
|
|
2127
2218
|
var coverage = (remote) => Object.keys(remote.entries).length;
|
|
2128
2219
|
function isBetterBasis(candidate, basis) {
|
|
2129
2220
|
if (basis.cached !== candidate.cached) return candidate.cached;
|
|
2130
|
-
|
|
2221
|
+
const byCoverage = coverage(candidate) - coverage(basis);
|
|
2222
|
+
return byCoverage === 0 ? compareStrings(candidate.name, basis.name) < 0 : byCoverage > 0;
|
|
2131
2223
|
}
|
|
2132
2224
|
function addRemoteToVersion(version, remote, isHost = false) {
|
|
2133
2225
|
if (isHost) {
|
|
@@ -2151,21 +2243,6 @@ function versionDemands(version) {
|
|
|
2151
2243
|
}
|
|
2152
2244
|
return Array.from(distinct.values());
|
|
2153
2245
|
}
|
|
2154
|
-
function findVersionForTag(versions, tag) {
|
|
2155
|
-
let scoped;
|
|
2156
|
-
for (const version of versions) {
|
|
2157
|
-
if (version.tag !== tag) continue;
|
|
2158
|
-
if (version.action !== "scope") return version;
|
|
2159
|
-
scoped ??= version;
|
|
2160
|
-
}
|
|
2161
|
-
return scoped;
|
|
2162
|
-
}
|
|
2163
|
-
function versionEntries(version) {
|
|
2164
|
-
return collectEntries(version, void 0);
|
|
2165
|
-
}
|
|
2166
|
-
function committedEntries(version) {
|
|
2167
|
-
return collectEntries(version, (remote) => remote.cached);
|
|
2168
|
-
}
|
|
2169
2246
|
function forEachVersionEntry(version, accepts, visit) {
|
|
2170
2247
|
for (const remote of version.remotes) {
|
|
2171
2248
|
if (remote.servedBy) continue;
|
|
@@ -2173,16 +2250,6 @@ function forEachVersionEntry(version, accepts, visit) {
|
|
|
2173
2250
|
for (const entrypoint in remote.entries) visit(entrypoint, remote);
|
|
2174
2251
|
}
|
|
2175
2252
|
}
|
|
2176
|
-
function collectEntries(version, accepts) {
|
|
2177
|
-
const entries = /* @__PURE__ */ new Map();
|
|
2178
|
-
forEachVersionEntry(version, accepts, (entrypoint, remote) => {
|
|
2179
|
-
if (!entries.has(entrypoint)) entries.set(entrypoint, remote);
|
|
2180
|
-
});
|
|
2181
|
-
return entries;
|
|
2182
|
-
}
|
|
2183
|
-
function uncoveredEntrypoints(remote, covered) {
|
|
2184
|
-
return Object.keys(remote.entries).filter((entrypoint) => !covered.has(entrypoint));
|
|
2185
|
-
}
|
|
2186
2253
|
function countUncoveredEntrypoints(remote, covered) {
|
|
2187
2254
|
let uncovered = 0;
|
|
2188
2255
|
for (const entrypoint in remote.entries) {
|
|
@@ -2191,6 +2258,89 @@ function countUncoveredEntrypoints(remote, covered) {
|
|
|
2191
2258
|
return uncovered;
|
|
2192
2259
|
}
|
|
2193
2260
|
|
|
2261
|
+
// src/lib/core/1.domain/externals/rows.ts
|
|
2262
|
+
var byTag = (compare) => (a, b) => compare(b.tag, a.tag);
|
|
2263
|
+
var rowAt = (versions, tag, action) => versions.find((v) => v.tag === tag && v.action === action);
|
|
2264
|
+
function mergeRows(versions, lead) {
|
|
2265
|
+
const merged = /* @__PURE__ */ new Map();
|
|
2266
|
+
if (lead) merged.set(`${lead.tag}|${lead.action}`, lead);
|
|
2267
|
+
return versions.filter((v) => {
|
|
2268
|
+
const key = `${v.tag}|${v.action}`;
|
|
2269
|
+
const first = merged.get(key);
|
|
2270
|
+
if (first === v) return true;
|
|
2271
|
+
if (!first) {
|
|
2272
|
+
merged.set(key, v);
|
|
2273
|
+
return true;
|
|
2274
|
+
}
|
|
2275
|
+
first.remotes.push(...v.remotes);
|
|
2276
|
+
return false;
|
|
2277
|
+
});
|
|
2278
|
+
}
|
|
2279
|
+
function findVersionForTag(versions, tag) {
|
|
2280
|
+
let scoped;
|
|
2281
|
+
for (const version of versions) {
|
|
2282
|
+
if (version.tag !== tag) continue;
|
|
2283
|
+
if (version.action !== "scope") return version;
|
|
2284
|
+
scoped ??= version;
|
|
2285
|
+
}
|
|
2286
|
+
return scoped;
|
|
2287
|
+
}
|
|
2288
|
+
function redirectRow(version, tag, acceptsTag) {
|
|
2289
|
+
const objecting = new Set(
|
|
2290
|
+
version.remotes.filter((r) => r.strictVersion && !acceptsTag(tag, version.tag, r.requiredVersion))
|
|
2291
|
+
);
|
|
2292
|
+
if (objecting.size === 0) {
|
|
2293
|
+
version.action = "skip";
|
|
2294
|
+
return [version];
|
|
2295
|
+
}
|
|
2296
|
+
if (objecting.size === version.remotes.length) {
|
|
2297
|
+
version.action = "scope";
|
|
2298
|
+
return [version];
|
|
2299
|
+
}
|
|
2300
|
+
version.remotes = version.remotes.filter((r) => !objecting.has(r));
|
|
2301
|
+
version.action = "skip";
|
|
2302
|
+
return [version, { tag: version.tag, host: false, action: "scope", remotes: [...objecting] }];
|
|
2303
|
+
}
|
|
2304
|
+
function scopeRemotes(versions, remotes) {
|
|
2305
|
+
if (remotes.length === 0) return versions;
|
|
2306
|
+
const turn = new Map(remotes.map((remote, i) => [remote, i]));
|
|
2307
|
+
const moves = /* @__PURE__ */ new Map();
|
|
2308
|
+
versions.forEach((source, at) => {
|
|
2309
|
+
if (source.action === "scope") return;
|
|
2310
|
+
const byTurn = /* @__PURE__ */ new Map();
|
|
2311
|
+
for (const copy of source.remotes) {
|
|
2312
|
+
const i = turn.get(copy.name);
|
|
2313
|
+
if (i === void 0) continue;
|
|
2314
|
+
const copies = byTurn.get(i);
|
|
2315
|
+
if (copies) copies.push(copy);
|
|
2316
|
+
else byTurn.set(i, [copy]);
|
|
2317
|
+
}
|
|
2318
|
+
if (byTurn.size === 0) return;
|
|
2319
|
+
const hostTurn = source.host ? turn.get(source.remotes[0].name) : void 0;
|
|
2320
|
+
source.remotes = source.remotes.filter((r) => !turn.has(r.name));
|
|
2321
|
+
if (hostTurn !== void 0) source.host = false;
|
|
2322
|
+
const tagMoves = moves.get(source.tag) ?? [];
|
|
2323
|
+
for (const [i, copies] of byTurn)
|
|
2324
|
+
tagMoves.push({ turn: i, at, source, copies, host: i === hostTurn });
|
|
2325
|
+
moves.set(source.tag, tagMoves);
|
|
2326
|
+
});
|
|
2327
|
+
const rows = [...versions];
|
|
2328
|
+
for (const [tag, tagMoves] of moves) {
|
|
2329
|
+
tagMoves.sort((a, b) => a.turn - b.turn || a.at - b.at);
|
|
2330
|
+
let scoped = rowAt(rows, tag, "scope");
|
|
2331
|
+
if (!scoped) {
|
|
2332
|
+
scoped = { tag, host: false, action: "scope", remotes: [] };
|
|
2333
|
+
rows.splice(rows.indexOf(tagMoves[0].source) + 1, 0, scoped);
|
|
2334
|
+
}
|
|
2335
|
+
for (const { copies, host } of tagMoves)
|
|
2336
|
+
if (host) {
|
|
2337
|
+
scoped.host = true;
|
|
2338
|
+
scoped.remotes.unshift(...copies);
|
|
2339
|
+
} else scoped.remotes.push(...copies);
|
|
2340
|
+
}
|
|
2341
|
+
return rows.filter((v) => v.remotes.length > 0);
|
|
2342
|
+
}
|
|
2343
|
+
|
|
2194
2344
|
// src/lib/core/2.app/steps/store-remote-entry.ts
|
|
2195
2345
|
function createRemoveCachedRemoteEntries(ports) {
|
|
2196
2346
|
return (remoteNames) => {
|
|
@@ -2224,6 +2374,11 @@ function createStoreRemoteEntry(config, ports, logStep) {
|
|
|
2224
2374
|
const tag = resolveVersion(remoteEntry, external);
|
|
2225
2375
|
if (tag === null) return;
|
|
2226
2376
|
if (external.singleton) {
|
|
2377
|
+
if (external.requiredVersion && !ports.versionCheck.isCompatible(tag, external.requiredVersion))
|
|
2378
|
+
config.log.warn(
|
|
2379
|
+
logStep,
|
|
2380
|
+
`[${remoteEntry.name}][${external.packageName}] requiredVersion '${external.requiredVersion}' excludes its own version '${tag}'; '${tag}' is still accepted for it.`
|
|
2381
|
+
);
|
|
2227
2382
|
onSharedExternal(remoteEntry, external, sharedExternalContext(remoteEntry, external, tag));
|
|
2228
2383
|
} else {
|
|
2229
2384
|
addScopedExternal(remoteEntry, external, tag);
|
|
@@ -2274,11 +2429,12 @@ function createStoreRemoteEntry(config, ports, logStep) {
|
|
|
2274
2429
|
...sharedInfo.pool?.trim() ? { pool: sharedInfo.pool } : {},
|
|
2275
2430
|
entries: sharedInfo.entries
|
|
2276
2431
|
};
|
|
2277
|
-
|
|
2432
|
+
let stored;
|
|
2433
|
+
const readRecord = () => stored ??= ports.sharedExternalsRepo.tryGet(sharedInfo.packageName, sharedInfo.shareScope).get() ?? { dirty: false, versions: [] };
|
|
2278
2434
|
return {
|
|
2279
2435
|
tag,
|
|
2280
2436
|
remote,
|
|
2281
|
-
|
|
2437
|
+
readRecord,
|
|
2282
2438
|
scopeType,
|
|
2283
2439
|
assertSameVersionCompatibility(matchingVersion) {
|
|
2284
2440
|
if (!remote.strictVersion) return;
|
|
@@ -2290,13 +2446,12 @@ function createStoreRemoteEntry(config, ports, logStep) {
|
|
|
2290
2446
|
}
|
|
2291
2447
|
config.log.warn(logStep, errorMsg);
|
|
2292
2448
|
},
|
|
2293
|
-
|
|
2449
|
+
write() {
|
|
2294
2450
|
ports.sharedExternalsRepo.addOrUpdate(
|
|
2295
2451
|
sharedInfo.packageName,
|
|
2296
2452
|
{
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
versions: cached2.versions.sort((a, b) => ports.versionCheck.compare(b.tag, a.tag))
|
|
2453
|
+
...readRecord(),
|
|
2454
|
+
versions: readRecord().versions.sort((a, b) => ports.versionCheck.compare(b.tag, a.tag))
|
|
2300
2455
|
},
|
|
2301
2456
|
sharedInfo.shareScope
|
|
2302
2457
|
);
|
|
@@ -2340,11 +2495,12 @@ function createProcessRemoteEntries(config, ports) {
|
|
|
2340
2495
|
function addSharedExternal(remoteEntry, _sharedInfo, {
|
|
2341
2496
|
tag,
|
|
2342
2497
|
remote,
|
|
2343
|
-
|
|
2498
|
+
readRecord,
|
|
2344
2499
|
scopeType,
|
|
2345
2500
|
assertSameVersionCompatibility,
|
|
2346
|
-
|
|
2501
|
+
write
|
|
2347
2502
|
}) {
|
|
2503
|
+
const cached2 = readRecord();
|
|
2348
2504
|
if (scopeType !== "strict") cached2.dirty = true;
|
|
2349
2505
|
const matchingVersion = findVersionForTag(cached2.versions, tag);
|
|
2350
2506
|
if (matchingVersion) {
|
|
@@ -2358,158 +2514,74 @@ function createProcessRemoteEntries(config, ports) {
|
|
|
2358
2514
|
remotes: [remote]
|
|
2359
2515
|
});
|
|
2360
2516
|
}
|
|
2361
|
-
|
|
2517
|
+
write();
|
|
2362
2518
|
}
|
|
2363
2519
|
}
|
|
2364
2520
|
|
|
2365
|
-
// src/lib/core/
|
|
2366
|
-
function
|
|
2521
|
+
// src/lib/core/1.domain/externals/compatibility.ts
|
|
2522
|
+
function createAcceptsTag(isCompatible, compare) {
|
|
2523
|
+
return (offered, ownTag, requiredVersion) => isCompatible(offered, requiredVersion) || offered === ownTag || compare(offered, ownTag) === 0;
|
|
2524
|
+
}
|
|
2525
|
+
function versionAcceptance(external, acceptsTag) {
|
|
2367
2526
|
const demands = new Map(
|
|
2368
2527
|
external.versions.map((v) => [v, versionDemands(v)])
|
|
2369
2528
|
);
|
|
2370
2529
|
return {
|
|
2371
|
-
accepts: (version, tag) => demands.get(version).every((d) =>
|
|
2372
|
-
objector: (version, tag) => demands.get(version).find((d) => d.strictVersion && !
|
|
2530
|
+
accepts: (version, tag) => demands.get(version).every((d) => acceptsTag(tag, version.tag, d.requiredVersion)),
|
|
2531
|
+
objector: (version, tag) => demands.get(version).find((d) => d.strictVersion && !acceptsTag(tag, version.tag, d.requiredVersion))
|
|
2373
2532
|
};
|
|
2374
2533
|
}
|
|
2534
|
+
|
|
2535
|
+
// src/lib/core/2.app/steps/apply-winner.ts
|
|
2536
|
+
function rejectIncompatible(config, copy, rejected, servedAt) {
|
|
2537
|
+
config.log.error(
|
|
2538
|
+
3,
|
|
2539
|
+
`[${copy.name}][${rejected}] Is not compatible with requiredRange '${copy.requiredVersion}' of shared ${servedAt}.`
|
|
2540
|
+
);
|
|
2541
|
+
throw new NFError(`External ${rejected} could not be shared.`);
|
|
2542
|
+
}
|
|
2375
2543
|
function createApplyWinner(config) {
|
|
2376
|
-
return function applyWinner(externalName, external, winner,
|
|
2544
|
+
return function applyWinner(externalName, external, winner, acceptsTag, acceptance) {
|
|
2377
2545
|
if (external.versions.length > 1) {
|
|
2378
|
-
const { accepts, objector } = acceptance ?? versionAcceptance(external, isCompatible);
|
|
2379
2546
|
const rebuilt = [];
|
|
2380
2547
|
for (const v of external.versions) {
|
|
2381
|
-
|
|
2382
|
-
if (
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
config.log.error(
|
|
2389
|
-
3,
|
|
2390
|
-
`[${strict.name}][${externalName}@${v.tag}] Is not compatible with requiredRange '${strict.requiredVersion}' of shared ${externalName}@${winner.tag}.`
|
|
2548
|
+
const objector = config.strict.strictExternalCompatibility && acceptance.objector(v, winner.tag);
|
|
2549
|
+
if (objector)
|
|
2550
|
+
rejectIncompatible(
|
|
2551
|
+
config,
|
|
2552
|
+
objector,
|
|
2553
|
+
`${externalName}@${v.tag}`,
|
|
2554
|
+
`${externalName}@${winner.tag}`
|
|
2391
2555
|
);
|
|
2392
|
-
|
|
2393
|
-
}
|
|
2394
|
-
if (v === winner) continue;
|
|
2395
|
-
if (!strict) {
|
|
2396
|
-
v.action = "skip";
|
|
2397
|
-
continue;
|
|
2398
|
-
}
|
|
2399
|
-
const objecting = new Set(
|
|
2400
|
-
v.remotes.filter((r) => r.strictVersion && !isCompatible(winner.tag, r.requiredVersion))
|
|
2401
|
-
);
|
|
2402
|
-
if (objecting.size === v.remotes.length) {
|
|
2403
|
-
v.action = "scope";
|
|
2404
|
-
continue;
|
|
2405
|
-
}
|
|
2406
|
-
v.remotes = v.remotes.filter((r) => !objecting.has(r));
|
|
2407
|
-
v.action = "skip";
|
|
2408
|
-
rebuilt.push({ tag: v.tag, host: false, action: "scope", remotes: [...objecting] });
|
|
2556
|
+
rebuilt.push(...redirectRow(v, winner.tag, acceptsTag));
|
|
2409
2557
|
}
|
|
2410
|
-
|
|
2411
|
-
external.versions = rebuilt.filter((v) => {
|
|
2412
|
-
const first = merged.get(`${v.tag}|${v.action}`);
|
|
2413
|
-
if (!first) {
|
|
2414
|
-
merged.set(`${v.tag}|${v.action}`, v);
|
|
2415
|
-
return true;
|
|
2416
|
-
}
|
|
2417
|
-
first.remotes.push(...v.remotes);
|
|
2418
|
-
return false;
|
|
2419
|
-
});
|
|
2558
|
+
external.versions = mergeRows(rebuilt, winner);
|
|
2420
2559
|
}
|
|
2421
2560
|
winner.action = "share";
|
|
2422
|
-
applyEntrypointCoveragePolicy(externalName, external);
|
|
2423
2561
|
external.dirty = false;
|
|
2424
2562
|
return external;
|
|
2425
2563
|
};
|
|
2426
|
-
function applyEntrypointCoveragePolicy(externalName, external) {
|
|
2427
|
-
const { strictEntryPointCoverage } = config.strict;
|
|
2428
|
-
if (!strictEntryPointCoverage && !config.profile.scopeUncoveredEntrypoints) return;
|
|
2429
|
-
const tears = findTears(external);
|
|
2430
|
-
if (tears.length === 0) return;
|
|
2431
|
-
if (strictEntryPointCoverage) {
|
|
2432
|
-
const { version, remote, uncovered } = tears[0];
|
|
2433
|
-
config.log.error(
|
|
2434
|
-
3,
|
|
2435
|
-
`[${externalName}@${version.tag}][${remote.name}] Entrypoints not covered by the shared version: ${uncovered.join(", ")}.`
|
|
2436
|
-
);
|
|
2437
|
-
throw new NFError(
|
|
2438
|
-
`External ${externalName} could not be shared without tearing entrypoints.`
|
|
2439
|
-
);
|
|
2440
|
-
}
|
|
2441
|
-
scopeTornRemotes(externalName, external, tears);
|
|
2442
|
-
}
|
|
2443
|
-
function findTears(external) {
|
|
2444
|
-
const shared = external.versions.find((v) => v.action === "share");
|
|
2445
|
-
if (!shared) return [];
|
|
2446
|
-
const basis = versionEntries(shared);
|
|
2447
|
-
const tears = [];
|
|
2448
|
-
for (const version of external.versions) {
|
|
2449
|
-
if (version.action === "scope") continue;
|
|
2450
|
-
if (version === shared) continue;
|
|
2451
|
-
version.remotes.forEach((remote) => {
|
|
2452
|
-
if (remote.servedBy) return;
|
|
2453
|
-
const uncovered = uncoveredEntrypoints(remote, basis);
|
|
2454
|
-
if (uncovered.length > 0) tears.push({ version, remote, uncovered });
|
|
2455
|
-
});
|
|
2456
|
-
}
|
|
2457
|
-
return tears;
|
|
2458
|
-
}
|
|
2459
|
-
function scopeTornRemotes(externalName, external, tears) {
|
|
2460
|
-
const torn = new Set(tears.map((t) => t.remote));
|
|
2461
|
-
const demotedByTag = /* @__PURE__ */ new Map();
|
|
2462
|
-
for (const { version, remote, uncovered } of tears) {
|
|
2463
|
-
const group = demotedByTag.get(version.tag);
|
|
2464
|
-
if (group) group.push(remote);
|
|
2465
|
-
else demotedByTag.set(version.tag, [remote]);
|
|
2466
|
-
config.log.debug(
|
|
2467
|
-
3,
|
|
2468
|
-
`[${externalName}@${version.tag}][${remote.name}] Scoped: entrypoints not covered by the shared version: ${uncovered.join(", ")}.`
|
|
2469
|
-
);
|
|
2470
|
-
}
|
|
2471
|
-
for (const version of external.versions) {
|
|
2472
|
-
if (version.remotes.some((r) => torn.has(r))) {
|
|
2473
|
-
version.remotes = version.remotes.filter((r) => !torn.has(r));
|
|
2474
|
-
}
|
|
2475
|
-
}
|
|
2476
|
-
external.versions = external.versions.filter((v) => v.remotes.length > 0);
|
|
2477
|
-
for (const [tag, remotes] of demotedByTag) {
|
|
2478
|
-
const scoped = external.versions.find((v) => v.tag === tag && v.action === "scope");
|
|
2479
|
-
if (scoped) scoped.remotes.push(...remotes);
|
|
2480
|
-
else external.versions.push({ tag, host: false, action: "scope", remotes });
|
|
2481
|
-
}
|
|
2482
|
-
}
|
|
2483
2564
|
}
|
|
2484
2565
|
|
|
2485
2566
|
// src/lib/core/2.app/steps/determine-shared-externals.ts
|
|
2486
2567
|
function createDetermineSharedExternals(config, ports) {
|
|
2487
2568
|
const applyWinner = createApplyWinner(config);
|
|
2488
2569
|
return () => {
|
|
2489
|
-
const
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
if (hit === void 0) {
|
|
2494
|
-
hit = ports.versionCheck.isCompatible(tag, requiredVersion);
|
|
2495
|
-
memo.set(key, hit);
|
|
2496
|
-
}
|
|
2497
|
-
return hit;
|
|
2498
|
-
};
|
|
2499
|
-
const touched = /* @__PURE__ */ new Map();
|
|
2570
|
+
const acceptsTag = createAcceptsTag(
|
|
2571
|
+
ports.versionCheck.isCompatible,
|
|
2572
|
+
ports.versionCheck.compare
|
|
2573
|
+
);
|
|
2500
2574
|
for (const shareScope of ports.sharedExternalsRepo.getScopes()) {
|
|
2575
|
+
if (ports.sharedExternalsRepo.scopeType(shareScope) === "strict") continue;
|
|
2501
2576
|
const sharedExternals = ports.sharedExternalsRepo.getFromScope(shareScope);
|
|
2502
2577
|
try {
|
|
2503
|
-
const elected = /* @__PURE__ */ new Set();
|
|
2504
2578
|
Object.entries(sharedExternals).filter(([_, e]) => e.dirty).forEach(([name, external]) => {
|
|
2505
2579
|
ports.sharedExternalsRepo.addOrUpdate(
|
|
2506
2580
|
name,
|
|
2507
|
-
setVersionActions(name, external,
|
|
2581
|
+
setVersionActions(name, external, acceptsTag),
|
|
2508
2582
|
shareScope
|
|
2509
2583
|
);
|
|
2510
|
-
elected.add(name);
|
|
2511
2584
|
});
|
|
2512
|
-
if (elected.size > 0) touched.set(shareScope, elected);
|
|
2513
2585
|
} catch (error) {
|
|
2514
2586
|
config.log.error(
|
|
2515
2587
|
3,
|
|
@@ -2527,24 +2599,22 @@ function createDetermineSharedExternals(config, ports) {
|
|
|
2527
2599
|
);
|
|
2528
2600
|
}
|
|
2529
2601
|
}
|
|
2530
|
-
return Promise.resolve(
|
|
2602
|
+
return Promise.resolve();
|
|
2531
2603
|
};
|
|
2532
2604
|
function uncoveredTears(external, winner, accepts) {
|
|
2533
|
-
const
|
|
2605
|
+
const covered = /* @__PURE__ */ new Set();
|
|
2606
|
+
forEachVersionEntry(winner, void 0, (entrypoint) => covered.add(entrypoint));
|
|
2534
2607
|
return external.versions.reduce((sum, v) => {
|
|
2535
2608
|
if (v === winner) return sum;
|
|
2536
2609
|
if (!accepts(v, winner.tag)) return sum;
|
|
2537
|
-
return v.remotes.reduce(
|
|
2538
|
-
(n, r) => r.servedBy ? n : n + countUncoveredEntrypoints(r, basis),
|
|
2539
|
-
sum
|
|
2540
|
-
);
|
|
2610
|
+
return v.remotes.reduce((n, r) => n + countUncoveredEntrypoints(r, covered), sum);
|
|
2541
2611
|
}, 0);
|
|
2542
2612
|
}
|
|
2543
|
-
function setVersionActions(externalName, external,
|
|
2613
|
+
function setVersionActions(externalName, external, acceptsTag) {
|
|
2614
|
+
const acceptance = versionAcceptance(external, acceptsTag);
|
|
2544
2615
|
if (external.versions.length === 1) {
|
|
2545
|
-
return applyWinner(externalName, external, external.versions[0],
|
|
2616
|
+
return applyWinner(externalName, external, external.versions[0], acceptsTag, acceptance);
|
|
2546
2617
|
}
|
|
2547
|
-
const acceptance = versionAcceptance(external, isCompatible);
|
|
2548
2618
|
const { accepts } = acceptance;
|
|
2549
2619
|
let sharedVersion = external.versions.find((v) => v.host);
|
|
2550
2620
|
if (!sharedVersion && config.profile.latestSharedExternal) {
|
|
@@ -2569,14 +2639,12 @@ function createDetermineSharedExternals(config, ports) {
|
|
|
2569
2639
|
return [v, [...groups.values()]];
|
|
2570
2640
|
})
|
|
2571
2641
|
);
|
|
2572
|
-
const costOf = (version, tag) => selfServing.get(version).reduce(
|
|
2642
|
+
const costOf = (version, tag) => selfServing.get(version).reduce(
|
|
2643
|
+
(n, g) => acceptsTag(tag, version.tag, g.requiredVersion) ? n : n + g.copies,
|
|
2644
|
+
0
|
|
2645
|
+
);
|
|
2573
2646
|
external.versions.forEach((vA) => {
|
|
2574
|
-
const extraDownloads = external.versions.reduce(
|
|
2575
|
-
// A copy of the winner is never redirected, so it never self-serves however its own range
|
|
2576
|
-
// reads — see `applyWinner`, which does not split the winner either.
|
|
2577
|
-
(sum, vB) => vB === vA ? sum : sum + costOf(vB, vA.tag),
|
|
2578
|
-
0
|
|
2579
|
-
);
|
|
2647
|
+
const extraDownloads = external.versions.reduce((sum, vB) => sum + costOf(vB, vA.tag), 0);
|
|
2580
2648
|
if (extraDownloads < leastExtraDownloads) {
|
|
2581
2649
|
leastExtraDownloads = extraDownloads;
|
|
2582
2650
|
leastTears = uncoveredTears(external, vA, accepts);
|
|
@@ -2594,582 +2662,582 @@ function createDetermineSharedExternals(config, ports) {
|
|
|
2594
2662
|
if (!sharedVersion) {
|
|
2595
2663
|
throw new NFError(`[${externalName}] Could not determine shared version!`);
|
|
2596
2664
|
}
|
|
2597
|
-
return applyWinner(externalName, external, sharedVersion,
|
|
2665
|
+
return applyWinner(externalName, external, sharedVersion, acceptsTag, acceptance);
|
|
2598
2666
|
}
|
|
2599
2667
|
}
|
|
2600
2668
|
|
|
2601
|
-
// src/lib/core/
|
|
2602
|
-
function
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
});
|
|
2611
|
-
return { builds: walkBuilds(members, void 0, false), global };
|
|
2669
|
+
// src/lib/core/1.domain/externals/specifier.ts
|
|
2670
|
+
function owningPackage(name) {
|
|
2671
|
+
const depth = name.startsWith("@") ? 2 : 1;
|
|
2672
|
+
let cut = -1;
|
|
2673
|
+
for (let seen = 0; seen < depth; seen++) {
|
|
2674
|
+
cut = name.indexOf("/", cut + 1);
|
|
2675
|
+
if (cut === -1) return void 0;
|
|
2676
|
+
}
|
|
2677
|
+
return name.slice(0, cut);
|
|
2612
2678
|
}
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
own.coverage.set(specifier, meta.entries[specifier]);
|
|
2634
|
-
if (!own.tags.has(specifier)) own.tags.set(specifier, version.tag);
|
|
2635
|
-
}
|
|
2636
|
-
}
|
|
2637
|
-
}
|
|
2679
|
+
var packageOf = (name) => owningPackage(name) ?? name;
|
|
2680
|
+
var SpecifierTags = class extends Map {
|
|
2681
|
+
packages = /* @__PURE__ */ new Map();
|
|
2682
|
+
constructor(entries = []) {
|
|
2683
|
+
super();
|
|
2684
|
+
for (const [specifier, tag] of entries) this.set(specifier, tag);
|
|
2685
|
+
}
|
|
2686
|
+
set(specifier, tag) {
|
|
2687
|
+
super.set(specifier, tag);
|
|
2688
|
+
const pkg = owningPackage(specifier);
|
|
2689
|
+
if (pkg === void 0) this.packages.set(specifier, tag);
|
|
2690
|
+
else if (!this.packages.has(pkg)) this.packages.set(pkg, tag);
|
|
2691
|
+
return this;
|
|
2692
|
+
}
|
|
2693
|
+
clear() {
|
|
2694
|
+
super.clear();
|
|
2695
|
+
this.packages.clear();
|
|
2696
|
+
}
|
|
2697
|
+
pinnedTag(specifier) {
|
|
2698
|
+
return this.get(specifier) ?? this.packages.get(owningPackage(specifier) ?? specifier);
|
|
2638
2699
|
}
|
|
2639
|
-
|
|
2700
|
+
};
|
|
2701
|
+
function groupByPackage(items, nameOf) {
|
|
2702
|
+
const packages = /* @__PURE__ */ new Map();
|
|
2703
|
+
for (const item of items) {
|
|
2704
|
+
const pkg = packageOf(nameOf(item));
|
|
2705
|
+
const group = packages.get(pkg);
|
|
2706
|
+
if (group) group.push(item);
|
|
2707
|
+
else packages.set(pkg, [item]);
|
|
2708
|
+
}
|
|
2709
|
+
return packages;
|
|
2640
2710
|
}
|
|
2641
|
-
|
|
2642
|
-
|
|
2711
|
+
|
|
2712
|
+
// src/lib/core/1.domain/pooling/builds.ts
|
|
2713
|
+
function copiesByRemote(members) {
|
|
2714
|
+
const shipped = /* @__PURE__ */ new Map();
|
|
2643
2715
|
for (const member of members) {
|
|
2644
|
-
|
|
2716
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2717
|
+
for (const version of member.external.versions)
|
|
2645
2718
|
for (const meta of version.remotes) {
|
|
2646
|
-
if (
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
if (!copies.
|
|
2650
|
-
|
|
2719
|
+
if (seen.has(meta.name)) continue;
|
|
2720
|
+
seen.add(meta.name);
|
|
2721
|
+
let copies = shipped.get(meta.name);
|
|
2722
|
+
if (!copies) shipped.set(meta.name, copies = []);
|
|
2723
|
+
copies.push({
|
|
2724
|
+
member: member.name,
|
|
2725
|
+
tag: version.tag,
|
|
2726
|
+
requiredVersion: meta.requiredVersion,
|
|
2727
|
+
strict: meta.strictVersion,
|
|
2728
|
+
host: version.host && meta === version.remotes[0],
|
|
2729
|
+
specifiers: Object.keys(meta.entries)
|
|
2730
|
+
});
|
|
2651
2731
|
}
|
|
2652
|
-
}
|
|
2653
2732
|
}
|
|
2654
|
-
return
|
|
2733
|
+
return shipped;
|
|
2655
2734
|
}
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
else if (list[list.length - 1] !== member.name) list.push(member.name);
|
|
2667
|
-
}
|
|
2668
|
-
}
|
|
2669
|
-
}
|
|
2670
|
-
return consumed;
|
|
2735
|
+
var specifierCount = (copies) => copies.reduce((n, copy) => n + copy.specifiers.length, 0);
|
|
2736
|
+
function buildOf(owner, copies) {
|
|
2737
|
+
const tags = new SpecifierTags();
|
|
2738
|
+
const tagByMember = /* @__PURE__ */ new Map();
|
|
2739
|
+
for (const copy of copies) {
|
|
2740
|
+
for (const specifier of copy.specifiers)
|
|
2741
|
+
if (!tags.has(specifier)) tags.set(specifier, copy.tag);
|
|
2742
|
+
tagByMember.set(copy.member, copy.tag);
|
|
2743
|
+
}
|
|
2744
|
+
return { owner, tags, tagByMember, host: copies.some((c) => c.host) };
|
|
2671
2745
|
}
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
for (
|
|
2677
|
-
const
|
|
2678
|
-
|
|
2679
|
-
const meta = remotes[r];
|
|
2680
|
-
let own = consumed.get(meta.name);
|
|
2681
|
-
if (!own) consumed.set(meta.name, own = /* @__PURE__ */ new Set());
|
|
2682
|
-
for (const specifier in meta.entries) own.add(specifier);
|
|
2683
|
-
}
|
|
2746
|
+
|
|
2747
|
+
// src/lib/core/1.domain/pooling/rules.ts
|
|
2748
|
+
function agrees(copies, tags) {
|
|
2749
|
+
for (const copy of copies)
|
|
2750
|
+
for (const s of copy.specifiers) {
|
|
2751
|
+
const tag = tags.pinnedTag(s);
|
|
2752
|
+
if (tag !== void 0 && tag !== copy.tag) return false;
|
|
2684
2753
|
}
|
|
2685
|
-
|
|
2686
|
-
return consumed;
|
|
2754
|
+
return true;
|
|
2687
2755
|
}
|
|
2688
|
-
function
|
|
2689
|
-
const
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
for (const meta of version.remotes) {
|
|
2694
|
-
if (islanded.has(meta.name)) continue;
|
|
2695
|
-
const into = meta.name === build ? served : wanted;
|
|
2696
|
-
for (const specifier in meta.entries) into.add(specifier);
|
|
2697
|
-
}
|
|
2756
|
+
function serves(copies, tags, acceptsTag) {
|
|
2757
|
+
for (const copy of copies)
|
|
2758
|
+
for (const s of copy.specifiers) {
|
|
2759
|
+
const tag = tags.get(s);
|
|
2760
|
+
if (tag === void 0 || !acceptsTag(tag, copy.tag, copy.requiredVersion)) return false;
|
|
2698
2761
|
}
|
|
2699
|
-
}
|
|
2700
|
-
for (const specifier of wanted) if (!served.has(specifier)) return false;
|
|
2701
2762
|
return true;
|
|
2702
2763
|
}
|
|
2703
|
-
function
|
|
2704
|
-
const
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
const
|
|
2708
|
-
if (
|
|
2709
|
-
|
|
2710
|
-
|
|
2764
|
+
function unwitnessedGap(copies, tags, builds) {
|
|
2765
|
+
const specifiers = copies.flatMap((c) => c.specifiers);
|
|
2766
|
+
let gap = 0;
|
|
2767
|
+
for (const build of builds) {
|
|
2768
|
+
const miss = specifiers.findIndex((s) => build.tags.pinnedTag(s) !== tags.get(s));
|
|
2769
|
+
if (miss === -1) return void 0;
|
|
2770
|
+
gap = Math.max(gap, miss);
|
|
2771
|
+
}
|
|
2772
|
+
return specifiers[gap];
|
|
2711
2773
|
}
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2774
|
+
|
|
2775
|
+
// src/lib/core/1.domain/pooling/election.ts
|
|
2776
|
+
function elect(input) {
|
|
2777
|
+
const shipped = copiesByRemote(input.members);
|
|
2778
|
+
const builds = /* @__PURE__ */ new Map();
|
|
2779
|
+
for (const [owner, copies] of shipped) builds.set(owner, buildOf(owner, copies));
|
|
2780
|
+
const pool = {
|
|
2781
|
+
...input,
|
|
2782
|
+
shipped,
|
|
2783
|
+
builds,
|
|
2784
|
+
packages: new Map([...shipped].map(([owner, copies]) => [owner, packageCopies(owner, copies)])),
|
|
2785
|
+
remotes: [...shipped.keys()],
|
|
2786
|
+
servesAt: (remote, tags) => serves(shipped.get(remote), tags, input.acceptsTag),
|
|
2787
|
+
agreesAt: (remote, tags) => agrees(shipped.get(remote), tags)
|
|
2788
|
+
};
|
|
2789
|
+
const hostRemote = pool.remotes.find((r) => builds.get(r).host);
|
|
2790
|
+
const state = roundOne(pool, hostRemote);
|
|
2791
|
+
formSubpools(pool, state);
|
|
2792
|
+
addExtensions(pool, state);
|
|
2793
|
+
promote(pool, state);
|
|
2794
|
+
while (settle(pool, state)) formSubpools(pool, state);
|
|
2795
|
+
const firstSeen = new Map(pool.remotes.map((r, i) => [r, i]));
|
|
2796
|
+
const self = state.pending.sort((a, b) => firstSeen.get(a) - firstSeen.get(b));
|
|
2797
|
+
return {
|
|
2798
|
+
winner: state.winner,
|
|
2799
|
+
winnerIsHost: hostRemote !== void 0,
|
|
2800
|
+
globalTags: state.globalTags,
|
|
2801
|
+
...placeRemotes(state.globalRemotes, state.subpools, self, (r) => missOf(pool, state, r))
|
|
2802
|
+
};
|
|
2723
2803
|
}
|
|
2724
|
-
function
|
|
2725
|
-
const
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2804
|
+
function newer(pool, a, b) {
|
|
2805
|
+
for (const { name } of pool.members) {
|
|
2806
|
+
const tagA = a.tagByMember.get(name);
|
|
2807
|
+
const tagB = b.tagByMember.get(name);
|
|
2808
|
+
if (tagA === void 0 || tagB === void 0) continue;
|
|
2809
|
+
const d = pool.compare(tagA, tagB);
|
|
2810
|
+
if (d !== 0) return d;
|
|
2811
|
+
}
|
|
2812
|
+
return 0;
|
|
2813
|
+
}
|
|
2814
|
+
function rank(pool, candidates, keys) {
|
|
2815
|
+
const scored = [...candidates].sort((a, b) => compareStrings(a.build.owner, b.build.owner)).map((c) => ({ c, k: keys.map((key) => key(c)) }));
|
|
2816
|
+
scored.sort((a, b) => {
|
|
2817
|
+
for (let i = 0; i < keys.length; i++) {
|
|
2818
|
+
const d = b.k[i] - a.k[i];
|
|
2819
|
+
if (d !== 0) return d;
|
|
2729
2820
|
}
|
|
2730
|
-
|
|
2731
|
-
|
|
2821
|
+
return newer(pool, b.c.build, a.c.build) || compareStrings(a.c.build.owner, b.c.build.owner);
|
|
2822
|
+
});
|
|
2823
|
+
return scored[0]?.c;
|
|
2732
2824
|
}
|
|
2733
|
-
function
|
|
2734
|
-
const
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2825
|
+
function roundOne(pool, hostRemote) {
|
|
2826
|
+
const { remotes } = pool;
|
|
2827
|
+
const scored = /* @__PURE__ */ new Map();
|
|
2828
|
+
const round1 = (hostRemote ? [hostRemote] : remotes).map((owner) => {
|
|
2829
|
+
const build = pool.builds.get(owner);
|
|
2830
|
+
const key = JSON.stringify([[...build.tags], [...build.tagByMember]]);
|
|
2831
|
+
let score = scored.get(key);
|
|
2832
|
+
if (!score) {
|
|
2833
|
+
const served = new Set(remotes.filter((r) => pool.servesAt(r, build.tags)));
|
|
2834
|
+
const agreeing = remotes.filter((r) => !served.has(r) && pool.agreesAt(r, build.tags)).length;
|
|
2835
|
+
scored.set(key, score = { served: [...served], agreeing });
|
|
2740
2836
|
}
|
|
2837
|
+
return { build, ...score };
|
|
2838
|
+
});
|
|
2839
|
+
const newerThan = (c) => round1.filter((o) => newer(pool, c.build, o.build) > 0).length;
|
|
2840
|
+
const round1Winner = rank(pool, round1, [
|
|
2841
|
+
...pool.latestFirst ? [newerThan] : [],
|
|
2842
|
+
(c) => c.served.length,
|
|
2843
|
+
(c) => c.agreeing,
|
|
2844
|
+
(c) => c.build.owner === pool.previousWinner ? 1 : 0
|
|
2845
|
+
]);
|
|
2846
|
+
const winner = round1Winner.build.owner;
|
|
2847
|
+
const globalTags = new SpecifierTags(round1Winner.build.tags);
|
|
2848
|
+
const globalRemotes = /* @__PURE__ */ new Set([winner, ...round1Winner.served]);
|
|
2849
|
+
const pending = remotes.filter((r) => !globalRemotes.has(r));
|
|
2850
|
+
return { winner, globalTags, globalRemotes, extensions: /* @__PURE__ */ new Set(), subpools: [], pending };
|
|
2851
|
+
}
|
|
2852
|
+
function formSubpools(pool, state) {
|
|
2853
|
+
const servable = new Map(
|
|
2854
|
+
state.pending.map((owner) => {
|
|
2855
|
+
const build = pool.builds.get(owner);
|
|
2856
|
+
const served = state.pending.filter((r) => pool.servesAt(r, build.tags));
|
|
2857
|
+
return [owner, { build, served }];
|
|
2858
|
+
})
|
|
2859
|
+
);
|
|
2860
|
+
for (; ; ) {
|
|
2861
|
+
const stillPending = new Set(state.pending);
|
|
2862
|
+
const candidates = state.pending.map((owner) => {
|
|
2863
|
+
const { build, served } = servable.get(owner);
|
|
2864
|
+
return { build, served: served.filter((r) => stillPending.has(r)) };
|
|
2865
|
+
}).filter((c) => c.served.includes(c.build.owner) && c.served.length >= 2);
|
|
2866
|
+
const best = rank(pool, candidates, [(c) => c.served.length]);
|
|
2867
|
+
if (!best) break;
|
|
2868
|
+
state.subpools.push({ owner: best.build.owner, remotes: best.served });
|
|
2869
|
+
state.pending = state.pending.filter((r) => !best.served.includes(r));
|
|
2741
2870
|
}
|
|
2742
|
-
return arrival;
|
|
2743
2871
|
}
|
|
2744
|
-
function
|
|
2745
|
-
const
|
|
2746
|
-
|
|
2747
|
-
|
|
2872
|
+
function promote(pool, state) {
|
|
2873
|
+
const { globalTags, globalRemotes } = state;
|
|
2874
|
+
const takesGlobal = (remote) => pool.servesAt(remote, globalTags) && unwitnessedGap(pool.shipped.get(remote), globalTags, pool.builds.values()) === void 0;
|
|
2875
|
+
for (const remote of state.pending) if (takesGlobal(remote)) globalRemotes.add(remote);
|
|
2876
|
+
state.pending = state.pending.filter((r) => !globalRemotes.has(r));
|
|
2877
|
+
for (const subpool of state.subpools) {
|
|
2878
|
+
const moved = subpool.remotes.filter((r) => r !== subpool.owner && takesGlobal(r));
|
|
2879
|
+
for (const remote of moved) globalRemotes.add(remote);
|
|
2880
|
+
subpool.remotes = subpool.remotes.filter((r) => !moved.includes(r));
|
|
2881
|
+
}
|
|
2882
|
+
state.subpools = state.subpools.filter((subpool) => {
|
|
2883
|
+
if (subpool.remotes.length >= 2) return true;
|
|
2884
|
+
if (takesGlobal(subpool.owner)) globalRemotes.add(subpool.owner);
|
|
2885
|
+
else state.pending.push(subpool.owner);
|
|
2886
|
+
return false;
|
|
2748
2887
|
});
|
|
2749
|
-
return shared;
|
|
2750
2888
|
}
|
|
2751
|
-
function
|
|
2752
|
-
const
|
|
2753
|
-
const
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2889
|
+
function addExtensions(pool, state) {
|
|
2890
|
+
const { globalTags, globalRemotes } = state;
|
|
2891
|
+
const runsOn = (remote) => state.subpools.find((p) => p.remotes.includes(remote))?.owner ?? remote;
|
|
2892
|
+
const agreeing = pool.remotes.filter((r) => !globalRemotes.has(r) && pool.agreesAt(r, globalTags));
|
|
2893
|
+
const runsGlobally = (remote) => pool.agreesAt(runsOn(remote), globalTags);
|
|
2894
|
+
const offered = offers(pool, globalTags, agreeing.filter(runsGlobally));
|
|
2895
|
+
const fill = pool.builds.get(state.winner).host ? agreeing.filter((r) => !runsGlobally(r)) : [];
|
|
2896
|
+
for (const [pkg, offer] of offers(pool, globalTags, fill))
|
|
2897
|
+
if (!offered.has(pkg)) offered.set(pkg, offer);
|
|
2898
|
+
for (const [pkg, offer] of offered) {
|
|
2899
|
+
if (!offer) continue;
|
|
2900
|
+
state.extensions.add(pkg);
|
|
2901
|
+
for (const s of offer.specifiers) globalTags.set(s, offer.tag);
|
|
2761
2902
|
}
|
|
2762
2903
|
}
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
let byMember = table.get(meta.name);
|
|
2773
|
-
if (!byMember) table.set(meta.name, byMember = /* @__PURE__ */ new Map());
|
|
2774
|
-
if (byMember.has(member.name)) continue;
|
|
2775
|
-
const accepted = /* @__PURE__ */ new Set();
|
|
2776
|
-
for (let t = 0; t < tags.length; t++) {
|
|
2777
|
-
if (isCompatible(tags[t], meta.requiredVersion)) accepted.add(tags[t]);
|
|
2778
|
-
}
|
|
2779
|
-
byMember.set(member.name, accepted);
|
|
2780
|
-
}
|
|
2904
|
+
function packageCopies(owner, copies) {
|
|
2905
|
+
const packages = /* @__PURE__ */ new Map();
|
|
2906
|
+
for (const copy of copies)
|
|
2907
|
+
for (const s of copy.specifiers) {
|
|
2908
|
+
const pkg = packageOf(s);
|
|
2909
|
+
const seen = packages.get(pkg);
|
|
2910
|
+
if (seen === void 0) packages.set(pkg, { owner, tag: copy.tag, specifiers: /* @__PURE__ */ new Set([s]) });
|
|
2911
|
+
else if (seen && seen.tag !== copy.tag) packages.set(pkg, null);
|
|
2912
|
+
else seen?.specifiers.add(s);
|
|
2781
2913
|
}
|
|
2782
|
-
|
|
2783
|
-
return table;
|
|
2914
|
+
return packages;
|
|
2784
2915
|
}
|
|
2785
|
-
function
|
|
2786
|
-
|
|
2787
|
-
|
|
2916
|
+
function offers(pool, globalTags, remotes) {
|
|
2917
|
+
const candidates = /* @__PURE__ */ new Map();
|
|
2918
|
+
for (const owner of remotes)
|
|
2919
|
+
for (const [pkg, copy] of pool.packages.get(owner))
|
|
2920
|
+
if (globalTags.pinnedTag(pkg) === void 0)
|
|
2921
|
+
candidates.set(pkg, [...candidates.get(pkg) ?? [], copy]);
|
|
2922
|
+
const offered = /* @__PURE__ */ new Map();
|
|
2923
|
+
for (const [pkg, copies] of candidates)
|
|
2924
|
+
offered.set(pkg, oneTag(copies) ? coveringCopy(copies) : null);
|
|
2925
|
+
return offered;
|
|
2788
2926
|
}
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2927
|
+
var oneTag = (copies) => copies.every((c) => c !== null && c.tag === copies[0].tag);
|
|
2928
|
+
function coveringCopy(candidates) {
|
|
2929
|
+
const scored = candidates.map((c) => ({
|
|
2930
|
+
c,
|
|
2931
|
+
covers: candidates.filter((o) => contains(c.specifiers, o.specifiers)).length
|
|
2932
|
+
}));
|
|
2933
|
+
scored.sort(
|
|
2934
|
+
(a, b) => b.covers - a.covers || b.c.specifiers.size - a.c.specifiers.size || compareStrings(a.c.owner, b.c.owner)
|
|
2935
|
+
);
|
|
2936
|
+
return scored[0].c;
|
|
2798
2937
|
}
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2938
|
+
var contains = (a, b) => [...b].every((s) => a.has(s));
|
|
2939
|
+
function settle(pool, state) {
|
|
2940
|
+
const { globalTags, globalRemotes, extensions } = state;
|
|
2941
|
+
let demoted = false;
|
|
2942
|
+
for (; ; ) {
|
|
2943
|
+
const dropped = new Set([...extensions].filter((pkg) => !publishedWhole(pool, state, pkg)));
|
|
2944
|
+
if (dropped.size === 0) return demoted;
|
|
2945
|
+
for (const pkg of dropped) extensions.delete(pkg);
|
|
2946
|
+
const kept = [...globalTags].filter(([s]) => !dropped.has(packageOf(s)));
|
|
2947
|
+
globalTags.clear();
|
|
2948
|
+
for (const [s, tag] of kept) globalTags.set(s, tag);
|
|
2949
|
+
for (const remote of globalRemotes)
|
|
2950
|
+
if (remote !== state.winner && !pool.servesAt(remote, globalTags)) {
|
|
2951
|
+
globalRemotes.delete(remote);
|
|
2952
|
+
const subpool = state.subpools.find(
|
|
2953
|
+
(p) => pool.servesAt(remote, pool.builds.get(p.owner).tags)
|
|
2954
|
+
);
|
|
2955
|
+
if (subpool) subpool.remotes.push(remote);
|
|
2956
|
+
else state.pending.push(remote);
|
|
2957
|
+
demoted = true;
|
|
2808
2958
|
}
|
|
2809
|
-
}
|
|
2810
|
-
if (matches2) return true;
|
|
2811
2959
|
}
|
|
2812
|
-
return false;
|
|
2813
2960
|
}
|
|
2814
|
-
function
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2961
|
+
function publishedWhole(pool, state, pkg) {
|
|
2962
|
+
const published = new Set([...state.globalTags.keys()].filter((s) => packageOf(s) === pkg));
|
|
2963
|
+
const tag = state.globalTags.pinnedTag(pkg);
|
|
2964
|
+
return [...state.globalRemotes].some((remote) => {
|
|
2965
|
+
const copy = pool.packages.get(remote).get(pkg);
|
|
2966
|
+
return !!copy && copy.tag === tag && copy.specifiers.size === published.size && contains(copy.specifiers, published);
|
|
2967
|
+
});
|
|
2968
|
+
}
|
|
2969
|
+
function missOf(pool, state, remote) {
|
|
2970
|
+
const { globalTags } = state;
|
|
2971
|
+
const copies = pool.shipped.get(remote);
|
|
2972
|
+
let rejected;
|
|
2973
|
+
for (const copy of copies)
|
|
2974
|
+
for (const s of copy.specifiers) {
|
|
2975
|
+
const tag = globalTags.get(s);
|
|
2976
|
+
if (tag === void 0 || pool.acceptsTag(tag, copy.tag, copy.requiredVersion)) continue;
|
|
2977
|
+
if (copy.strict) return { cause: "incompatible", member: copy.member, tag, strict: true };
|
|
2978
|
+
rejected ??= { cause: "incompatible", member: copy.member, tag, strict: false };
|
|
2826
2979
|
}
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
}
|
|
2832
|
-
|
|
2980
|
+
if (rejected) return rejected;
|
|
2981
|
+
for (const copy of copies)
|
|
2982
|
+
for (const s of copy.specifiers) if (!globalTags.has(s)) return { cause: "uncovered", gap: s };
|
|
2983
|
+
const gap = unwitnessedGap(copies, globalTags, pool.builds.values());
|
|
2984
|
+
if (gap) return { cause: "uncovered", gap, unshipped: true };
|
|
2985
|
+
if (state.subpools.some((p) => p.owner === remote)) return void 0;
|
|
2986
|
+
throw new Error(`'${remote}' missed the global map with nothing it rejects or lacks.`);
|
|
2833
2987
|
}
|
|
2834
|
-
function
|
|
2835
|
-
const
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
return void 0;
|
|
2988
|
+
function previousWinner(members) {
|
|
2989
|
+
const winners = new Set(members.flatMap((m) => m.external.poolWinner ?? []));
|
|
2990
|
+
const [winner] = winners;
|
|
2991
|
+
if (winners.size !== 1) return void 0;
|
|
2992
|
+
const ships = members.some(
|
|
2993
|
+
(m) => m.external.versions.some((v) => v.remotes.some((r) => r.name === winner))
|
|
2994
|
+
);
|
|
2995
|
+
return ships ? winner : void 0;
|
|
2843
2996
|
}
|
|
2844
|
-
function
|
|
2845
|
-
const
|
|
2846
|
-
const
|
|
2847
|
-
|
|
2848
|
-
const
|
|
2849
|
-
const
|
|
2850
|
-
const
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
};
|
|
2856
|
-
const electAnchor = (anchor, served) => {
|
|
2857
|
-
pending.delete(anchor);
|
|
2858
|
-
assignment.set(anchor, void 0);
|
|
2859
|
-
for (const consumer of served) {
|
|
2860
|
-
pending.delete(consumer);
|
|
2861
|
-
anchored.add(consumer);
|
|
2862
|
-
assignment.set(consumer, anchor);
|
|
2863
|
-
}
|
|
2864
|
-
};
|
|
2865
|
-
for (const host of [...hosts].sort(byPriority)) {
|
|
2866
|
-
const served = [];
|
|
2867
|
-
if (builds.has(host)) {
|
|
2868
|
-
for (const consumer of pending) {
|
|
2869
|
-
if (consumer !== host && canServe(host, consumer)) served.push(consumer);
|
|
2870
|
-
}
|
|
2871
|
-
}
|
|
2872
|
-
electAnchor(host, served);
|
|
2873
|
-
}
|
|
2874
|
-
for (; ; ) {
|
|
2875
|
-
let best;
|
|
2876
|
-
let bestServed = [];
|
|
2877
|
-
for (const candidate of candidates) {
|
|
2878
|
-
if (anchored.has(candidate)) continue;
|
|
2879
|
-
const served = [];
|
|
2880
|
-
for (const consumer of pending) {
|
|
2881
|
-
if (consumer !== candidate && canServe(candidate, consumer)) served.push(consumer);
|
|
2882
|
-
}
|
|
2883
|
-
if (served.length > bestServed.length) {
|
|
2884
|
-
best = candidate;
|
|
2885
|
-
bestServed = served;
|
|
2886
|
-
}
|
|
2887
|
-
}
|
|
2888
|
-
if (best === void 0) break;
|
|
2889
|
-
electAnchor(best, bestServed);
|
|
2890
|
-
}
|
|
2891
|
-
for (const consumer of pending) assignment.set(consumer, void 0);
|
|
2892
|
-
return assignment;
|
|
2997
|
+
function placeRemotes(globalRemotes, subpools, self, missOf2) {
|
|
2998
|
+
const misses = /* @__PURE__ */ new Map();
|
|
2999
|
+
for (const remote of [...subpools.flatMap((p) => p.remotes), ...self])
|
|
3000
|
+
misses.set(remote, missOf2(remote));
|
|
3001
|
+
const placements = /* @__PURE__ */ new Map();
|
|
3002
|
+
for (const remote of globalRemotes) placements.set(remote, { kind: "global" });
|
|
3003
|
+
for (const { owner, remotes } of subpools)
|
|
3004
|
+
for (const remote of remotes) placements.set(remote, { kind: "subpool", owner });
|
|
3005
|
+
for (const remote of self)
|
|
3006
|
+
placements.set(remote, { kind: "self", cause: misses.get(remote).cause });
|
|
3007
|
+
return { placements, misses };
|
|
2893
3008
|
}
|
|
2894
3009
|
|
|
2895
|
-
// src/lib/core/
|
|
2896
|
-
function
|
|
2897
|
-
const
|
|
2898
|
-
const
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
ids.set(key, id);
|
|
2905
|
-
parent.push(id);
|
|
2906
|
-
size.push(1);
|
|
3010
|
+
// src/lib/core/1.domain/pooling/membership.ts
|
|
3011
|
+
function unionFind() {
|
|
3012
|
+
const parent = /* @__PURE__ */ new Map();
|
|
3013
|
+
const find = (node) => {
|
|
3014
|
+
for (let up = parent.get(node); up !== void 0; up = parent.get(node)) {
|
|
3015
|
+
const grand = parent.get(up);
|
|
3016
|
+
if (grand === void 0) return up;
|
|
3017
|
+
parent.set(node, grand);
|
|
3018
|
+
node = grand;
|
|
2907
3019
|
}
|
|
2908
|
-
return
|
|
3020
|
+
return node;
|
|
2909
3021
|
};
|
|
2910
|
-
const
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
}
|
|
2915
|
-
return x;
|
|
2916
|
-
};
|
|
2917
|
-
return {
|
|
2918
|
-
union(a, b) {
|
|
2919
|
-
let rootA = findRoot(intern(a));
|
|
2920
|
-
let rootB = findRoot(intern(b));
|
|
2921
|
-
if (rootA === rootB) return;
|
|
2922
|
-
if (size[rootA] < size[rootB]) [rootA, rootB] = [rootB, rootA];
|
|
2923
|
-
parent[rootB] = rootA;
|
|
2924
|
-
size[rootA] = size[rootA] + size[rootB];
|
|
2925
|
-
},
|
|
2926
|
-
// Interns the key if unseen — an isolated key is its own component.
|
|
2927
|
-
component(key) {
|
|
2928
|
-
return findRoot(intern(key));
|
|
2929
|
-
}
|
|
3022
|
+
const union = (a, b) => {
|
|
3023
|
+
const rootA = find(a);
|
|
3024
|
+
const rootB = find(b);
|
|
3025
|
+
if (rootA !== rootB) parent.set(rootB, rootA);
|
|
2930
3026
|
};
|
|
3027
|
+
return { find, union };
|
|
2931
3028
|
}
|
|
2932
3029
|
var extNode = (name) => `ext\0${name}`;
|
|
2933
|
-
var
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
for (
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
3030
|
+
var labelNode = (label) => `label\0${label}`;
|
|
3031
|
+
var labelOf = (pool) => pool?.trim() || void 0;
|
|
3032
|
+
function labelsIn(sharedExternals) {
|
|
3033
|
+
const labelsOf = /* @__PURE__ */ new Map();
|
|
3034
|
+
for (const [name, external] of Object.entries(sharedExternals))
|
|
3035
|
+
labelsOf.set(
|
|
3036
|
+
name,
|
|
3037
|
+
external.versions.flatMap((v) => v.remotes.flatMap((r) => labelOf(r.pool) ?? []))
|
|
3038
|
+
);
|
|
3039
|
+
return labelsOf;
|
|
2942
3040
|
}
|
|
2943
|
-
function
|
|
2944
|
-
const
|
|
2945
|
-
const
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
}
|
|
2951
|
-
}
|
|
2952
|
-
const declared = new Set(candidates.map((c) => c.name));
|
|
2953
|
-
for (const candidate of candidates) {
|
|
2954
|
-
const owner = owningPackage(candidate.name);
|
|
2955
|
-
if (owner !== void 0 && declared.has(owner))
|
|
2956
|
-
dsu.union(extNode(candidate.name), extNode(owner));
|
|
3041
|
+
function labelledComponents(labelsOf) {
|
|
3042
|
+
const { find, union } = unionFind();
|
|
3043
|
+
for (const [name, labels] of labelsOf)
|
|
3044
|
+
for (const label of labels) union(extNode(name), labelNode(label));
|
|
3045
|
+
for (const name of labelsOf.keys()) {
|
|
3046
|
+
const owner = owningPackage(name);
|
|
3047
|
+
if (owner !== void 0) union(extNode(name), extNode(owner));
|
|
2957
3048
|
}
|
|
2958
3049
|
const byComponent = /* @__PURE__ */ new Map();
|
|
2959
|
-
for (const
|
|
2960
|
-
const root =
|
|
2961
|
-
const
|
|
2962
|
-
|
|
2963
|
-
}
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
3050
|
+
for (const name of labelsOf.keys()) {
|
|
3051
|
+
const root = find(extNode(name));
|
|
3052
|
+
const names = byComponent.get(root) ?? byComponent.set(root, []).get(root);
|
|
3053
|
+
names.push(name);
|
|
3054
|
+
}
|
|
3055
|
+
return [...byComponent.values()].filter((names) => names.some((n) => labelsOf.get(n).length > 0));
|
|
3056
|
+
}
|
|
3057
|
+
function buildPools(sharedExternals) {
|
|
3058
|
+
const labelsOf = labelsIn(sharedExternals);
|
|
3059
|
+
const pools = /* @__PURE__ */ new Map();
|
|
3060
|
+
const labelledAlone = [];
|
|
3061
|
+
for (const names of labelledComponents(labelsOf)) {
|
|
3062
|
+
if (names.length < 2) labelledAlone.push(names[0]);
|
|
3063
|
+
else {
|
|
3064
|
+
const members = names.map((name) => ({ name, external: sharedExternals[name] }));
|
|
3065
|
+
pools.set(
|
|
3066
|
+
mostDeclaredLabel(members, labelsOf),
|
|
3067
|
+
members.sort((a, b) => compareStrings(a.name, b.name))
|
|
3068
|
+
);
|
|
2977
3069
|
}
|
|
2978
|
-
pools.push(members);
|
|
2979
|
-
}
|
|
2980
|
-
pools.sort((a, b) => a[0].name.localeCompare(b[0].name));
|
|
2981
|
-
const named = /* @__PURE__ */ new Map();
|
|
2982
|
-
for (const members of pools) {
|
|
2983
|
-
const label = mostDeclaredTag(members) ?? members[0].name;
|
|
2984
|
-
let name = label;
|
|
2985
|
-
for (let n = 2; named.has(name); n++) name = `${label}~${n}`;
|
|
2986
|
-
named.set(
|
|
2987
|
-
name,
|
|
2988
|
-
members.map((m) => m.value)
|
|
2989
|
-
);
|
|
2990
3070
|
}
|
|
2991
|
-
return
|
|
3071
|
+
return { pools, labelledAlone };
|
|
3072
|
+
}
|
|
3073
|
+
function pooledPackagesWith(sharedExternals, copies) {
|
|
3074
|
+
const labelsOf = labelsIn(sharedExternals);
|
|
3075
|
+
for (const { name, pool } of copies) {
|
|
3076
|
+
const labels = labelsOf.get(name) ?? [];
|
|
3077
|
+
const label = labelOf(pool);
|
|
3078
|
+
labelsOf.set(name, label === void 0 ? labels : [...labels, label]);
|
|
3079
|
+
}
|
|
3080
|
+
return new Set(
|
|
3081
|
+
labelledComponents(labelsOf).filter((names) => names.length > 1).flatMap((names) => names.map(packageOf))
|
|
3082
|
+
);
|
|
2992
3083
|
}
|
|
2993
|
-
function
|
|
3084
|
+
function mostDeclaredLabel(members, labelsOf) {
|
|
2994
3085
|
const counts = /* @__PURE__ */ new Map();
|
|
2995
3086
|
for (const member of members)
|
|
2996
|
-
for (const
|
|
2997
|
-
let best;
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
if (count > top || count === top &&
|
|
3001
|
-
|
|
3087
|
+
for (const label of labelsOf.get(member.name)) counts.set(label, (counts.get(label) ?? 0) + 1);
|
|
3088
|
+
let best = "";
|
|
3089
|
+
let top = 0;
|
|
3090
|
+
for (const [label, count] of counts)
|
|
3091
|
+
if (count > top || count === top && compareStrings(label, best) < 0)
|
|
3092
|
+
[best, top] = [label, count];
|
|
3002
3093
|
return best;
|
|
3003
3094
|
}
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
const tag = r.pool?.trim();
|
|
3011
|
-
return tag ? [{ remote: r.name, tag }] : [];
|
|
3012
|
-
})
|
|
3013
|
-
),
|
|
3014
|
-
value: { name, external }
|
|
3015
|
-
})
|
|
3095
|
+
|
|
3096
|
+
// src/lib/core/1.domain/pooling/pool-state.ts
|
|
3097
|
+
function hasPoolResults(external) {
|
|
3098
|
+
if (external.poolName !== void 0 || external.poolWinner !== void 0) return true;
|
|
3099
|
+
return external.versions.some(
|
|
3100
|
+
(v) => v.remotes.some((r) => r.servedBy !== void 0 || r.poolCause !== void 0)
|
|
3016
3101
|
);
|
|
3017
|
-
|
|
3102
|
+
}
|
|
3103
|
+
function scopeHasPoolState(scope) {
|
|
3104
|
+
for (const name in scope) {
|
|
3105
|
+
const external = scope[name];
|
|
3106
|
+
if (hasPoolResults(external)) return true;
|
|
3107
|
+
for (const version of external.versions)
|
|
3108
|
+
for (const remote of version.remotes) if (remote.pool?.trim()) return true;
|
|
3109
|
+
}
|
|
3110
|
+
return false;
|
|
3018
3111
|
}
|
|
3019
3112
|
|
|
3020
|
-
// src/lib/core/
|
|
3021
|
-
function
|
|
3022
|
-
|
|
3023
|
-
|
|
3113
|
+
// src/lib/core/1.domain/pooling/records.ts
|
|
3114
|
+
function memberRecord(member, pool, compare) {
|
|
3115
|
+
const { globalTags, placements } = pool;
|
|
3116
|
+
let sharedTag = globalTags.get(member.name);
|
|
3117
|
+
for (const version of member.external.versions)
|
|
3118
|
+
for (const meta of version.remotes)
|
|
3119
|
+
for (const s in meta.entries) sharedTag ??= globalTags.get(s);
|
|
3120
|
+
const claimsGlobally = (name) => placements.get(name).kind === "global";
|
|
3121
|
+
const placed = [];
|
|
3122
|
+
let winnerRow;
|
|
3123
|
+
const place = (tag, action, meta) => {
|
|
3124
|
+
const row = { tag, host: false, action, remotes: [meta] };
|
|
3125
|
+
placed.push(row);
|
|
3126
|
+
if (meta.name !== pool.winner) return;
|
|
3127
|
+
winnerRow = row;
|
|
3128
|
+
row.host = pool.winnerIsHost;
|
|
3129
|
+
};
|
|
3130
|
+
for (const version of member.external.versions)
|
|
3131
|
+
for (const stored of version.remotes) {
|
|
3132
|
+
const { servedBy: _servedBy, poolCause: _poolCause, ...rest } = stored;
|
|
3133
|
+
const meta = { ...rest, cached: false };
|
|
3134
|
+
const placement = placements.get(meta.name);
|
|
3135
|
+
if (placement.kind === "global")
|
|
3136
|
+
place(version.tag, version.tag === sharedTag ? "share" : "skip", meta);
|
|
3137
|
+
else if (placement.kind === "subpool")
|
|
3138
|
+
place(version.tag, "skip", { ...meta, servedBy: placement.owner });
|
|
3139
|
+
else place(version.tag, "scope", { ...meta, poolCause: placement.cause });
|
|
3140
|
+
}
|
|
3141
|
+
const rows = mergeRows(placed, winnerRow);
|
|
3142
|
+
for (const row of rows)
|
|
3143
|
+
row.remotes.sort((a, b) => Number(!claimsGlobally(a.name)) - Number(!claimsGlobally(b.name)));
|
|
3144
|
+
const order = { share: 0, skip: 1, scope: 2 };
|
|
3145
|
+
const newest = byTag(compare);
|
|
3146
|
+
const versions = rows.sort((a, b) => newest(a, b) || order[a.action] - order[b.action]);
|
|
3147
|
+
return { dirty: false, poolName: pool.poolName, versions };
|
|
3024
3148
|
}
|
|
3025
|
-
function
|
|
3026
|
-
return
|
|
3027
|
-
...new Set(members.flatMap((m) => m.external.versions.flatMap((v) => v.remotes.map((r) => r.name))))
|
|
3028
|
-
];
|
|
3149
|
+
function movedRecord(external, remote, move, compare) {
|
|
3150
|
+
return withoutStaleServingBuild(moveCopy(external, remote, move, compare));
|
|
3029
3151
|
}
|
|
3030
|
-
function
|
|
3031
|
-
|
|
3152
|
+
function withoutStaleServingBuild(record) {
|
|
3153
|
+
const { servingBuild, ...rest } = record;
|
|
3154
|
+
if (servingBuild === void 0) return record;
|
|
3155
|
+
const shares = record.versions.some(
|
|
3156
|
+
(v) => v.action === "share" && v.remotes.some((r) => r.name === servingBuild)
|
|
3157
|
+
);
|
|
3158
|
+
return shares ? record : rest;
|
|
3032
3159
|
}
|
|
3033
|
-
function
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
const
|
|
3038
|
-
if (
|
|
3039
|
-
|
|
3040
|
-
const {
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
}
|
|
3160
|
+
function moveCopy(external, remote, move, compare) {
|
|
3161
|
+
if ("fromMap" in move) return recordFromMap(external, remote);
|
|
3162
|
+
const moved = [];
|
|
3163
|
+
const versions = external.versions.map((v) => {
|
|
3164
|
+
const copy = v.remotes.find((r) => r.name === remote);
|
|
3165
|
+
if (!copy) return v;
|
|
3166
|
+
const { servedBy: _servedBy, ...rest } = copy;
|
|
3167
|
+
const meta = { ...rest, cached: true, poolCause: move.cause };
|
|
3168
|
+
if (v.action === "scope")
|
|
3169
|
+
return { ...v, remotes: v.remotes.map((r) => r === copy ? meta : r) };
|
|
3170
|
+
moved.push({ tag: v.tag, meta });
|
|
3171
|
+
return { ...v, remotes: v.remotes.filter((r) => r !== copy) };
|
|
3172
|
+
}).filter((v) => v.remotes.length > 0);
|
|
3173
|
+
for (const { tag, meta } of moved) {
|
|
3174
|
+
const scoped = rowAt(versions, tag, "scope");
|
|
3175
|
+
if (!scoped) versions.push({ tag, action: "scope", host: false, remotes: [meta] });
|
|
3176
|
+
else versions[versions.indexOf(scoped)] = { ...scoped, remotes: [...scoped.remotes, meta] };
|
|
3177
|
+
}
|
|
3178
|
+
return { ...external, versions: versions.sort(byTag(compare)) };
|
|
3053
3179
|
}
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
return islanded;
|
|
3180
|
+
function recordFromMap(external, remote) {
|
|
3181
|
+
const opened = external.versions.find(
|
|
3182
|
+
(v) => v.action !== "skip" && v.remotes.some((r) => r.name === remote)
|
|
3183
|
+
);
|
|
3184
|
+
const meta = { ...opened.remotes.find((r) => r.name === remote), cached: false };
|
|
3185
|
+
const joined = rowAt(external.versions, opened.tag, "skip");
|
|
3186
|
+
const into = joined ? { ...joined, remotes: [...joined.remotes] } : { ...opened, action: "skip", remotes: [] };
|
|
3187
|
+
addRemoteToVersion(into, meta);
|
|
3188
|
+
const versions = external.versions.flatMap((v) => {
|
|
3189
|
+
if (v === joined) return [into];
|
|
3190
|
+
if (v !== opened) return [v];
|
|
3191
|
+
const left = { ...v, remotes: v.remotes.filter((r) => r.name !== remote) };
|
|
3192
|
+
return [...left.remotes.length > 0 ? [left] : [], ...joined ? [] : [into]];
|
|
3193
|
+
});
|
|
3194
|
+
return { ...external, versions };
|
|
3070
3195
|
}
|
|
3071
|
-
|
|
3072
|
-
const
|
|
3073
|
-
return
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
if (basis.get(name) !== build) byMember.set(name, build);
|
|
3081
|
-
}
|
|
3082
|
-
if (byMember.size > 0) served.set(remote, byMember);
|
|
3083
|
-
}
|
|
3084
|
-
return served;
|
|
3196
|
+
function withoutPoolResults(external) {
|
|
3197
|
+
const { poolName: _poolName, poolWinner: _poolWinner, ...rest } = external;
|
|
3198
|
+
return {
|
|
3199
|
+
...rest,
|
|
3200
|
+
versions: external.versions.map((v) => ({
|
|
3201
|
+
...v,
|
|
3202
|
+
remotes: v.remotes.map(({ servedBy: _servedBy, poolCause: _poolCause, ...meta }) => meta)
|
|
3203
|
+
}))
|
|
3204
|
+
};
|
|
3085
3205
|
}
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
);
|
|
3091
|
-
|
|
3092
|
-
const { suspect, selfServed } = movedRemotes(members, islanded, basis, served, hosts);
|
|
3093
|
-
if (suspect.size === 0) return [];
|
|
3094
|
-
const builds = liveBuilds(members, islanded);
|
|
3095
|
-
const shared = sharedTagPerSpecifier(members, islanded);
|
|
3096
|
-
const own = ownCopies(members, suspect);
|
|
3097
|
-
const torn = [];
|
|
3098
|
-
for (const remote of suspect) {
|
|
3099
|
-
const copies = own.get(remote) ?? [];
|
|
3100
|
-
const mine = selfServed.get(remote);
|
|
3101
|
-
const anchors = served.get(remote);
|
|
3102
|
-
const ownTags = /* @__PURE__ */ new Map();
|
|
3103
|
-
const resolved = /* @__PURE__ */ new Map();
|
|
3104
|
-
for (const copy of copies) {
|
|
3105
|
-
const from = mine?.has(copy.member) ? remote : anchors?.get(copy.member);
|
|
3106
|
-
for (const specifier in copy.entries) {
|
|
3107
|
-
if (!ownTags.has(specifier)) ownTags.set(specifier, copy.tag);
|
|
3108
|
-
const tag = from === remote ? copy.tag : from !== void 0 ? builds.get(from)?.tags.get(specifier) : shared.get(specifier);
|
|
3109
|
-
if (tag !== void 0 && !resolved.has(specifier)) resolved.set(specifier, tag);
|
|
3110
|
-
}
|
|
3111
|
-
}
|
|
3112
|
-
if (resolved.size === 0) continue;
|
|
3113
|
-
if (ships(ownTags, resolved)) continue;
|
|
3114
|
-
let witnessed = false;
|
|
3115
|
-
for (const [, build] of builds) {
|
|
3116
|
-
if (ships(build.tags, resolved)) {
|
|
3117
|
-
witnessed = true;
|
|
3118
|
-
break;
|
|
3119
|
-
}
|
|
3120
|
-
}
|
|
3121
|
-
if (!witnessed) {
|
|
3122
|
-
torn.push({
|
|
3123
|
-
remote,
|
|
3124
|
-
combination: [...resolved].map(([specifier, tag]) => `${specifier}@${tag}`).join(", ")
|
|
3125
|
-
});
|
|
3126
|
-
}
|
|
3127
|
-
}
|
|
3128
|
-
return torn;
|
|
3206
|
+
function renamedRecords(scope, pools) {
|
|
3207
|
+
const renamed = [];
|
|
3208
|
+
for (const [poolName, members] of pools)
|
|
3209
|
+
for (const { name } of members)
|
|
3210
|
+
if (scope[name].poolName !== poolName) renamed.push([name, { ...scope[name], poolName }]);
|
|
3211
|
+
return renamed;
|
|
3129
3212
|
}
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
};
|
|
3135
|
-
|
|
3136
|
-
const
|
|
3137
|
-
const
|
|
3138
|
-
const
|
|
3139
|
-
if (!
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
for (const member of members) {
|
|
3143
|
-
const unpublished = !basis.has(member.name);
|
|
3144
|
-
for (const version of member.external.versions) {
|
|
3145
|
-
const scoped = version.action === "scope";
|
|
3146
|
-
if (!unpublished && !scoped) continue;
|
|
3147
|
-
for (const meta of version.remotes) {
|
|
3148
|
-
add(meta.name);
|
|
3149
|
-
const servesItself = scoped || unpublished && served.get(meta.name)?.get(member.name) === void 0;
|
|
3150
|
-
if (!servesItself) continue;
|
|
3151
|
-
let mine = selfServed.get(meta.name);
|
|
3152
|
-
if (!mine) selfServed.set(meta.name, mine = /* @__PURE__ */ new Set());
|
|
3153
|
-
mine.add(member.name);
|
|
3154
|
-
}
|
|
3155
|
-
}
|
|
3156
|
-
}
|
|
3157
|
-
return { suspect, selfServed };
|
|
3213
|
+
|
|
3214
|
+
// src/lib/core/1.domain/pooling/plan.ts
|
|
3215
|
+
function planElection(scope, poolable) {
|
|
3216
|
+
if (!poolable || !Object.values(scope).some((external) => external.dirty) || !scopeHasPoolState(scope))
|
|
3217
|
+
return { pools: /* @__PURE__ */ new Map(), stripped: /* @__PURE__ */ new Map(), labelledAlone: [] };
|
|
3218
|
+
const { pools, labelledAlone } = buildPools(scope);
|
|
3219
|
+
const pooled = new Set([...pools.values()].flatMap((members) => members.map((m) => m.name)));
|
|
3220
|
+
const stripped = /* @__PURE__ */ new Map();
|
|
3221
|
+
for (const [name, external] of Object.entries(scope))
|
|
3222
|
+
if (!pooled.has(name) && hasPoolResults(external))
|
|
3223
|
+
stripped.set(name, { ...withoutPoolResults(external), dirty: true });
|
|
3224
|
+
return { pools, stripped, labelledAlone };
|
|
3158
3225
|
}
|
|
3226
|
+
|
|
3227
|
+
// src/lib/core/2.app/steps/pooling/pool-shared-externals.ts
|
|
3159
3228
|
function createPoolSharedExternals(config, ports) {
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
const sharedExternals =
|
|
3229
|
+
const { compare } = ports.versionCheck;
|
|
3230
|
+
return () => {
|
|
3231
|
+
const repo = ports.sharedExternalsRepo;
|
|
3232
|
+
for (const scope of repo.getScopes()) {
|
|
3233
|
+
const sharedExternals = repo.getFromScope(scope);
|
|
3165
3234
|
try {
|
|
3166
|
-
const
|
|
3167
|
-
|
|
3168
|
-
for (const [poolName, members] of pools)
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
syncPoolNames(sharedExternals, pools, ports.sharedExternalsRepo, scope, rebuilt);
|
|
3235
|
+
const plan = planElection(sharedExternals, repo.scopeType(scope) !== "strict");
|
|
3236
|
+
logPlan(plan, sharedExternals, scope);
|
|
3237
|
+
for (const [poolName, members] of plan.pools)
|
|
3238
|
+
for (const [name, record] of poolRecords(poolName, members, scope))
|
|
3239
|
+
repo.addOrUpdate(name, record, scope);
|
|
3240
|
+
for (const [name, external] of plan.stripped) repo.addOrUpdate(name, external, scope);
|
|
3173
3241
|
} catch (error) {
|
|
3174
3242
|
if (error instanceof NFError) return Promise.reject(error);
|
|
3175
3243
|
config.log.error(3, `[${scope}] failed to pool shared externals.`, {
|
|
@@ -3183,253 +3251,439 @@ function createPoolSharedExternals(config, ports) {
|
|
|
3183
3251
|
}
|
|
3184
3252
|
return Promise.resolve();
|
|
3185
3253
|
};
|
|
3186
|
-
function
|
|
3187
|
-
if (
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
ports.sharedExternalsRepo.addOrUpdate(
|
|
3194
|
-
member.name,
|
|
3195
|
-
rebuildMember(poolName, member, /* @__PURE__ */ new Map(), /* @__PURE__ */ new Map(), basis2.get(member.name)),
|
|
3196
|
-
scope
|
|
3197
|
-
);
|
|
3198
|
-
return true;
|
|
3199
|
-
}
|
|
3200
|
-
const islanded = islandedRemotes(members);
|
|
3201
|
-
const consumed = consumedMembers(members);
|
|
3202
|
-
const hosts = hostRemotes(members);
|
|
3203
|
-
const views = {
|
|
3204
|
-
consumedSpecifiers: lazy(() => consumedSpecifiers(members)),
|
|
3205
|
-
acceptance: lazy(() => acceptanceTable(members, ports.versionCheck.isCompatible)),
|
|
3206
|
-
arrival: lazy(() => arrivalOrder(members))
|
|
3207
|
-
};
|
|
3208
|
-
config.log.debug(
|
|
3209
|
-
3,
|
|
3210
|
-
`[${scope}][pool:${poolName}] ${members.length} members across ${allRemotes.length} remotes, incompatible={${[...islanded.keys()].join(", ") || "\u2205"}}
|
|
3211
|
-
` + members.map((m) => ` - ${m.name}`).join("\n")
|
|
3212
|
-
);
|
|
3213
|
-
const assign = () => {
|
|
3214
|
-
const serving2 = assignServingBuilds(members, islanded, consumed, hosts, views);
|
|
3215
|
-
const basis2 = basisFor(members, islanded, serving2);
|
|
3216
|
-
return { serving: serving2, basis: basis2, served: servedPerRemote(consumed, serving2, basis2) };
|
|
3217
|
-
};
|
|
3218
|
-
let { serving, basis, served } = assign();
|
|
3219
|
-
if (islanded.size === 0 && serving.size === 0 && !anyVerdictStored(members)) return false;
|
|
3220
|
-
for (; ; ) {
|
|
3221
|
-
const torn = findTornRemotes(members, islanded, basis, served, hosts);
|
|
3222
|
-
if (torn.length === 0) break;
|
|
3223
|
-
for (const { remote, combination } of torn)
|
|
3224
|
-
islanded.set(remote, { kind: "torn", combination });
|
|
3225
|
-
({ serving, basis, served } = assign());
|
|
3254
|
+
function logPlan(plan, sharedExternals, scope) {
|
|
3255
|
+
if (plan.pools.size > 0) {
|
|
3256
|
+
const dirty = Object.values(sharedExternals).filter((external) => external.dirty).length;
|
|
3257
|
+
config.log.debug(
|
|
3258
|
+
3,
|
|
3259
|
+
`[${scope}] re-electing ${plan.pools.size} pool(s): ${dirty} dirty external(s).`
|
|
3260
|
+
);
|
|
3226
3261
|
}
|
|
3227
|
-
|
|
3228
|
-
|
|
3262
|
+
if (plan.stripped.size > 0)
|
|
3263
|
+
config.log.debug(
|
|
3264
|
+
3,
|
|
3265
|
+
`[${scope}] ${plan.stripped.size} external(s) left every pool; cleared their pool state for re-election.`
|
|
3266
|
+
);
|
|
3267
|
+
for (const name of plan.labelledAlone)
|
|
3268
|
+
config.log.debug(
|
|
3269
|
+
3,
|
|
3270
|
+
`[${scope}] '${name}' has a 'pool' label that joins no other external; likely a typo or a missing sibling.`
|
|
3271
|
+
);
|
|
3272
|
+
}
|
|
3273
|
+
function poolRecords(poolName, members, scope) {
|
|
3274
|
+
const election = elect({
|
|
3275
|
+
members,
|
|
3276
|
+
acceptsTag: createAcceptsTag(ports.versionCheck.isCompatible, compare),
|
|
3277
|
+
compare,
|
|
3278
|
+
previousWinner: previousWinner(members),
|
|
3279
|
+
latestFirst: config.profile.latestSharedExternal
|
|
3280
|
+
});
|
|
3281
|
+
const placed = { ...election, poolName };
|
|
3282
|
+
report(poolName, members, placed, scope);
|
|
3283
|
+
return members.map((m) => [
|
|
3284
|
+
m.name,
|
|
3285
|
+
{ ...memberRecord(m, placed, compare), poolWinner: placed.winner }
|
|
3286
|
+
]);
|
|
3287
|
+
}
|
|
3288
|
+
function report(poolName, members, placed, scope) {
|
|
3289
|
+
const { misses } = placed;
|
|
3290
|
+
const rejecting = [...misses].filter(([, miss]) => miss?.cause === "incompatible" && miss.strict).map(([remote]) => remote);
|
|
3291
|
+
if (config.strict.strictExternalCompatibility && rejecting.length > 0) {
|
|
3229
3292
|
config.log.error(
|
|
3230
3293
|
3,
|
|
3231
|
-
`[${scope}][pool:${poolName}] version-incompatible remotes cannot be pooled: {${
|
|
3294
|
+
`[${scope}][pool:${poolName}] version-incompatible remotes cannot be pooled: {${rejecting.join(", ")}}.`
|
|
3232
3295
|
);
|
|
3233
3296
|
throw new NFError(`Could not pool '${poolName}' in scope ${scope}.`);
|
|
3234
3297
|
}
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
for (; ; ) {
|
|
3248
|
-
const bases = servingBuilds(members, islanded);
|
|
3249
|
-
const distinct = new Set(bases.values());
|
|
3250
|
-
if (distinct.size === 1 && bases.size === members.length) {
|
|
3251
|
-
const sole = [...distinct][0];
|
|
3252
|
-
if (coversWholePool(members, sole, islanded)) return /* @__PURE__ */ new Map();
|
|
3253
|
-
}
|
|
3254
|
-
const builds = liveBuilds(members, islanded);
|
|
3255
|
-
const shared = sharedTagPerSpecifier(members, islanded);
|
|
3256
|
-
const consumedSpec = views.consumedSpecifiers();
|
|
3257
|
-
const needAnchor = /* @__PURE__ */ new Map();
|
|
3258
|
-
const needSpecifiers = /* @__PURE__ */ new Map();
|
|
3259
|
-
for (const [remote, wants] of consumed) {
|
|
3260
|
-
if (islanded.has(remote)) continue;
|
|
3261
|
-
if (isWitnessed(consumedSpec.get(remote) ?? [], shared, builds)) continue;
|
|
3262
|
-
needAnchor.set(remote, wants);
|
|
3263
|
-
needSpecifiers.set(remote, consumedSpec.get(remote) ?? /* @__PURE__ */ new Set());
|
|
3264
|
-
}
|
|
3265
|
-
if (needAnchor.size === 0) return /* @__PURE__ */ new Map();
|
|
3266
|
-
const acceptance = views.acceptance();
|
|
3267
|
-
const assignment = assignAnchors({
|
|
3268
|
-
builds,
|
|
3269
|
-
acceptance,
|
|
3270
|
-
consumedSpecifiers: needSpecifiers,
|
|
3271
|
-
consumedMembers: needAnchor,
|
|
3272
|
-
hosts,
|
|
3273
|
-
arrival: views.arrival()
|
|
3274
|
-
});
|
|
3275
|
-
const anchors = /* @__PURE__ */ new Set();
|
|
3276
|
-
for (const build of assignment.values()) if (build !== void 0) anchors.add(build);
|
|
3277
|
-
const serving = /* @__PURE__ */ new Map();
|
|
3278
|
-
let scoped = false;
|
|
3279
|
-
for (const [remote, wants] of needAnchor) {
|
|
3280
|
-
const assigned = assignment.get(remote);
|
|
3281
|
-
const build = assigned ?? (anchors.has(remote) || hosts.has(remote) ? remote : void 0);
|
|
3282
|
-
if (build === void 0) {
|
|
3283
|
-
islanded.set(remote, {
|
|
3284
|
-
kind: "uncovered",
|
|
3285
|
-
...explainSelfServe(remote, wants, needSpecifiers.get(remote), { builds, acceptance })
|
|
3286
|
-
});
|
|
3287
|
-
scoped = true;
|
|
3288
|
-
continue;
|
|
3289
|
-
}
|
|
3290
|
-
serving.set(remote, build);
|
|
3298
|
+
const subpoolSizes = /* @__PURE__ */ new Map();
|
|
3299
|
+
for (const placement of placed.placements.values())
|
|
3300
|
+
if (placement.kind === "subpool")
|
|
3301
|
+
subpoolSizes.set(placement.owner, (subpoolSizes.get(placement.owner) ?? 0) + 1);
|
|
3302
|
+
let shipped;
|
|
3303
|
+
for (const [remote, miss] of misses) {
|
|
3304
|
+
if (miss === void 0) {
|
|
3305
|
+
config.log.warn(
|
|
3306
|
+
3,
|
|
3307
|
+
`[${scope}][pool:${poolName}] '${remote}' keeps subpool '${remote}': the elected build would serve it, but ${subpoolSizes.get(remote) - 1} other remote(s) in it need its build.`
|
|
3308
|
+
);
|
|
3309
|
+
continue;
|
|
3291
3310
|
}
|
|
3292
|
-
|
|
3311
|
+
shipped ??= copiesByRemote(members);
|
|
3312
|
+
const counts = {
|
|
3313
|
+
imports: specifierCount(shipped.get(remote)),
|
|
3314
|
+
subpoolSize: subpoolSizes.get(remote) ?? 0
|
|
3315
|
+
};
|
|
3316
|
+
config.log.warn(
|
|
3317
|
+
3,
|
|
3318
|
+
`[${scope}][pool:${poolName}] ${missWarning(remote, miss, placed, counts)}`
|
|
3319
|
+
);
|
|
3293
3320
|
}
|
|
3294
3321
|
}
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3322
|
+
}
|
|
3323
|
+
function missWarning(remote, miss, placed, counts) {
|
|
3324
|
+
const { imports, subpoolSize } = counts;
|
|
3325
|
+
const placement = placed.placements.get(remote);
|
|
3326
|
+
const where = placement.kind === "subpool" && placement.owner !== remote ? `It runs in subpool '${placement.owner}' for all ${imports} of its entrypoints.` : subpoolSize > 1 ? `Its build serves subpool '${remote}': its ${imports} entrypoints and ${subpoolSize - 1} other remote(s).` : `All ${imports} of its entrypoints are scoped for it.`;
|
|
3327
|
+
if (miss.cause === "incompatible")
|
|
3328
|
+
return `'${remote}' is islanded: its range rejects '${miss.member}@${miss.tag}' of the elected build '${placed.winner}'. ${where}`;
|
|
3329
|
+
if (miss.unshipped)
|
|
3330
|
+
return `'${remote}' serves its own family: no build shipped its entrypoints together at the elected versions (gap '${miss.gap}', closest '${placed.winner}'). ${where}`;
|
|
3331
|
+
return `'${remote}' serves its own family: no elected build offers every entrypoint it imports at a version it accepts (gap '${miss.gap}', closest '${placed.winner}'). ${where}`;
|
|
3332
|
+
}
|
|
3333
|
+
|
|
3334
|
+
// src/lib/core/1.domain/externals/serving.ts
|
|
3335
|
+
var byServingRank = (a, b) => Number(b.host) - Number(a.host) || a.downloads - b.downloads || a.fallbacks - b.fallbacks || Number(b.rootBasis) - Number(a.rootBasis) || compareStrings(a.remote, b.remote);
|
|
3336
|
+
var packagesOf = (scope) => groupByPackage(Object.keys(scope), (name) => name);
|
|
3337
|
+
var isPooled = (externals) => externals.some(([, e]) => e.poolName !== void 0);
|
|
3338
|
+
function servingBuildOf(externals, accepts) {
|
|
3339
|
+
if (externals.length === 0) return void 0;
|
|
3340
|
+
const pkg = packageOf(externals[0][0]);
|
|
3341
|
+
const rootRows = shareRows(externals.find(([name]) => name === pkg)?.[1]);
|
|
3342
|
+
const rows = rootRows.length > 0 ? rootRows : externals.flatMap(([, e]) => shareRows(e));
|
|
3343
|
+
if (isPooled(externals)) return pooledBuild(externals, rows);
|
|
3344
|
+
const consumers = consumersOf(externals);
|
|
3345
|
+
const builds = buildsOf(rows, rootRows, consumers);
|
|
3346
|
+
const hosts = builds.filter((b) => b.host);
|
|
3347
|
+
const field = hosts.length > 0 ? hosts : builds;
|
|
3348
|
+
if (field.length <= 1) return field[0] && { remote: field[0].remote, tag: field[0].tag };
|
|
3349
|
+
return cheapestBuild(field, costTable(consumers, accepts));
|
|
3350
|
+
}
|
|
3351
|
+
function cheapestBuild(field, costOf) {
|
|
3352
|
+
let best;
|
|
3353
|
+
for (const b of field) {
|
|
3354
|
+
const { downloads, fallbacks } = costOf(b);
|
|
3355
|
+
const candidate = {
|
|
3356
|
+
remote: b.remote,
|
|
3357
|
+
tag: b.tag,
|
|
3358
|
+
host: b.host,
|
|
3359
|
+
rootBasis: b.rootBasis,
|
|
3360
|
+
downloads,
|
|
3361
|
+
fallbacks
|
|
3362
|
+
};
|
|
3363
|
+
if (!best || byServingRank(candidate, best) < 0) best = candidate;
|
|
3307
3364
|
}
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3365
|
+
const { remote, tag } = best;
|
|
3366
|
+
return { remote, tag };
|
|
3367
|
+
}
|
|
3368
|
+
function shareServingCopies(external, serving) {
|
|
3369
|
+
for (const row of external.versions) {
|
|
3370
|
+
const copy = row.remotes.find((c) => c.name === serving.remote);
|
|
3371
|
+
if (row.action !== "skip" || !copy || rowAt(external.versions, row.tag, "share")) continue;
|
|
3372
|
+
row.action = "share";
|
|
3373
|
+
if (row.remotes[0] !== copy) row.host = false;
|
|
3374
|
+
row.remotes = [copy, ...row.remotes.filter((c) => c !== copy)];
|
|
3375
|
+
}
|
|
3376
|
+
}
|
|
3377
|
+
function followServingBuild(external, serving, accepts) {
|
|
3378
|
+
let objector;
|
|
3379
|
+
const rebuilt = external.versions.flatMap((version) => {
|
|
3380
|
+
if (version.action !== "share" || version.tag === serving.tag || version.remotes[0].name === serving.remote)
|
|
3381
|
+
return [version];
|
|
3382
|
+
const rows = redirectRow(version, serving.tag, accepts);
|
|
3383
|
+
const scoped = rows.find((row) => row.action === "scope");
|
|
3384
|
+
if (scoped && !objector) objector = { copy: scoped.remotes[0], tag: scoped.tag };
|
|
3385
|
+
return rows;
|
|
3386
|
+
});
|
|
3387
|
+
external.versions = mergeRows(rebuilt);
|
|
3388
|
+
return { external, objector };
|
|
3389
|
+
}
|
|
3390
|
+
var sameTagOnly = (offered, ownTag) => offered === ownTag;
|
|
3391
|
+
var servedScopes = (scope, strict) => strict ? strictTagScopes(scope) : [scope];
|
|
3392
|
+
function strictTagScopes(scope) {
|
|
3393
|
+
const tagScopes = [];
|
|
3394
|
+
for (const [pkg, names] of packagesOf(scope)) {
|
|
3395
|
+
const byTag2 = /* @__PURE__ */ new Map();
|
|
3396
|
+
for (const name of names)
|
|
3397
|
+
for (const row of scope[name].versions) {
|
|
3398
|
+
let tagScope = byTag2.get(row.tag);
|
|
3399
|
+
if (!tagScope) byTag2.set(row.tag, tagScope = {});
|
|
3400
|
+
const external = tagScope[name] ??= { ...scope[name], versions: [] };
|
|
3401
|
+
external.versions.push({ ...row, remotes: [...row.remotes] });
|
|
3321
3402
|
}
|
|
3403
|
+
for (const [tag, tagScope] of byTag2) {
|
|
3404
|
+
const lead = [tagScope[pkg], ...Object.values(tagScope)].map((external) => external && rowAt(external.versions, tag, "share")).find((row) => row !== void 0);
|
|
3405
|
+
if (lead) {
|
|
3406
|
+
const serving = { remote: lead.remotes[0].name, tag };
|
|
3407
|
+
for (const external of Object.values(tagScope)) external.servingBuild = serving.remote;
|
|
3408
|
+
scopeFallbacks(Object.entries(tagScope), serving, sameTagOnly);
|
|
3409
|
+
}
|
|
3410
|
+
tagScopes.push(tagScope);
|
|
3322
3411
|
}
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3412
|
+
}
|
|
3413
|
+
return tagScopes;
|
|
3414
|
+
}
|
|
3415
|
+
function shareRows(external) {
|
|
3416
|
+
return external?.versions.filter((v) => v.action === "share") ?? [];
|
|
3417
|
+
}
|
|
3418
|
+
function buildsOf(rows, rootRows, consumers) {
|
|
3419
|
+
const candidates = /* @__PURE__ */ new Map();
|
|
3420
|
+
for (const row of rows)
|
|
3421
|
+
for (const copy of row.remotes) {
|
|
3422
|
+
const key = `${row.tag}|${copy.name}`;
|
|
3423
|
+
if (copy.servedBy || candidates.has(key) || shipsTwoTags(consumers.get(copy.name))) continue;
|
|
3424
|
+
const basis = copy === row.remotes[0];
|
|
3425
|
+
candidates.set(key, {
|
|
3426
|
+
remote: copy.name,
|
|
3427
|
+
tag: row.tag,
|
|
3428
|
+
host: basis && row.host,
|
|
3429
|
+
rootBasis: basis && rootRows.includes(row)
|
|
3430
|
+
});
|
|
3333
3431
|
}
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3432
|
+
return [...candidates.values()];
|
|
3433
|
+
}
|
|
3434
|
+
function pooledBuild(externals, rows) {
|
|
3435
|
+
const winner = externals.find(([, e]) => e.poolWinner !== void 0)?.[1].poolWinner;
|
|
3436
|
+
const shipped = rows.flatMap(
|
|
3437
|
+
(row) => row.remotes.filter((copy) => !copy.servedBy).map((copy) => ({ remote: copy.name, tag: row.tag }))
|
|
3438
|
+
);
|
|
3439
|
+
const own = shipped.find((c) => c.remote === winner);
|
|
3440
|
+
if (own) return own;
|
|
3441
|
+
const width = ({ remote, tag }) => externals.reduce((n, [, e]) => {
|
|
3442
|
+
const copy = shareRows(e).find((row) => row.tag === tag)?.remotes.find((c) => c.name === remote);
|
|
3443
|
+
return n + Object.keys(copy?.entries ?? {}).length;
|
|
3444
|
+
}, 0);
|
|
3445
|
+
return shipped.map((c) => ({ c, width: width(c) })).sort((a, b) => b.width - a.width || compareStrings(a.c.remote, b.c.remote))[0]?.c;
|
|
3446
|
+
}
|
|
3447
|
+
function consumersOf(externals) {
|
|
3448
|
+
const consumers = /* @__PURE__ */ new Map();
|
|
3449
|
+
for (const [, external] of externals)
|
|
3450
|
+
for (const row of external.versions)
|
|
3451
|
+
for (const copy of row.remotes) {
|
|
3452
|
+
const placed = consumers.get(copy.name);
|
|
3453
|
+
if (placed) placed.push({ row, copy });
|
|
3454
|
+
else consumers.set(copy.name, [{ row, copy }]);
|
|
3356
3455
|
}
|
|
3357
|
-
|
|
3358
|
-
|
|
3456
|
+
return consumers;
|
|
3457
|
+
}
|
|
3458
|
+
function costTable(consumers, accepts) {
|
|
3459
|
+
const scanned = /* @__PURE__ */ new Map();
|
|
3460
|
+
const keyed = /* @__PURE__ */ new Map();
|
|
3461
|
+
const tollFor = (build, surface) => {
|
|
3462
|
+
let atTag = scanned.get(build.tag);
|
|
3463
|
+
if (!atTag) scanned.set(build.tag, atTag = []);
|
|
3464
|
+
const known = atTag.find((t) => sameSurface(t.surface, surface));
|
|
3465
|
+
if (known) return known.toll;
|
|
3466
|
+
if (atTag.length < SCANNED_SURFACES_PER_TAG) {
|
|
3467
|
+
const toll2 = tollOf(consumers, surface, build.tag, accepts);
|
|
3468
|
+
atTag.push({ surface, toll: toll2 });
|
|
3469
|
+
return toll2;
|
|
3470
|
+
}
|
|
3471
|
+
const key = [build.tag, ...[...surface].map(([s, tag]) => `${s}@${tag}`).sort()].join("\n");
|
|
3472
|
+
let toll = keyed.get(key);
|
|
3473
|
+
if (!toll) keyed.set(key, toll = tollOf(consumers, surface, build.tag, accepts));
|
|
3474
|
+
return toll;
|
|
3475
|
+
};
|
|
3476
|
+
return (build) => {
|
|
3477
|
+
const surface = surfaceOf(consumers, build);
|
|
3478
|
+
const toll = tollFor(build, surface);
|
|
3479
|
+
const others = (remotes) => remotes.size - Number(remotes.has(build.remote));
|
|
3359
3480
|
return {
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
versions: [
|
|
3363
|
-
...shareVersion,
|
|
3364
|
-
...byTag(
|
|
3365
|
-
clean.filter((e) => e.action === "skip"),
|
|
3366
|
-
"skip"
|
|
3367
|
-
),
|
|
3368
|
-
...byTag(scoped, "scope")
|
|
3369
|
-
].sort((a, b) => ports.versionCheck.compare(b.tag, a.tag))
|
|
3481
|
+
downloads: others(toll.downloads) + Number(!allCached(consumers.get(build.remote) ?? [])),
|
|
3482
|
+
fallbacks: others(toll.fallbacks)
|
|
3370
3483
|
};
|
|
3371
|
-
}
|
|
3484
|
+
};
|
|
3372
3485
|
}
|
|
3373
|
-
function
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
}
|
|
3383
|
-
}
|
|
3486
|
+
function tollOf(consumers, surface, tag, accepts) {
|
|
3487
|
+
const toll = { fallbacks: /* @__PURE__ */ new Set(), downloads: /* @__PURE__ */ new Set() };
|
|
3488
|
+
for (const [remote, placed] of consumers) {
|
|
3489
|
+
const fallback = fallsBack(placed, surface, tag, accepts);
|
|
3490
|
+
if (fallback) toll.fallbacks.add(remote);
|
|
3491
|
+
if (!allCached(placed) && (fallback || runsOffMap(placed, tag, accepts)))
|
|
3492
|
+
toll.downloads.add(remote);
|
|
3493
|
+
}
|
|
3494
|
+
return toll;
|
|
3384
3495
|
}
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3496
|
+
var allCached = (placed) => placed.every(({ copy }) => copy.cached);
|
|
3497
|
+
function fallbacksOf(externals, serving, accepts) {
|
|
3498
|
+
const consumers = consumersOf(externals);
|
|
3499
|
+
const surface = serving ? surfaceOf(consumers, serving) : /* @__PURE__ */ new Map();
|
|
3500
|
+
return [...consumers].filter(
|
|
3501
|
+
([remote, placed]) => remote !== serving?.remote && fallsBack(placed, surface, serving?.tag, accepts)
|
|
3502
|
+
).map(([remote]) => remote);
|
|
3503
|
+
}
|
|
3504
|
+
function scopeFallbacks(externals, serving, accepts) {
|
|
3505
|
+
const remotes = fallbacksOf(externals, serving, accepts);
|
|
3506
|
+
for (const [, external] of externals)
|
|
3507
|
+
external.versions = scopeRemotes(external.versions, remotes);
|
|
3508
|
+
}
|
|
3509
|
+
function strictObjectorOf(externals, serving, accepts) {
|
|
3510
|
+
const consumers = consumersOf(externals);
|
|
3511
|
+
const surface = surfaceOf(consumers, serving);
|
|
3512
|
+
for (const [name, external] of externals)
|
|
3513
|
+
for (const row of external.versions)
|
|
3514
|
+
for (const copy of row.remotes)
|
|
3515
|
+
if (copy.name !== serving.remote && rejectsServingTag({ row, copy }, surface, serving.tag, accepts) && !splitsPackage(consumers.get(copy.name), surface, serving.tag, accepts))
|
|
3516
|
+
return { external: name, copy, tag: row.tag };
|
|
3517
|
+
return void 0;
|
|
3518
|
+
}
|
|
3519
|
+
var SCANNED_SURFACES_PER_TAG = 8;
|
|
3520
|
+
function sameSurface(a, b) {
|
|
3521
|
+
if (a.size !== b.size) return false;
|
|
3522
|
+
for (const [specifier, tag] of a) if (b.get(specifier) !== tag) return false;
|
|
3523
|
+
return true;
|
|
3524
|
+
}
|
|
3525
|
+
function surfaceOf(consumers, serving) {
|
|
3526
|
+
const surface = /* @__PURE__ */ new Map();
|
|
3527
|
+
for (const p of consumers.get(serving.remote) ?? [])
|
|
3528
|
+
if (p.row.tag === serving.tag && onMap(p) || leadsShareRow(p)) {
|
|
3529
|
+
for (const s in p.copy.entries) if (!surface.has(s)) surface.set(s, p.row.tag);
|
|
3395
3530
|
}
|
|
3396
|
-
return
|
|
3531
|
+
return surface;
|
|
3532
|
+
}
|
|
3533
|
+
var leadsShareRow = ({ row, copy }) => row.action === "share" && row.remotes[0] === copy;
|
|
3534
|
+
var onMap = ({ row }) => row.action !== "scope";
|
|
3535
|
+
function splitsPackage(placed, surface, tag, accepts) {
|
|
3536
|
+
const uncovered = (p) => Object.keys(p.copy.entries).some((s) => {
|
|
3537
|
+
const servedAt = surface.get(s);
|
|
3538
|
+
return servedAt === void 0 || servedAt !== tag && rejectsTag(p, servedAt, accepts);
|
|
3539
|
+
});
|
|
3540
|
+
return placed.some(onMap) && (shipsTwoTags(placed) || placed.some((p) => !onMap(p) || uncovered(p)));
|
|
3541
|
+
}
|
|
3542
|
+
function loadSplitsPackage(copies, claims, accepts) {
|
|
3543
|
+
const root = claims.find(([specifier]) => specifier === packageOf(specifier))?.[1];
|
|
3544
|
+
const servingTag = root?.tag ?? claims[0][1].tag;
|
|
3545
|
+
const surface = new Map(
|
|
3546
|
+
claims.map(([specifier, { tag }]) => [specifier, root ? tag : servingTag])
|
|
3547
|
+
);
|
|
3548
|
+
const placed = copies.map(({ tag, copy }) => ({
|
|
3549
|
+
row: { tag, action: "share", host: false, remotes: [copy] },
|
|
3550
|
+
copy
|
|
3551
|
+
}));
|
|
3552
|
+
return splitsPackage(placed, surface, servingTag, accepts);
|
|
3553
|
+
}
|
|
3554
|
+
function fallsBack(placed, surface, tag, accepts) {
|
|
3555
|
+
return splitsPackage(placed, surface, tag, accepts) || tag !== void 0 && placed.some((p) => rejectsServingTag(p, surface, tag, accepts));
|
|
3397
3556
|
}
|
|
3398
|
-
function
|
|
3557
|
+
function rejectsServingTag(p, surface, tag, accepts) {
|
|
3558
|
+
return onMap(p) && rejectsTag(p, tag, accepts) && Object.keys(p.copy.entries).some((s) => surface.get(s) === tag);
|
|
3559
|
+
}
|
|
3560
|
+
var rejectsTag = ({ row, copy }, tag, accepts) => copy.strictVersion && !accepts(tag, row.tag, copy.requiredVersion);
|
|
3561
|
+
var shipsTwoTags = (placed) => !placed.some((p) => p.row.host && p.row.remotes[0] === p.copy) && placed.some((p) => p.row.tag !== placed[0].row.tag);
|
|
3562
|
+
function runsOffMap(placed, tag, accepts) {
|
|
3563
|
+
return placed.some((p) => !onMap(p) || rejectsTag(p, tag, accepts));
|
|
3564
|
+
}
|
|
3565
|
+
|
|
3566
|
+
// src/lib/core/2.app/steps/decide-serving-builds.ts
|
|
3567
|
+
function createDecideServingBuilds(config, ports) {
|
|
3399
3568
|
return () => {
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
const
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
}
|
|
3414
|
-
}
|
|
3415
|
-
for (const [name, external] of Object.entries(sharedExternals)) {
|
|
3416
|
-
if (pooled.has(name) || !clearPoolState(external)) continue;
|
|
3417
|
-
external.dirty = true;
|
|
3418
|
-
unpooled++;
|
|
3569
|
+
const accepts = createAcceptsTag(ports.versionCheck.isCompatible, ports.versionCheck.compare);
|
|
3570
|
+
const repo = ports.sharedExternalsRepo;
|
|
3571
|
+
const written = new Set(repo.uncommittedScopes());
|
|
3572
|
+
const needsDeciding = (shareScope) => written.has(shareScope) || repo.isUndecided(shareScope);
|
|
3573
|
+
try {
|
|
3574
|
+
for (const shareScope of repo.getScopes()) {
|
|
3575
|
+
if (repo.scopeType(shareScope) === "strict" || !needsDeciding(shareScope)) continue;
|
|
3576
|
+
const sharedExternals = repo.getFromScope(shareScope);
|
|
3577
|
+
for (const [pkg, names] of packagesOf(sharedExternals)) {
|
|
3578
|
+
const externals = names.map((name) => [name, sharedExternals[name]]);
|
|
3579
|
+
decidePackage(shareScope, pkg, externals, accepts);
|
|
3580
|
+
}
|
|
3581
|
+
repo.markDecided(shareScope);
|
|
3419
3582
|
}
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3583
|
+
return Promise.resolve();
|
|
3584
|
+
} catch (e) {
|
|
3585
|
+
return Promise.reject(e);
|
|
3586
|
+
}
|
|
3587
|
+
};
|
|
3588
|
+
function decidePackage(shareScope, pkg, externals, accepts) {
|
|
3589
|
+
const serving = servingBuildOf(externals, accepts);
|
|
3590
|
+
const before = externals.map(([, external]) => shapeOf(external));
|
|
3591
|
+
const pooled = isPooled(externals);
|
|
3592
|
+
const toFollow = pooled ? void 0 : serving;
|
|
3593
|
+
if (!pooled) fallBack(pkg, externals, accepts, serving);
|
|
3594
|
+
externals.forEach(([name, external], i) => {
|
|
3595
|
+
const decided = withServingBuild(
|
|
3596
|
+
toFollow ? follow(pkg, name, external, toFollow, accepts) : external,
|
|
3597
|
+
serving?.remote
|
|
3598
|
+
);
|
|
3599
|
+
if (!sameShape(before[i], decided))
|
|
3600
|
+
ports.sharedExternalsRepo.addOrUpdate(name, decided, shareScope);
|
|
3601
|
+
});
|
|
3602
|
+
}
|
|
3603
|
+
function follow(pkg, name, external, serving, accepts) {
|
|
3604
|
+
const { external: followed, objector } = followServingBuild(external, serving, accepts);
|
|
3605
|
+
if (objector && config.strict.strictExternalCompatibility)
|
|
3606
|
+
rejectIncompatible(config, objector.copy, `${name}@${objector.tag}`, `${pkg}@${serving.tag}`);
|
|
3607
|
+
return followed;
|
|
3608
|
+
}
|
|
3609
|
+
function fallBack(pkg, externals, accepts, serving) {
|
|
3610
|
+
if (serving) {
|
|
3611
|
+
for (const [, external] of externals) shareServingCopies(external, serving);
|
|
3612
|
+
const objector = config.strict.strictExternalCompatibility && strictObjectorOf(externals, serving, accepts);
|
|
3613
|
+
if (objector)
|
|
3614
|
+
rejectIncompatible(
|
|
3615
|
+
config,
|
|
3616
|
+
objector.copy,
|
|
3617
|
+
`${objector.external}@${objector.tag}`,
|
|
3618
|
+
`${pkg}@${serving.tag}`
|
|
3426
3619
|
);
|
|
3427
3620
|
}
|
|
3428
|
-
|
|
3621
|
+
scopeFallbacks(externals, serving, accepts);
|
|
3622
|
+
}
|
|
3623
|
+
}
|
|
3624
|
+
function withServingBuild(external, servingBuild) {
|
|
3625
|
+
if (external.servingBuild === servingBuild) return external;
|
|
3626
|
+
const { servingBuild: _, ...rest } = external;
|
|
3627
|
+
return servingBuild ? { ...rest, servingBuild } : rest;
|
|
3628
|
+
}
|
|
3629
|
+
var shapeOf = (external) => ({
|
|
3630
|
+
servingBuild: external.servingBuild,
|
|
3631
|
+
rows: external.versions.map(
|
|
3632
|
+
({ tag, action, host, remotes }) => ({ tag, action, host, remotes: [...remotes] })
|
|
3633
|
+
)
|
|
3634
|
+
});
|
|
3635
|
+
var sameRow = (was, row) => Object.keys(was).every(
|
|
3636
|
+
(key) => key === "remotes" ? was.remotes.length === row.remotes.length && row.remotes.every((copy, j) => copy === was.remotes[j]) : was[key] === row[key]
|
|
3637
|
+
);
|
|
3638
|
+
var sameShape = (shape, external) => shape.servingBuild === external.servingBuild && shape.rows.length === external.versions.length && external.versions.every((row, i) => sameRow(shape.rows[i], row));
|
|
3639
|
+
|
|
3640
|
+
// src/lib/core/1.domain/externals/claims.ts
|
|
3641
|
+
function claimTable(externals, publishes) {
|
|
3642
|
+
const table = { claims: /* @__PURE__ */ new Map(), duplicates: /* @__PURE__ */ new Set(), shareClaims: /* @__PURE__ */ new Map() };
|
|
3643
|
+
const ordered = [...externals];
|
|
3644
|
+
const claim = (into, external, version, accepts = publishes) => forEachVersionEntry(version, accepts, (specifier, copy) => {
|
|
3645
|
+
if (!into.has(specifier)) into.set(specifier, { external, tag: version.tag, copy });
|
|
3646
|
+
});
|
|
3647
|
+
const seedServingBuild = (name, external) => {
|
|
3648
|
+
const isServing = (copy) => copy.name === external.servingBuild && (!publishes || publishes(copy));
|
|
3649
|
+
for (const version of external.versions) {
|
|
3650
|
+
if (version.action !== "share") continue;
|
|
3651
|
+
claim(table.shareClaims, name, version, isServing);
|
|
3652
|
+
claim(table.claims, name, version, isServing);
|
|
3653
|
+
}
|
|
3654
|
+
};
|
|
3655
|
+
const claimShareRow = (name, external, version) => {
|
|
3656
|
+
const prior = table.claims.get(name);
|
|
3657
|
+
const duplicate = prior !== void 0 && prior.tag !== version.tag;
|
|
3658
|
+
if (duplicate && !(version.host && version.remotes[0].name === external.servingBuild))
|
|
3659
|
+
table.duplicates.add(version);
|
|
3660
|
+
if (external.servingBuild) return;
|
|
3661
|
+
claim(table.shareClaims, name, version);
|
|
3662
|
+
if (!duplicate && (!prior || prior.external === name)) claim(table.claims, name, version);
|
|
3429
3663
|
};
|
|
3664
|
+
for (const [name, external] of ordered)
|
|
3665
|
+
if (external.servingBuild) seedServingBuild(name, external);
|
|
3666
|
+
for (const [name, external] of ordered)
|
|
3667
|
+
for (const version of external.versions)
|
|
3668
|
+
if (version.action === "share") claimShareRow(name, external, version);
|
|
3669
|
+
return table;
|
|
3670
|
+
}
|
|
3671
|
+
function committedClaims(externals, { shareScope, without }) {
|
|
3672
|
+
const publishes = (copy) => copy.name !== without && (shareScope !== GLOBAL_SCOPE || copy.cached);
|
|
3673
|
+
return claimTable(externals, publishes).claims;
|
|
3674
|
+
}
|
|
3675
|
+
var claimsByPackage = (claims) => groupByPackage(claims, ([specifier]) => specifier);
|
|
3676
|
+
function versionClaims(version, servingBuild) {
|
|
3677
|
+
const claims = /* @__PURE__ */ new Map();
|
|
3678
|
+
const claim = (specifier, copy) => {
|
|
3679
|
+
if (!claims.has(specifier)) claims.set(specifier, copy);
|
|
3680
|
+
};
|
|
3681
|
+
const accepts = servingBuild ? (copy) => copy.name === servingBuild : void 0;
|
|
3682
|
+
forEachVersionEntry(version, accepts, claim);
|
|
3683
|
+
return claims;
|
|
3430
3684
|
}
|
|
3431
3685
|
|
|
3432
|
-
// node_modules/.pnpm/@softarc+native-federation@4.
|
|
3686
|
+
// node_modules/.pnpm/@softarc+native-federation@4.8.0/node_modules/@softarc/native-federation/dist/lib/domain/core/chunk.js
|
|
3433
3687
|
var CHUNK_PREFIX = "@nf-internal";
|
|
3434
3688
|
function toChunkImport(fileName) {
|
|
3435
3689
|
if (fileName.startsWith("./")) {
|
|
@@ -3446,8 +3700,8 @@ function createGenerateImportMap(config, ports) {
|
|
|
3446
3700
|
const chunkBundles = {};
|
|
3447
3701
|
try {
|
|
3448
3702
|
addScopedExternals(importMap, chunkBundles);
|
|
3449
|
-
|
|
3450
|
-
|
|
3703
|
+
addNamedShareScopes(importMap, chunkBundles);
|
|
3704
|
+
addGlobalShareScope(importMap, chunkBundles);
|
|
3451
3705
|
addRemoteInfos(importMap, chunkBundles);
|
|
3452
3706
|
addChunkImports(importMap, chunkBundles);
|
|
3453
3707
|
return Promise.resolve(importMap);
|
|
@@ -3481,47 +3735,61 @@ function createGenerateImportMap(config, ports) {
|
|
|
3481
3735
|
}
|
|
3482
3736
|
return modules;
|
|
3483
3737
|
}
|
|
3484
|
-
function
|
|
3738
|
+
function addNamedShareScopes(importMap, chunkBundles) {
|
|
3485
3739
|
const shareScopes = ports.sharedExternalsRepo.getScopes({ includeGlobal: false });
|
|
3486
3740
|
for (const shareScope of shareScopes) {
|
|
3487
|
-
|
|
3741
|
+
const sharedExternals = ports.sharedExternalsRepo.getFromScope(shareScope);
|
|
3742
|
+
const cachedBefore = /* @__PURE__ */ new Map();
|
|
3743
|
+
for (const [externalName, external] of Object.entries(sharedExternals))
|
|
3744
|
+
cachedBefore.set(externalName, cachedFingerprint(external));
|
|
3745
|
+
const strict = ports.sharedExternalsRepo.scopeType(shareScope) === "strict";
|
|
3746
|
+
for (const served of servedScopes(sharedExternals, strict))
|
|
3747
|
+
addShareScope(importMap, shareScope, served, chunkBundles);
|
|
3748
|
+
for (const [externalName, external] of Object.entries(sharedExternals)) {
|
|
3749
|
+
if (cachedFingerprint(external) !== cachedBefore.get(externalName)) {
|
|
3750
|
+
ports.sharedExternalsRepo.addOrUpdate(externalName, external, shareScope);
|
|
3751
|
+
}
|
|
3752
|
+
}
|
|
3488
3753
|
}
|
|
3489
3754
|
return importMap;
|
|
3490
3755
|
}
|
|
3491
|
-
function
|
|
3492
|
-
const sharedExternals = ports.sharedExternalsRepo.getFromScope(shareScope);
|
|
3756
|
+
function addShareScope(importMap, shareScope, sharedExternals, chunkBundles) {
|
|
3493
3757
|
const index = buildServedIndex(sharedExternals);
|
|
3758
|
+
const claims = shareRowMappings(sharedExternals, shareScope);
|
|
3759
|
+
const claimedElsewhere = (externalName, specifier) => {
|
|
3760
|
+
const claim = claims.get(specifier);
|
|
3761
|
+
return claim?.external === externalName ? void 0 : claim;
|
|
3762
|
+
};
|
|
3494
3763
|
for (const [externalName, external] of Object.entries(sharedExternals)) {
|
|
3495
3764
|
let override = void 0;
|
|
3496
|
-
const cachedBefore = cachedFingerprint(external);
|
|
3497
3765
|
for (const version of external.versions) {
|
|
3498
3766
|
if (version.action === "scope") {
|
|
3499
|
-
|
|
3500
|
-
const remoteScope = getScope2(shareScope, remote.name, externalName);
|
|
3501
|
-
for (const [packageName, file] of Object.entries(remote.entries)) {
|
|
3502
|
-
const url = join(remoteScope, file);
|
|
3503
|
-
addToScope(importMap, remoteScope, { [packageName]: url });
|
|
3504
|
-
addIntegrity(importMap, url, remote.name, file);
|
|
3505
|
-
}
|
|
3506
|
-
registerBundleChunks(chunkBundles, remote.name, remote.bundle);
|
|
3507
|
-
remote.cached = true;
|
|
3508
|
-
}
|
|
3767
|
+
addScopeRow(importMap, chunkBundles, shareScope, externalName, version);
|
|
3509
3768
|
continue;
|
|
3510
3769
|
}
|
|
3511
|
-
version.remotes[0].cached = true;
|
|
3512
3770
|
let serving = version;
|
|
3513
3771
|
if (version.action === "skip") {
|
|
3514
3772
|
if (!override) {
|
|
3515
|
-
override = findOverride(
|
|
3773
|
+
override = findOverride(
|
|
3774
|
+
external,
|
|
3775
|
+
shareScope,
|
|
3776
|
+
externalName,
|
|
3777
|
+
(s) => Boolean(claimedElsewhere(externalName, s))
|
|
3778
|
+
) ?? "NOT_AVAILABLE";
|
|
3516
3779
|
}
|
|
3517
3780
|
if (override !== "NOT_AVAILABLE") {
|
|
3518
3781
|
serving = override;
|
|
3519
3782
|
version.remotes[0].cached = false;
|
|
3520
3783
|
}
|
|
3521
3784
|
}
|
|
3522
|
-
const provided =
|
|
3785
|
+
const provided = versionClaims(serving, external.servingBuild);
|
|
3523
3786
|
const mappings = [];
|
|
3524
3787
|
for (const [packageName, provider] of provided) {
|
|
3788
|
+
const claim = claimedElsewhere(externalName, packageName);
|
|
3789
|
+
if (claim) {
|
|
3790
|
+
mappings.push({ packageName, ...claim });
|
|
3791
|
+
continue;
|
|
3792
|
+
}
|
|
3525
3793
|
const file = provider.entries[packageName];
|
|
3526
3794
|
mappings.push({
|
|
3527
3795
|
packageName,
|
|
@@ -3534,56 +3802,60 @@ function createGenerateImportMap(config, ports) {
|
|
|
3534
3802
|
registerBundleChunks(chunkBundles, provider.name, provider.bundle);
|
|
3535
3803
|
}
|
|
3536
3804
|
}
|
|
3537
|
-
version.remotes.forEach((
|
|
3538
|
-
const
|
|
3539
|
-
if (index &&
|
|
3540
|
-
const files = index.get(
|
|
3541
|
-
const servedScope = getScope2(shareScope,
|
|
3542
|
-
for (const specifier of Object.keys(
|
|
3805
|
+
version.remotes.forEach((consumer) => {
|
|
3806
|
+
const consumerScope = getScope2(shareScope, consumer.name, externalName);
|
|
3807
|
+
if (index && consumer.servedBy) {
|
|
3808
|
+
const files = index.get(consumer.servedBy);
|
|
3809
|
+
const servedScope = getScope2(shareScope, consumer.servedBy, externalName);
|
|
3810
|
+
for (const specifier of Object.keys(consumer.entries)) {
|
|
3543
3811
|
const source = files?.get(specifier);
|
|
3544
3812
|
if (!source) continue;
|
|
3545
3813
|
const url = join(servedScope, source.file);
|
|
3546
|
-
addToScope(importMap,
|
|
3547
|
-
addIntegrity(importMap, url,
|
|
3548
|
-
registerBundleChunks(chunkBundles,
|
|
3814
|
+
addToScope(importMap, consumerScope, { [specifier]: url });
|
|
3815
|
+
addIntegrity(importMap, url, consumer.servedBy, source.file);
|
|
3816
|
+
registerBundleChunks(chunkBundles, consumer.servedBy, source.bundle);
|
|
3549
3817
|
source.meta.cached = true;
|
|
3550
3818
|
}
|
|
3551
3819
|
return;
|
|
3552
3820
|
}
|
|
3553
3821
|
for (const m of mappings) {
|
|
3554
|
-
addToScope(importMap,
|
|
3822
|
+
addToScope(importMap, consumerScope, { [m.packageName]: m.url });
|
|
3555
3823
|
}
|
|
3556
|
-
for (const
|
|
3824
|
+
for (const packageName of Object.keys(consumer.entries)) {
|
|
3557
3825
|
if (provided.has(packageName)) continue;
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
continue;
|
|
3561
|
-
}
|
|
3562
|
-
const url = join(rScope, file);
|
|
3563
|
-
addToScope(importMap, rScope, { [packageName]: url });
|
|
3564
|
-
addIntegrity(importMap, url, r.name, file);
|
|
3565
|
-
r.cached = true;
|
|
3826
|
+
const claim = claimedElsewhere(externalName, packageName);
|
|
3827
|
+
if (claim) addToScope(importMap, consumerScope, { [packageName]: claim.url });
|
|
3566
3828
|
}
|
|
3567
3829
|
});
|
|
3568
3830
|
for (const m of mappings) {
|
|
3569
3831
|
addIntegrity(importMap, m.url, m.name, m.file);
|
|
3570
3832
|
}
|
|
3571
3833
|
}
|
|
3572
|
-
|
|
3573
|
-
|
|
3834
|
+
}
|
|
3835
|
+
}
|
|
3836
|
+
function addScopeRow(importMap, chunkBundles, shareScope, externalName, version) {
|
|
3837
|
+
for (const remote of version.remotes) {
|
|
3838
|
+
const remoteScope = getScope2(shareScope, remote.name, externalName);
|
|
3839
|
+
for (const [packageName, file] of Object.entries(remote.entries)) {
|
|
3840
|
+
const url = join(remoteScope, file);
|
|
3841
|
+
addToScope(importMap, remoteScope, { [packageName]: url });
|
|
3842
|
+
addIntegrity(importMap, url, remote.name, file);
|
|
3574
3843
|
}
|
|
3844
|
+
registerBundleChunks(chunkBundles, remote.name, remote.bundle);
|
|
3845
|
+
remote.cached = true;
|
|
3575
3846
|
}
|
|
3576
3847
|
}
|
|
3577
3848
|
function cachedFingerprint(external) {
|
|
3578
3849
|
return external.versions.map((v) => v.remotes.map((r) => r.cached ? 1 : 0).join("")).join("|");
|
|
3579
3850
|
}
|
|
3580
|
-
function findOverride(external, shareScope, externalName) {
|
|
3851
|
+
function findOverride(external, shareScope, externalName, servedElsewhere) {
|
|
3581
3852
|
const sharedVersions = external.versions.filter((v) => v.action === "share");
|
|
3582
3853
|
const scopedExternalName = `${shareScope}.${externalName}`;
|
|
3583
3854
|
if (sharedVersions.length > 1) {
|
|
3584
3855
|
handleMultipleSharedVersions(scopedExternalName);
|
|
3585
3856
|
}
|
|
3586
|
-
|
|
3857
|
+
const servedByClaims = () => external.versions.filter((v) => v.action === "skip").every((v) => shipsOnly(v, servedElsewhere));
|
|
3858
|
+
if (sharedVersions.length < 1 && !servedByClaims()) {
|
|
3587
3859
|
if (config.strict.strictImportMap) {
|
|
3588
3860
|
config.log.error(4, `[${shareScope}][${externalName}] shareScope has no override version.`);
|
|
3589
3861
|
throw new NFError("Could not create ImportMap.");
|
|
@@ -3628,17 +3900,30 @@ function createGenerateImportMap(config, ports) {
|
|
|
3628
3900
|
}
|
|
3629
3901
|
return index;
|
|
3630
3902
|
}
|
|
3631
|
-
function
|
|
3903
|
+
function shareRowMappings(externals, shareScope) {
|
|
3904
|
+
const mappings = /* @__PURE__ */ new Map();
|
|
3905
|
+
for (const [specifier, { external, copy }] of claimTable(Object.entries(externals)).shareClaims) {
|
|
3906
|
+
const file = copy.entries[specifier];
|
|
3907
|
+
mappings.set(specifier, {
|
|
3908
|
+
url: join(getScope2(shareScope, copy.name, external), file),
|
|
3909
|
+
name: copy.name,
|
|
3910
|
+
file,
|
|
3911
|
+
external
|
|
3912
|
+
});
|
|
3913
|
+
}
|
|
3914
|
+
return mappings;
|
|
3915
|
+
}
|
|
3916
|
+
function collectServed(into, index, shareScope, externalName, version) {
|
|
3632
3917
|
for (const consumer of version.remotes) {
|
|
3633
3918
|
if (!consumer.servedBy) continue;
|
|
3634
3919
|
const files = index.get(consumer.servedBy);
|
|
3635
|
-
const scope = getScope2(
|
|
3920
|
+
const scope = getScope2(shareScope, consumer.name, externalName);
|
|
3636
3921
|
for (const specifier of Object.keys(consumer.entries)) {
|
|
3637
3922
|
const source = files?.get(specifier);
|
|
3638
3923
|
if (!source) {
|
|
3639
3924
|
config.log.warn(
|
|
3640
3925
|
4,
|
|
3641
|
-
`[${
|
|
3926
|
+
`[${shareScope}][${externalName}][${consumer.name}] '${consumer.servedBy}' does not serve '${specifier}'.`
|
|
3642
3927
|
);
|
|
3643
3928
|
continue;
|
|
3644
3929
|
}
|
|
@@ -3656,9 +3941,11 @@ function createGenerateImportMap(config, ports) {
|
|
|
3656
3941
|
source.meta.cached = true;
|
|
3657
3942
|
}
|
|
3658
3943
|
}
|
|
3659
|
-
function
|
|
3944
|
+
function addGlobalShareScope(importMap, chunkBundles) {
|
|
3660
3945
|
const sharedExternals = ports.sharedExternalsRepo.getFromScope();
|
|
3661
3946
|
const index = buildServedIndex(sharedExternals);
|
|
3947
|
+
const { claims, duplicates } = claimTable(Object.entries(sharedExternals));
|
|
3948
|
+
const claimed = claimsByCopy(claims);
|
|
3662
3949
|
const served = [];
|
|
3663
3950
|
const cachedBefore = /* @__PURE__ */ new Map();
|
|
3664
3951
|
for (const [externalName, external] of Object.entries(sharedExternals)) {
|
|
@@ -3669,27 +3956,14 @@ function createGenerateImportMap(config, ports) {
|
|
|
3669
3956
|
}
|
|
3670
3957
|
if (version.action === "skip") continue;
|
|
3671
3958
|
if (version.action === "scope") {
|
|
3672
|
-
|
|
3673
|
-
const remoteScope = getScope2(GLOBAL_SCOPE, remote.name, externalName);
|
|
3674
|
-
for (const [packageName, file] of Object.entries(remote.entries)) {
|
|
3675
|
-
const url = join(remoteScope, file);
|
|
3676
|
-
addToScope(importMap, remoteScope, { [packageName]: url });
|
|
3677
|
-
addIntegrity(importMap, url, remote.name, file);
|
|
3678
|
-
}
|
|
3679
|
-
remote.cached = true;
|
|
3680
|
-
registerBundleChunks(chunkBundles, remote.name, remote.bundle);
|
|
3681
|
-
}
|
|
3959
|
+
addScopeRow(importMap, chunkBundles, GLOBAL_SCOPE, externalName, version);
|
|
3682
3960
|
continue;
|
|
3683
3961
|
}
|
|
3684
|
-
if (
|
|
3962
|
+
if (duplicates.has(version)) {
|
|
3685
3963
|
notifyDuplicateGlobalExternal(externalName);
|
|
3686
3964
|
continue;
|
|
3687
3965
|
}
|
|
3688
|
-
|
|
3689
|
-
}
|
|
3690
|
-
for (const version of external.versions) {
|
|
3691
|
-
if (version.action !== "skip") continue;
|
|
3692
|
-
selfFillUncovered(importMap, chunkBundles, externalName, version.remotes);
|
|
3966
|
+
publishClaims(importMap, chunkBundles, externalName, version, claimed);
|
|
3693
3967
|
}
|
|
3694
3968
|
}
|
|
3695
3969
|
flushServed(importMap, chunkBundles, served);
|
|
@@ -3700,54 +3974,30 @@ function createGenerateImportMap(config, ports) {
|
|
|
3700
3974
|
}
|
|
3701
3975
|
return importMap;
|
|
3702
3976
|
}
|
|
3703
|
-
function
|
|
3704
|
-
const
|
|
3705
|
-
for (const [
|
|
3706
|
-
|
|
3707
|
-
|
|
3708
|
-
|
|
3709
|
-
`[${GLOBAL_SCOPE}][${externalName}][${remote.name}] Entrypoint '${packageName}' is already mapped by another external, keeping '${importMap.imports[packageName]}'.`
|
|
3710
|
-
);
|
|
3711
|
-
continue;
|
|
3712
|
-
}
|
|
3713
|
-
let scope = scopes.get(remote.name);
|
|
3714
|
-
if (scope === void 0) {
|
|
3715
|
-
scopes.set(remote.name, scope = getScope2(GLOBAL_SCOPE, remote.name, externalName));
|
|
3716
|
-
registerBundleChunks(chunkBundles, remote.name, remote.bundle);
|
|
3717
|
-
remote.cached = true;
|
|
3718
|
-
}
|
|
3719
|
-
const file = remote.entries[packageName];
|
|
3720
|
-
const url = join(scope, file);
|
|
3721
|
-
addToGlobal(importMap, { [packageName]: url });
|
|
3722
|
-
addIntegrity(importMap, url, remote.name, file);
|
|
3977
|
+
function claimsByCopy(claims) {
|
|
3978
|
+
const claimed = /* @__PURE__ */ new Map();
|
|
3979
|
+
for (const [specifier, { copy }] of claims) {
|
|
3980
|
+
const specifiers = claimed.get(copy);
|
|
3981
|
+
if (specifiers) specifiers.push(specifier);
|
|
3982
|
+
else claimed.set(copy, [specifier]);
|
|
3723
3983
|
}
|
|
3724
|
-
|
|
3725
|
-
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
|
|
3731
|
-
|
|
3732
|
-
|
|
3733
|
-
|
|
3734
|
-
const
|
|
3984
|
+
return claimed;
|
|
3985
|
+
}
|
|
3986
|
+
function publishClaims(importMap, chunkBundles, externalName, version, claimed) {
|
|
3987
|
+
for (const copy of version.remotes) {
|
|
3988
|
+
const specifiers = claimed.get(copy);
|
|
3989
|
+
if (!specifiers) continue;
|
|
3990
|
+
const scope = getScope2(GLOBAL_SCOPE, copy.name, externalName);
|
|
3991
|
+
registerBundleChunks(chunkBundles, copy.name, copy.bundle);
|
|
3992
|
+
copy.cached = true;
|
|
3993
|
+
for (const specifier of specifiers) {
|
|
3994
|
+
const file = copy.entries[specifier];
|
|
3735
3995
|
const url = join(scope, file);
|
|
3736
|
-
addToGlobal(importMap, { [
|
|
3737
|
-
addIntegrity(importMap, url,
|
|
3738
|
-
registerBundleChunks(chunkBundles, remote.name, remote.bundle);
|
|
3739
|
-
remote.cached = true;
|
|
3996
|
+
addToGlobal(importMap, { [specifier]: url });
|
|
3997
|
+
addIntegrity(importMap, url, copy.name, file);
|
|
3740
3998
|
}
|
|
3741
3999
|
}
|
|
3742
4000
|
}
|
|
3743
|
-
function warnUncoveredEntrypoint(scope, externalName, remoteName, packageName) {
|
|
3744
|
-
const msg = `[${scope}][${externalName}][${remoteName}] Entrypoint '${packageName}' is not covered by the shared version.`;
|
|
3745
|
-
if (config.strict.strictEntryPointCoverage || config.strict.strictImportMap) {
|
|
3746
|
-
config.log.error(4, msg);
|
|
3747
|
-
throw new NFError("Could not create ImportMap.");
|
|
3748
|
-
}
|
|
3749
|
-
config.log.warn(4, msg);
|
|
3750
|
-
}
|
|
3751
4001
|
function notifyDuplicateGlobalExternal(externalName) {
|
|
3752
4002
|
if (config.strict.strictImportMap) {
|
|
3753
4003
|
config.log.error(4, `[${externalName}] Shared external has multiple shared versions.`);
|
|
@@ -3821,15 +4071,30 @@ function createGenerateImportMap(config, ports) {
|
|
|
3821
4071
|
});
|
|
3822
4072
|
}
|
|
3823
4073
|
}
|
|
4074
|
+
function shipsOnly(version, served) {
|
|
4075
|
+
let only = true;
|
|
4076
|
+
forEachVersionEntry(version, void 0, (specifier) => {
|
|
4077
|
+
only &&= served(specifier);
|
|
4078
|
+
});
|
|
4079
|
+
return only;
|
|
4080
|
+
}
|
|
3824
4081
|
|
|
3825
4082
|
// src/lib/core/2.app/steps/commit-changes.ts
|
|
3826
4083
|
function createCommitChanges(config, ports) {
|
|
3827
|
-
return (importMap,
|
|
4084
|
+
return (importMap, { markUndecided = false, ...browserOpts } = {}) => Promise.resolve(importMap).then((map) => addToBrowser(map, browserOpts)).then(() => {
|
|
4085
|
+
if (markUndecided) markUndecidedScopes();
|
|
4086
|
+
persistRepositoryChanges();
|
|
4087
|
+
});
|
|
3828
4088
|
function addToBrowser(importMap, opts) {
|
|
3829
4089
|
ports.browser.setImportMapFn(importMap, opts);
|
|
3830
4090
|
config.log.debug(5, "Added import map to browser.", importMap);
|
|
3831
4091
|
return importMap;
|
|
3832
4092
|
}
|
|
4093
|
+
function markUndecidedScopes() {
|
|
4094
|
+
const repo = ports.sharedExternalsRepo;
|
|
4095
|
+
for (const shareScope of repo.uncommittedScopes())
|
|
4096
|
+
if (repo.scopeType(shareScope) !== "strict") repo.markUndecided(shareScope);
|
|
4097
|
+
}
|
|
3833
4098
|
function persistRepositoryChanges() {
|
|
3834
4099
|
ports.remoteInfoRepo.commit();
|
|
3835
4100
|
ports.scopedExternalsRepo.commit();
|
|
@@ -3839,6 +4104,29 @@ function createCommitChanges(config, ports) {
|
|
|
3839
4104
|
}
|
|
3840
4105
|
}
|
|
3841
4106
|
|
|
4107
|
+
// src/lib/core/2.app/steps/rollback-changes.ts
|
|
4108
|
+
var createStateDump = (config, ports) => (msg) => config.log.debug(0, msg, {
|
|
4109
|
+
remotes: { ...ports.remoteInfoRepo.getAll() },
|
|
4110
|
+
"shared-externals": ports.sharedExternalsRepo.getScopes({ includeGlobal: true }).reduce(
|
|
4111
|
+
(acc, scope) => ({ ...acc, [scope]: ports.sharedExternalsRepo.getFromScope(scope) }),
|
|
4112
|
+
{}
|
|
4113
|
+
),
|
|
4114
|
+
"scoped-externals": ports.scopedExternalsRepo.getAll()
|
|
4115
|
+
});
|
|
4116
|
+
function createRollbackChanges(config, ports) {
|
|
4117
|
+
const stateDump = createStateDump(config, ports);
|
|
4118
|
+
return (label) => Promise.resolve().then(() => {
|
|
4119
|
+
try {
|
|
4120
|
+
stateDump(`${label} STATE DUMP`);
|
|
4121
|
+
} finally {
|
|
4122
|
+
ports.remoteInfoRepo.rollback();
|
|
4123
|
+
ports.scopedExternalsRepo.rollback();
|
|
4124
|
+
ports.sharedExternalsRepo.rollback();
|
|
4125
|
+
ports.sharedChunksRepo.rollback();
|
|
4126
|
+
}
|
|
4127
|
+
});
|
|
4128
|
+
}
|
|
4129
|
+
|
|
3842
4130
|
// src/lib/core/2.app/steps/expose-module-loader.ts
|
|
3843
4131
|
function createExposeModuleLoader(config, ports) {
|
|
3844
4132
|
const loadRemoteModule = (remoteName, exposedModule) => {
|
|
@@ -3874,10 +4162,16 @@ var normalizeRemoteRef = (remote) => {
|
|
|
3874
4162
|
function createGetRemoteEntry(config, ports) {
|
|
3875
4163
|
return async (remoteEntryUrl, remote) => {
|
|
3876
4164
|
const { name: remoteName, integrity } = normalizeRemoteRef(remote);
|
|
3877
|
-
|
|
3878
|
-
|
|
4165
|
+
const knownAtUrl = findCachedRemoteAt(remoteEntryUrl);
|
|
4166
|
+
if (knownAtUrl) {
|
|
4167
|
+
config.log.debug(
|
|
4168
|
+
7,
|
|
4169
|
+
`Found remote '${knownAtUrl}' at '${remoteEntryUrl}' in storage, omitting fetch.`
|
|
4170
|
+
);
|
|
3879
4171
|
return Optional.empty();
|
|
3880
4172
|
}
|
|
4173
|
+
if (!!remoteName && ports.remoteInfoRepo.contains(remoteName))
|
|
4174
|
+
return warnInitialized(remoteName, remoteEntryUrl);
|
|
3881
4175
|
try {
|
|
3882
4176
|
const remoteEntry = integrity ? await ports.remoteEntryProvider.provide(remoteEntryUrl, { integrity }) : await ports.remoteEntryProvider.provide(remoteEntryUrl);
|
|
3883
4177
|
config.log.debug(
|
|
@@ -3892,10 +4186,8 @@ function createGetRemoteEntry(config, ports) {
|
|
|
3892
4186
|
}
|
|
3893
4187
|
config.log.warn(7, errorMsg + " Omitting expected name.");
|
|
3894
4188
|
}
|
|
3895
|
-
if (ports.remoteInfoRepo.contains(remoteEntry.name))
|
|
3896
|
-
remoteEntry.
|
|
3897
|
-
config.log.debug(7, `Overriding existing remote '${remoteName}' with '${remoteEntryUrl}'.`);
|
|
3898
|
-
}
|
|
4189
|
+
if (ports.remoteInfoRepo.contains(remoteEntry.name))
|
|
4190
|
+
return warnInitialized(remoteEntry.name, remoteEntryUrl);
|
|
3899
4191
|
return Optional.of(checkForSSE(remoteEntry));
|
|
3900
4192
|
} catch (error) {
|
|
3901
4193
|
config.log.error(
|
|
@@ -3908,10 +4200,18 @@ function createGetRemoteEntry(config, ports) {
|
|
|
3908
4200
|
);
|
|
3909
4201
|
}
|
|
3910
4202
|
};
|
|
3911
|
-
function
|
|
3912
|
-
|
|
3913
|
-
|
|
3914
|
-
|
|
4203
|
+
function warnInitialized(name, remoteEntryUrl) {
|
|
4204
|
+
const initializedFrom = ports.remoteInfoRepo.tryGet(name).map((info) => join(info.scopeUrl, "remoteEntry.json")).get();
|
|
4205
|
+
config.log.warn(
|
|
4206
|
+
7,
|
|
4207
|
+
`[${name}] Already initialized from '${initializedFrom}', ignoring '${remoteEntryUrl}'.`
|
|
4208
|
+
);
|
|
4209
|
+
return Optional.empty();
|
|
4210
|
+
}
|
|
4211
|
+
function findCachedRemoteAt(remoteEntryUrl) {
|
|
4212
|
+
return Object.entries(ports.remoteInfoRepo.getAll()).find(
|
|
4213
|
+
([, info]) => join(info.scopeUrl, "remoteEntry.json") === remoteEntryUrl
|
|
4214
|
+
)?.[0];
|
|
3915
4215
|
}
|
|
3916
4216
|
function checkForSSE(entry) {
|
|
3917
4217
|
if (config.sse) {
|
|
@@ -3931,92 +4231,170 @@ function createGetRemoteEntry(config, ports) {
|
|
|
3931
4231
|
// src/lib/core/2.app/steps/update-cache.ts
|
|
3932
4232
|
function createUpdateCache(config, ports) {
|
|
3933
4233
|
const storeRemoteEntry = createStoreRemoteEntry(config, ports, 8);
|
|
3934
|
-
const
|
|
4234
|
+
const accepts = createAcceptsTag(
|
|
4235
|
+
(tag, range) => ports.versionCheck.isCompatible(tag, range),
|
|
4236
|
+
(a, b) => ports.versionCheck.compare(a, b)
|
|
4237
|
+
);
|
|
3935
4238
|
return (remoteEntry) => {
|
|
3936
4239
|
try {
|
|
4240
|
+
const pending = [];
|
|
4241
|
+
storeRemoteEntry(remoteEntry, (_, external, ctx) => pending.push({ external, ctx }));
|
|
4242
|
+
const decisions = decidePackages(remoteEntry, pending);
|
|
3937
4243
|
const actions = {};
|
|
3938
|
-
|
|
3939
|
-
|
|
3940
|
-
const { action, provided, sameVersion } = resolveSharedExternal(entry, external, ctx);
|
|
3941
|
-
actions[external.packageName] = { action };
|
|
3942
|
-
if (action === "skip" && provided) {
|
|
3943
|
-
actions[external.packageName].covered = Array.from(provided.keys());
|
|
3944
|
-
if (sameVersion) actions[external.packageName].sameVersion = true;
|
|
3945
|
-
if (external.shareScope) {
|
|
3946
|
-
actions[external.packageName].override = resolveOverrideEntries(
|
|
3947
|
-
entry,
|
|
3948
|
-
external,
|
|
3949
|
-
provided
|
|
3950
|
-
);
|
|
3951
|
-
}
|
|
3952
|
-
}
|
|
3953
|
-
});
|
|
4244
|
+
for (const item of pending)
|
|
4245
|
+
actions[item.external.packageName] = applyDecision(remoteEntry, item, decisions.get(item));
|
|
3954
4246
|
return Promise.resolve({ entry: remoteEntry, actions });
|
|
3955
4247
|
} catch (error) {
|
|
3956
4248
|
return Promise.reject(error);
|
|
3957
4249
|
}
|
|
3958
4250
|
};
|
|
3959
|
-
function
|
|
3960
|
-
|
|
3961
|
-
|
|
3962
|
-
|
|
3963
|
-
|
|
3964
|
-
|
|
3965
|
-
commit
|
|
3966
|
-
}) {
|
|
3967
|
-
let action = scopeType === "strict" ? "share" : "skip";
|
|
3968
|
-
const sharedVersion = cached2.versions.find((c) => c.action === "share");
|
|
3969
|
-
const isCompatible = !sharedVersion || ports.versionCheck.isCompatible(sharedVersion.tag, remote.requiredVersion);
|
|
3970
|
-
if (action === "skip" && !isCompatible && remote.strictVersion) {
|
|
3971
|
-
action = "scope";
|
|
3972
|
-
const errorMsg = `[${sharedInfo.shareScope ?? GLOBAL_SCOPE}][${remoteEntry.name}] ${sharedInfo.packageName}@${sharedInfo.version} Is not compatible with existing ${sharedInfo.packageName}@${sharedVersion.tag} requiredRange '${sharedVersion.remotes[0]?.requiredVersion}'`;
|
|
3973
|
-
if (config.strict.strictExternalCompatibility) {
|
|
3974
|
-
config.log.error(8, errorMsg);
|
|
3975
|
-
throw new NFError(`Could not process remote '${remoteEntry.name}'`);
|
|
3976
|
-
}
|
|
3977
|
-
config.log.warn(8, errorMsg);
|
|
4251
|
+
function decidePackages(remoteEntry, pending) {
|
|
4252
|
+
const decisions = /* @__PURE__ */ new Map();
|
|
4253
|
+
const viewOf = loadViewOf(remoteEntry, pending);
|
|
4254
|
+
for (const group of pendingPerPackage(pending)) {
|
|
4255
|
+
const decision = group[0].ctx.scopeType === "strict" ? { action: "share" } : decidePackage(remoteEntry, group, viewOf(group[0].external.shareScope));
|
|
4256
|
+
for (const item of group) decisions.set(item, decision);
|
|
3978
4257
|
}
|
|
3979
|
-
|
|
3980
|
-
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
|
|
3987
|
-
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
|
|
3992
|
-
|
|
4258
|
+
scopeOpenedPackages(decisions);
|
|
4259
|
+
return decisions;
|
|
4260
|
+
}
|
|
4261
|
+
function pendingPerPackage(pending) {
|
|
4262
|
+
const packages = /* @__PURE__ */ new Map();
|
|
4263
|
+
for (const item of pending) {
|
|
4264
|
+
const { shareScope, packageName } = item.external;
|
|
4265
|
+
const key = `${shareScope ?? GLOBAL_SCOPE}\0${packageOf(packageName)}`;
|
|
4266
|
+
const group = packages.get(key);
|
|
4267
|
+
if (group) group.push(item);
|
|
4268
|
+
else packages.set(key, [item]);
|
|
4269
|
+
}
|
|
4270
|
+
return [...packages.values()];
|
|
4271
|
+
}
|
|
4272
|
+
function loadViewOf(remoteEntry, pending) {
|
|
4273
|
+
const views = /* @__PURE__ */ new Map();
|
|
4274
|
+
return (shareScope) => {
|
|
4275
|
+
const scope = shareScope ?? GLOBAL_SCOPE;
|
|
4276
|
+
let view = views.get(scope);
|
|
4277
|
+
if (!view) {
|
|
4278
|
+
const record = ports.sharedExternalsRepo.getFromScope(shareScope);
|
|
4279
|
+
const claims = committedClaims(Object.entries(record), {
|
|
4280
|
+
shareScope: scope,
|
|
4281
|
+
without: remoteEntry.name
|
|
4282
|
+
});
|
|
4283
|
+
const claimsIn = claimsByPackage(claims);
|
|
4284
|
+
let pooled;
|
|
4285
|
+
view = {
|
|
4286
|
+
claims,
|
|
4287
|
+
claimsOf: (pkg) => claimsIn.get(pkg) ?? [],
|
|
4288
|
+
inPool: (pkg) => (pooled ??= pooledPackagesWith(record, copiesIn(pending, scope))).has(pkg)
|
|
4289
|
+
};
|
|
4290
|
+
views.set(scope, view);
|
|
3993
4291
|
}
|
|
4292
|
+
return view;
|
|
4293
|
+
};
|
|
4294
|
+
}
|
|
4295
|
+
function copiesIn(pending, scope) {
|
|
4296
|
+
return pending.filter(({ external }) => (external.shareScope ?? GLOBAL_SCOPE) === scope).map(({ external, ctx }) => ({ name: external.packageName, pool: ctx.remote.pool }));
|
|
4297
|
+
}
|
|
4298
|
+
function scopeOpenedPackages(decisions) {
|
|
4299
|
+
if (![...decisions.values()].some((d) => d.action === "scope" && d.partlyServed)) return;
|
|
4300
|
+
for (const [item, decision] of decisions)
|
|
4301
|
+
if (decision.action === "share" && item.ctx.scopeType !== "strict")
|
|
4302
|
+
decisions.set(item, { action: "scope", partlyServed: false });
|
|
4303
|
+
}
|
|
4304
|
+
function decidePackage(remoteEntry, group, view) {
|
|
4305
|
+
const pkg = packageOf(group[0].external.packageName);
|
|
4306
|
+
const claims = view.claimsOf(pkg);
|
|
4307
|
+
if (claims.length === 0) return { action: "share" };
|
|
4308
|
+
const specifiers = group.flatMap(({ external }) => Object.keys(external.entries));
|
|
4309
|
+
const builds = specifiers.every((specifier) => view.claims.has(specifier)) ? new Set(specifiers.map((specifier) => view.claims.get(specifier).copy.name)) : void 0;
|
|
4310
|
+
const rejections = group.flatMap((item) => rejectionsOf(item, view.claims));
|
|
4311
|
+
if (rejections.length > 0) {
|
|
4312
|
+
const fromTwoBuilds = builds !== void 0 && builds.size > 1;
|
|
4313
|
+
for (const rejection of refused(group, rejections, fromTwoBuilds, claims, view.inPool(pkg)))
|
|
4314
|
+
reportIncompatible(remoteEntry, rejection);
|
|
4315
|
+
return { action: "scope", partlyServed: false };
|
|
3994
4316
|
}
|
|
3995
|
-
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
|
|
4005
|
-
|
|
4006
|
-
|
|
4007
|
-
|
|
4317
|
+
if (!builds || builds.size > 1) return { action: "scope", partlyServed: true };
|
|
4318
|
+
return { action: "skip", files: servingFiles(claims, builds) };
|
|
4319
|
+
}
|
|
4320
|
+
function servingFiles(claims, builds) {
|
|
4321
|
+
const files = /* @__PURE__ */ new Map();
|
|
4322
|
+
for (const [specifier, { copy }] of claims)
|
|
4323
|
+
if (builds.has(copy.name)) files.set(specifier, copy);
|
|
4324
|
+
return files;
|
|
4325
|
+
}
|
|
4326
|
+
function rejectionsOf(item, claims) {
|
|
4327
|
+
const { external, ctx } = item;
|
|
4328
|
+
if (!ctx.remote.strictVersion) return [];
|
|
4329
|
+
return Object.keys(external.entries).flatMap((specifier) => {
|
|
4330
|
+
const claim = claims.get(specifier);
|
|
4331
|
+
return claim && !accepts(claim.tag, ctx.tag, ctx.remote.requiredVersion) ? [{ item, specifier, claim }] : [];
|
|
4332
|
+
});
|
|
4333
|
+
}
|
|
4334
|
+
function refused(group, rejections, fromTwoBuilds, claims, pooled) {
|
|
4335
|
+
const copies = group.map(({ ctx }) => ({ tag: ctx.tag, copy: ctx.remote }));
|
|
4336
|
+
const splits = fromTwoBuilds || loadSplitsPackage(copies, claims, accepts);
|
|
4337
|
+
const first = /* @__PURE__ */ new Map();
|
|
4338
|
+
for (const rejection of rejections)
|
|
4339
|
+
if (!first.has(rejection.item) && (pooled || !splits || electionKeeps(rejection)))
|
|
4340
|
+
first.set(rejection.item, rejection);
|
|
4341
|
+
return [...first.values()];
|
|
4342
|
+
}
|
|
4343
|
+
function electionKeeps({ item: { external, ctx }, claim }) {
|
|
4344
|
+
if (claim.external !== external.packageName) return false;
|
|
4345
|
+
const versions = ports.sharedExternalsRepo.tryGet(claim.external, external.shareScope).get()?.versions ?? [];
|
|
4346
|
+
const strict = [
|
|
4347
|
+
{ tag: ctx.tag, copy: ctx.remote },
|
|
4348
|
+
...versions.flatMap(
|
|
4349
|
+
(v) => v.remotes.filter((r) => r.strictVersion).map((copy) => ({ tag: v.tag, copy }))
|
|
4350
|
+
)
|
|
4351
|
+
];
|
|
4352
|
+
const tags = [ctx.tag, ...versions.map((v) => v.tag)];
|
|
4353
|
+
if (!tags.some((tag) => strict.every((s) => accepts(tag, s.tag, s.copy.requiredVersion))))
|
|
4354
|
+
return true;
|
|
4355
|
+
const host = versions.find((v) => v.host);
|
|
4356
|
+
if (host) return host.tag === claim.tag;
|
|
4357
|
+
if (!config.profile.latestSharedExternal) return false;
|
|
4358
|
+
return tags.every((tag) => ports.versionCheck.compare(claim.tag, tag) >= 0);
|
|
4359
|
+
}
|
|
4360
|
+
function reportIncompatible(remoteEntry, { item: { external, ctx }, specifier, claim }) {
|
|
4361
|
+
const errorMsg = `[${external.shareScope ?? GLOBAL_SCOPE}][${remoteEntry.name}] ${specifier}@${ctx.tag} Is not compatible with existing ${specifier}@${claim.tag} requiredRange '${ctx.remote.requiredVersion}'`;
|
|
4362
|
+
if (config.strict.strictExternalCompatibility) {
|
|
4363
|
+
config.log.error(8, errorMsg);
|
|
4364
|
+
throw new NFError(`Could not process remote '${remoteEntry.name}'`);
|
|
4008
4365
|
}
|
|
4009
|
-
|
|
4010
|
-
return { action, provided, sameVersion };
|
|
4366
|
+
config.log.warn(8, errorMsg);
|
|
4011
4367
|
}
|
|
4012
|
-
function
|
|
4368
|
+
function applyDecision(remoteEntry, { external, ctx }, decision) {
|
|
4369
|
+
storeCopy(ctx, decision.action);
|
|
4370
|
+
if (decision.action !== "skip") return { action: decision.action };
|
|
4371
|
+
const covered = Object.keys(external.entries);
|
|
4372
|
+
if (!external.shareScope) return { action: "skip", covered };
|
|
4373
|
+
return {
|
|
4374
|
+
action: "skip",
|
|
4375
|
+
covered,
|
|
4376
|
+
override: overrideFrom(remoteEntry, external, decision.files)
|
|
4377
|
+
};
|
|
4378
|
+
}
|
|
4379
|
+
function storeCopy(ctx, action) {
|
|
4380
|
+
const { tag, remote } = ctx;
|
|
4381
|
+
const cached2 = ctx.readRecord();
|
|
4382
|
+
const shared = cached2.versions.find((v) => v.tag === tag && v.action === "share");
|
|
4383
|
+
if (shared && action !== "scope") ctx.assertSameVersionCompatibility(shared);
|
|
4384
|
+
remote.cached = action !== "skip";
|
|
4385
|
+
const row = action === "share" ? shared : cached2.versions.find((v) => v.tag === tag && v.action === action);
|
|
4386
|
+
const loaderServes = action === "share" && ctx.scopeType !== "strict";
|
|
4387
|
+
if (!row) cached2.versions.push({ tag, action, host: false, remotes: [remote] });
|
|
4388
|
+
else if (loaderServes) addRemoteToVersion(row, remote);
|
|
4389
|
+
else row.remotes.push(remote);
|
|
4390
|
+
if (loaderServes) cached2.servingBuild = remote.name;
|
|
4391
|
+
ctx.write();
|
|
4392
|
+
}
|
|
4393
|
+
function overrideFrom(remoteEntry, external, files) {
|
|
4013
4394
|
return Object.fromEntries(
|
|
4014
|
-
Array.from(
|
|
4015
|
-
|
|
4016
|
-
join(
|
|
4017
|
-
providerScopeUrl(remoteEntry, external, provider.name),
|
|
4018
|
-
provider.entries[packageName]
|
|
4019
|
-
)
|
|
4395
|
+
Array.from(files, ([specifier, copy]) => [
|
|
4396
|
+
specifier,
|
|
4397
|
+
join(providerScopeUrl(remoteEntry, external, copy.name), copy.entries[specifier])
|
|
4020
4398
|
])
|
|
4021
4399
|
);
|
|
4022
4400
|
}
|
|
@@ -4031,181 +4409,298 @@ function createUpdateCache(config, ports) {
|
|
|
4031
4409
|
}
|
|
4032
4410
|
}
|
|
4033
4411
|
|
|
4034
|
-
// src/lib/core/
|
|
4035
|
-
function
|
|
4036
|
-
|
|
4037
|
-
|
|
4038
|
-
|
|
4039
|
-
|
|
4040
|
-
|
|
4041
|
-
|
|
4042
|
-
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
|
|
4046
|
-
|
|
4047
|
-
|
|
4048
|
-
|
|
4049
|
-
const
|
|
4050
|
-
|
|
4051
|
-
|
|
4052
|
-
|
|
4053
|
-
|
|
4054
|
-
|
|
4055
|
-
|
|
4056
|
-
|
|
4057
|
-
|
|
4058
|
-
|
|
4059
|
-
|
|
4060
|
-
|
|
4061
|
-
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
|
|
4065
|
-
|
|
4066
|
-
|
|
4067
|
-
|
|
4068
|
-
|
|
4069
|
-
if (
|
|
4070
|
-
|
|
4071
|
-
|
|
4072
|
-
|
|
4073
|
-
|
|
4074
|
-
|
|
4412
|
+
// src/lib/core/1.domain/pooling/gate.ts
|
|
4413
|
+
function committedView(members, shipped, recordOrder, { shareScope, without }) {
|
|
4414
|
+
const global = /* @__PURE__ */ new Map();
|
|
4415
|
+
const claims = committedClaims(inRecordOrder(members, recordOrder), { shareScope, without });
|
|
4416
|
+
for (const [specifier, { tag, copy }] of claims)
|
|
4417
|
+
global.set(specifier, { tag, remote: copy.name, file: copy.entries[specifier] });
|
|
4418
|
+
const builds = /* @__PURE__ */ new Map();
|
|
4419
|
+
for (const [owner, copies] of shipped)
|
|
4420
|
+
if (owner !== without) builds.set(owner, buildOf(owner, copies));
|
|
4421
|
+
const fileOf = subpoolFilesOf(members);
|
|
4422
|
+
return { builds, global, runsOffMap: runsOffMapOf(members, global, builds, fileOf) };
|
|
4423
|
+
}
|
|
4424
|
+
function subpoolFilesOf(members) {
|
|
4425
|
+
const subpoolOwners = /* @__PURE__ */ new Set();
|
|
4426
|
+
for (const member of members)
|
|
4427
|
+
for (const version of member.external.versions)
|
|
4428
|
+
for (const { servedBy } of version.remotes) if (servedBy) subpoolOwners.add(servedBy);
|
|
4429
|
+
const fileOf = /* @__PURE__ */ new Map();
|
|
4430
|
+
for (const member of members)
|
|
4431
|
+
for (const version of member.external.versions)
|
|
4432
|
+
for (const { name, entries } of version.remotes) {
|
|
4433
|
+
if (!subpoolOwners.has(name)) continue;
|
|
4434
|
+
let files = fileOf.get(name);
|
|
4435
|
+
if (!files) fileOf.set(name, files = /* @__PURE__ */ new Map());
|
|
4436
|
+
for (const specifier in entries)
|
|
4437
|
+
if (!files.has(specifier)) files.set(specifier, entries[specifier]);
|
|
4438
|
+
}
|
|
4439
|
+
return fileOf;
|
|
4440
|
+
}
|
|
4441
|
+
function runsOffMapOf(members, global, builds, fileOf) {
|
|
4442
|
+
const runsOffMap2 = /* @__PURE__ */ new Map();
|
|
4443
|
+
for (const member of members)
|
|
4444
|
+
for (const version of member.external.versions)
|
|
4445
|
+
for (const meta of version.remotes) {
|
|
4446
|
+
const { servedBy } = meta;
|
|
4447
|
+
if (version.action === "share" && !servedBy) continue;
|
|
4448
|
+
let runs = runsOffMap2.get(meta.name);
|
|
4449
|
+
if (!runs) runsOffMap2.set(meta.name, runs = /* @__PURE__ */ new Map());
|
|
4450
|
+
for (const specifier in meta.entries) {
|
|
4451
|
+
if (version.action === "skip" && !servedBy && global.has(specifier)) continue;
|
|
4452
|
+
const tag = (servedBy ? builds.get(servedBy)?.tags.pinnedTag(specifier) : void 0) ?? version.tag;
|
|
4453
|
+
const file = servedBy && fileOf.get(servedBy)?.get(specifier);
|
|
4454
|
+
runs.set(
|
|
4455
|
+
specifier,
|
|
4456
|
+
file ? { tag, remote: servedBy, file } : { tag, remote: meta.name, file: meta.entries[specifier] }
|
|
4075
4457
|
);
|
|
4076
|
-
mine.forEach((member) => scope(member.name, "uncovered"));
|
|
4077
|
-
continue;
|
|
4078
4458
|
}
|
|
4079
|
-
for (const name of redirect(entry.name, anchor, pool, asked.view, actions, shareScope))
|
|
4080
|
-
verdicts.set(name, { servedBy: anchor });
|
|
4081
4459
|
}
|
|
4082
|
-
|
|
4083
|
-
|
|
4084
|
-
|
|
4085
|
-
|
|
4460
|
+
return runsOffMap2;
|
|
4461
|
+
}
|
|
4462
|
+
function judgeRemote(copies, view, accepts) {
|
|
4463
|
+
const { rejected, missing } = scan(copies, view, accepts);
|
|
4464
|
+
if (rejected) return { cause: "incompatible", ...rejected };
|
|
4465
|
+
const globalTags = new SpecifierTags([...view.global].map(([s, { tag }]) => [s, tag]));
|
|
4466
|
+
const binding = offMapBinding(copies, view, globalTags);
|
|
4467
|
+
if (binding) return { cause: "uncovered", ...binding };
|
|
4468
|
+
if (agrees(copies, globalTags)) return "global";
|
|
4469
|
+
if (missing !== void 0) return { cause: "uncovered", specifier: missing };
|
|
4470
|
+
if (unwitnessedGap(copies, globalTags, view.builds.values()) === void 0) return "global";
|
|
4471
|
+
return { cause: "uncovered", unshipped: true };
|
|
4472
|
+
}
|
|
4473
|
+
function coverFromMap(remote, mine, view, actions, policy) {
|
|
4474
|
+
const files = [];
|
|
4475
|
+
for (const member of mine) {
|
|
4476
|
+
const action = actions[member.name];
|
|
4477
|
+
const copy = member.external.versions.flatMap((v) => v.remotes).find((r) => r.name === remote);
|
|
4478
|
+
if (!copy) continue;
|
|
4479
|
+
const mapped = mapFiles(copy, view, policy, action);
|
|
4480
|
+
if ("unmapped" in mapped) return mapped;
|
|
4481
|
+
files.push({ member, action, copy, ...mapped });
|
|
4482
|
+
}
|
|
4483
|
+
const reaches = reachesOf(remote, files, view);
|
|
4484
|
+
const bound = boundByFile(reaches, view);
|
|
4485
|
+
if (bound !== void 0) return { bound };
|
|
4486
|
+
const servedPackages = new Set([...view.global.keys()].map(packageOf));
|
|
4487
|
+
const gap = files.map((file) => gapOf(file, servedPackages)).find((specifier) => specifier !== void 0);
|
|
4488
|
+
if (gap !== void 0) return { gap };
|
|
4489
|
+
return { covers: files.filter(covering).map(coverOf) };
|
|
4490
|
+
}
|
|
4491
|
+
function gapOf({ copy, covered }, servedPackages) {
|
|
4492
|
+
const entrypoints = Object.keys(copy.entries);
|
|
4493
|
+
const missing = entrypoints.filter((specifier) => !covered.has(specifier));
|
|
4494
|
+
if (missing.length < entrypoints.length) return missing[0];
|
|
4495
|
+
return missing.find((specifier) => servedPackages.has(packageOf(specifier)));
|
|
4496
|
+
}
|
|
4497
|
+
function covering({ action, copy, covered }) {
|
|
4498
|
+
if (action.action === "skip") return true;
|
|
4499
|
+
const entrypoints = Object.keys(copy.entries);
|
|
4500
|
+
return entrypoints.length > 0 && entrypoints.every((specifier) => covered.has(specifier));
|
|
4501
|
+
}
|
|
4502
|
+
function coverOf({ member, action, copy, covered, override }) {
|
|
4503
|
+
const toSkip = action.action !== "skip" || covered.size > action.covered.length && member.external.versions.some((v) => v.action === "share" && v.remotes.includes(copy));
|
|
4504
|
+
return { name: member.name, toSkip, override };
|
|
4505
|
+
}
|
|
4506
|
+
function reachesOf(remote, files, view) {
|
|
4507
|
+
const reaches = /* @__PURE__ */ new Map();
|
|
4508
|
+
const fromMap = (specifier) => {
|
|
4509
|
+
const served = view.global.get(specifier);
|
|
4510
|
+
return served && { remote: served.remote, file: served.file };
|
|
4511
|
+
};
|
|
4512
|
+
for (const file of files) {
|
|
4513
|
+
const takesMap = covering(file);
|
|
4514
|
+
for (const [specifier, own] of Object.entries(file.copy.entries)) {
|
|
4515
|
+
if (!takesMap || !file.covered.has(specifier)) reaches.set(specifier, { remote, file: own });
|
|
4516
|
+
else {
|
|
4517
|
+
const reach = fromMap(specifier);
|
|
4518
|
+
if (reach) reaches.set(specifier, reach);
|
|
4086
4519
|
}
|
|
4087
|
-
syncPoolNames({ ...committed, ...written }, pools, ports.sharedExternalsRepo, shareScope);
|
|
4088
4520
|
}
|
|
4089
|
-
return Promise.resolve({ entry, actions });
|
|
4090
|
-
};
|
|
4091
|
-
function gateViews(remote, pool) {
|
|
4092
|
-
return {
|
|
4093
|
-
pool,
|
|
4094
|
-
view: committedView(pool),
|
|
4095
|
-
wants: consumedMembers(pool).get(remote) ?? [],
|
|
4096
|
-
specifiers: consumedSpecifiers(pool).get(remote) ?? /* @__PURE__ */ new Set(),
|
|
4097
|
-
acceptance: lazy(() => acceptanceTable(pool, ports.versionCheck.isCompatible))
|
|
4098
|
-
};
|
|
4099
4521
|
}
|
|
4100
|
-
|
|
4101
|
-
|
|
4102
|
-
|
|
4103
|
-
|
|
4104
|
-
|
|
4105
|
-
|
|
4106
|
-
|
|
4107
|
-
|
|
4108
|
-
|
|
4109
|
-
if (
|
|
4110
|
-
|
|
4111
|
-
|
|
4522
|
+
return reaches;
|
|
4523
|
+
}
|
|
4524
|
+
function boundByFile(reaches, view) {
|
|
4525
|
+
const servers = /* @__PURE__ */ new Set();
|
|
4526
|
+
for (const { remote } of reaches.values()) if (view.builds.has(remote)) servers.add(remote);
|
|
4527
|
+
const firstRun = /* @__PURE__ */ new Map();
|
|
4528
|
+
for (const build of servers)
|
|
4529
|
+
for (const specifier of view.builds.get(build).tags.keys()) {
|
|
4530
|
+
const served = view.runsOffMap.get(build)?.get(specifier) ?? view.global.get(specifier);
|
|
4531
|
+
if (served === void 0) continue;
|
|
4532
|
+
const reached = reaches.get(specifier) ?? firstRun.get(specifier);
|
|
4533
|
+
if (reached === void 0) {
|
|
4534
|
+
firstRun.set(specifier, served);
|
|
4535
|
+
if (view.builds.has(served.remote)) servers.add(served.remote);
|
|
4536
|
+
} else if (reached.remote !== served.remote || reached.file !== served.file)
|
|
4537
|
+
return { owner: build, specifier, tag: served.tag };
|
|
4112
4538
|
}
|
|
4113
|
-
|
|
4114
|
-
|
|
4115
|
-
|
|
4116
|
-
|
|
4117
|
-
|
|
4118
|
-
|
|
4119
|
-
const
|
|
4120
|
-
|
|
4121
|
-
|
|
4122
|
-
|
|
4123
|
-
|
|
4124
|
-
|
|
4125
|
-
let allScoped = true;
|
|
4126
|
-
for (const member of pool) {
|
|
4127
|
-
for (const version of member.external.versions) {
|
|
4128
|
-
const meta = version.remotes.find((r) => r.name === build);
|
|
4129
|
-
if (!meta) continue;
|
|
4130
|
-
if (meta.servedBy !== void 0) return false;
|
|
4131
|
-
ships2++;
|
|
4132
|
-
if (version.action !== "scope") allScoped = false;
|
|
4133
|
-
if (basis.get(member.name) !== build) winsAll = false;
|
|
4134
|
-
}
|
|
4539
|
+
return void 0;
|
|
4540
|
+
}
|
|
4541
|
+
function mapFiles(copy, view, policy, action) {
|
|
4542
|
+
const covered = new Set(action.covered);
|
|
4543
|
+
const override = { ...action.override };
|
|
4544
|
+
for (const specifier in copy.entries) {
|
|
4545
|
+
const source = view.global.get(specifier);
|
|
4546
|
+
if (!source) continue;
|
|
4547
|
+
if (policy.shareScope !== GLOBAL_SCOPE) {
|
|
4548
|
+
const scopeUrl = policy.scopeUrlOf(source.remote);
|
|
4549
|
+
if (!scopeUrl) return { unmapped: source.remote };
|
|
4550
|
+
override[specifier] = join(scopeUrl, source.file);
|
|
4135
4551
|
}
|
|
4136
|
-
|
|
4137
|
-
}
|
|
4138
|
-
function explainDynamic(remote, { view, wants, specifiers, acceptance }) {
|
|
4139
|
-
return explainSelfServe(remote, wants, specifiers, {
|
|
4140
|
-
builds: view.builds,
|
|
4141
|
-
acceptance: acceptance()
|
|
4142
|
-
});
|
|
4552
|
+
covered.add(specifier);
|
|
4143
4553
|
}
|
|
4144
|
-
|
|
4145
|
-
|
|
4146
|
-
|
|
4147
|
-
|
|
4148
|
-
|
|
4149
|
-
|
|
4150
|
-
|
|
4151
|
-
);
|
|
4152
|
-
|
|
4554
|
+
return { covered, override };
|
|
4555
|
+
}
|
|
4556
|
+
function scan(copies, view, accepts) {
|
|
4557
|
+
let rejected;
|
|
4558
|
+
let missing;
|
|
4559
|
+
for (const copy of copies)
|
|
4560
|
+
for (const s of copy.specifiers) {
|
|
4561
|
+
const global = view.global.get(s);
|
|
4562
|
+
if (global === void 0) missing ??= s;
|
|
4563
|
+
else if (!accepts(global.tag, copy.tag, copy.requiredVersion))
|
|
4564
|
+
rejected ??= { specifier: s, tag: global.tag };
|
|
4153
4565
|
}
|
|
4154
|
-
|
|
4155
|
-
|
|
4156
|
-
|
|
4157
|
-
|
|
4158
|
-
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
const
|
|
4162
|
-
if (
|
|
4163
|
-
const override = {};
|
|
4164
|
-
for (const specifier of specifiers) {
|
|
4165
|
-
if (view.global.get(specifier)?.remote === anchor) continue;
|
|
4166
|
-
const file = files.get(specifier);
|
|
4167
|
-
if (file) override[specifier] = join(scopeUrl, file);
|
|
4168
|
-
}
|
|
4169
|
-
action.covered = specifiers;
|
|
4170
|
-
if (Object.keys(override).length > 0) action.override = override;
|
|
4171
|
-
if (basis.get(member.name) !== anchor) served.push(member.name);
|
|
4566
|
+
return { rejected, missing };
|
|
4567
|
+
}
|
|
4568
|
+
function offMapBinding(copies, view, globalTags) {
|
|
4569
|
+
const own = new SpecifierTags(copies.flatMap((c) => c.specifiers.map((s) => [s, c.tag])));
|
|
4570
|
+
const servers = /* @__PURE__ */ new Set();
|
|
4571
|
+
for (const copy of copies)
|
|
4572
|
+
for (const specifier of copy.specifiers) {
|
|
4573
|
+
const build = view.global.get(specifier)?.remote;
|
|
4574
|
+
if (build !== void 0) servers.add(build);
|
|
4172
4575
|
}
|
|
4173
|
-
|
|
4174
|
-
|
|
4175
|
-
|
|
4176
|
-
|
|
4177
|
-
|
|
4178
|
-
|
|
4179
|
-
versions: external.versions.map((v) => ({
|
|
4180
|
-
...v,
|
|
4181
|
-
remotes: v.remotes.map(
|
|
4182
|
-
(r) => r.name === remote ? { ...r, servedBy: verdict.servedBy } : r
|
|
4183
|
-
)
|
|
4184
|
-
}))
|
|
4185
|
-
};
|
|
4576
|
+
const firstRun = new SpecifierTags();
|
|
4577
|
+
for (const build of servers)
|
|
4578
|
+
for (const [specifier, { tag }] of view.runsOffMap.get(build) ?? []) {
|
|
4579
|
+
const reached = globalTags.pinnedTag(specifier) ?? own.pinnedTag(specifier) ?? firstRun.pinnedTag(specifier);
|
|
4580
|
+
if (reached === void 0) firstRun.set(specifier, tag);
|
|
4581
|
+
else if (reached !== tag) return { owner: build, specifier, tag };
|
|
4186
4582
|
}
|
|
4187
|
-
|
|
4188
|
-
|
|
4189
|
-
|
|
4190
|
-
|
|
4191
|
-
|
|
4192
|
-
|
|
4193
|
-
|
|
4194
|
-
|
|
4195
|
-
|
|
4196
|
-
|
|
4197
|
-
|
|
4198
|
-
|
|
4199
|
-
|
|
4200
|
-
|
|
4201
|
-
|
|
4583
|
+
return void 0;
|
|
4584
|
+
}
|
|
4585
|
+
function inRecordOrder(members, recordOrder) {
|
|
4586
|
+
const rank2 = new Map(recordOrder.map((name, i) => [name, i]));
|
|
4587
|
+
return [...members].sort((a, b) => rank2.get(a.name) - rank2.get(b.name)).map((m) => [m.name, m.external]);
|
|
4588
|
+
}
|
|
4589
|
+
|
|
4590
|
+
// src/lib/core/2.app/steps/pooling/pool-dynamic-externals.ts
|
|
4591
|
+
function createPoolDynamicExternals(config, ports) {
|
|
4592
|
+
const accepts = createAcceptsTag(
|
|
4593
|
+
(tag, range) => ports.versionCheck.isCompatible(tag, range),
|
|
4594
|
+
(a, b) => ports.versionCheck.compare(a, b)
|
|
4595
|
+
);
|
|
4596
|
+
const scopeUrlOf = (remote) => ports.remoteInfoRepo.tryGet(remote).get()?.scopeUrl;
|
|
4597
|
+
return ({ entry, actions }) => {
|
|
4598
|
+
for (const [scope, names] of poolableNames(entry, actions)) {
|
|
4599
|
+
const committed = ports.sharedExternalsRepo.getFromScope(scope);
|
|
4600
|
+
if (!scopeHasPoolState(committed)) continue;
|
|
4601
|
+
const { pools } = buildPools(committed);
|
|
4602
|
+
const moves = judgeScope(entry.name, names, actions, pools, Object.keys(committed), scope);
|
|
4603
|
+
writeMoves(entry.name, names, moves, committed, pools, scope);
|
|
4202
4604
|
}
|
|
4203
|
-
return {
|
|
4204
|
-
|
|
4205
|
-
|
|
4605
|
+
return Promise.resolve({ entry, actions: publicActions(actions) });
|
|
4606
|
+
};
|
|
4607
|
+
function judgeScope(remote, names, actions, pools, recordOrder, scope) {
|
|
4608
|
+
const policy = { shareScope: scope, scopeUrlOf };
|
|
4609
|
+
const moves = /* @__PURE__ */ new Map();
|
|
4610
|
+
const selfServe = (members, cause) => {
|
|
4611
|
+
for (const { name } of members) {
|
|
4612
|
+
actions[name].action = "scope";
|
|
4613
|
+
delete actions[name].override;
|
|
4614
|
+
moves.set(name, { cause });
|
|
4615
|
+
}
|
|
4206
4616
|
};
|
|
4617
|
+
for (const pool of pools.values()) {
|
|
4618
|
+
const mine = pool.filter((member) => names.has(member.name));
|
|
4619
|
+
if (mine.length === 0) continue;
|
|
4620
|
+
const shipped = copiesByRemote(pool);
|
|
4621
|
+
const view = committedView(pool, shipped, recordOrder, {
|
|
4622
|
+
shareScope: scope,
|
|
4623
|
+
without: remote
|
|
4624
|
+
});
|
|
4625
|
+
const own = shipped.get(remote) ?? [];
|
|
4626
|
+
const verdict = judgeRemote(own, view, accepts);
|
|
4627
|
+
if (verdict !== "global") {
|
|
4628
|
+
config.log.warn(8, `[${scope}] ${selfServeWarning(remote, verdict, specifierCount(own))}`);
|
|
4629
|
+
selfServe(mine, verdict.cause);
|
|
4630
|
+
continue;
|
|
4631
|
+
}
|
|
4632
|
+
const cover = coverFromMap(remote, mine, view, actions, policy);
|
|
4633
|
+
if (!("covers" in cover)) {
|
|
4634
|
+
if ("unmapped" in cover) warnUnmapped(scope, remote, cover.unmapped);
|
|
4635
|
+
else
|
|
4636
|
+
config.log.warn(
|
|
4637
|
+
8,
|
|
4638
|
+
`[${scope}] ${selfServeWarning(remote, coverMiss(cover), specifierCount(own))}`
|
|
4639
|
+
);
|
|
4640
|
+
selfServe(mine, "uncovered");
|
|
4641
|
+
continue;
|
|
4642
|
+
}
|
|
4643
|
+
for (const { name, toSkip, override } of cover.covers) {
|
|
4644
|
+
const action = actions[name];
|
|
4645
|
+
if (toSkip) {
|
|
4646
|
+
action.action = "skip";
|
|
4647
|
+
moves.set(name, { fromMap: true });
|
|
4648
|
+
}
|
|
4649
|
+
if (Object.keys(override).length > 0) action.override = override;
|
|
4650
|
+
}
|
|
4651
|
+
}
|
|
4652
|
+
return moves;
|
|
4653
|
+
}
|
|
4654
|
+
function writeMoves(remote, names, moves, committed, pools, scope) {
|
|
4655
|
+
const written = {};
|
|
4656
|
+
for (const [name, move] of moves)
|
|
4657
|
+
written[name] = movedRecord(committed[name], remote, move, ports.versionCheck.compare);
|
|
4658
|
+
for (const [name, record] of renamedRecords({ ...committed, ...written }, pools))
|
|
4659
|
+
written[name] = record;
|
|
4660
|
+
if (!config.strict.strictExternalCompatibility)
|
|
4661
|
+
for (const { name } of [...pools.values()].flat().filter((m) => names.has(m.name)))
|
|
4662
|
+
written[name] = { ...written[name] ?? committed[name], dirty: true };
|
|
4663
|
+
for (const [name, record] of Object.entries(written))
|
|
4664
|
+
ports.sharedExternalsRepo.addOrUpdate(name, record, scope);
|
|
4665
|
+
}
|
|
4666
|
+
function warnUnmapped(scope, remote, build) {
|
|
4667
|
+
config.log.warn(
|
|
4668
|
+
8,
|
|
4669
|
+
`[${scope}][${remote}] '${build}' is not in the cache, so its files cannot be mapped.`
|
|
4670
|
+
);
|
|
4207
4671
|
}
|
|
4208
4672
|
}
|
|
4673
|
+
function publicActions(actions) {
|
|
4674
|
+
for (const action of Object.values(actions)) delete action.covered;
|
|
4675
|
+
return actions;
|
|
4676
|
+
}
|
|
4677
|
+
function poolableNames(entry, actions) {
|
|
4678
|
+
const declared = /* @__PURE__ */ new Map();
|
|
4679
|
+
for (const external of entry.shared ?? []) {
|
|
4680
|
+
const name = external.packageName;
|
|
4681
|
+
if (!external.singleton || !actions[name]) continue;
|
|
4682
|
+
if (external.shareScope === STRICT_SCOPE) continue;
|
|
4683
|
+
const scope = external.shareScope ?? GLOBAL_SCOPE;
|
|
4684
|
+
let names = declared.get(scope);
|
|
4685
|
+
if (!names) declared.set(scope, names = /* @__PURE__ */ new Set());
|
|
4686
|
+
names.add(name);
|
|
4687
|
+
}
|
|
4688
|
+
return declared;
|
|
4689
|
+
}
|
|
4690
|
+
function coverMiss(cover) {
|
|
4691
|
+
if ("gap" in cover) return { cause: "uncovered", specifier: cover.gap };
|
|
4692
|
+
return { cause: "uncovered", ...cover.bound };
|
|
4693
|
+
}
|
|
4694
|
+
function selfServeWarning(remote, miss, imports) {
|
|
4695
|
+
const where = `All ${imports} of its entrypoints are scoped for it.`;
|
|
4696
|
+
if (miss.cause === "incompatible")
|
|
4697
|
+
return `'${remote}' is islanded: its range rejects '${miss.specifier}@${miss.tag}' of the committed map. ${where}`;
|
|
4698
|
+
if ("owner" in miss)
|
|
4699
|
+
return `'${remote}' serves its own family: committed build '${miss.owner}', whose files it would reach, resolves '${miss.specifier}' to another file than it would. ${where}`;
|
|
4700
|
+
if ("unshipped" in miss)
|
|
4701
|
+
return `'${remote}' serves its own family: no committed build shipped the map's combination for it. ${where}`;
|
|
4702
|
+
return `'${remote}' serves its own family: no committed build offers every entrypoint it imports at a version it accepts (gap '${miss.specifier}'). ${where}`;
|
|
4703
|
+
}
|
|
4209
4704
|
|
|
4210
4705
|
// src/lib/core/2.app/steps/convert-to-import-map.ts
|
|
4211
4706
|
function createConvertToImportMap(config, ports) {
|
|
@@ -4242,27 +4737,14 @@ function createConvertToImportMap(config, ports) {
|
|
|
4242
4737
|
return;
|
|
4243
4738
|
}
|
|
4244
4739
|
if (actions[external.packageName].action === "skip") {
|
|
4245
|
-
const { override
|
|
4740
|
+
const { override } = actions[external.packageName];
|
|
4246
4741
|
if (override) {
|
|
4247
4742
|
Object.entries(override).forEach(([packageName, url]) => {
|
|
4248
4743
|
addToScopes(remoteEntryScope, packageName, url, importMap);
|
|
4249
4744
|
});
|
|
4250
|
-
serveUncovered(
|
|
4251
|
-
remoteEntry,
|
|
4252
|
-
external,
|
|
4253
|
-
covered ?? Object.keys(override),
|
|
4254
|
-
remoteEntryScope,
|
|
4255
|
-
importMap,
|
|
4256
|
-
sameVersion
|
|
4257
|
-
);
|
|
4258
|
-
return;
|
|
4259
|
-
}
|
|
4260
|
-
if (!external.shareScope) {
|
|
4261
|
-
if (covered) {
|
|
4262
|
-
serveUncovered(remoteEntry, external, covered, remoteEntryScope, importMap, sameVersion);
|
|
4263
|
-
}
|
|
4264
4745
|
return;
|
|
4265
4746
|
}
|
|
4747
|
+
if (!external.shareScope) return;
|
|
4266
4748
|
log.error(
|
|
4267
4749
|
9,
|
|
4268
4750
|
`[${remoteEntry.name}][${external.packageName}] shareScope skip has no override.`
|
|
@@ -4327,27 +4809,6 @@ function createConvertToImportMap(config, ports) {
|
|
|
4327
4809
|
});
|
|
4328
4810
|
return importMap;
|
|
4329
4811
|
}
|
|
4330
|
-
function serveUncovered(remoteEntry, external, covered, remoteEntryScope, importMap, sameVersion = false) {
|
|
4331
|
-
const provided = new Set(covered);
|
|
4332
|
-
Object.entries(external.entries).forEach(([packageName, fileName]) => {
|
|
4333
|
-
if (provided.has(packageName)) return;
|
|
4334
|
-
if (!sameVersion && (config.strict.strictEntryPointCoverage || config.profile.scopeUncoveredEntrypoints)) {
|
|
4335
|
-
warnUncoveredEntrypoint(remoteEntry.name, external.packageName, packageName);
|
|
4336
|
-
return;
|
|
4337
|
-
}
|
|
4338
|
-
const url = join(remoteEntryScope, fileName);
|
|
4339
|
-
addToScopes(remoteEntryScope, packageName, url, importMap);
|
|
4340
|
-
addIntegrity(importMap, url, remoteEntry.integrity, fileName);
|
|
4341
|
-
});
|
|
4342
|
-
}
|
|
4343
|
-
function warnUncoveredEntrypoint(remoteName, externalName, packageName) {
|
|
4344
|
-
const msg = `[${remoteName}][${externalName}] Entrypoint '${packageName}' is not covered by the override.`;
|
|
4345
|
-
if (config.strict.strictEntryPointCoverage || config.strict.strictImportMap) {
|
|
4346
|
-
log.error(9, msg);
|
|
4347
|
-
throw new NFError("Could not create ImportMap.");
|
|
4348
|
-
}
|
|
4349
|
-
log.warn(9, msg);
|
|
4350
|
-
}
|
|
4351
4812
|
function addIntegrity(importMap, url, integrityMap, file) {
|
|
4352
4813
|
const hash = integrityMap?.[file];
|
|
4353
4814
|
if (!hash) return;
|
|
@@ -4363,11 +4824,12 @@ var createInitDrivers = ({
|
|
|
4363
4824
|
}) => ({
|
|
4364
4825
|
getRemoteEntries: createGetRemoteEntries(config, adapters),
|
|
4365
4826
|
processRemoteEntries: createProcessRemoteEntries(config, adapters),
|
|
4366
|
-
markPoolsForReelection: createMarkPoolsForReelection(config, adapters),
|
|
4367
4827
|
determineSharedExternals: createDetermineSharedExternals(config, adapters),
|
|
4368
4828
|
poolSharedExternals: createPoolSharedExternals(config, adapters),
|
|
4829
|
+
decideServingBuilds: createDecideServingBuilds(config, adapters),
|
|
4369
4830
|
generateImportMap: createGenerateImportMap(config, adapters),
|
|
4370
4831
|
commitChanges: createCommitChanges(config, adapters),
|
|
4832
|
+
rollbackChanges: createRollbackChanges(config, adapters),
|
|
4371
4833
|
exposeModuleLoader: createExposeModuleLoader(config, adapters),
|
|
4372
4834
|
getRemoteEntry: createGetRemoteEntry(config, adapters),
|
|
4373
4835
|
updateCache: createUpdateCache(config, adapters),
|
|
@@ -4387,14 +4849,6 @@ var INIT_FLOW_FACTORY = ({
|
|
|
4387
4849
|
};
|
|
4388
4850
|
|
|
4389
4851
|
// src/lib/core/5.di/federation-result.factory.ts
|
|
4390
|
-
var createStateDump = (config, adapters) => (msg) => config.log.debug(0, msg, {
|
|
4391
|
-
remotes: { ...adapters.remoteInfoRepo.getAll() },
|
|
4392
|
-
"shared-externals": adapters.sharedExternalsRepo.getScopes({ includeGlobal: true }).reduce(
|
|
4393
|
-
(acc, scope) => ({ ...acc, [scope]: adapters.sharedExternalsRepo.getFromScope(scope) }),
|
|
4394
|
-
{}
|
|
4395
|
-
),
|
|
4396
|
-
"scoped-externals": adapters.scopedExternalsRepo.getAll()
|
|
4397
|
-
});
|
|
4398
4852
|
var createFederationResult = ({
|
|
4399
4853
|
config,
|
|
4400
4854
|
adapters,
|
|
@@ -4402,11 +4856,8 @@ var createFederationResult = ({
|
|
|
4402
4856
|
initRemoteEntryFlow,
|
|
4403
4857
|
afterInitRemoteEntry
|
|
4404
4858
|
}) => {
|
|
4405
|
-
const stateDump = createStateDump(config, adapters);
|
|
4406
4859
|
const initRemoteEntry = async (remoteEntryUrl, remote) => {
|
|
4407
|
-
const remoteName = typeof remote === "string" ? remote : remote?.name;
|
|
4408
4860
|
return initRemoteEntryFlow(remoteEntryUrl, remote).catch((e) => {
|
|
4409
|
-
stateDump(`[dynamic-init][${remoteName ?? remoteEntryUrl}] STATE DUMP`);
|
|
4410
4861
|
if (config.strict.strictRemoteEntry) return Promise.reject(e);
|
|
4411
4862
|
else console.warn("Failed to initialize remote entry, continuing anyway.");
|
|
4412
4863
|
return Promise.resolve();
|