everything-dev
Version:
A consolidated product package for building Module Federation apps with oRPC APIs.
304 lines (302 loc) • 11.6 kB
JavaScript
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
const require_bundle_cache = require('./bundle-cache.cjs');
const require_integrity = require('./integrity.cjs');
let node_fs = require("node:fs");
let _module_federation_enhanced_runtime = require("@module-federation/enhanced/runtime");
let _module_federation_runtime_core = require("@module-federation/runtime-core");
let every_plugin_build_rspack = require("every-plugin/build/rspack");
//#region src/mf.ts
let mfInstance = null;
function fetchWithTimeout(url, init, timeoutMs) {
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), timeoutMs);
return fetch(url, {
...init,
signal: controller.signal
}).finally(() => clearTimeout(timer));
}
function patchManifestFetchForSsrPublicPath(mf) {
if (!mf || !mf.loaderHook?.lifecycle?.fetch?.on) return;
if (mf.__everythingDevPatchedManifestFetch === true) return;
mf.__everythingDevPatchedManifestFetch = true;
mf.loaderHook.lifecycle.fetch.on((url, init) => {
if (typeof url !== "string" || !url.endsWith("/mf-manifest.json")) return;
return fetchWithTimeout(url, init, 15e3).then((res) => res.json()).then((json) => {
json.metaData = json.metaData ?? {};
json.metaData.ssrPublicPath = json.metaData.ssrPublicPath ?? url.replace(/\/mf-manifest\.json$/, "/");
if (json.metaData.publicPath === "auto" || json.metaData.publicPath === "") json.metaData.publicPath = "/";
return new Response(JSON.stringify(json), { headers: { "content-type": "application/json" } });
});
});
}
function installIntegrityFetchHook(mf, registry, options) {
if (!mf || !mf.loaderHook?.lifecycle?.fetch?.on) {
console.warn("[SRI] MF lifecycle fetch hook not available, skipping integrity-in-pipeline");
return;
}
if (mf.__everythingDevIntegrityHook === true) return;
mf.__everythingDevIntegrityHook = true;
mf.loaderHook.lifecycle.fetch.on((url, init) => {
if (typeof url !== "string") return;
const expectedHash = registry.get(url);
if (!expectedHash) return;
return fetchWithTimeout(url, init, 15e3).then(async (res) => {
const buffer = Buffer.from(await res.arrayBuffer());
const computed = require_integrity.computeSriHash(buffer);
if (computed !== expectedHash) {
const cached = await require_bundle_cache.readBundleCache(url, { cacheDir: options?.cacheDir });
if (cached && require_integrity.computeSriHash(Buffer.from(cached)) === expectedHash) {
console.warn(`[SRI] Origin bytes failed verification for ${url} — serving last-known-good from the bundle cache`);
return new Response(cached.slice().buffer, {
status: 200,
headers: { "x-bundle-cache": "stale" }
});
}
console.error(`[SRI] Integrity check failed in MF fetch pipeline for ${url}\n Expected: ${expectedHash}\n Computed: ${computed}`);
return new Response(`Integrity check failed for ${url}`, {
status: 500,
statusText: "Integrity Check Failed"
});
}
await require_bundle_cache.writeBundleCache(url, new Uint8Array(buffer), { cacheDir: options?.cacheDir }).catch(() => {});
return new Response(buffer, {
status: res.status,
statusText: res.statusText,
headers: res.headers
});
});
});
}
function getFederationInstance() {
if (mfInstance) return mfInstance;
const existing = (0, _module_federation_enhanced_runtime.getInstance)();
if (existing) {
mfInstance = existing;
(0, _module_federation_runtime_core.setGlobalFederationInstance)(mfInstance);
patchManifestFetchForSsrPublicPath(mfInstance);
return mfInstance;
}
mfInstance = (0, _module_federation_enhanced_runtime.createInstance)({
name: "host",
remotes: []
});
(0, _module_federation_runtime_core.setGlobalFederationInstance)(mfInstance);
patchManifestFetchForSsrPublicPath(mfInstance);
return mfInstance;
}
async function registerRemote(opts) {
const instance = getFederationInstance();
const inferType = () => {
if (opts.type) return opts.type;
if (opts.entry.endsWith("/mf-manifest.json")) return "manifest";
if (/\/remoteEntry(\.[a-f0-9]{8,})?\.js$/.test(opts.entry)) return "script";
return typeof window === "undefined" ? "script" : "manifest";
};
const remoteType = inferType();
instance.registerRemotes([{
name: opts.name,
entry: opts.entry,
type: remoteType
}]);
}
async function loadRemoteModule(specifier, options) {
const instance = getFederationInstance();
if (typeof window === "undefined") await instance.initializeSharing?.("default");
const mod = await instance.loadRemote(specifier, options);
if (!mod) throw new Error(`Failed to load remote module: ${specifier}`);
return mod;
}
async function ensureNodeRuntimePlugin() {
const instance = getFederationInstance();
if (typeof window !== "undefined") return;
if (instance.__nodeRuntimePluginLoaded) return;
const mod = await import("@module-federation/node/runtimePlugin");
const factory = mod?.default ?? mod;
const plugin = typeof factory === "function" ? factory() : null;
if (plugin) instance.registerPlugins([plugin]);
instance.__nodeRuntimePluginLoaded = true;
}
function parseSemver(v) {
const m = typeof v === "string" ? /^(\d+)\.(\d+)\.(\d+)/.exec(v) : null;
return m ? {
major: +m[1],
minor: +m[2],
patch: +m[3]
} : null;
}
function satisfiesConstraint(version, constraint) {
const trimmed = constraint.trim();
if (!trimmed) return true;
if (![
"^",
"~",
">",
"<",
"=",
"x"
].some((c) => trimmed.startsWith(c))) return version === trimmed;
const m = /^\s*\^\s*(\d+)\.(\d+)\.(\d+)/.exec(trimmed);
if (!m) return true;
const v = parseSemver(version);
if (!v) return false;
if (v.major !== +m[1]) return false;
if (v.minor < +m[2] || v.minor === +m[2] && v.patch < +m[3]) return false;
const constraintPrerelease = trimmed.match(/-([0-9A-Za-z.-]+)/);
const versionPrerelease = /-([0-9A-Za-z.-]+)\s*$/.exec(version);
if (constraintPrerelease) {
if (!versionPrerelease) return false;
const cPre = constraintPrerelease[1].split(".");
const vPre = versionPrerelease[1].split(".");
for (let i = 0; i < Math.max(cPre.length, vPre.length); i += 1) {
const c = cPre[i] ?? "0";
const p = vPre[i] ?? "0";
if (c === p) continue;
const cNum = /^\d+$/.test(c) ? +c : null;
const pNum = /^\d+$/.test(p) ? +p : null;
if (cNum !== null && pNum !== null) return cNum < pNum;
return cNum === null ? c.localeCompare(p) < 0 : false;
}
return true;
}
if (versionPrerelease) return false;
return true;
}
function manifestUrl(remoteUrl) {
return remoteUrl.endsWith("/") ? `${remoteUrl}mf-manifest.json` : `${remoteUrl}/mf-manifest.json`;
}
async function fetchManifest(url, timeoutMs) {
const res = await fetch(manifestUrl(url), { signal: AbortSignal.timeout(timeoutMs) });
if (!res.ok) throw new Error(`HTTP ${res.status} ${res.statusText}`);
return res.json();
}
function listFederationRemotes(bosConfig) {
const out = [];
for (const role of ["api", "auth"]) {
const url = readProductionUrl((bosConfig?.app)?.[role]);
if (url) out.push({
role,
url
});
}
for (const [id, slot] of Object.entries(bosConfig?.plugins ?? {})) {
const url = readProductionUrl(slot);
if (url) out.push({
role: id,
url
});
}
return out;
}
function readProductionUrl(entry) {
if (typeof entry === "string") return entry.length > 0 ? entry : null;
if (entry && typeof entry.production === "string") {
const url = entry.production;
return url.length > 0 ? url : null;
}
return null;
}
async function checkFederationCompat(bosConfig, opts = {}) {
const timeoutMs = opts.timeoutMs ?? 15e3;
if (!bosConfig?.app?.host?.production) return {
ok: true,
hostVersion: null,
hostReachable: false,
remotes: []
};
const hostUrl = bosConfig.app.host.production;
let hostManifest = null;
let hostReason;
try {
hostManifest = await fetchManifest(hostUrl, timeoutMs);
} catch (e) {
hostReason = e instanceof Error ? e.message : String(e);
}
const hostVersion = hostManifest?.metaData?.pluginVersion ?? null;
const hostShared = (hostManifest?.shared ?? []).filter((s) => typeof s.requiredVersion === "string");
const remotes = listFederationRemotes(bosConfig);
const remoteReports = (await Promise.allSettled(remotes.map(async ({ role, url }) => {
let manifest = null;
let reason;
try {
manifest = await fetchManifest(url, timeoutMs);
} catch (e) {
reason = e instanceof Error ? e.message : String(e);
}
const version = manifest?.metaData?.pluginVersion ?? null;
const versionOk = version !== null && version === hostVersion;
if (!manifest) return {
role,
url,
reachable: false,
pluginVersion: null,
ok: false,
reason: reason ?? "unreachable"
};
if (!versionOk) return {
role,
url,
reachable: true,
pluginVersion: version,
ok: false,
reason: hostVersion ? `expected pluginVersion=${hostVersion} (host), got ${version ?? "missing"} — redeploy this plugin` : `host manifest missing pluginVersion`
};
const missingShared = [];
const mismatchedShared = [];
for (const hostDep of hostShared) {
const remDep = manifest.shared?.find((s) => s.name === hostDep.name);
if (!remDep) {
missingShared.push(`${hostDep.name}@${hostDep.requiredVersion}`);
continue;
}
if (!satisfiesConstraint(remDep.version ?? "", hostDep.requiredVersion)) mismatchedShared.push(`${hostDep.name}: remote=${remDep.version ?? "?"} hostReq=${hostDep.requiredVersion}`);
if ((0, every_plugin_build_rspack.isEffectCriticalSharedDep)(hostDep.name)) {
const hostVersion = hostDep.version ?? "";
const remoteVersion = remDep.version ?? "";
if (remoteVersion && remoteVersion !== hostVersion) mismatchedShared.push(`${hostDep.name}: exact-version skew — remote=${remoteVersion} host=${hostVersion} — bundle was built against a different prerelease build; redeploy host and plugin together`);
const remoteRequired = typeof remDep.requiredVersion === "string" ? remDep.requiredVersion.trim() : "";
if (remoteRequired.length > 0 && remoteRequired[0] !== "^" && !satisfiesConstraint(hostVersion, remoteRequired)) mismatchedShared.push(`${hostDep.name}: train skew — host=${hostVersion} vs remote requiredVersion=${remoteRequired}`);
}
}
const reasons = [...missingShared.map((d) => `missing shared "${d}" required by host`), ...mismatchedShared];
return {
role,
url,
reachable: true,
pluginVersion: version,
ok: reasons.length === 0,
reason: reasons[0]
};
}))).map((s, i) => {
const placeholder = {
role: remotes[i].role,
url: remotes[i].url,
reachable: false,
pluginVersion: null,
ok: false
};
return s.status === "fulfilled" ? s.value : {
...placeholder,
reason: String(s.reason)
};
});
return {
ok: hostManifest !== null && remoteReports.every((r) => r.ok && r.reachable && r.pluginVersion !== null),
hostVersion,
hostReachable: hostManifest !== null,
hostReason,
remotes: remoteReports
};
}
function loadBosConfigForMfCheck(configPath) {
return JSON.parse((0, node_fs.readFileSync)(configPath, "utf8"));
}
//#endregion
exports.checkFederationCompat = checkFederationCompat;
exports.ensureNodeRuntimePlugin = ensureNodeRuntimePlugin;
exports.getFederationInstance = getFederationInstance;
exports.installIntegrityFetchHook = installIntegrityFetchHook;
exports.loadBosConfigForMfCheck = loadBosConfigForMfCheck;
exports.loadRemoteModule = loadRemoteModule;
exports.patchManifestFetchForSsrPublicPath = patchManifestFetchForSsrPublicPath;
exports.registerRemote = registerRemote;
//# sourceMappingURL=mf.cjs.map