@softarc/native-federation-orchestrator 4.6.1 → 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/README.md +2 -2
- package/fesm2022/@softarc/native-federation-orchestrator.mjs +1880 -1281
- 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 +1867 -1257
- package/fesm2022/node.mjs.map +4 -4
- package/fesm2022/options.mjs +7 -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/node-loader/loader.mjs +7 -4
- package/node-loader/loader.mjs.map +2 -2
- 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 +4 -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 +2 -0
- 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/index.d.ts +2 -1
- package/types/lib/core/2.app/config/mode.contract.d.ts +8 -5
- package/types/lib/core/2.app/config/orchestrator-global.contract.d.ts +15 -0
- package/types/lib/core/2.app/config/storage.contract.d.ts +19 -2
- 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/3.adapters/storage/chunk.repository.d.ts +1 -1
- package/types/lib/core/3.adapters/storage/remote-info.repository.d.ts +1 -1
- package/types/lib/core/3.adapters/storage/scoped-externals.repository.d.ts +1 -1
- package/types/lib/core/3.adapters/storage/shared-externals.repository.d.ts +1 -1
- package/types/lib/core/4.config/orchestrator-version.d.ts +1 -0
- package/types/lib/core/4.config/storage/publish-storage-info.d.ts +9 -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 -30
- 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 -18
- package/types/lib/testing/handlers/log.handler.d.ts +0 -3
- package/types/lib/testing/pooling/no-tear.d.ts +0 -37
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
|
};
|
|
@@ -1430,6 +1441,15 @@ var createNoopSSE = () => ({
|
|
|
1430
1441
|
}
|
|
1431
1442
|
});
|
|
1432
1443
|
|
|
1444
|
+
// src/lib/core/2.app/config/storage.contract.ts
|
|
1445
|
+
var STORAGE_KEYS = {
|
|
1446
|
+
remotes: "remotes",
|
|
1447
|
+
sharedExternals: "shared-externals",
|
|
1448
|
+
scopedExternals: "scoped-externals",
|
|
1449
|
+
sharedChunks: "shared-chunks",
|
|
1450
|
+
cacheState: "cache-state"
|
|
1451
|
+
};
|
|
1452
|
+
|
|
1433
1453
|
// src/lib/utils/path.ts
|
|
1434
1454
|
function join(pathA, pathB) {
|
|
1435
1455
|
pathA = pathA.endsWith("/") ? pathA.slice(0, -1) : pathA;
|
|
@@ -1487,9 +1507,9 @@ var Optional = class _Optional {
|
|
|
1487
1507
|
|
|
1488
1508
|
// src/lib/core/3.adapters/storage/remote-info.repository.ts
|
|
1489
1509
|
var createRemoteInfoRepository = (config) => {
|
|
1490
|
-
const STORAGE = config.storage(
|
|
1510
|
+
const STORAGE = config.storage(STORAGE_KEYS.remotes, {});
|
|
1491
1511
|
if (config.clearStorage) STORAGE.clear();
|
|
1492
|
-
|
|
1512
|
+
let _cache = STORAGE.get() ?? {};
|
|
1493
1513
|
let _dirty = false;
|
|
1494
1514
|
return {
|
|
1495
1515
|
contains: function(remoteName) {
|
|
@@ -1523,15 +1543,20 @@ var createRemoteInfoRepository = (config) => {
|
|
|
1523
1543
|
STORAGE.set(_cache);
|
|
1524
1544
|
_dirty = false;
|
|
1525
1545
|
return this;
|
|
1546
|
+
},
|
|
1547
|
+
rollback: function() {
|
|
1548
|
+
_cache = STORAGE.get() ?? {};
|
|
1549
|
+
_dirty = false;
|
|
1550
|
+
return this;
|
|
1526
1551
|
}
|
|
1527
1552
|
};
|
|
1528
1553
|
};
|
|
1529
1554
|
|
|
1530
1555
|
// src/lib/core/3.adapters/storage/scoped-externals.repository.ts
|
|
1531
1556
|
var createScopedExternalsRepository = (config) => {
|
|
1532
|
-
const STORAGE = config.storage(
|
|
1557
|
+
const STORAGE = config.storage(STORAGE_KEYS.scopedExternals, {});
|
|
1533
1558
|
if (config.clearStorage) STORAGE.clear();
|
|
1534
|
-
|
|
1559
|
+
let _cache = STORAGE.get() ?? {};
|
|
1535
1560
|
let _dirty = false;
|
|
1536
1561
|
return {
|
|
1537
1562
|
addExternal: function(remoteName, external, version) {
|
|
@@ -1558,6 +1583,11 @@ var createScopedExternalsRepository = (config) => {
|
|
|
1558
1583
|
STORAGE.set(_cache);
|
|
1559
1584
|
_dirty = false;
|
|
1560
1585
|
return this;
|
|
1586
|
+
},
|
|
1587
|
+
rollback: function() {
|
|
1588
|
+
_cache = STORAGE.get() ?? {};
|
|
1589
|
+
_dirty = false;
|
|
1590
|
+
return this;
|
|
1561
1591
|
}
|
|
1562
1592
|
};
|
|
1563
1593
|
};
|
|
@@ -1566,34 +1596,48 @@ var createScopedExternalsRepository = (config) => {
|
|
|
1566
1596
|
var GLOBAL_SCOPE = "__GLOBAL__";
|
|
1567
1597
|
var STRICT_SCOPE = "strict";
|
|
1568
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
|
+
|
|
1569
1617
|
// src/lib/core/3.adapters/storage/shared-externals.repository.ts
|
|
1570
1618
|
var createSharedExternalsRepository = (config) => {
|
|
1571
1619
|
const STORAGE = config.storage(
|
|
1572
|
-
|
|
1620
|
+
STORAGE_KEYS.sharedExternals,
|
|
1573
1621
|
{ [GLOBAL_SCOPE]: {} }
|
|
1574
1622
|
);
|
|
1575
|
-
|
|
1576
|
-
const
|
|
1577
|
-
|
|
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;
|
|
1578
1633
|
return {
|
|
1579
|
-
// Read from the cache rather than remembered from this init's entries: a warm init may not refetch
|
|
1580
|
-
// the tagged remote at all, and pooling has to coordinate its pool anyway. Exits on the first hit.
|
|
1581
|
-
// Per share scope, because a pool never spans one: a tag in another scope is no reason to pool here.
|
|
1582
|
-
hasPoolTag: function(shareScope) {
|
|
1583
|
-
const scope = _cache[shareScope ?? GLOBAL_SCOPE];
|
|
1584
|
-
if (!scope) return false;
|
|
1585
|
-
for (const external of Object.values(scope))
|
|
1586
|
-
for (const version of external.versions)
|
|
1587
|
-
for (const remote of version.remotes) if (remote.pool?.trim()) return true;
|
|
1588
|
-
return false;
|
|
1589
|
-
},
|
|
1590
1634
|
getFromScope: function(shareScope) {
|
|
1591
1635
|
return { ..._cache[shareScope ?? GLOBAL_SCOPE] };
|
|
1592
1636
|
},
|
|
1593
1637
|
addOrUpdate: function(externalName, external, shareScope) {
|
|
1594
1638
|
if (!_cache[shareScope ?? GLOBAL_SCOPE]) _cache[shareScope ?? GLOBAL_SCOPE] = {};
|
|
1595
1639
|
_cache[shareScope ?? GLOBAL_SCOPE][externalName] = external;
|
|
1596
|
-
|
|
1640
|
+
_uncommitted.add(shareScope ?? GLOBAL_SCOPE);
|
|
1597
1641
|
return this;
|
|
1598
1642
|
},
|
|
1599
1643
|
getScopes: function(o = { includeGlobal: true }) {
|
|
@@ -1603,7 +1647,7 @@ var createSharedExternalsRepository = (config) => {
|
|
|
1603
1647
|
// Batched: one traversal of the graph per init instead of one per overridden remote.
|
|
1604
1648
|
removeFromAllScopes: function(remoteNames) {
|
|
1605
1649
|
if (remoteNames.size === 0) return;
|
|
1606
|
-
Object.
|
|
1650
|
+
Object.entries(_cache).forEach(([scopeName, scope]) => {
|
|
1607
1651
|
const removeExternals = [];
|
|
1608
1652
|
Object.entries(scope).forEach(([name, external]) => {
|
|
1609
1653
|
let removedCopy = false;
|
|
@@ -1619,12 +1663,12 @@ var createSharedExternalsRepository = (config) => {
|
|
|
1619
1663
|
if (keep !== remotes.length) {
|
|
1620
1664
|
remotes.length = keep;
|
|
1621
1665
|
removedCopy = true;
|
|
1622
|
-
|
|
1666
|
+
_uncommitted.add(scopeName);
|
|
1623
1667
|
if (host !== void 0 && remoteNames.has(host)) version.host = false;
|
|
1624
1668
|
}
|
|
1625
1669
|
if (remotes.length === 0) {
|
|
1626
1670
|
external.versions.splice(i, 1);
|
|
1627
|
-
|
|
1671
|
+
_uncommitted.add(scopeName);
|
|
1628
1672
|
}
|
|
1629
1673
|
}
|
|
1630
1674
|
if (removedCopy) {
|
|
@@ -1632,7 +1676,17 @@ var createSharedExternalsRepository = (config) => {
|
|
|
1632
1676
|
if (external.versions.length === 0) removeExternals.push(name);
|
|
1633
1677
|
}
|
|
1634
1678
|
});
|
|
1635
|
-
|
|
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
|
+
}
|
|
1636
1690
|
});
|
|
1637
1691
|
},
|
|
1638
1692
|
scopeType: function(shareScope) {
|
|
@@ -1650,10 +1704,36 @@ var createSharedExternalsRepository = (config) => {
|
|
|
1650
1704
|
tryGet: function(external, shareScope) {
|
|
1651
1705
|
return Optional.of(_cache[shareScope ?? GLOBAL_SCOPE]?.[external]);
|
|
1652
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.
|
|
1653
1723
|
commit: function() {
|
|
1654
|
-
if (
|
|
1655
|
-
|
|
1656
|
-
|
|
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;
|
|
1657
1737
|
return this;
|
|
1658
1738
|
}
|
|
1659
1739
|
};
|
|
@@ -1661,9 +1741,9 @@ var createSharedExternalsRepository = (config) => {
|
|
|
1661
1741
|
|
|
1662
1742
|
// src/lib/core/3.adapters/storage/chunk.repository.ts
|
|
1663
1743
|
var createChunkRepository = (config) => {
|
|
1664
|
-
const STORAGE = config.storage(
|
|
1744
|
+
const STORAGE = config.storage(STORAGE_KEYS.sharedChunks, {});
|
|
1665
1745
|
if (config.clearStorage) STORAGE.clear();
|
|
1666
|
-
|
|
1746
|
+
let _cache = STORAGE.get() ?? {};
|
|
1667
1747
|
let _dirty = false;
|
|
1668
1748
|
return {
|
|
1669
1749
|
addOrReplace: function(remoteName, bundleName, chunks) {
|
|
@@ -1688,6 +1768,11 @@ var createChunkRepository = (config) => {
|
|
|
1688
1768
|
STORAGE.set(_cache);
|
|
1689
1769
|
_dirty = false;
|
|
1690
1770
|
return this;
|
|
1771
|
+
},
|
|
1772
|
+
rollback: function() {
|
|
1773
|
+
_cache = STORAGE.get() ?? {};
|
|
1774
|
+
_dirty = false;
|
|
1775
|
+
return this;
|
|
1691
1776
|
}
|
|
1692
1777
|
};
|
|
1693
1778
|
};
|
|
@@ -1891,18 +1976,74 @@ var globalThisStorageEntry = (namespace) => (key, initialValue) => {
|
|
|
1891
1976
|
};
|
|
1892
1977
|
return entry;
|
|
1893
1978
|
};
|
|
1979
|
+
globalThisStorageEntry.type = "globalThis";
|
|
1980
|
+
Object.freeze(globalThisStorageEntry);
|
|
1981
|
+
|
|
1982
|
+
// src/lib/core/4.config/orchestrator-version.ts
|
|
1983
|
+
var ORCHESTRATOR_VERSION = true ? "4.8.0" : "dev";
|
|
1984
|
+
|
|
1985
|
+
// src/lib/core/4.config/storage/publish-storage-info.ts
|
|
1986
|
+
var KEYS = Object.freeze(Object.values(STORAGE_KEYS));
|
|
1987
|
+
var isStorageKey = (key) => KEYS.includes(key);
|
|
1988
|
+
var createStorageInfo = ({
|
|
1989
|
+
type,
|
|
1990
|
+
namespace,
|
|
1991
|
+
storage,
|
|
1992
|
+
exposeGetter
|
|
1993
|
+
}) => {
|
|
1994
|
+
const info = {
|
|
1995
|
+
version: ORCHESTRATOR_VERSION,
|
|
1996
|
+
type,
|
|
1997
|
+
namespace,
|
|
1998
|
+
keys: KEYS
|
|
1999
|
+
};
|
|
2000
|
+
if (!exposeGetter) return Object.freeze(info);
|
|
2001
|
+
return Object.freeze({
|
|
2002
|
+
...info,
|
|
2003
|
+
// Cloned again because custom storages may hand out live references.
|
|
2004
|
+
get: (key) => isStorageKey(key) ? cloneEntry(key, storage(key, void 0).get()) : void 0
|
|
2005
|
+
});
|
|
2006
|
+
};
|
|
2007
|
+
var publishStorageInfo = (options) => {
|
|
2008
|
+
try {
|
|
2009
|
+
globalThis.__NF_ORCHESTRATOR__ = Object.freeze({
|
|
2010
|
+
storage: Object.freeze({
|
|
2011
|
+
...globalThis.__NF_ORCHESTRATOR__?.storage,
|
|
2012
|
+
[options.namespace]: createStorageInfo(options)
|
|
2013
|
+
})
|
|
2014
|
+
});
|
|
2015
|
+
} catch {
|
|
2016
|
+
}
|
|
2017
|
+
};
|
|
1894
2018
|
|
|
1895
2019
|
// src/lib/core/4.config/storage/storage.config.ts
|
|
1896
|
-
var
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
});
|
|
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
|
+
};
|
|
2026
|
+
var createStorageConfig = (override) => {
|
|
2027
|
+
const creator = override.storage ?? globalThisStorageEntry;
|
|
2028
|
+
const namespace = override.storageNamespace ?? "__NATIVE_FEDERATION__";
|
|
2029
|
+
const storage = creator(namespace);
|
|
2030
|
+
publishStorageInfo({
|
|
2031
|
+
type: creator.type ?? "custom",
|
|
2032
|
+
namespace,
|
|
2033
|
+
storage,
|
|
2034
|
+
exposeGetter: override.exposeStorageGetter ?? true
|
|
2035
|
+
});
|
|
2036
|
+
const outdated = isOutdatedCache(storage);
|
|
2037
|
+
return {
|
|
2038
|
+
storage,
|
|
2039
|
+
clearStorage: (override.clearStorage ?? false) || outdated
|
|
2040
|
+
};
|
|
2041
|
+
};
|
|
1900
2042
|
|
|
1901
2043
|
// src/lib/core/4.config/mode/default.profile.ts
|
|
1902
2044
|
var defaultProfile = {
|
|
1903
2045
|
latestSharedExternal: false,
|
|
1904
2046
|
skipInvalidExternalVersions: false,
|
|
1905
|
-
scopeUncoveredEntrypoints: false,
|
|
1906
2047
|
overrideCachedRemotes: "init-only",
|
|
1907
2048
|
overrideCachedRemotesIfURLMatches: false
|
|
1908
2049
|
};
|
|
@@ -1914,22 +2055,19 @@ var createModeConfig = (override) => {
|
|
|
1914
2055
|
strictExternalCompatibility: override.strict,
|
|
1915
2056
|
strictExternalSameVersionCompatibility: override.strict,
|
|
1916
2057
|
strictExternalVersion: override.strict,
|
|
1917
|
-
strictImportMap: override.strict
|
|
1918
|
-
strictEntryPointCoverage: override.strict
|
|
2058
|
+
strictImportMap: override.strict
|
|
1919
2059
|
} : {
|
|
1920
2060
|
strictRemoteEntry: override.strict?.strictRemoteEntry ?? false,
|
|
1921
2061
|
strictExternalCompatibility: override.strict?.strictExternalCompatibility ?? false,
|
|
1922
2062
|
strictExternalSameVersionCompatibility: override.strict?.strictExternalSameVersionCompatibility ?? false,
|
|
1923
2063
|
strictExternalVersion: override.strict?.strictExternalVersion ?? false,
|
|
1924
|
-
strictImportMap: override.strict?.strictImportMap ?? false
|
|
1925
|
-
strictEntryPointCoverage: override.strict?.strictEntryPointCoverage ?? false
|
|
2064
|
+
strictImportMap: override.strict?.strictImportMap ?? false
|
|
1926
2065
|
};
|
|
1927
2066
|
return {
|
|
1928
2067
|
strict: strictnessConfig,
|
|
1929
2068
|
profile: { ...defaultProfile, ...override.profile ?? {} },
|
|
1930
2069
|
feature: {
|
|
1931
|
-
convertFlatSharedInfo: override.feature?.convertFlatSharedInfo ?? false
|
|
1932
|
-
useAutoExternalPooling: override.feature?.useAutoExternalPooling ?? false
|
|
2070
|
+
convertFlatSharedInfo: override.feature?.convertFlatSharedInfo ?? false
|
|
1933
2071
|
}
|
|
1934
2072
|
};
|
|
1935
2073
|
};
|
|
@@ -1949,7 +2087,7 @@ var createInitFlow = ({
|
|
|
1949
2087
|
adapters,
|
|
1950
2088
|
config
|
|
1951
2089
|
}) => {
|
|
1952
|
-
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) => ({
|
|
1953
2091
|
config,
|
|
1954
2092
|
adapters,
|
|
1955
2093
|
loadRemoteModule
|
|
@@ -1960,8 +2098,11 @@ var createInitFlow = ({
|
|
|
1960
2098
|
var createInitRemoteEntryFlow = ({
|
|
1961
2099
|
flow
|
|
1962
2100
|
}) => {
|
|
1963
|
-
const processDynamicRemoteEntry = (remoteEntry) => flow.updateCache(remoteEntry).then(flow.poolDynamicExternals).then(flow.convertToImportMap).then(flow.commitChanges);
|
|
1964
|
-
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
|
+
});
|
|
1965
2106
|
};
|
|
1966
2107
|
|
|
1967
2108
|
// src/lib/core/2.app/steps/get-remote-entries.ts
|
|
@@ -2068,11 +2209,17 @@ function createGetRemoteEntries(config, ports) {
|
|
|
2068
2209
|
}
|
|
2069
2210
|
}
|
|
2070
2211
|
|
|
2212
|
+
// src/lib/utils/compare-strings.ts
|
|
2213
|
+
function compareStrings(a, b) {
|
|
2214
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
2215
|
+
}
|
|
2216
|
+
|
|
2071
2217
|
// src/lib/core/1.domain/externals/basis.ts
|
|
2072
2218
|
var coverage = (remote) => Object.keys(remote.entries).length;
|
|
2073
2219
|
function isBetterBasis(candidate, basis) {
|
|
2074
2220
|
if (basis.cached !== candidate.cached) return candidate.cached;
|
|
2075
|
-
|
|
2221
|
+
const byCoverage = coverage(candidate) - coverage(basis);
|
|
2222
|
+
return byCoverage === 0 ? compareStrings(candidate.name, basis.name) < 0 : byCoverage > 0;
|
|
2076
2223
|
}
|
|
2077
2224
|
function addRemoteToVersion(version, remote, isHost = false) {
|
|
2078
2225
|
if (isHost) {
|
|
@@ -2096,21 +2243,6 @@ function versionDemands(version) {
|
|
|
2096
2243
|
}
|
|
2097
2244
|
return Array.from(distinct.values());
|
|
2098
2245
|
}
|
|
2099
|
-
function findVersionForTag(versions, tag) {
|
|
2100
|
-
let scoped;
|
|
2101
|
-
for (const version of versions) {
|
|
2102
|
-
if (version.tag !== tag) continue;
|
|
2103
|
-
if (version.action !== "scope") return version;
|
|
2104
|
-
scoped ??= version;
|
|
2105
|
-
}
|
|
2106
|
-
return scoped;
|
|
2107
|
-
}
|
|
2108
|
-
function versionEntries(version) {
|
|
2109
|
-
return collectEntries(version, void 0);
|
|
2110
|
-
}
|
|
2111
|
-
function committedEntries(version) {
|
|
2112
|
-
return collectEntries(version, (remote) => remote.cached);
|
|
2113
|
-
}
|
|
2114
2246
|
function forEachVersionEntry(version, accepts, visit) {
|
|
2115
2247
|
for (const remote of version.remotes) {
|
|
2116
2248
|
if (remote.servedBy) continue;
|
|
@@ -2118,16 +2250,6 @@ function forEachVersionEntry(version, accepts, visit) {
|
|
|
2118
2250
|
for (const entrypoint in remote.entries) visit(entrypoint, remote);
|
|
2119
2251
|
}
|
|
2120
2252
|
}
|
|
2121
|
-
function collectEntries(version, accepts) {
|
|
2122
|
-
const entries = /* @__PURE__ */ new Map();
|
|
2123
|
-
forEachVersionEntry(version, accepts, (entrypoint, remote) => {
|
|
2124
|
-
if (!entries.has(entrypoint)) entries.set(entrypoint, remote);
|
|
2125
|
-
});
|
|
2126
|
-
return entries;
|
|
2127
|
-
}
|
|
2128
|
-
function uncoveredEntrypoints(remote, covered) {
|
|
2129
|
-
return Object.keys(remote.entries).filter((entrypoint) => !covered.has(entrypoint));
|
|
2130
|
-
}
|
|
2131
2253
|
function countUncoveredEntrypoints(remote, covered) {
|
|
2132
2254
|
let uncovered = 0;
|
|
2133
2255
|
for (const entrypoint in remote.entries) {
|
|
@@ -2136,6 +2258,89 @@ function countUncoveredEntrypoints(remote, covered) {
|
|
|
2136
2258
|
return uncovered;
|
|
2137
2259
|
}
|
|
2138
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
|
+
|
|
2139
2344
|
// src/lib/core/2.app/steps/store-remote-entry.ts
|
|
2140
2345
|
function createRemoveCachedRemoteEntries(ports) {
|
|
2141
2346
|
return (remoteNames) => {
|
|
@@ -2169,6 +2374,11 @@ function createStoreRemoteEntry(config, ports, logStep) {
|
|
|
2169
2374
|
const tag = resolveVersion(remoteEntry, external);
|
|
2170
2375
|
if (tag === null) return;
|
|
2171
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
|
+
);
|
|
2172
2382
|
onSharedExternal(remoteEntry, external, sharedExternalContext(remoteEntry, external, tag));
|
|
2173
2383
|
} else {
|
|
2174
2384
|
addScopedExternal(remoteEntry, external, tag);
|
|
@@ -2219,11 +2429,12 @@ function createStoreRemoteEntry(config, ports, logStep) {
|
|
|
2219
2429
|
...sharedInfo.pool?.trim() ? { pool: sharedInfo.pool } : {},
|
|
2220
2430
|
entries: sharedInfo.entries
|
|
2221
2431
|
};
|
|
2222
|
-
|
|
2432
|
+
let stored;
|
|
2433
|
+
const readRecord = () => stored ??= ports.sharedExternalsRepo.tryGet(sharedInfo.packageName, sharedInfo.shareScope).get() ?? { dirty: false, versions: [] };
|
|
2223
2434
|
return {
|
|
2224
2435
|
tag,
|
|
2225
2436
|
remote,
|
|
2226
|
-
|
|
2437
|
+
readRecord,
|
|
2227
2438
|
scopeType,
|
|
2228
2439
|
assertSameVersionCompatibility(matchingVersion) {
|
|
2229
2440
|
if (!remote.strictVersion) return;
|
|
@@ -2235,12 +2446,12 @@ function createStoreRemoteEntry(config, ports, logStep) {
|
|
|
2235
2446
|
}
|
|
2236
2447
|
config.log.warn(logStep, errorMsg);
|
|
2237
2448
|
},
|
|
2238
|
-
|
|
2449
|
+
write() {
|
|
2239
2450
|
ports.sharedExternalsRepo.addOrUpdate(
|
|
2240
2451
|
sharedInfo.packageName,
|
|
2241
2452
|
{
|
|
2242
|
-
|
|
2243
|
-
versions:
|
|
2453
|
+
...readRecord(),
|
|
2454
|
+
versions: readRecord().versions.sort((a, b) => ports.versionCheck.compare(b.tag, a.tag))
|
|
2244
2455
|
},
|
|
2245
2456
|
sharedInfo.shareScope
|
|
2246
2457
|
);
|
|
@@ -2284,11 +2495,12 @@ function createProcessRemoteEntries(config, ports) {
|
|
|
2284
2495
|
function addSharedExternal(remoteEntry, _sharedInfo, {
|
|
2285
2496
|
tag,
|
|
2286
2497
|
remote,
|
|
2287
|
-
|
|
2498
|
+
readRecord,
|
|
2288
2499
|
scopeType,
|
|
2289
2500
|
assertSameVersionCompatibility,
|
|
2290
|
-
|
|
2501
|
+
write
|
|
2291
2502
|
}) {
|
|
2503
|
+
const cached2 = readRecord();
|
|
2292
2504
|
if (scopeType !== "strict") cached2.dirty = true;
|
|
2293
2505
|
const matchingVersion = findVersionForTag(cached2.versions, tag);
|
|
2294
2506
|
if (matchingVersion) {
|
|
@@ -2302,158 +2514,74 @@ function createProcessRemoteEntries(config, ports) {
|
|
|
2302
2514
|
remotes: [remote]
|
|
2303
2515
|
});
|
|
2304
2516
|
}
|
|
2305
|
-
|
|
2517
|
+
write();
|
|
2306
2518
|
}
|
|
2307
2519
|
}
|
|
2308
2520
|
|
|
2309
|
-
// src/lib/core/
|
|
2310
|
-
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) {
|
|
2311
2526
|
const demands = new Map(
|
|
2312
2527
|
external.versions.map((v) => [v, versionDemands(v)])
|
|
2313
2528
|
);
|
|
2314
2529
|
return {
|
|
2315
|
-
accepts: (version, tag) => demands.get(version).every((d) =>
|
|
2316
|
-
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))
|
|
2317
2532
|
};
|
|
2318
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
|
+
}
|
|
2319
2543
|
function createApplyWinner(config) {
|
|
2320
|
-
return function applyWinner(externalName, external, winner,
|
|
2544
|
+
return function applyWinner(externalName, external, winner, acceptsTag, acceptance) {
|
|
2321
2545
|
if (external.versions.length > 1) {
|
|
2322
|
-
const { accepts, objector } = acceptance ?? versionAcceptance(external, isCompatible);
|
|
2323
2546
|
const rebuilt = [];
|
|
2324
2547
|
for (const v of external.versions) {
|
|
2325
|
-
|
|
2326
|
-
if (
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
config.log.error(
|
|
2333
|
-
3,
|
|
2334
|
-
`[${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}`
|
|
2335
2555
|
);
|
|
2336
|
-
|
|
2337
|
-
}
|
|
2338
|
-
if (v === winner) continue;
|
|
2339
|
-
if (!strict) {
|
|
2340
|
-
v.action = "skip";
|
|
2341
|
-
continue;
|
|
2342
|
-
}
|
|
2343
|
-
const objecting = new Set(
|
|
2344
|
-
v.remotes.filter((r) => r.strictVersion && !isCompatible(winner.tag, r.requiredVersion))
|
|
2345
|
-
);
|
|
2346
|
-
if (objecting.size === v.remotes.length) {
|
|
2347
|
-
v.action = "scope";
|
|
2348
|
-
continue;
|
|
2349
|
-
}
|
|
2350
|
-
v.remotes = v.remotes.filter((r) => !objecting.has(r));
|
|
2351
|
-
v.action = "skip";
|
|
2352
|
-
rebuilt.push({ tag: v.tag, host: false, action: "scope", remotes: [...objecting] });
|
|
2556
|
+
rebuilt.push(...redirectRow(v, winner.tag, acceptsTag));
|
|
2353
2557
|
}
|
|
2354
|
-
|
|
2355
|
-
external.versions = rebuilt.filter((v) => {
|
|
2356
|
-
const first = merged.get(`${v.tag}|${v.action}`);
|
|
2357
|
-
if (!first) {
|
|
2358
|
-
merged.set(`${v.tag}|${v.action}`, v);
|
|
2359
|
-
return true;
|
|
2360
|
-
}
|
|
2361
|
-
first.remotes.push(...v.remotes);
|
|
2362
|
-
return false;
|
|
2363
|
-
});
|
|
2558
|
+
external.versions = mergeRows(rebuilt, winner);
|
|
2364
2559
|
}
|
|
2365
2560
|
winner.action = "share";
|
|
2366
|
-
applyEntrypointCoveragePolicy(externalName, external);
|
|
2367
2561
|
external.dirty = false;
|
|
2368
2562
|
return external;
|
|
2369
2563
|
};
|
|
2370
|
-
function applyEntrypointCoveragePolicy(externalName, external) {
|
|
2371
|
-
const { strictEntryPointCoverage } = config.strict;
|
|
2372
|
-
if (!strictEntryPointCoverage && !config.profile.scopeUncoveredEntrypoints) return;
|
|
2373
|
-
const tears = findTears(external);
|
|
2374
|
-
if (tears.length === 0) return;
|
|
2375
|
-
if (strictEntryPointCoverage) {
|
|
2376
|
-
const { version, remote, uncovered } = tears[0];
|
|
2377
|
-
config.log.error(
|
|
2378
|
-
3,
|
|
2379
|
-
`[${externalName}@${version.tag}][${remote.name}] Entrypoints not covered by the shared version: ${uncovered.join(", ")}.`
|
|
2380
|
-
);
|
|
2381
|
-
throw new NFError(
|
|
2382
|
-
`External ${externalName} could not be shared without tearing entrypoints.`
|
|
2383
|
-
);
|
|
2384
|
-
}
|
|
2385
|
-
scopeTornRemotes(externalName, external, tears);
|
|
2386
|
-
}
|
|
2387
|
-
function findTears(external) {
|
|
2388
|
-
const shared = external.versions.find((v) => v.action === "share");
|
|
2389
|
-
if (!shared) return [];
|
|
2390
|
-
const basis = versionEntries(shared);
|
|
2391
|
-
const tears = [];
|
|
2392
|
-
for (const version of external.versions) {
|
|
2393
|
-
if (version.action === "scope") continue;
|
|
2394
|
-
if (version === shared) continue;
|
|
2395
|
-
version.remotes.forEach((remote) => {
|
|
2396
|
-
if (remote.servedBy) return;
|
|
2397
|
-
const uncovered = uncoveredEntrypoints(remote, basis);
|
|
2398
|
-
if (uncovered.length > 0) tears.push({ version, remote, uncovered });
|
|
2399
|
-
});
|
|
2400
|
-
}
|
|
2401
|
-
return tears;
|
|
2402
|
-
}
|
|
2403
|
-
function scopeTornRemotes(externalName, external, tears) {
|
|
2404
|
-
const torn = new Set(tears.map((t) => t.remote));
|
|
2405
|
-
const demotedByTag = /* @__PURE__ */ new Map();
|
|
2406
|
-
for (const { version, remote, uncovered } of tears) {
|
|
2407
|
-
const group = demotedByTag.get(version.tag);
|
|
2408
|
-
if (group) group.push(remote);
|
|
2409
|
-
else demotedByTag.set(version.tag, [remote]);
|
|
2410
|
-
config.log.debug(
|
|
2411
|
-
3,
|
|
2412
|
-
`[${externalName}@${version.tag}][${remote.name}] Scoped: entrypoints not covered by the shared version: ${uncovered.join(", ")}.`
|
|
2413
|
-
);
|
|
2414
|
-
}
|
|
2415
|
-
for (const version of external.versions) {
|
|
2416
|
-
if (version.remotes.some((r) => torn.has(r))) {
|
|
2417
|
-
version.remotes = version.remotes.filter((r) => !torn.has(r));
|
|
2418
|
-
}
|
|
2419
|
-
}
|
|
2420
|
-
external.versions = external.versions.filter((v) => v.remotes.length > 0);
|
|
2421
|
-
for (const [tag, remotes] of demotedByTag) {
|
|
2422
|
-
const scoped = external.versions.find((v) => v.tag === tag && v.action === "scope");
|
|
2423
|
-
if (scoped) scoped.remotes.push(...remotes);
|
|
2424
|
-
else external.versions.push({ tag, host: false, action: "scope", remotes });
|
|
2425
|
-
}
|
|
2426
|
-
}
|
|
2427
2564
|
}
|
|
2428
2565
|
|
|
2429
2566
|
// src/lib/core/2.app/steps/determine-shared-externals.ts
|
|
2430
2567
|
function createDetermineSharedExternals(config, ports) {
|
|
2431
2568
|
const applyWinner = createApplyWinner(config);
|
|
2432
2569
|
return () => {
|
|
2433
|
-
const
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
if (hit === void 0) {
|
|
2438
|
-
hit = ports.versionCheck.isCompatible(tag, requiredVersion);
|
|
2439
|
-
memo.set(key, hit);
|
|
2440
|
-
}
|
|
2441
|
-
return hit;
|
|
2442
|
-
};
|
|
2443
|
-
const touched = /* @__PURE__ */ new Map();
|
|
2570
|
+
const acceptsTag = createAcceptsTag(
|
|
2571
|
+
ports.versionCheck.isCompatible,
|
|
2572
|
+
ports.versionCheck.compare
|
|
2573
|
+
);
|
|
2444
2574
|
for (const shareScope of ports.sharedExternalsRepo.getScopes()) {
|
|
2575
|
+
if (ports.sharedExternalsRepo.scopeType(shareScope) === "strict") continue;
|
|
2445
2576
|
const sharedExternals = ports.sharedExternalsRepo.getFromScope(shareScope);
|
|
2446
2577
|
try {
|
|
2447
|
-
const elected = /* @__PURE__ */ new Set();
|
|
2448
2578
|
Object.entries(sharedExternals).filter(([_, e]) => e.dirty).forEach(([name, external]) => {
|
|
2449
2579
|
ports.sharedExternalsRepo.addOrUpdate(
|
|
2450
2580
|
name,
|
|
2451
|
-
setVersionActions(name, external,
|
|
2581
|
+
setVersionActions(name, external, acceptsTag),
|
|
2452
2582
|
shareScope
|
|
2453
2583
|
);
|
|
2454
|
-
elected.add(name);
|
|
2455
2584
|
});
|
|
2456
|
-
if (elected.size > 0) touched.set(shareScope, elected);
|
|
2457
2585
|
} catch (error) {
|
|
2458
2586
|
config.log.error(
|
|
2459
2587
|
3,
|
|
@@ -2471,24 +2599,22 @@ function createDetermineSharedExternals(config, ports) {
|
|
|
2471
2599
|
);
|
|
2472
2600
|
}
|
|
2473
2601
|
}
|
|
2474
|
-
return Promise.resolve(
|
|
2602
|
+
return Promise.resolve();
|
|
2475
2603
|
};
|
|
2476
2604
|
function uncoveredTears(external, winner, accepts) {
|
|
2477
|
-
const
|
|
2605
|
+
const covered = /* @__PURE__ */ new Set();
|
|
2606
|
+
forEachVersionEntry(winner, void 0, (entrypoint) => covered.add(entrypoint));
|
|
2478
2607
|
return external.versions.reduce((sum, v) => {
|
|
2479
2608
|
if (v === winner) return sum;
|
|
2480
2609
|
if (!accepts(v, winner.tag)) return sum;
|
|
2481
|
-
return v.remotes.reduce(
|
|
2482
|
-
(n, r) => r.servedBy ? n : n + countUncoveredEntrypoints(r, basis),
|
|
2483
|
-
sum
|
|
2484
|
-
);
|
|
2610
|
+
return v.remotes.reduce((n, r) => n + countUncoveredEntrypoints(r, covered), sum);
|
|
2485
2611
|
}, 0);
|
|
2486
2612
|
}
|
|
2487
|
-
function setVersionActions(externalName, external,
|
|
2613
|
+
function setVersionActions(externalName, external, acceptsTag) {
|
|
2614
|
+
const acceptance = versionAcceptance(external, acceptsTag);
|
|
2488
2615
|
if (external.versions.length === 1) {
|
|
2489
|
-
return applyWinner(externalName, external, external.versions[0],
|
|
2616
|
+
return applyWinner(externalName, external, external.versions[0], acceptsTag, acceptance);
|
|
2490
2617
|
}
|
|
2491
|
-
const acceptance = versionAcceptance(external, isCompatible);
|
|
2492
2618
|
const { accepts } = acceptance;
|
|
2493
2619
|
let sharedVersion = external.versions.find((v) => v.host);
|
|
2494
2620
|
if (!sharedVersion && config.profile.latestSharedExternal) {
|
|
@@ -2513,14 +2639,12 @@ function createDetermineSharedExternals(config, ports) {
|
|
|
2513
2639
|
return [v, [...groups.values()]];
|
|
2514
2640
|
})
|
|
2515
2641
|
);
|
|
2516
|
-
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
|
+
);
|
|
2517
2646
|
external.versions.forEach((vA) => {
|
|
2518
|
-
const extraDownloads = external.versions.reduce(
|
|
2519
|
-
// A copy of the winner is never redirected, so it never self-serves however its own range
|
|
2520
|
-
// reads — see `applyWinner`, which does not split the winner either.
|
|
2521
|
-
(sum, vB) => vB === vA ? sum : sum + costOf(vB, vA.tag),
|
|
2522
|
-
0
|
|
2523
|
-
);
|
|
2647
|
+
const extraDownloads = external.versions.reduce((sum, vB) => sum + costOf(vB, vA.tag), 0);
|
|
2524
2648
|
if (extraDownloads < leastExtraDownloads) {
|
|
2525
2649
|
leastExtraDownloads = extraDownloads;
|
|
2526
2650
|
leastTears = uncoveredTears(external, vA, accepts);
|
|
@@ -2538,564 +2662,582 @@ function createDetermineSharedExternals(config, ports) {
|
|
|
2538
2662
|
if (!sharedVersion) {
|
|
2539
2663
|
throw new NFError(`[${externalName}] Could not determine shared version!`);
|
|
2540
2664
|
}
|
|
2541
|
-
return applyWinner(externalName, external, sharedVersion,
|
|
2665
|
+
return applyWinner(externalName, external, sharedVersion, acceptsTag, acceptance);
|
|
2542
2666
|
}
|
|
2543
2667
|
}
|
|
2544
2668
|
|
|
2545
|
-
// src/lib/core/
|
|
2546
|
-
function
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
});
|
|
2555
|
-
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);
|
|
2556
2678
|
}
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
own.coverage.set(specifier, meta.entries[specifier]);
|
|
2578
|
-
if (!own.tags.has(specifier)) own.tags.set(specifier, version.tag);
|
|
2579
|
-
}
|
|
2580
|
-
}
|
|
2581
|
-
}
|
|
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);
|
|
2582
2699
|
}
|
|
2583
|
-
|
|
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;
|
|
2584
2710
|
}
|
|
2585
|
-
|
|
2586
|
-
|
|
2711
|
+
|
|
2712
|
+
// src/lib/core/1.domain/pooling/builds.ts
|
|
2713
|
+
function copiesByRemote(members) {
|
|
2714
|
+
const shipped = /* @__PURE__ */ new Map();
|
|
2587
2715
|
for (const member of members) {
|
|
2588
|
-
|
|
2716
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2717
|
+
for (const version of member.external.versions)
|
|
2589
2718
|
for (const meta of version.remotes) {
|
|
2590
|
-
if (
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
if (!copies.
|
|
2594
|
-
|
|
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
|
+
});
|
|
2595
2731
|
}
|
|
2596
|
-
}
|
|
2597
2732
|
}
|
|
2598
|
-
return
|
|
2733
|
+
return shipped;
|
|
2599
2734
|
}
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
else if (list[list.length - 1] !== member.name) list.push(member.name);
|
|
2611
|
-
}
|
|
2612
|
-
}
|
|
2613
|
-
}
|
|
2614
|
-
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) };
|
|
2615
2745
|
}
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
for (
|
|
2621
|
-
const
|
|
2622
|
-
|
|
2623
|
-
const meta = remotes[r];
|
|
2624
|
-
let own = consumed.get(meta.name);
|
|
2625
|
-
if (!own) consumed.set(meta.name, own = /* @__PURE__ */ new Set());
|
|
2626
|
-
for (const specifier in meta.entries) own.add(specifier);
|
|
2627
|
-
}
|
|
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;
|
|
2628
2753
|
}
|
|
2629
|
-
|
|
2630
|
-
return consumed;
|
|
2754
|
+
return true;
|
|
2631
2755
|
}
|
|
2632
|
-
function
|
|
2633
|
-
const
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
for (const meta of version.remotes) {
|
|
2638
|
-
if (islanded.has(meta.name)) continue;
|
|
2639
|
-
const into = meta.name === build ? served : wanted;
|
|
2640
|
-
for (const specifier in meta.entries) into.add(specifier);
|
|
2641
|
-
}
|
|
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;
|
|
2642
2761
|
}
|
|
2643
|
-
}
|
|
2644
|
-
for (const specifier of wanted) if (!served.has(specifier)) return false;
|
|
2645
2762
|
return true;
|
|
2646
2763
|
}
|
|
2647
|
-
function
|
|
2648
|
-
const
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
const
|
|
2652
|
-
if (
|
|
2653
|
-
|
|
2654
|
-
|
|
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];
|
|
2655
2773
|
}
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
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
|
+
};
|
|
2667
2803
|
}
|
|
2668
|
-
function
|
|
2669
|
-
const
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
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;
|
|
2673
2820
|
}
|
|
2674
|
-
|
|
2675
|
-
|
|
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;
|
|
2676
2824
|
}
|
|
2677
|
-
function
|
|
2678
|
-
const
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
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 });
|
|
2684
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));
|
|
2685
2870
|
}
|
|
2686
|
-
return arrival;
|
|
2687
2871
|
}
|
|
2688
|
-
function
|
|
2689
|
-
const
|
|
2690
|
-
|
|
2691
|
-
|
|
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;
|
|
2692
2887
|
});
|
|
2693
|
-
return shared;
|
|
2694
2888
|
}
|
|
2695
|
-
function
|
|
2696
|
-
const
|
|
2697
|
-
const
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
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);
|
|
2705
2902
|
}
|
|
2706
2903
|
}
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
let byMember = table.get(meta.name);
|
|
2717
|
-
if (!byMember) table.set(meta.name, byMember = /* @__PURE__ */ new Map());
|
|
2718
|
-
if (byMember.has(member.name)) continue;
|
|
2719
|
-
const accepted = /* @__PURE__ */ new Set();
|
|
2720
|
-
for (let t = 0; t < tags.length; t++) {
|
|
2721
|
-
if (isCompatible(tags[t], meta.requiredVersion)) accepted.add(tags[t]);
|
|
2722
|
-
}
|
|
2723
|
-
byMember.set(member.name, accepted);
|
|
2724
|
-
}
|
|
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);
|
|
2725
2913
|
}
|
|
2726
|
-
|
|
2727
|
-
return table;
|
|
2914
|
+
return packages;
|
|
2728
2915
|
}
|
|
2729
|
-
function
|
|
2730
|
-
|
|
2731
|
-
|
|
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;
|
|
2732
2926
|
}
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
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;
|
|
2742
2937
|
}
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
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;
|
|
2752
2958
|
}
|
|
2753
|
-
}
|
|
2754
|
-
if (matches2) return true;
|
|
2755
2959
|
}
|
|
2756
|
-
return false;
|
|
2757
2960
|
}
|
|
2758
|
-
function
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
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 };
|
|
2770
2979
|
}
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
}
|
|
2776
|
-
|
|
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.`);
|
|
2777
2987
|
}
|
|
2778
|
-
function
|
|
2779
|
-
const
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
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;
|
|
2787
2996
|
}
|
|
2788
|
-
function
|
|
2789
|
-
const
|
|
2790
|
-
const
|
|
2791
|
-
|
|
2792
|
-
const
|
|
2793
|
-
const
|
|
2794
|
-
const
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
};
|
|
2800
|
-
const electAnchor = (anchor, served) => {
|
|
2801
|
-
pending.delete(anchor);
|
|
2802
|
-
assignment.set(anchor, void 0);
|
|
2803
|
-
for (const consumer of served) {
|
|
2804
|
-
pending.delete(consumer);
|
|
2805
|
-
anchored.add(consumer);
|
|
2806
|
-
assignment.set(consumer, anchor);
|
|
2807
|
-
}
|
|
2808
|
-
};
|
|
2809
|
-
for (const host of [...hosts].sort(byPriority)) {
|
|
2810
|
-
const served = [];
|
|
2811
|
-
if (builds.has(host)) {
|
|
2812
|
-
for (const consumer of pending) {
|
|
2813
|
-
if (consumer !== host && canServe(host, consumer)) served.push(consumer);
|
|
2814
|
-
}
|
|
2815
|
-
}
|
|
2816
|
-
electAnchor(host, served);
|
|
2817
|
-
}
|
|
2818
|
-
for (; ; ) {
|
|
2819
|
-
let best;
|
|
2820
|
-
let bestServed = [];
|
|
2821
|
-
for (const candidate of candidates) {
|
|
2822
|
-
if (anchored.has(candidate)) continue;
|
|
2823
|
-
const served = [];
|
|
2824
|
-
for (const consumer of pending) {
|
|
2825
|
-
if (consumer !== candidate && canServe(candidate, consumer)) served.push(consumer);
|
|
2826
|
-
}
|
|
2827
|
-
if (served.length > bestServed.length) {
|
|
2828
|
-
best = candidate;
|
|
2829
|
-
bestServed = served;
|
|
2830
|
-
}
|
|
2831
|
-
}
|
|
2832
|
-
if (best === void 0) break;
|
|
2833
|
-
electAnchor(best, bestServed);
|
|
2834
|
-
}
|
|
2835
|
-
for (const consumer of pending) assignment.set(consumer, void 0);
|
|
2836
|
-
return assignment;
|
|
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 };
|
|
2837
3008
|
}
|
|
2838
3009
|
|
|
2839
|
-
// src/lib/core/
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
const
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
id = parent.length;
|
|
2849
|
-
ids.set(key, id);
|
|
2850
|
-
parent.push(id);
|
|
2851
|
-
size.push(1);
|
|
2852
|
-
}
|
|
2853
|
-
return id;
|
|
2854
|
-
};
|
|
2855
|
-
const findRoot = (x) => {
|
|
2856
|
-
while (parent[x] !== x) {
|
|
2857
|
-
parent[x] = parent[parent[x]];
|
|
2858
|
-
x = parent[x];
|
|
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;
|
|
2859
3019
|
}
|
|
2860
|
-
return
|
|
3020
|
+
return node;
|
|
2861
3021
|
};
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
if (rootA === rootB) return;
|
|
2867
|
-
if (size[rootA] < size[rootB]) [rootA, rootB] = [rootB, rootA];
|
|
2868
|
-
parent[rootB] = rootA;
|
|
2869
|
-
size[rootA] = size[rootA] + size[rootB];
|
|
2870
|
-
},
|
|
2871
|
-
// Interns the key if unseen — an isolated key is its own component.
|
|
2872
|
-
component(key) {
|
|
2873
|
-
return findRoot(intern(key));
|
|
2874
|
-
}
|
|
3022
|
+
const union = (a, b) => {
|
|
3023
|
+
const rootA = find(a);
|
|
3024
|
+
const rootB = find(b);
|
|
3025
|
+
if (rootA !== rootB) parent.set(rootB, rootA);
|
|
2875
3026
|
};
|
|
3027
|
+
return { find, union };
|
|
2876
3028
|
}
|
|
2877
3029
|
var extNode = (name) => `ext\0${name}`;
|
|
2878
|
-
var
|
|
2879
|
-
var
|
|
2880
|
-
function
|
|
2881
|
-
|
|
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;
|
|
2882
3040
|
}
|
|
2883
|
-
function
|
|
2884
|
-
const
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
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));
|
|
2889
3048
|
}
|
|
2890
|
-
|
|
3049
|
+
const byComponent = /* @__PURE__ */ new Map();
|
|
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));
|
|
2891
3056
|
}
|
|
2892
|
-
function
|
|
2893
|
-
const
|
|
2894
|
-
const
|
|
2895
|
-
const
|
|
2896
|
-
const
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
tagged.add(candidate.name);
|
|
2905
|
-
joined.add(candidate.name);
|
|
2906
|
-
}
|
|
2907
|
-
if (candidate.scope === void 0) continue;
|
|
2908
|
-
for (const remote of candidate.remotes) {
|
|
2909
|
-
if (tagRules.has(`${remote}\0${candidate.scope}`)) continue;
|
|
2910
|
-
dsu.union(extNode(candidate.name), scopeNode(remote, candidate.scope));
|
|
2911
|
-
joined.add(candidate.name);
|
|
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
|
+
);
|
|
2912
3069
|
}
|
|
2913
3070
|
}
|
|
2914
|
-
|
|
2915
|
-
for (const candidate of candidates) {
|
|
2916
|
-
const owner = owningPackage(candidate.name);
|
|
2917
|
-
if (owner !== void 0 && declared.has(owner))
|
|
2918
|
-
dsu.union(extNode(candidate.name), extNode(owner));
|
|
2919
|
-
}
|
|
2920
|
-
const byComponent = /* @__PURE__ */ new Map();
|
|
2921
|
-
for (const candidate of candidates) {
|
|
2922
|
-
const root = dsu.component(extNode(candidate.name));
|
|
2923
|
-
const members = byComponent.get(root) ?? byComponent.set(root, []).get(root);
|
|
2924
|
-
members.push({ name: candidate.name, value: candidate.value });
|
|
2925
|
-
}
|
|
2926
|
-
const pools = [];
|
|
2927
|
-
for (const members of byComponent.values()) {
|
|
2928
|
-
if (!members.some((m) => joined.has(m.name))) continue;
|
|
2929
|
-
members.sort((a, b) => a.name.localeCompare(b.name));
|
|
2930
|
-
if (members.length < 2) {
|
|
2931
|
-
const only = members[0];
|
|
2932
|
-
if (tagged.has(only.name)) {
|
|
2933
|
-
log?.warn(
|
|
2934
|
-
3,
|
|
2935
|
-
`[${only.name}] declares a 'pool' tag but no other external joined its pool; likely a typo or a missing sibling.`
|
|
2936
|
-
);
|
|
2937
|
-
}
|
|
2938
|
-
continue;
|
|
2939
|
-
}
|
|
2940
|
-
pools.push(members);
|
|
2941
|
-
}
|
|
2942
|
-
pools.sort((a, b) => a[0].name.localeCompare(b[0].name));
|
|
2943
|
-
return new Map(pools.map((members) => [members[0].name, members.map((m) => m.value)]));
|
|
3071
|
+
return { pools, labelledAlone };
|
|
2944
3072
|
}
|
|
2945
|
-
function
|
|
2946
|
-
const
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
})
|
|
2955
|
-
),
|
|
2956
|
-
remotes: external.versions.flatMap((v) => v.remotes.map((r) => r.name)),
|
|
2957
|
-
value: { name, external }
|
|
2958
|
-
})
|
|
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))
|
|
2959
3082
|
);
|
|
2960
|
-
|
|
3083
|
+
}
|
|
3084
|
+
function mostDeclaredLabel(members, labelsOf) {
|
|
3085
|
+
const counts = /* @__PURE__ */ new Map();
|
|
3086
|
+
for (const member of members)
|
|
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];
|
|
3093
|
+
return best;
|
|
2961
3094
|
}
|
|
2962
3095
|
|
|
2963
|
-
// src/lib/core/
|
|
2964
|
-
function
|
|
2965
|
-
|
|
2966
|
-
return (
|
|
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)
|
|
3101
|
+
);
|
|
2967
3102
|
}
|
|
2968
|
-
function
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
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;
|
|
2972
3111
|
}
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
)
|
|
3112
|
+
|
|
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;
|
|
2980
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 };
|
|
2981
3148
|
}
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
function islandedRemotes(members) {
|
|
2985
|
-
const islanded = /* @__PURE__ */ new Map();
|
|
2986
|
-
for (const member of members)
|
|
2987
|
-
for (const version of member.external.versions)
|
|
2988
|
-
if (version.action === "scope") {
|
|
2989
|
-
for (const remote of version.remotes)
|
|
2990
|
-
if (!islanded.has(remote.name))
|
|
2991
|
-
islanded.set(remote.name, {
|
|
2992
|
-
kind: "incompatible",
|
|
2993
|
-
member: member.name,
|
|
2994
|
-
tag: version.tag
|
|
2995
|
-
});
|
|
2996
|
-
}
|
|
2997
|
-
return islanded;
|
|
3149
|
+
function movedRecord(external, remote, move, compare) {
|
|
3150
|
+
return withoutStaleServingBuild(moveCopy(external, remote, move, compare));
|
|
2998
3151
|
}
|
|
2999
|
-
|
|
3000
|
-
const
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
const byMember = /* @__PURE__ */ new Map();
|
|
3007
|
-
for (const name of consumed.get(remote) ?? []) {
|
|
3008
|
-
if (basis.get(name) !== build) byMember.set(name, build);
|
|
3009
|
-
}
|
|
3010
|
-
if (byMember.size > 0) served.set(remote, byMember);
|
|
3011
|
-
}
|
|
3012
|
-
return served;
|
|
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;
|
|
3013
3159
|
}
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
const
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
if (tag !== void 0 && !resolved.has(specifier)) resolved.set(specifier, tag);
|
|
3034
|
-
}
|
|
3035
|
-
}
|
|
3036
|
-
if (resolved.size === 0) continue;
|
|
3037
|
-
if (ships(ownTags, resolved)) continue;
|
|
3038
|
-
let witnessed = false;
|
|
3039
|
-
for (const [, build] of builds) {
|
|
3040
|
-
if (ships(build.tags, resolved)) {
|
|
3041
|
-
witnessed = true;
|
|
3042
|
-
break;
|
|
3043
|
-
}
|
|
3044
|
-
}
|
|
3045
|
-
if (!witnessed) {
|
|
3046
|
-
torn.push({
|
|
3047
|
-
remote,
|
|
3048
|
-
combination: [...resolved].map(([specifier, tag]) => `${specifier}@${tag}`).join(", ")
|
|
3049
|
-
});
|
|
3050
|
-
}
|
|
3051
|
-
}
|
|
3052
|
-
return torn;
|
|
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
|
-
const
|
|
3061
|
-
|
|
3062
|
-
const
|
|
3063
|
-
if (
|
|
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 };
|
|
3195
|
+
}
|
|
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
|
+
}))
|
|
3064
3204
|
};
|
|
3065
|
-
for (const remote of served.keys()) add(remote);
|
|
3066
|
-
for (const member of members) {
|
|
3067
|
-
const unpublished = !basis.has(member.name);
|
|
3068
|
-
for (const version of member.external.versions) {
|
|
3069
|
-
const scoped = version.action === "scope";
|
|
3070
|
-
if (!unpublished && !scoped) continue;
|
|
3071
|
-
for (const meta of version.remotes) {
|
|
3072
|
-
add(meta.name);
|
|
3073
|
-
const servesItself = scoped || unpublished && served.get(meta.name)?.get(member.name) === void 0;
|
|
3074
|
-
if (!servesItself) continue;
|
|
3075
|
-
let mine = selfServed.get(meta.name);
|
|
3076
|
-
if (!mine) selfServed.set(meta.name, mine = /* @__PURE__ */ new Set());
|
|
3077
|
-
mine.add(member.name);
|
|
3078
|
-
}
|
|
3079
|
-
}
|
|
3080
|
-
}
|
|
3081
|
-
return { suspect, selfServed };
|
|
3082
3205
|
}
|
|
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;
|
|
3212
|
+
}
|
|
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 };
|
|
3225
|
+
}
|
|
3226
|
+
|
|
3227
|
+
// src/lib/core/2.app/steps/pooling/pool-shared-externals.ts
|
|
3083
3228
|
function createPoolSharedExternals(config, ports) {
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
const sharedExternals = ports.sharedExternalsRepo.getFromScope(scope);
|
|
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);
|
|
3090
3234
|
try {
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
poolFamily(poolName, members, scope);
|
|
3098
|
-
}
|
|
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);
|
|
3099
3241
|
} catch (error) {
|
|
3100
3242
|
if (error instanceof NFError) return Promise.reject(error);
|
|
3101
3243
|
config.log.error(3, `[${scope}] failed to pool shared externals.`, {
|
|
@@ -3109,215 +3251,439 @@ function createPoolSharedExternals(config, ports) {
|
|
|
3109
3251
|
}
|
|
3110
3252
|
return Promise.resolve();
|
|
3111
3253
|
};
|
|
3112
|
-
function
|
|
3113
|
-
if (
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
const views = {
|
|
3120
|
-
consumedSpecifiers: lazy(() => consumedSpecifiers(members)),
|
|
3121
|
-
acceptance: lazy(() => acceptanceTable(members, ports.versionCheck.isCompatible)),
|
|
3122
|
-
arrival: lazy(() => arrivalOrder(members))
|
|
3123
|
-
};
|
|
3124
|
-
config.log.debug(
|
|
3125
|
-
3,
|
|
3126
|
-
`[${scope}][pool:${poolName}] ${members.length} members across ${allRemotes.length} remotes, incompatible={${[...islanded.keys()].join(", ") || "\u2205"}}
|
|
3127
|
-
` + members.map((m) => ` - ${m.name}`).join("\n")
|
|
3128
|
-
);
|
|
3129
|
-
const assign = () => {
|
|
3130
|
-
const serving2 = assignServingBuilds(members, islanded, consumed, hosts, views);
|
|
3131
|
-
const basis2 = basisFor(members, islanded, serving2);
|
|
3132
|
-
return { serving: serving2, basis: basis2, served: servedPerRemote(consumed, serving2, basis2) };
|
|
3133
|
-
};
|
|
3134
|
-
let { serving, basis, served } = assign();
|
|
3135
|
-
if (islanded.size === 0 && serving.size === 0 && !anyAnchorStored(members)) return;
|
|
3136
|
-
for (; ; ) {
|
|
3137
|
-
const torn = findTornRemotes(members, islanded, basis, served, hosts);
|
|
3138
|
-
if (torn.length === 0) break;
|
|
3139
|
-
for (const { remote, combination } of torn)
|
|
3140
|
-
islanded.set(remote, { kind: "torn", combination });
|
|
3141
|
-
({ serving, basis, served } = assign());
|
|
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
|
+
);
|
|
3142
3261
|
}
|
|
3143
|
-
|
|
3144
|
-
|
|
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) {
|
|
3145
3292
|
config.log.error(
|
|
3146
3293
|
3,
|
|
3147
|
-
`[${scope}][pool:${poolName}] version-incompatible remotes cannot be pooled: {${
|
|
3294
|
+
`[${scope}][pool:${poolName}] version-incompatible remotes cannot be pooled: {${rejecting.join(", ")}}.`
|
|
3148
3295
|
);
|
|
3149
3296
|
throw new NFError(`Could not pool '${poolName}' in scope ${scope}.`);
|
|
3150
3297
|
}
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
const bases = servingBuilds(members, islanded);
|
|
3164
|
-
const distinct = new Set(bases.values());
|
|
3165
|
-
if (distinct.size === 1 && bases.size === members.length) {
|
|
3166
|
-
const sole = [...distinct][0];
|
|
3167
|
-
if (coversWholePool(members, sole, islanded)) return /* @__PURE__ */ new Map();
|
|
3168
|
-
}
|
|
3169
|
-
const builds = liveBuilds(members, islanded);
|
|
3170
|
-
const shared = sharedTagPerSpecifier(members, islanded);
|
|
3171
|
-
const consumedSpec = views.consumedSpecifiers();
|
|
3172
|
-
const needAnchor = /* @__PURE__ */ new Map();
|
|
3173
|
-
const needSpecifiers = /* @__PURE__ */ new Map();
|
|
3174
|
-
for (const [remote, wants] of consumed) {
|
|
3175
|
-
if (islanded.has(remote)) continue;
|
|
3176
|
-
if (isWitnessed(consumedSpec.get(remote) ?? [], shared, builds)) continue;
|
|
3177
|
-
needAnchor.set(remote, wants);
|
|
3178
|
-
needSpecifiers.set(remote, consumedSpec.get(remote) ?? /* @__PURE__ */ new Set());
|
|
3179
|
-
}
|
|
3180
|
-
if (needAnchor.size === 0) return /* @__PURE__ */ new Map();
|
|
3181
|
-
const acceptance = views.acceptance();
|
|
3182
|
-
const assignment = assignAnchors({
|
|
3183
|
-
builds,
|
|
3184
|
-
acceptance,
|
|
3185
|
-
consumedSpecifiers: needSpecifiers,
|
|
3186
|
-
consumedMembers: needAnchor,
|
|
3187
|
-
hosts,
|
|
3188
|
-
arrival: views.arrival()
|
|
3189
|
-
});
|
|
3190
|
-
const anchors = /* @__PURE__ */ new Set();
|
|
3191
|
-
for (const build of assignment.values()) if (build !== void 0) anchors.add(build);
|
|
3192
|
-
const serving = /* @__PURE__ */ new Map();
|
|
3193
|
-
let scoped = false;
|
|
3194
|
-
for (const [remote, wants] of needAnchor) {
|
|
3195
|
-
const assigned = assignment.get(remote);
|
|
3196
|
-
const build = assigned ?? (anchors.has(remote) || hosts.has(remote) ? remote : void 0);
|
|
3197
|
-
if (build === void 0) {
|
|
3198
|
-
islanded.set(remote, {
|
|
3199
|
-
kind: "uncovered",
|
|
3200
|
-
...explainSelfServe(remote, wants, needSpecifiers.get(remote), { builds, acceptance })
|
|
3201
|
-
});
|
|
3202
|
-
scoped = true;
|
|
3203
|
-
continue;
|
|
3204
|
-
}
|
|
3205
|
-
serving.set(remote, build);
|
|
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;
|
|
3206
3310
|
}
|
|
3207
|
-
|
|
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
|
+
);
|
|
3208
3320
|
}
|
|
3209
3321
|
}
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
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;
|
|
3222
3364
|
}
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
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] });
|
|
3236
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);
|
|
3237
3411
|
}
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
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
|
+
});
|
|
3247
3431
|
}
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
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 }]);
|
|
3268
3455
|
}
|
|
3269
|
-
|
|
3270
|
-
|
|
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));
|
|
3271
3480
|
return {
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
...shareVersion,
|
|
3275
|
-
...byTag(
|
|
3276
|
-
clean.filter((e) => e.action === "skip"),
|
|
3277
|
-
"skip"
|
|
3278
|
-
),
|
|
3279
|
-
...byTag(scoped, "scope")
|
|
3280
|
-
].sort((a, b) => ports.versionCheck.compare(b.tag, a.tag))
|
|
3481
|
+
downloads: others(toll.downloads) + Number(!allCached(consumers.get(build.remote) ?? [])),
|
|
3482
|
+
fallbacks: others(toll.fallbacks)
|
|
3281
3483
|
};
|
|
3282
|
-
}
|
|
3484
|
+
};
|
|
3485
|
+
}
|
|
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;
|
|
3495
|
+
}
|
|
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);
|
|
3283
3503
|
}
|
|
3284
|
-
function
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
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);
|
|
3293
3530
|
}
|
|
3294
|
-
|
|
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));
|
|
3556
|
+
}
|
|
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));
|
|
3295
3564
|
}
|
|
3296
3565
|
|
|
3297
|
-
// src/lib/core/2.app/steps/
|
|
3298
|
-
function
|
|
3566
|
+
// src/lib/core/2.app/steps/decide-serving-builds.ts
|
|
3567
|
+
function createDecideServingBuilds(config, ports) {
|
|
3299
3568
|
return () => {
|
|
3300
|
-
const
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
for (const
|
|
3306
|
-
if (
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
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);
|
|
3312
3582
|
}
|
|
3313
|
-
|
|
3314
|
-
|
|
3583
|
+
return Promise.resolve();
|
|
3584
|
+
} catch (e) {
|
|
3585
|
+
return Promise.reject(e);
|
|
3315
3586
|
}
|
|
3316
|
-
return Promise.resolve();
|
|
3317
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}`
|
|
3619
|
+
);
|
|
3620
|
+
}
|
|
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);
|
|
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;
|
|
3318
3684
|
}
|
|
3319
3685
|
|
|
3320
|
-
// 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
|
|
3321
3687
|
var CHUNK_PREFIX = "@nf-internal";
|
|
3322
3688
|
function toChunkImport(fileName) {
|
|
3323
3689
|
if (fileName.startsWith("./")) {
|
|
@@ -3334,8 +3700,8 @@ function createGenerateImportMap(config, ports) {
|
|
|
3334
3700
|
const chunkBundles = {};
|
|
3335
3701
|
try {
|
|
3336
3702
|
addScopedExternals(importMap, chunkBundles);
|
|
3337
|
-
|
|
3338
|
-
|
|
3703
|
+
addNamedShareScopes(importMap, chunkBundles);
|
|
3704
|
+
addGlobalShareScope(importMap, chunkBundles);
|
|
3339
3705
|
addRemoteInfos(importMap, chunkBundles);
|
|
3340
3706
|
addChunkImports(importMap, chunkBundles);
|
|
3341
3707
|
return Promise.resolve(importMap);
|
|
@@ -3369,47 +3735,61 @@ function createGenerateImportMap(config, ports) {
|
|
|
3369
3735
|
}
|
|
3370
3736
|
return modules;
|
|
3371
3737
|
}
|
|
3372
|
-
function
|
|
3738
|
+
function addNamedShareScopes(importMap, chunkBundles) {
|
|
3373
3739
|
const shareScopes = ports.sharedExternalsRepo.getScopes({ includeGlobal: false });
|
|
3374
3740
|
for (const shareScope of shareScopes) {
|
|
3375
|
-
|
|
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
|
+
}
|
|
3376
3753
|
}
|
|
3377
3754
|
return importMap;
|
|
3378
3755
|
}
|
|
3379
|
-
function
|
|
3380
|
-
const sharedExternals = ports.sharedExternalsRepo.getFromScope(shareScope);
|
|
3756
|
+
function addShareScope(importMap, shareScope, sharedExternals, chunkBundles) {
|
|
3381
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
|
+
};
|
|
3382
3763
|
for (const [externalName, external] of Object.entries(sharedExternals)) {
|
|
3383
3764
|
let override = void 0;
|
|
3384
|
-
const cachedBefore = cachedFingerprint(external);
|
|
3385
3765
|
for (const version of external.versions) {
|
|
3386
3766
|
if (version.action === "scope") {
|
|
3387
|
-
|
|
3388
|
-
const remoteScope = getScope2(shareScope, remote.name, externalName);
|
|
3389
|
-
for (const [packageName, file] of Object.entries(remote.entries)) {
|
|
3390
|
-
const url = join(remoteScope, file);
|
|
3391
|
-
addToScope(importMap, remoteScope, { [packageName]: url });
|
|
3392
|
-
addIntegrity(importMap, url, remote.name, file);
|
|
3393
|
-
}
|
|
3394
|
-
registerBundleChunks(chunkBundles, remote.name, remote.bundle);
|
|
3395
|
-
remote.cached = true;
|
|
3396
|
-
}
|
|
3767
|
+
addScopeRow(importMap, chunkBundles, shareScope, externalName, version);
|
|
3397
3768
|
continue;
|
|
3398
3769
|
}
|
|
3399
|
-
version.remotes[0].cached = true;
|
|
3400
3770
|
let serving = version;
|
|
3401
3771
|
if (version.action === "skip") {
|
|
3402
3772
|
if (!override) {
|
|
3403
|
-
override = findOverride(
|
|
3773
|
+
override = findOverride(
|
|
3774
|
+
external,
|
|
3775
|
+
shareScope,
|
|
3776
|
+
externalName,
|
|
3777
|
+
(s) => Boolean(claimedElsewhere(externalName, s))
|
|
3778
|
+
) ?? "NOT_AVAILABLE";
|
|
3404
3779
|
}
|
|
3405
3780
|
if (override !== "NOT_AVAILABLE") {
|
|
3406
3781
|
serving = override;
|
|
3407
3782
|
version.remotes[0].cached = false;
|
|
3408
3783
|
}
|
|
3409
3784
|
}
|
|
3410
|
-
const provided =
|
|
3785
|
+
const provided = versionClaims(serving, external.servingBuild);
|
|
3411
3786
|
const mappings = [];
|
|
3412
3787
|
for (const [packageName, provider] of provided) {
|
|
3788
|
+
const claim = claimedElsewhere(externalName, packageName);
|
|
3789
|
+
if (claim) {
|
|
3790
|
+
mappings.push({ packageName, ...claim });
|
|
3791
|
+
continue;
|
|
3792
|
+
}
|
|
3413
3793
|
const file = provider.entries[packageName];
|
|
3414
3794
|
mappings.push({
|
|
3415
3795
|
packageName,
|
|
@@ -3422,56 +3802,60 @@ function createGenerateImportMap(config, ports) {
|
|
|
3422
3802
|
registerBundleChunks(chunkBundles, provider.name, provider.bundle);
|
|
3423
3803
|
}
|
|
3424
3804
|
}
|
|
3425
|
-
version.remotes.forEach((
|
|
3426
|
-
const
|
|
3427
|
-
if (index &&
|
|
3428
|
-
const files = index.get(
|
|
3429
|
-
const servedScope = getScope2(shareScope,
|
|
3430
|
-
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)) {
|
|
3431
3811
|
const source = files?.get(specifier);
|
|
3432
3812
|
if (!source) continue;
|
|
3433
3813
|
const url = join(servedScope, source.file);
|
|
3434
|
-
addToScope(importMap,
|
|
3435
|
-
addIntegrity(importMap, url,
|
|
3436
|
-
registerBundleChunks(chunkBundles,
|
|
3814
|
+
addToScope(importMap, consumerScope, { [specifier]: url });
|
|
3815
|
+
addIntegrity(importMap, url, consumer.servedBy, source.file);
|
|
3816
|
+
registerBundleChunks(chunkBundles, consumer.servedBy, source.bundle);
|
|
3437
3817
|
source.meta.cached = true;
|
|
3438
3818
|
}
|
|
3439
3819
|
return;
|
|
3440
3820
|
}
|
|
3441
3821
|
for (const m of mappings) {
|
|
3442
|
-
addToScope(importMap,
|
|
3822
|
+
addToScope(importMap, consumerScope, { [m.packageName]: m.url });
|
|
3443
3823
|
}
|
|
3444
|
-
for (const
|
|
3824
|
+
for (const packageName of Object.keys(consumer.entries)) {
|
|
3445
3825
|
if (provided.has(packageName)) continue;
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
continue;
|
|
3449
|
-
}
|
|
3450
|
-
const url = join(rScope, file);
|
|
3451
|
-
addToScope(importMap, rScope, { [packageName]: url });
|
|
3452
|
-
addIntegrity(importMap, url, r.name, file);
|
|
3453
|
-
r.cached = true;
|
|
3826
|
+
const claim = claimedElsewhere(externalName, packageName);
|
|
3827
|
+
if (claim) addToScope(importMap, consumerScope, { [packageName]: claim.url });
|
|
3454
3828
|
}
|
|
3455
3829
|
});
|
|
3456
3830
|
for (const m of mappings) {
|
|
3457
3831
|
addIntegrity(importMap, m.url, m.name, m.file);
|
|
3458
3832
|
}
|
|
3459
3833
|
}
|
|
3460
|
-
|
|
3461
|
-
|
|
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);
|
|
3462
3843
|
}
|
|
3844
|
+
registerBundleChunks(chunkBundles, remote.name, remote.bundle);
|
|
3845
|
+
remote.cached = true;
|
|
3463
3846
|
}
|
|
3464
3847
|
}
|
|
3465
3848
|
function cachedFingerprint(external) {
|
|
3466
3849
|
return external.versions.map((v) => v.remotes.map((r) => r.cached ? 1 : 0).join("")).join("|");
|
|
3467
3850
|
}
|
|
3468
|
-
function findOverride(external, shareScope, externalName) {
|
|
3851
|
+
function findOverride(external, shareScope, externalName, servedElsewhere) {
|
|
3469
3852
|
const sharedVersions = external.versions.filter((v) => v.action === "share");
|
|
3470
3853
|
const scopedExternalName = `${shareScope}.${externalName}`;
|
|
3471
3854
|
if (sharedVersions.length > 1) {
|
|
3472
3855
|
handleMultipleSharedVersions(scopedExternalName);
|
|
3473
3856
|
}
|
|
3474
|
-
|
|
3857
|
+
const servedByClaims = () => external.versions.filter((v) => v.action === "skip").every((v) => shipsOnly(v, servedElsewhere));
|
|
3858
|
+
if (sharedVersions.length < 1 && !servedByClaims()) {
|
|
3475
3859
|
if (config.strict.strictImportMap) {
|
|
3476
3860
|
config.log.error(4, `[${shareScope}][${externalName}] shareScope has no override version.`);
|
|
3477
3861
|
throw new NFError("Could not create ImportMap.");
|
|
@@ -3516,17 +3900,30 @@ function createGenerateImportMap(config, ports) {
|
|
|
3516
3900
|
}
|
|
3517
3901
|
return index;
|
|
3518
3902
|
}
|
|
3519
|
-
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) {
|
|
3520
3917
|
for (const consumer of version.remotes) {
|
|
3521
3918
|
if (!consumer.servedBy) continue;
|
|
3522
3919
|
const files = index.get(consumer.servedBy);
|
|
3523
|
-
const scope = getScope2(
|
|
3920
|
+
const scope = getScope2(shareScope, consumer.name, externalName);
|
|
3524
3921
|
for (const specifier of Object.keys(consumer.entries)) {
|
|
3525
3922
|
const source = files?.get(specifier);
|
|
3526
3923
|
if (!source) {
|
|
3527
3924
|
config.log.warn(
|
|
3528
3925
|
4,
|
|
3529
|
-
`[${
|
|
3926
|
+
`[${shareScope}][${externalName}][${consumer.name}] '${consumer.servedBy}' does not serve '${specifier}'.`
|
|
3530
3927
|
);
|
|
3531
3928
|
continue;
|
|
3532
3929
|
}
|
|
@@ -3544,9 +3941,11 @@ function createGenerateImportMap(config, ports) {
|
|
|
3544
3941
|
source.meta.cached = true;
|
|
3545
3942
|
}
|
|
3546
3943
|
}
|
|
3547
|
-
function
|
|
3944
|
+
function addGlobalShareScope(importMap, chunkBundles) {
|
|
3548
3945
|
const sharedExternals = ports.sharedExternalsRepo.getFromScope();
|
|
3549
3946
|
const index = buildServedIndex(sharedExternals);
|
|
3947
|
+
const { claims, duplicates } = claimTable(Object.entries(sharedExternals));
|
|
3948
|
+
const claimed = claimsByCopy(claims);
|
|
3550
3949
|
const served = [];
|
|
3551
3950
|
const cachedBefore = /* @__PURE__ */ new Map();
|
|
3552
3951
|
for (const [externalName, external] of Object.entries(sharedExternals)) {
|
|
@@ -3557,27 +3956,14 @@ function createGenerateImportMap(config, ports) {
|
|
|
3557
3956
|
}
|
|
3558
3957
|
if (version.action === "skip") continue;
|
|
3559
3958
|
if (version.action === "scope") {
|
|
3560
|
-
|
|
3561
|
-
const remoteScope = getScope2(GLOBAL_SCOPE, remote.name, externalName);
|
|
3562
|
-
for (const [packageName, file] of Object.entries(remote.entries)) {
|
|
3563
|
-
const url = join(remoteScope, file);
|
|
3564
|
-
addToScope(importMap, remoteScope, { [packageName]: url });
|
|
3565
|
-
addIntegrity(importMap, url, remote.name, file);
|
|
3566
|
-
}
|
|
3567
|
-
remote.cached = true;
|
|
3568
|
-
registerBundleChunks(chunkBundles, remote.name, remote.bundle);
|
|
3569
|
-
}
|
|
3959
|
+
addScopeRow(importMap, chunkBundles, GLOBAL_SCOPE, externalName, version);
|
|
3570
3960
|
continue;
|
|
3571
3961
|
}
|
|
3572
|
-
if (
|
|
3962
|
+
if (duplicates.has(version)) {
|
|
3573
3963
|
notifyDuplicateGlobalExternal(externalName);
|
|
3574
3964
|
continue;
|
|
3575
3965
|
}
|
|
3576
|
-
|
|
3577
|
-
}
|
|
3578
|
-
for (const version of external.versions) {
|
|
3579
|
-
if (version.action !== "skip") continue;
|
|
3580
|
-
selfFillUncovered(importMap, chunkBundles, externalName, version.remotes);
|
|
3966
|
+
publishClaims(importMap, chunkBundles, externalName, version, claimed);
|
|
3581
3967
|
}
|
|
3582
3968
|
}
|
|
3583
3969
|
flushServed(importMap, chunkBundles, served);
|
|
@@ -3588,54 +3974,30 @@ function createGenerateImportMap(config, ports) {
|
|
|
3588
3974
|
}
|
|
3589
3975
|
return importMap;
|
|
3590
3976
|
}
|
|
3591
|
-
function
|
|
3592
|
-
const
|
|
3593
|
-
for (const [
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
`[${GLOBAL_SCOPE}][${externalName}][${remote.name}] Entrypoint '${packageName}' is already mapped by another external, keeping '${importMap.imports[packageName]}'.`
|
|
3598
|
-
);
|
|
3599
|
-
continue;
|
|
3600
|
-
}
|
|
3601
|
-
let scope = scopes.get(remote.name);
|
|
3602
|
-
if (scope === void 0) {
|
|
3603
|
-
scopes.set(remote.name, scope = getScope2(GLOBAL_SCOPE, remote.name, externalName));
|
|
3604
|
-
registerBundleChunks(chunkBundles, remote.name, remote.bundle);
|
|
3605
|
-
remote.cached = true;
|
|
3606
|
-
}
|
|
3607
|
-
const file = remote.entries[packageName];
|
|
3608
|
-
const url = join(scope, file);
|
|
3609
|
-
addToGlobal(importMap, { [packageName]: url });
|
|
3610
|
-
addIntegrity(importMap, url, remote.name, file);
|
|
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]);
|
|
3611
3983
|
}
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
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];
|
|
3623
3995
|
const url = join(scope, file);
|
|
3624
|
-
addToGlobal(importMap, { [
|
|
3625
|
-
addIntegrity(importMap, url,
|
|
3626
|
-
registerBundleChunks(chunkBundles, remote.name, remote.bundle);
|
|
3627
|
-
remote.cached = true;
|
|
3996
|
+
addToGlobal(importMap, { [specifier]: url });
|
|
3997
|
+
addIntegrity(importMap, url, copy.name, file);
|
|
3628
3998
|
}
|
|
3629
3999
|
}
|
|
3630
4000
|
}
|
|
3631
|
-
function warnUncoveredEntrypoint(scope, externalName, remoteName, packageName) {
|
|
3632
|
-
const msg = `[${scope}][${externalName}][${remoteName}] Entrypoint '${packageName}' is not covered by the shared version.`;
|
|
3633
|
-
if (config.strict.strictEntryPointCoverage || config.strict.strictImportMap) {
|
|
3634
|
-
config.log.error(4, msg);
|
|
3635
|
-
throw new NFError("Could not create ImportMap.");
|
|
3636
|
-
}
|
|
3637
|
-
config.log.warn(4, msg);
|
|
3638
|
-
}
|
|
3639
4001
|
function notifyDuplicateGlobalExternal(externalName) {
|
|
3640
4002
|
if (config.strict.strictImportMap) {
|
|
3641
4003
|
config.log.error(4, `[${externalName}] Shared external has multiple shared versions.`);
|
|
@@ -3709,15 +4071,30 @@ function createGenerateImportMap(config, ports) {
|
|
|
3709
4071
|
});
|
|
3710
4072
|
}
|
|
3711
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
|
+
}
|
|
3712
4081
|
|
|
3713
4082
|
// src/lib/core/2.app/steps/commit-changes.ts
|
|
3714
4083
|
function createCommitChanges(config, ports) {
|
|
3715
|
-
return (importMap,
|
|
4084
|
+
return (importMap, { markUndecided = false, ...browserOpts } = {}) => Promise.resolve(importMap).then((map) => addToBrowser(map, browserOpts)).then(() => {
|
|
4085
|
+
if (markUndecided) markUndecidedScopes();
|
|
4086
|
+
persistRepositoryChanges();
|
|
4087
|
+
});
|
|
3716
4088
|
function addToBrowser(importMap, opts) {
|
|
3717
4089
|
ports.browser.setImportMapFn(importMap, opts);
|
|
3718
4090
|
config.log.debug(5, "Added import map to browser.", importMap);
|
|
3719
4091
|
return importMap;
|
|
3720
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
|
+
}
|
|
3721
4098
|
function persistRepositoryChanges() {
|
|
3722
4099
|
ports.remoteInfoRepo.commit();
|
|
3723
4100
|
ports.scopedExternalsRepo.commit();
|
|
@@ -3727,6 +4104,29 @@ function createCommitChanges(config, ports) {
|
|
|
3727
4104
|
}
|
|
3728
4105
|
}
|
|
3729
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
|
+
|
|
3730
4130
|
// src/lib/core/2.app/steps/expose-module-loader.ts
|
|
3731
4131
|
function createExposeModuleLoader(config, ports) {
|
|
3732
4132
|
const loadRemoteModule = (remoteName, exposedModule) => {
|
|
@@ -3762,10 +4162,16 @@ var normalizeRemoteRef = (remote) => {
|
|
|
3762
4162
|
function createGetRemoteEntry(config, ports) {
|
|
3763
4163
|
return async (remoteEntryUrl, remote) => {
|
|
3764
4164
|
const { name: remoteName, integrity } = normalizeRemoteRef(remote);
|
|
3765
|
-
|
|
3766
|
-
|
|
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
|
+
);
|
|
3767
4171
|
return Optional.empty();
|
|
3768
4172
|
}
|
|
4173
|
+
if (!!remoteName && ports.remoteInfoRepo.contains(remoteName))
|
|
4174
|
+
return warnInitialized(remoteName, remoteEntryUrl);
|
|
3769
4175
|
try {
|
|
3770
4176
|
const remoteEntry = integrity ? await ports.remoteEntryProvider.provide(remoteEntryUrl, { integrity }) : await ports.remoteEntryProvider.provide(remoteEntryUrl);
|
|
3771
4177
|
config.log.debug(
|
|
@@ -3780,10 +4186,8 @@ function createGetRemoteEntry(config, ports) {
|
|
|
3780
4186
|
}
|
|
3781
4187
|
config.log.warn(7, errorMsg + " Omitting expected name.");
|
|
3782
4188
|
}
|
|
3783
|
-
if (ports.remoteInfoRepo.contains(remoteEntry.name))
|
|
3784
|
-
remoteEntry.
|
|
3785
|
-
config.log.debug(7, `Overriding existing remote '${remoteName}' with '${remoteEntryUrl}'.`);
|
|
3786
|
-
}
|
|
4189
|
+
if (ports.remoteInfoRepo.contains(remoteEntry.name))
|
|
4190
|
+
return warnInitialized(remoteEntry.name, remoteEntryUrl);
|
|
3787
4191
|
return Optional.of(checkForSSE(remoteEntry));
|
|
3788
4192
|
} catch (error) {
|
|
3789
4193
|
config.log.error(
|
|
@@ -3796,10 +4200,18 @@ function createGetRemoteEntry(config, ports) {
|
|
|
3796
4200
|
);
|
|
3797
4201
|
}
|
|
3798
4202
|
};
|
|
3799
|
-
function
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
|
|
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];
|
|
3803
4215
|
}
|
|
3804
4216
|
function checkForSSE(entry) {
|
|
3805
4217
|
if (config.sse) {
|
|
@@ -3819,92 +4231,170 @@ function createGetRemoteEntry(config, ports) {
|
|
|
3819
4231
|
// src/lib/core/2.app/steps/update-cache.ts
|
|
3820
4232
|
function createUpdateCache(config, ports) {
|
|
3821
4233
|
const storeRemoteEntry = createStoreRemoteEntry(config, ports, 8);
|
|
3822
|
-
const
|
|
4234
|
+
const accepts = createAcceptsTag(
|
|
4235
|
+
(tag, range) => ports.versionCheck.isCompatible(tag, range),
|
|
4236
|
+
(a, b) => ports.versionCheck.compare(a, b)
|
|
4237
|
+
);
|
|
3823
4238
|
return (remoteEntry) => {
|
|
3824
4239
|
try {
|
|
4240
|
+
const pending = [];
|
|
4241
|
+
storeRemoteEntry(remoteEntry, (_, external, ctx) => pending.push({ external, ctx }));
|
|
4242
|
+
const decisions = decidePackages(remoteEntry, pending);
|
|
3825
4243
|
const actions = {};
|
|
3826
|
-
|
|
3827
|
-
|
|
3828
|
-
const { action, provided, sameVersion } = resolveSharedExternal(entry, external, ctx);
|
|
3829
|
-
actions[external.packageName] = { action };
|
|
3830
|
-
if (action === "skip" && provided) {
|
|
3831
|
-
actions[external.packageName].covered = Array.from(provided.keys());
|
|
3832
|
-
if (sameVersion) actions[external.packageName].sameVersion = true;
|
|
3833
|
-
if (external.shareScope) {
|
|
3834
|
-
actions[external.packageName].override = resolveOverrideEntries(
|
|
3835
|
-
entry,
|
|
3836
|
-
external,
|
|
3837
|
-
provided
|
|
3838
|
-
);
|
|
3839
|
-
}
|
|
3840
|
-
}
|
|
3841
|
-
});
|
|
4244
|
+
for (const item of pending)
|
|
4245
|
+
actions[item.external.packageName] = applyDecision(remoteEntry, item, decisions.get(item));
|
|
3842
4246
|
return Promise.resolve({ entry: remoteEntry, actions });
|
|
3843
4247
|
} catch (error) {
|
|
3844
4248
|
return Promise.reject(error);
|
|
3845
4249
|
}
|
|
3846
4250
|
};
|
|
3847
|
-
function
|
|
3848
|
-
|
|
3849
|
-
|
|
3850
|
-
|
|
3851
|
-
|
|
3852
|
-
|
|
3853
|
-
commit
|
|
3854
|
-
}) {
|
|
3855
|
-
let action = scopeType === "strict" ? "share" : "skip";
|
|
3856
|
-
const sharedVersion = cached2.versions.find((c) => c.action === "share");
|
|
3857
|
-
const isCompatible = !sharedVersion || ports.versionCheck.isCompatible(sharedVersion.tag, remote.requiredVersion);
|
|
3858
|
-
if (action === "skip" && !isCompatible && remote.strictVersion) {
|
|
3859
|
-
action = "scope";
|
|
3860
|
-
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}'`;
|
|
3861
|
-
if (config.strict.strictExternalCompatibility) {
|
|
3862
|
-
config.log.error(8, errorMsg);
|
|
3863
|
-
throw new NFError(`Could not process remote '${remoteEntry.name}'`);
|
|
3864
|
-
}
|
|
3865
|
-
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);
|
|
3866
4257
|
}
|
|
3867
|
-
|
|
3868
|
-
|
|
3869
|
-
|
|
3870
|
-
|
|
3871
|
-
|
|
3872
|
-
|
|
3873
|
-
|
|
3874
|
-
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
|
|
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);
|
|
3881
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 };
|
|
3882
4316
|
}
|
|
3883
|
-
|
|
3884
|
-
|
|
3885
|
-
|
|
3886
|
-
|
|
3887
|
-
|
|
3888
|
-
|
|
3889
|
-
|
|
3890
|
-
|
|
3891
|
-
|
|
3892
|
-
|
|
3893
|
-
|
|
3894
|
-
|
|
3895
|
-
|
|
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}'`);
|
|
3896
4365
|
}
|
|
3897
|
-
|
|
3898
|
-
|
|
4366
|
+
config.log.warn(8, errorMsg);
|
|
4367
|
+
}
|
|
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
|
+
};
|
|
3899
4378
|
}
|
|
3900
|
-
function
|
|
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) {
|
|
3901
4394
|
return Object.fromEntries(
|
|
3902
|
-
Array.from(
|
|
3903
|
-
|
|
3904
|
-
join(
|
|
3905
|
-
providerScopeUrl(remoteEntry, external, provider.name),
|
|
3906
|
-
provider.entries[packageName]
|
|
3907
|
-
)
|
|
4395
|
+
Array.from(files, ([specifier, copy]) => [
|
|
4396
|
+
specifier,
|
|
4397
|
+
join(providerScopeUrl(remoteEntry, external, copy.name), copy.entries[specifier])
|
|
3908
4398
|
])
|
|
3909
4399
|
);
|
|
3910
4400
|
}
|
|
@@ -3919,134 +4409,298 @@ function createUpdateCache(config, ports) {
|
|
|
3919
4409
|
}
|
|
3920
4410
|
}
|
|
3921
4411
|
|
|
3922
|
-
// src/lib/core/
|
|
3923
|
-
function
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
|
|
3928
|
-
|
|
3929
|
-
|
|
3930
|
-
|
|
3931
|
-
|
|
3932
|
-
|
|
3933
|
-
|
|
3934
|
-
|
|
3935
|
-
|
|
3936
|
-
|
|
3937
|
-
const
|
|
3938
|
-
|
|
3939
|
-
|
|
3940
|
-
|
|
3941
|
-
for (const
|
|
3942
|
-
|
|
3943
|
-
|
|
3944
|
-
|
|
3945
|
-
|
|
3946
|
-
|
|
3947
|
-
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
|
|
3951
|
-
|
|
3952
|
-
|
|
3953
|
-
|
|
3954
|
-
|
|
3955
|
-
|
|
3956
|
-
|
|
3957
|
-
|
|
3958
|
-
|
|
3959
|
-
|
|
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] }
|
|
3960
4457
|
);
|
|
3961
|
-
mine.forEach((member) => scope(member.name));
|
|
3962
|
-
continue;
|
|
3963
4458
|
}
|
|
3964
|
-
redirect(entry.name, anchor, pool, asked.view, actions, shareScope);
|
|
3965
4459
|
}
|
|
3966
|
-
|
|
3967
|
-
|
|
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 };
|
|
3968
4511
|
};
|
|
3969
|
-
|
|
3970
|
-
|
|
3971
|
-
|
|
3972
|
-
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
};
|
|
3977
|
-
}
|
|
3978
|
-
function anchorFor(remote, { pool, view, wants, specifiers, acceptance }) {
|
|
3979
|
-
const shared = /* @__PURE__ */ new Map();
|
|
3980
|
-
for (const [specifier, source] of view.global) shared.set(specifier, source.tag);
|
|
3981
|
-
if (isWitnessed(specifiers, shared, view.builds)) return "witnessed";
|
|
3982
|
-
const basis = basisPerMember(pool);
|
|
3983
|
-
for (const build of candidateOrder(pool, view)) {
|
|
3984
|
-
if (build === remote) continue;
|
|
3985
|
-
const candidate = view.builds.get(build);
|
|
3986
|
-
if (!servesItsOwnFamily(build, pool, basis)) continue;
|
|
3987
|
-
if (!covers(candidate.coverage, specifiers)) continue;
|
|
3988
|
-
if (!acceptsAll(acceptance(), candidate.instance, remote, wants)) continue;
|
|
3989
|
-
return build;
|
|
3990
|
-
}
|
|
3991
|
-
return void 0;
|
|
3992
|
-
}
|
|
3993
|
-
function candidateOrder(pool, view) {
|
|
3994
|
-
const serving = /* @__PURE__ */ new Set();
|
|
3995
|
-
for (const source of view.global.values()) serving.add(source.remote);
|
|
3996
|
-
const hosts = hostRemotes(pool);
|
|
3997
|
-
const rank = (build) => serving.has(build) ? 0 : hosts.has(build) ? 1 : 2;
|
|
3998
|
-
return [...view.builds.keys()].sort((a, b) => rank(a) - rank(b) || a.localeCompare(b));
|
|
3999
|
-
}
|
|
4000
|
-
function servesItsOwnFamily(build, pool, basis) {
|
|
4001
|
-
let ships2 = 0;
|
|
4002
|
-
let winsAll = true;
|
|
4003
|
-
let allScoped = true;
|
|
4004
|
-
for (const member of pool) {
|
|
4005
|
-
for (const version of member.external.versions) {
|
|
4006
|
-
const meta = version.remotes.find((r) => r.name === build);
|
|
4007
|
-
if (!meta) continue;
|
|
4008
|
-
if (meta.servedBy !== void 0) return false;
|
|
4009
|
-
ships2++;
|
|
4010
|
-
if (version.action !== "scope") allScoped = false;
|
|
4011
|
-
if (basis.get(member.name) !== build) winsAll = false;
|
|
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);
|
|
4012
4519
|
}
|
|
4013
4520
|
}
|
|
4014
|
-
return ships2 > 0 && (winsAll || allScoped);
|
|
4015
4521
|
}
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
|
|
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 };
|
|
4538
|
+
}
|
|
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);
|
|
4551
|
+
}
|
|
4552
|
+
covered.add(specifier);
|
|
4021
4553
|
}
|
|
4022
|
-
|
|
4023
|
-
|
|
4024
|
-
|
|
4025
|
-
|
|
4026
|
-
|
|
4027
|
-
|
|
4028
|
-
|
|
4029
|
-
);
|
|
4030
|
-
|
|
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 };
|
|
4031
4565
|
}
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
|
|
4035
|
-
|
|
4036
|
-
|
|
4037
|
-
|
|
4038
|
-
|
|
4039
|
-
const
|
|
4040
|
-
|
|
4041
|
-
|
|
4042
|
-
|
|
4043
|
-
|
|
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);
|
|
4575
|
+
}
|
|
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 };
|
|
4582
|
+
}
|
|
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);
|
|
4604
|
+
}
|
|
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
|
+
}
|
|
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;
|
|
4044
4650
|
}
|
|
4045
|
-
action.covered = specifiers;
|
|
4046
|
-
if (Object.keys(override).length > 0) action.override = override;
|
|
4047
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
|
+
);
|
|
4048
4671
|
}
|
|
4049
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
|
+
}
|
|
4050
4704
|
|
|
4051
4705
|
// src/lib/core/2.app/steps/convert-to-import-map.ts
|
|
4052
4706
|
function createConvertToImportMap(config, ports) {
|
|
@@ -4083,27 +4737,14 @@ function createConvertToImportMap(config, ports) {
|
|
|
4083
4737
|
return;
|
|
4084
4738
|
}
|
|
4085
4739
|
if (actions[external.packageName].action === "skip") {
|
|
4086
|
-
const { override
|
|
4740
|
+
const { override } = actions[external.packageName];
|
|
4087
4741
|
if (override) {
|
|
4088
4742
|
Object.entries(override).forEach(([packageName, url]) => {
|
|
4089
4743
|
addToScopes(remoteEntryScope, packageName, url, importMap);
|
|
4090
4744
|
});
|
|
4091
|
-
serveUncovered(
|
|
4092
|
-
remoteEntry,
|
|
4093
|
-
external,
|
|
4094
|
-
covered ?? Object.keys(override),
|
|
4095
|
-
remoteEntryScope,
|
|
4096
|
-
importMap,
|
|
4097
|
-
sameVersion
|
|
4098
|
-
);
|
|
4099
|
-
return;
|
|
4100
|
-
}
|
|
4101
|
-
if (!external.shareScope) {
|
|
4102
|
-
if (covered) {
|
|
4103
|
-
serveUncovered(remoteEntry, external, covered, remoteEntryScope, importMap, sameVersion);
|
|
4104
|
-
}
|
|
4105
4745
|
return;
|
|
4106
4746
|
}
|
|
4747
|
+
if (!external.shareScope) return;
|
|
4107
4748
|
log.error(
|
|
4108
4749
|
9,
|
|
4109
4750
|
`[${remoteEntry.name}][${external.packageName}] shareScope skip has no override.`
|
|
@@ -4168,27 +4809,6 @@ function createConvertToImportMap(config, ports) {
|
|
|
4168
4809
|
});
|
|
4169
4810
|
return importMap;
|
|
4170
4811
|
}
|
|
4171
|
-
function serveUncovered(remoteEntry, external, covered, remoteEntryScope, importMap, sameVersion = false) {
|
|
4172
|
-
const provided = new Set(covered);
|
|
4173
|
-
Object.entries(external.entries).forEach(([packageName, fileName]) => {
|
|
4174
|
-
if (provided.has(packageName)) return;
|
|
4175
|
-
if (!sameVersion && (config.strict.strictEntryPointCoverage || config.profile.scopeUncoveredEntrypoints)) {
|
|
4176
|
-
warnUncoveredEntrypoint(remoteEntry.name, external.packageName, packageName);
|
|
4177
|
-
return;
|
|
4178
|
-
}
|
|
4179
|
-
const url = join(remoteEntryScope, fileName);
|
|
4180
|
-
addToScopes(remoteEntryScope, packageName, url, importMap);
|
|
4181
|
-
addIntegrity(importMap, url, remoteEntry.integrity, fileName);
|
|
4182
|
-
});
|
|
4183
|
-
}
|
|
4184
|
-
function warnUncoveredEntrypoint(remoteName, externalName, packageName) {
|
|
4185
|
-
const msg = `[${remoteName}][${externalName}] Entrypoint '${packageName}' is not covered by the override.`;
|
|
4186
|
-
if (config.strict.strictEntryPointCoverage || config.strict.strictImportMap) {
|
|
4187
|
-
log.error(9, msg);
|
|
4188
|
-
throw new NFError("Could not create ImportMap.");
|
|
4189
|
-
}
|
|
4190
|
-
log.warn(9, msg);
|
|
4191
|
-
}
|
|
4192
4812
|
function addIntegrity(importMap, url, integrityMap, file) {
|
|
4193
4813
|
const hash = integrityMap?.[file];
|
|
4194
4814
|
if (!hash) return;
|
|
@@ -4204,11 +4824,12 @@ var createInitDrivers = ({
|
|
|
4204
4824
|
}) => ({
|
|
4205
4825
|
getRemoteEntries: createGetRemoteEntries(config, adapters),
|
|
4206
4826
|
processRemoteEntries: createProcessRemoteEntries(config, adapters),
|
|
4207
|
-
markPoolsForReelection: createMarkPoolsForReelection(config, adapters),
|
|
4208
4827
|
determineSharedExternals: createDetermineSharedExternals(config, adapters),
|
|
4209
4828
|
poolSharedExternals: createPoolSharedExternals(config, adapters),
|
|
4829
|
+
decideServingBuilds: createDecideServingBuilds(config, adapters),
|
|
4210
4830
|
generateImportMap: createGenerateImportMap(config, adapters),
|
|
4211
4831
|
commitChanges: createCommitChanges(config, adapters),
|
|
4832
|
+
rollbackChanges: createRollbackChanges(config, adapters),
|
|
4212
4833
|
exposeModuleLoader: createExposeModuleLoader(config, adapters),
|
|
4213
4834
|
getRemoteEntry: createGetRemoteEntry(config, adapters),
|
|
4214
4835
|
updateCache: createUpdateCache(config, adapters),
|
|
@@ -4228,14 +4849,6 @@ var INIT_FLOW_FACTORY = ({
|
|
|
4228
4849
|
};
|
|
4229
4850
|
|
|
4230
4851
|
// src/lib/core/5.di/federation-result.factory.ts
|
|
4231
|
-
var createStateDump = (config, adapters) => (msg) => config.log.debug(0, msg, {
|
|
4232
|
-
remotes: { ...adapters.remoteInfoRepo.getAll() },
|
|
4233
|
-
"shared-externals": adapters.sharedExternalsRepo.getScopes({ includeGlobal: true }).reduce(
|
|
4234
|
-
(acc, scope) => ({ ...acc, [scope]: adapters.sharedExternalsRepo.getFromScope(scope) }),
|
|
4235
|
-
{}
|
|
4236
|
-
),
|
|
4237
|
-
"scoped-externals": adapters.scopedExternalsRepo.getAll()
|
|
4238
|
-
});
|
|
4239
4852
|
var createFederationResult = ({
|
|
4240
4853
|
config,
|
|
4241
4854
|
adapters,
|
|
@@ -4243,11 +4856,8 @@ var createFederationResult = ({
|
|
|
4243
4856
|
initRemoteEntryFlow,
|
|
4244
4857
|
afterInitRemoteEntry
|
|
4245
4858
|
}) => {
|
|
4246
|
-
const stateDump = createStateDump(config, adapters);
|
|
4247
4859
|
const initRemoteEntry = async (remoteEntryUrl, remote) => {
|
|
4248
|
-
const remoteName = typeof remote === "string" ? remote : remote?.name;
|
|
4249
4860
|
return initRemoteEntryFlow(remoteEntryUrl, remote).catch((e) => {
|
|
4250
|
-
stateDump(`[dynamic-init][${remoteName ?? remoteEntryUrl}] STATE DUMP`);
|
|
4251
4861
|
if (config.strict.strictRemoteEntry) return Promise.reject(e);
|
|
4252
4862
|
else console.warn("Failed to initialize remote entry, continuing anyway.");
|
|
4253
4863
|
return Promise.resolve();
|