Project Init

This commit is contained in:
Muluhabt
2026-05-29 15:23:46 +03:00
commit 2fbc557aac
67387 changed files with 6063341 additions and 0 deletions

View File

@@ -0,0 +1,47 @@
//#region src/server/message/Message.ts
var Message = class {
constructor(type, kind) {
this.type = type;
this.kind = kind;
this.time = Date.now();
}
};
//#endregion
//#region src/server/constant.ts
const DEFAULT_WEB_SOCKET_PORT = 16322;
const WEB_SOCKET_CONNECT_MAGIC_ID = "1hpzW-zo2z-o8io-gfmV1-2cb1d82";
const MF_SERVER_IDENTIFIER = "Module Federation DTS";
const WEB_CLIENT_OPTIONS_IDENTIFIER = "__WEB_CLIENT_OPTIONS__";
const DEFAULT_TAR_NAME = "@mf-types.zip";
let UpdateMode = /* @__PURE__ */ function(UpdateMode) {
UpdateMode["POSITIVE"] = "POSITIVE";
UpdateMode["PASSIVE"] = "PASSIVE";
return UpdateMode;
}({});
//#endregion
//#region src/server/message/Action/Action.ts
let ActionKind = /* @__PURE__ */ function(ActionKind) {
ActionKind["ADD_SUBSCRIBER"] = "ADD_SUBSCRIBER";
ActionKind["EXIT_SUBSCRIBER"] = "EXIT_SUBSCRIBER";
ActionKind["ADD_PUBLISHER"] = "ADD_PUBLISHER";
ActionKind["UPDATE_PUBLISHER"] = "UPDATE_PUBLISHER";
ActionKind["NOTIFY_SUBSCRIBER"] = "NOTIFY_SUBSCRIBER";
ActionKind["EXIT_PUBLISHER"] = "EXIT_PUBLISHER";
ActionKind["ADD_WEB_CLIENT"] = "ADD_WEB_CLIENT";
ActionKind["NOTIFY_WEB_CLIENT"] = "NOTIFY_WEB_CLIENT";
ActionKind["FETCH_TYPES"] = "FETCH_TYPES";
ActionKind["ADD_DYNAMIC_REMOTE"] = "ADD_DYNAMIC_REMOTE";
return ActionKind;
}({});
var Action = class extends Message {
constructor(content, kind) {
super("Action", kind);
const { payload } = content;
this.payload = payload;
}
};
//#endregion
export { MF_SERVER_IDENTIFIER as a, WEB_SOCKET_CONNECT_MAGIC_ID as c, DEFAULT_WEB_SOCKET_PORT as i, Message as l, ActionKind as n, UpdateMode as o, DEFAULT_TAR_NAME as r, WEB_CLIENT_OPTIONS_IDENTIFIER as s, Action as t };

View File

@@ -0,0 +1,718 @@
import { a as MF_SERVER_IDENTIFIER, c as WEB_SOCKET_CONNECT_MAGIC_ID, i as DEFAULT_WEB_SOCKET_PORT, l as Message, n as ActionKind, o as UpdateMode } from "./Action-DNNg2YDh.mjs";
import * as fs$1 from "fs";
import * as path$1 from "path";
import { SEPARATOR, createLogger } from "@module-federation/sdk";
import net from "net";
import os from "os";
import WebSocket from "isomorphic-ws";
import { createServer } from "http";
import schedule from "node-schedule";
import { parse } from "url";
//#region src/server/message/API/API.ts
let APIKind = /* @__PURE__ */ function(APIKind) {
APIKind["UPDATE_SUBSCRIBER"] = "UPDATE_SUBSCRIBER";
APIKind["RELOAD_WEB_CLIENT"] = "RELOAD_WEB_CLIENT";
APIKind["FETCH_TYPES"] = "FETCH_TYPES";
return APIKind;
}({});
var API = class extends Message {
constructor(content, kind) {
super("API", kind);
const { code, payload } = content;
this.code = code;
this.payload = payload;
}
};
//#endregion
//#region src/server/message/API/UpdateSubscriber.ts
var UpdateSubscriberAPI = class extends API {
constructor(payload) {
super({
code: 0,
payload
}, APIKind.UPDATE_SUBSCRIBER);
}
};
//#endregion
//#region src/server/message/API/ReloadWebClient.ts
var ReloadWebClientAPI = class extends API {
constructor(payload) {
super({
code: 0,
payload
}, APIKind.RELOAD_WEB_CLIENT);
}
};
//#endregion
//#region src/server/message/API/FetchTypes.ts
var FetchTypesAPI = class extends API {
constructor(payload) {
super({
code: 0,
payload
}, APIKind.FETCH_TYPES);
}
};
//#endregion
//#region src/server/message/Log/Log.ts
let LogLevel = /* @__PURE__ */ function(LogLevel) {
LogLevel["LOG"] = "LOG";
LogLevel["WARN"] = "WARN";
LogLevel["ERROR"] = "ERROR";
return LogLevel;
}({});
let LogKind = /* @__PURE__ */ function(LogKind) {
LogKind["BrokerExitLog"] = "BrokerExitLog";
LogKind["PublisherRegisteredLog"] = "PublisherRegisteredLog";
return LogKind;
}({});
var Log = class extends Message {
constructor(level, kind, ignoreVerbose = false) {
super("Log", kind);
this.ignoreVerbose = false;
this.level = level;
this.ignoreVerbose = ignoreVerbose;
}
};
//#endregion
//#region src/server/message/Log/BrokerExitLog.ts
var BrokerExitLog = class extends Log {
constructor() {
super(LogLevel.LOG, LogKind.BrokerExitLog);
}
};
//#endregion
//#region src/server/utils/log.ts
const logger$1 = createLogger(`[ ${MF_SERVER_IDENTIFIER} ]`);
function fileLog(msg, module, level) {
if (!process?.env?.["FEDERATION_DEBUG"]) return;
try {
const logDir = ".mf";
const logFile = path$1.join(logDir, "typesGenerate.log");
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
if (!fs$1.existsSync(logDir)) fs$1.mkdirSync(logDir, { recursive: true });
fs$1.appendFileSync(logFile, `[${timestamp}] [${level.toUpperCase()}] ${module} - ${msg}\n`);
} catch {}
}
function error(error, action, from) {
const err = error instanceof Error ? error : /* @__PURE__ */ new Error(`${action} error`);
fileLog(`[${action}] error: ${err}`, from, "fatal");
return err.toString();
}
//#endregion
//#region src/server/utils/getIPV4.ts
const localIpv4 = "127.0.0.1";
const getIpv4Interfaces = () => {
try {
const interfaces = os.networkInterfaces();
const ipv4Interfaces = [];
Object.values(interfaces).forEach((detail) => {
detail?.forEach((detail) => {
const familyV4Value = typeof detail.family === "string" ? "IPv4" : 4;
if (detail.family === familyV4Value && detail.address !== localIpv4) ipv4Interfaces.push(detail);
});
});
return ipv4Interfaces;
} catch (_err) {
return [];
}
};
const getIPV4 = () => {
return (getIpv4Interfaces()[0] || { address: localIpv4 }).address;
};
//#endregion
//#region src/server/utils/index.ts
function getIdentifier(options) {
const { ip, name } = options;
return `mf ${SEPARATOR}${name}${ip ? `${SEPARATOR}${ip}` : ""}`;
}
function fib(n) {
let i = 2;
const res = [
0,
1,
1
];
while (i <= n) {
res[i] = res[i - 1] + res[i - 2];
i++;
}
return res[n];
}
function getFreePort() {
return new Promise((resolve, reject) => {
const server = net.createServer();
server.unref();
server.on("error", reject);
server.listen(0, () => {
const { port } = server.address();
server.close(() => {
resolve(port);
});
});
});
}
//#endregion
//#region src/server/Publisher.ts
var Publisher = class {
constructor(ctx) {
this._name = ctx.name;
this._ip = ctx.ip;
this._remoteTypeTarPath = ctx.remoteTypeTarPath;
this._subscribers = /* @__PURE__ */ new Map();
this._ws = ctx.ws;
this.dynamicRemoteMap = /* @__PURE__ */ new Map();
}
get identifier() {
return getIdentifier({
name: this._name,
ip: this._ip
});
}
get name() {
return this._name;
}
get ip() {
return this._ip;
}
get remoteTypeTarPath() {
return this._remoteTypeTarPath;
}
get hasSubscribes() {
return Boolean(this._subscribers.size);
}
get subscribers() {
return this._subscribers;
}
addSubscriber(identifier, subscriber) {
fileLog(`${this.name} set subscriber: ${identifier}`, "Publisher", "info");
this._subscribers.set(identifier, subscriber);
}
removeSubscriber(identifier) {
if (this._subscribers.has(identifier)) {
fileLog(`${this.name} removeSubscriber: ${identifier}`, "Publisher", "warn");
this._subscribers.delete(identifier);
}
}
notifySubscriber(subscriberIdentifier, options) {
const subscriber = this._subscribers.get(subscriberIdentifier);
if (!subscriber) {
fileLog(`[notifySubscriber] ${this.name} notifySubscriber: ${subscriberIdentifier}, does not exits`, "Publisher", "error");
return;
}
const api = new UpdateSubscriberAPI(options);
subscriber.send(JSON.stringify(api));
fileLog(`[notifySubscriber] ${this.name} notifySubscriber: ${JSON.stringify(subscriberIdentifier)}, message: ${JSON.stringify(api)}`, "Publisher", "info");
}
fetchRemoteTypes(options) {
fileLog(`[fetchRemoteTypes] ${this.name} fetchRemoteTypes, options: ${JSON.stringify(options)}, ws: ${Boolean(this._ws)}`, "Publisher", "info");
if (!this._ws) return;
const api = new FetchTypesAPI(options);
this._ws.send(JSON.stringify(api));
}
notifySubscribers(options) {
const api = new UpdateSubscriberAPI(options);
this.broadcast(api);
}
broadcast(message) {
if (this.hasSubscribes) this._subscribers.forEach((subscriber, key) => {
fileLog(`[BroadCast] ${this.name} notifySubscriber: ${key}, PID: ${process.pid}, message: ${JSON.stringify(message)}`, "Publisher", "info");
subscriber.send(JSON.stringify(message));
});
else fileLog(`[BroadCast] ${this.name}'s subscribe is empty`, "Publisher", "warn");
}
close() {
this._ws = void 0;
this._subscribers.forEach((_subscriber, identifier) => {
fileLog(`[BroadCast] close ${this.name} remove: ${identifier}`, "Publisher", "warn");
this.removeSubscriber(identifier);
});
}
};
//#endregion
//#region src/server/message/Action/Update.ts
let UpdateKind = /* @__PURE__ */ function(UpdateKind) {
UpdateKind["UPDATE_TYPE"] = "UPDATE_TYPE";
UpdateKind["RELOAD_PAGE"] = "RELOAD_PAGE";
return UpdateKind;
}({});
//#endregion
//#region src/server/broker/Broker.ts
var Broker = class Broker {
static {
this.WEB_SOCKET_CONNECT_MAGIC_ID = WEB_SOCKET_CONNECT_MAGIC_ID;
}
static {
this.DEFAULT_WEB_SOCKET_PORT = DEFAULT_WEB_SOCKET_PORT;
}
static {
this.DEFAULT_SECURE_WEB_SOCKET_PORT = 16324;
}
static {
this.DEFAULT_WAITING_TIME = 1.5 * 60 * 60 * 1e3;
}
constructor() {
this._publisherMap = /* @__PURE__ */ new Map();
this._webClientMap = /* @__PURE__ */ new Map();
this._tmpSubscriberShelter = /* @__PURE__ */ new Map();
this._scheduleJob = null;
this._setSchedule();
this._startWsServer();
this._stopWhenSIGTERMOrSIGINT();
this._handleUnexpectedExit();
}
get hasPublishers() {
return Boolean(this._publisherMap.size);
}
async _startWsServer() {
const wsHandler = (ws, req) => {
const { url: reqUrl = "" } = req;
const { query } = parse(reqUrl, true);
const { WEB_SOCKET_CONNECT_MAGIC_ID } = query;
if (WEB_SOCKET_CONNECT_MAGIC_ID === Broker.WEB_SOCKET_CONNECT_MAGIC_ID) {
ws.on("message", (message) => {
try {
const text = message.toString();
const action = JSON.parse(text);
fileLog(`${action?.kind} action received `, "Broker", "info");
this._takeAction(action, ws);
} catch (error) {
fileLog(`parse action message error: ${error}`, "Broker", "error");
}
});
ws.on("error", (e) => {
fileLog(`parse action message error: ${e}`, "Broker", "error");
});
} else {
ws.send("Invalid CONNECT ID.");
fileLog("Invalid CONNECT ID.", "Broker", "warn");
ws.close();
}
};
const server = createServer();
this._webSocketServer = new WebSocket.Server({ noServer: true });
this._webSocketServer.on("error", (err) => {
fileLog(`ws error: \n${err.message}\n ${err.stack}`, "Broker", "error");
});
this._webSocketServer.on("listening", () => {
fileLog(`WebSocket server is listening on port ${Broker.DEFAULT_WEB_SOCKET_PORT}`, "Broker", "info");
});
this._webSocketServer.on("connection", wsHandler);
this._webSocketServer.on("close", (code) => {
fileLog(`WebSocket Server Close with Code ${code}`, "Broker", "warn");
this._webSocketServer && this._webSocketServer.close();
this._webSocketServer = void 0;
});
server.on("upgrade", (req, socket, head) => {
if (req.url) {
const { pathname } = parse(req.url);
if (pathname === "/") this._webSocketServer?.handleUpgrade(req, socket, head, (ws) => {
this._webSocketServer?.emit("connection", ws, req);
});
}
});
server.listen(Broker.DEFAULT_WEB_SOCKET_PORT);
}
async _takeAction(action, client) {
const { kind, payload } = action;
if (kind === ActionKind.ADD_PUBLISHER) await this._addPublisher(payload, client);
if (kind === ActionKind.UPDATE_PUBLISHER) await this._updatePublisher(payload, client);
if (kind === ActionKind.ADD_SUBSCRIBER) await this._addSubscriber(payload, client);
if (kind === ActionKind.EXIT_SUBSCRIBER) await this._removeSubscriber(payload, client);
if (kind === ActionKind.EXIT_PUBLISHER) await this._removePublisher(payload, client);
if (kind === ActionKind.ADD_WEB_CLIENT) await this._addWebClient(payload, client);
if (kind === ActionKind.NOTIFY_WEB_CLIENT) await this._notifyWebClient(payload, client);
if (kind === ActionKind.FETCH_TYPES) await this._fetchTypes(payload, client);
if (kind === ActionKind.ADD_DYNAMIC_REMOTE) this._addDynamicRemote(payload);
}
async _addPublisher(context, client) {
const { name, ip, remoteTypeTarPath } = context ?? {};
const identifier = getIdentifier({
name,
ip
});
if (this._publisherMap.has(identifier)) {
fileLog(`[${ActionKind.ADD_PUBLISHER}] ${identifier} has been added, this action will be ignored`, "Broker", "warn");
return;
}
try {
const publisher = new Publisher({
name,
ip,
remoteTypeTarPath,
ws: client
});
this._publisherMap.set(identifier, publisher);
fileLog(`[${ActionKind.ADD_PUBLISHER}] ${identifier} Adding Publisher Succeed`, "Broker", "info");
const tmpSubScribers = this._getTmpSubScribers(identifier);
if (tmpSubScribers) {
fileLog(`[${ActionKind.ADD_PUBLISHER}] consumeTmpSubscriber set ${publisher.name}s subscribers `, "Broker", "info");
this._consumeTmpSubScribers(publisher, tmpSubScribers);
this._clearTmpSubScriberRelation(identifier);
}
} catch (err) {
const msg = error(err, ActionKind.ADD_PUBLISHER, "Broker");
client.send(msg);
client.close();
}
}
async _updatePublisher(context, client) {
const { name, updateMode, updateKind, updateSourcePaths, remoteTypeTarPath, ip } = context ?? {};
const identifier = getIdentifier({
name,
ip
});
if (!this._publisherMap.has(identifier)) {
fileLog(`[${ActionKind.UPDATE_PUBLISHER}] ${identifier} has not been started, this action will be ignored
this._publisherMap: ${JSON.stringify(this._publisherMap.entries())}
`, "Broker", "warn");
return;
}
try {
const publisher = this._publisherMap.get(identifier);
fileLog(`[${ActionKind.UPDATE_PUBLISHER}] ${identifier} update, and notify subscribers to update`, "Broker", "info");
if (publisher) {
publisher.notifySubscribers({
remoteTypeTarPath,
name,
updateMode,
updateKind,
updateSourcePaths: updateSourcePaths || []
});
this._publisherMap.forEach((p) => {
if (p.name === publisher.name) return;
const dynamicRemoteInfo = p.dynamicRemoteMap.get(identifier);
if (dynamicRemoteInfo) {
fileLog(`dynamicRemoteInfo: ${JSON.stringify(dynamicRemoteInfo)}, identifier:${identifier} publish: ${p.name}`, "Broker", "info");
p.fetchRemoteTypes({
remoteInfo: dynamicRemoteInfo,
once: false
});
}
});
}
} catch (err) {
const msg = error(err, ActionKind.UPDATE_PUBLISHER, "Broker");
client.send(msg);
client.close();
}
}
async _fetchTypes(context, _client) {
const { name, ip, remoteInfo } = context ?? {};
const identifier = getIdentifier({
name,
ip
});
try {
const publisher = this._publisherMap.get(identifier);
fileLog(`[${ActionKind.FETCH_TYPES}] ${identifier} fetch types`, "Broker", "info");
if (publisher) publisher.fetchRemoteTypes({
remoteInfo,
once: true
});
} catch (err) {
fileLog(`[${ActionKind.FETCH_TYPES}] ${identifier} fetch types fail , error info: ${err}`, "Broker", "error");
}
}
_addDynamicRemote(context) {
const { name, ip, remoteInfo, remoteIp } = context ?? {};
const identifier = getIdentifier({
name,
ip
});
const publisher = this._publisherMap.get(identifier);
const remoteId = getIdentifier({
name: remoteInfo.name,
ip: remoteIp
});
fileLog(`[${ActionKind.ADD_DYNAMIC_REMOTE}] identifier:${identifier},publisher: ${publisher.name}, remoteId:${remoteId}`, "Broker", "error");
if (!publisher || publisher.dynamicRemoteMap.has(remoteId)) return;
publisher.dynamicRemoteMap.set(remoteId, remoteInfo);
}
async _addSubscriber(context, client) {
const { publishers, name: subscriberName } = context ?? {};
publishers.forEach((publisher) => {
const { name, ip } = publisher;
const identifier = getIdentifier({
name,
ip
});
if (!this._publisherMap.has(identifier)) {
fileLog(`[${ActionKind.ADD_SUBSCRIBER}]: ${identifier} has not been started, ${subscriberName} will add the relation to tmp shelter`, "Broker", "warn");
this._addTmpSubScriberRelation({
name: getIdentifier({
name: context.name,
ip: context.ip
}),
client
}, publisher);
return;
}
try {
const registeredPublisher = this._publisherMap.get(identifier);
if (registeredPublisher) {
registeredPublisher.addSubscriber(getIdentifier({
name: subscriberName,
ip: context.ip
}), client);
fileLog(`[${ActionKind.ADD_SUBSCRIBER}]: ${identifier} has been started, Adding Subscriber ${subscriberName} Succeed, this.__publisherMap are: ${JSON.stringify(Array.from(this._publisherMap.entries()))}`, "Broker", "info");
registeredPublisher.notifySubscriber(getIdentifier({
name: subscriberName,
ip: context.ip
}), {
updateKind: UpdateKind.UPDATE_TYPE,
updateMode: UpdateMode.PASSIVE,
updateSourcePaths: [registeredPublisher.name],
remoteTypeTarPath: registeredPublisher.remoteTypeTarPath,
name: registeredPublisher.name
});
fileLog(`[${ActionKind.ADD_SUBSCRIBER}]: notifySubscriber Subscriber ${subscriberName}, updateMode: "PASSIVE", updateSourcePaths: ${registeredPublisher.name}`, "Broker", "info");
}
} catch (err) {
const msg = error(err, ActionKind.ADD_SUBSCRIBER, "Broker");
client.send(msg);
client.close();
}
});
}
async _removeSubscriber(context, client) {
const { publishers } = context ?? {};
const subscriberIdentifier = getIdentifier({
name: context?.name,
ip: context?.ip
});
publishers.forEach((publisher) => {
const { name, ip } = publisher;
const identifier = getIdentifier({
name,
ip
});
const registeredPublisher = this._publisherMap.get(identifier);
if (!registeredPublisher) {
fileLog(`[${ActionKind.EXIT_SUBSCRIBER}], ${identifier} does not exit `, "Broker", "warn");
return;
}
try {
fileLog(`[${ActionKind.EXIT_SUBSCRIBER}], ${identifier} will exit `, "Broker", "INFO");
registeredPublisher.removeSubscriber(subscriberIdentifier);
this._clearTmpSubScriberRelation(identifier);
if (!registeredPublisher.hasSubscribes) this._publisherMap.delete(identifier);
if (!this.hasPublishers) this.exit();
} catch (err) {
const msg = error(err, ActionKind.EXIT_SUBSCRIBER, "Broker");
client.send(msg);
client.close();
}
});
}
async _removePublisher(context, client) {
const { name, ip } = context ?? {};
const identifier = getIdentifier({
name,
ip
});
const publisher = this._publisherMap.get(identifier);
if (!publisher) {
fileLog(`[${ActionKind.EXIT_PUBLISHER}]: ${identifier}} has not been added, this action will be ingored`, "Broker", "warn");
return;
}
try {
const { subscribers } = publisher;
subscribers.forEach((subscriber, subscriberIdentifier) => {
this._addTmpSubScriberRelation({
name: subscriberIdentifier,
client: subscriber
}, {
name: publisher.name,
ip: publisher.ip
});
fileLog(`[${ActionKind.EXIT_PUBLISHER}]: ${identifier} is removing , subscriber: ${subscriberIdentifier} will be add tmpSubScriberRelation`, "Broker", "info");
});
this._publisherMap.delete(identifier);
fileLog(`[${ActionKind.EXIT_PUBLISHER}]: ${identifier} is removed `, "Broker", "info");
if (!this.hasPublishers) {
fileLog(`[${ActionKind.EXIT_PUBLISHER}]: _publisherMap is empty, all server will exit `, "Broker", "warn");
this.exit();
}
} catch (err) {
const msg = error(err, ActionKind.EXIT_PUBLISHER, "Broker");
client.send(msg);
client.close();
}
}
async _addWebClient(context, client) {
const { name } = context ?? {};
const identifier = getIdentifier({ name });
if (this._webClientMap.has(identifier)) fileLog(`${identifier}} has been added, this action will override prev WebClient`, "Broker", "warn");
try {
this._webClientMap.set(identifier, client);
fileLog(`${identifier} adding WebClient Succeed`, "Broker", "info");
} catch (err) {
const msg = error(err, ActionKind.ADD_WEB_CLIENT, "Broker");
client.send(msg);
client.close();
}
}
async _notifyWebClient(context, client) {
const { name, updateMode } = context ?? {};
const identifier = getIdentifier({ name });
const webClient = this._webClientMap.get(identifier);
if (!webClient) {
fileLog(`[${ActionKind.NOTIFY_WEB_CLIENT}] ${identifier} has not been added, this action will be ignored`, "Broker", "warn");
return;
}
try {
const api = new ReloadWebClientAPI({
name,
updateMode
});
webClient.send(JSON.stringify(api));
fileLog(`[${ActionKind.NOTIFY_WEB_CLIENT}] Notify ${name} WebClient Succeed`, "Broker", "info");
} catch (err) {
const msg = error(err, ActionKind.NOTIFY_WEB_CLIENT, "Broker");
client.send(msg);
client.close();
}
}
_addTmpSubScriberRelation(subscriber, publisher) {
const publisherIdentifier = getIdentifier({
name: publisher.name,
ip: publisher.ip
});
const subscriberIdentifier = subscriber.name;
const shelter = this._tmpSubscriberShelter.get(publisherIdentifier);
if (!shelter) {
const map = /* @__PURE__ */ new Map();
map.set(subscriberIdentifier, subscriber);
this._tmpSubscriberShelter.set(publisherIdentifier, {
subscribers: map,
timestamp: Date.now()
});
fileLog(`[AddTmpSubscriberRelation] ${publisherIdentifier}'s subscriber has ${subscriberIdentifier} `, "Broker", "info");
return;
}
if (shelter.subscribers.get(subscriberIdentifier)) {
fileLog(`[AddTmpSubscriberRelation] ${publisherIdentifier} and ${subscriberIdentifier} relation has been added`, "Broker", "warn");
shelter.subscribers.set(subscriberIdentifier, subscriber);
shelter.timestamp = Date.now();
} else {
fileLog(`AddTmpSubscriberLog ${publisherIdentifier}'s shelter has been added, update shelter.subscribers ${subscriberIdentifier}`, "Broker", "warn");
shelter.subscribers.set(subscriberIdentifier, subscriber);
}
}
_getTmpSubScribers(publisherIdentifier) {
return this._tmpSubscriberShelter.get(publisherIdentifier)?.subscribers;
}
_consumeTmpSubScribers(publisher, tmpSubScribers) {
tmpSubScribers.forEach((tmpSubScriber, identifier) => {
fileLog(`notifyTmpSubScribers ${publisher.name} will be add a subscriber: ${identifier} `, "Broker", "warn");
publisher.addSubscriber(identifier, tmpSubScriber.client);
publisher.notifySubscriber(identifier, {
updateKind: UpdateKind.UPDATE_TYPE,
updateMode: UpdateMode.PASSIVE,
updateSourcePaths: [publisher.name],
remoteTypeTarPath: publisher.remoteTypeTarPath,
name: publisher.name
});
});
}
_clearTmpSubScriberRelation(identifier) {
this._tmpSubscriberShelter.delete(identifier);
}
_clearTmpSubScriberRelations() {
this._tmpSubscriberShelter.clear();
}
_disconnect() {
this._publisherMap.forEach((publisher) => {
publisher.close();
});
}
_setSchedule() {
const rule = new schedule.RecurrenceRule();
if (Number(process.env["FEDERATION_SERVER_TEST"])) {
const interval = Number(process.env["FEDERATION_SERVER_TEST"]) / 1e3;
const second = [];
for (let i = 0; i < 60; i = i + interval) second.push(i);
rule.second = second;
} else {
rule.second = 0;
rule.hour = [
0,
3,
6,
9,
12,
15,
18
];
rule.minute = 0;
}
const serverTest = Number(process.env["FEDERATION_SERVER_TEST"]);
this._scheduleJob = schedule.scheduleJob(rule, () => {
this._tmpSubscriberShelter.forEach((tmpSubscriber, identifier) => {
fileLog(` _clearTmpSubScriberRelation ${identifier}, ${Date.now() - tmpSubscriber.timestamp >= (process.env["GARFISH_MODULE_SERVER_TEST"] ? serverTest : Broker.DEFAULT_WAITING_TIME)}`, "Broker", "info");
if (Date.now() - tmpSubscriber.timestamp >= (process.env["FEDERATION_SERVER_TEST"] ? serverTest : Broker.DEFAULT_WAITING_TIME)) this._clearTmpSubScriberRelation(identifier);
});
});
}
_clearSchedule() {
if (!this._scheduleJob) return;
this._scheduleJob.cancel();
this._scheduleJob = null;
}
_stopWhenSIGTERMOrSIGINT() {
process.on("SIGTERM", () => {
this.exit();
});
process.on("SIGINT", () => {
this.exit();
});
}
_handleUnexpectedExit() {
process.on("unhandledRejection", (error) => {
console.error("Unhandled Rejection Error: ", error);
fileLog(`Unhandled Rejection Error: ${error}`, "Broker", "fatal");
process.exit(1);
});
process.on("uncaughtException", (error) => {
console.error("Unhandled Exception Error: ", error);
fileLog(`Unhandled Rejection Error: ${error}`, "Broker", "fatal");
process.exit(1);
});
}
async start() {}
exit() {
const brokerExitLog = new BrokerExitLog();
this.broadcast(JSON.stringify(brokerExitLog));
this._disconnect();
this._clearSchedule();
this._clearTmpSubScriberRelations();
this._webSocketServer && this._webSocketServer.close();
this._secureWebSocketServer && this._secureWebSocketServer.close();
process.exit(0);
}
broadcast(message) {
fileLog(`[broadcast] exit info : ${JSON.stringify(message)}`, "Broker", "warn");
this._webSocketServer?.clients.forEach((client) => {
client.send(JSON.stringify(message));
});
this._secureWebSocketServer?.clients.forEach((client) => {
client.send(JSON.stringify(message));
});
}
};
//#endregion
export { getIdentifier as a, logger$1 as c, getFreePort as i, LogKind as l, UpdateKind as n, getIPV4 as o, fib as r, fileLog as s, Broker as t, APIKind as u };

View File

@@ -0,0 +1,208 @@
import { a as cloneDeepOptions, n as RpcGMCallTypes, o as getDTSManagerConstructor, s as isDebugMode, t as exposeRpc, x as __exportAll } from "./expose-rpc-BgxOTFXQ.mjs";
import path from "path";
import { randomUUID } from "crypto";
import * as child_process from "child_process";
import { fileURLToPath } from "url";
import * as process$2 from "process";
//#region src/core/rpc/rpc-error.ts
var RpcExitError = class extends Error {
constructor(message, code, signal) {
super(message);
this.code = code;
this.signal = signal;
this.name = "RpcExitError";
}
};
//#endregion
//#region src/core/rpc/wrap-rpc.ts
function createControlledPromise() {
let resolve = () => void 0;
let reject = () => void 0;
return {
promise: new Promise((aResolve, aReject) => {
resolve = aResolve;
reject = aReject;
}),
resolve,
reject
};
}
function wrapRpc(childProcess, options) {
return (async (...args) => {
if (!childProcess.send) throw new Error(`Process ${childProcess.pid} doesn't have IPC channels`);
else if (!childProcess.connected) throw new Error(`Process ${childProcess.pid} doesn't have open IPC channels`);
const { id, once } = options;
const { promise: resultPromise, resolve: resolveResult, reject: rejectResult } = createControlledPromise();
const { promise: sendPromise, resolve: resolveSend, reject: rejectSend } = createControlledPromise();
const handleMessage = (message) => {
if (message?.id === id) {
if (message.type === RpcGMCallTypes.RESOLVE) resolveResult(message.value);
else if (message.type === RpcGMCallTypes.REJECT) rejectResult(message.error);
}
if (once && childProcess?.kill) childProcess.kill("SIGTERM");
};
const handleClose = (code, signal) => {
rejectResult(new RpcExitError(code ? `Process ${childProcess.pid} exited with code ${code}${signal ? ` [${signal}]` : ""}` : `Process ${childProcess.pid} exited${signal ? ` [${signal}]` : ""}`, code, signal));
removeHandlers();
};
const removeHandlers = () => {
childProcess.off("message", handleMessage);
childProcess.off("close", handleClose);
};
if (once) childProcess.once("message", handleMessage);
else childProcess.on("message", handleMessage);
childProcess.on("close", handleClose);
childProcess.send({
type: RpcGMCallTypes.CALL,
id,
args
}, (error) => {
if (error) {
rejectSend(error);
removeHandlers();
} else resolveSend(void 0);
});
return sendPromise.then(() => resultPromise);
});
}
//#endregion
//#region src/core/rpc/rpc-worker.ts
const FEDERATION_WORKER_DATA_ENV_KEY = "VMOK_WORKER_DATA_ENV";
function createRpcWorker(modulePath, data, memoryLimit, once) {
const options = {
env: {
...process$2.env,
[FEDERATION_WORKER_DATA_ENV_KEY]: JSON.stringify(data || {})
},
stdio: [
"inherit",
"inherit",
"inherit",
"ipc"
],
serialization: "advanced"
};
if (memoryLimit) options.execArgv = [`--max-old-space-size=${memoryLimit}`];
let childProcess, remoteMethod;
const id = randomUUID();
return {
connect(...args) {
if (childProcess && !childProcess.connected) {
childProcess.send({
type: RpcGMCallTypes.EXIT,
id
});
childProcess = void 0;
remoteMethod = void 0;
}
if (!childProcess?.connected) {
childProcess = child_process.fork(modulePath, options);
remoteMethod = wrapRpc(childProcess, {
id,
once
});
}
if (!remoteMethod) return Promise.reject(/* @__PURE__ */ new Error("Worker is not connected - cannot perform RPC."));
return remoteMethod(...args);
},
terminate() {
try {
if (childProcess.connected) childProcess.send({
type: RpcGMCallTypes.EXIT,
id
}, (err) => {
if (err) console.error("Error sending message:", err);
});
} catch (error) {
if (error.code === "EPIPE") console.error("Pipe closed before message could be sent:", error);
else console.error("Unexpected error:", error);
}
childProcess = void 0;
remoteMethod = void 0;
},
get connected() {
return Boolean(childProcess?.connected);
},
get process() {
return childProcess;
},
get id() {
return id;
}
};
}
function getRpcWorkerData() {
return JSON.parse(process$2.env[FEDERATION_WORKER_DATA_ENV_KEY] || "{}");
}
//#endregion
//#region src/core/rpc/index.ts
var rpc_exports = /* @__PURE__ */ __exportAll({
RpcExitError: () => RpcExitError,
RpcGMCallTypes: () => RpcGMCallTypes,
createRpcWorker: () => createRpcWorker,
exposeRpc: () => exposeRpc,
getRpcWorkerData: () => getRpcWorkerData,
wrapRpc: () => wrapRpc
});
//#endregion
//#region src/core/lib/DtsWorker.ts
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const __extname = path.extname(__filename);
var DtsWorker = class {
constructor(options) {
this._options = cloneDeepOptions(options);
this.removeUnSerializationOptions();
this.rpcWorker = createRpcWorker(path.resolve(__dirname, `./fork-generate-dts${__extname}`), {}, void 0, true);
this._res = this.rpcWorker.connect(this._options);
}
removeUnSerializationOptions() {
if (this._options.remote?.moduleFederationConfig?.manifest) delete this._options.remote?.moduleFederationConfig?.manifest;
if (this._options.host?.moduleFederationConfig?.manifest) delete this._options.host?.moduleFederationConfig?.manifest;
}
get controlledPromise() {
const ensureChildProcessExit = () => {
try {
const pid = this.rpcWorker.process?.pid;
const rootPid = process.pid;
if (pid && rootPid !== pid) process.kill(pid, 0);
} catch (error) {
if (isDebugMode()) console.error(error);
}
};
return Promise.resolve(this._res).then(() => {
this.exit();
ensureChildProcessExit();
}).catch((err) => {
if (isDebugMode()) console.error(err);
ensureChildProcessExit();
});
}
exit() {
try {
this.rpcWorker?.terminate();
} catch (err) {
if (isDebugMode()) console.error(err);
}
}
};
//#endregion
//#region src/core/lib/generateTypesInChildProcess.ts
async function generateTypesInChildProcess(options) {
return new DtsWorker(options).controlledPromise;
}
//#endregion
//#region src/core/lib/consumeTypes.ts
async function consumeTypes(options) {
await new (getDTSManagerConstructor(options.host?.implementation))(options).consumeTypes();
}
//#endregion
export { createRpcWorker as a, rpc_exports as i, generateTypesInChildProcess as n, DtsWorker as r, consumeTypes as t };

View File

@@ -0,0 +1,5 @@
import { _ as retrieveMfTypesPath, c as isTSProject, d as DTSManager, f as HOST_API_TYPES_FILE_NAME, g as retrieveTypesZipPath, h as retrieveHostConfig, i as retrieveRemoteConfig, l as retrieveTypesAssetsInfo, m as REMOTE_API_TYPES_FILE_NAME, o as getDTSManagerConstructor, p as REMOTE_ALIAS_IDENTIFIER, r as generateTypes, u as validateOptions, v as retrieveOriginalOutDir } from "./expose-rpc-BgxOTFXQ.mjs";
import "./Broker-z82OgzMe.mjs";
import { i as rpc_exports, n as generateTypesInChildProcess, r as DtsWorker, t as consumeTypes } from "./consumeTypes-DCGF9bG3.mjs";
export { DTSManager, DtsWorker, HOST_API_TYPES_FILE_NAME, REMOTE_ALIAS_IDENTIFIER, REMOTE_API_TYPES_FILE_NAME, consumeTypes, generateTypes, generateTypesInChildProcess, getDTSManagerConstructor, isTSProject, retrieveHostConfig, retrieveMfTypesPath, retrieveOriginalOutDir, retrieveRemoteConfig, retrieveTypesAssetsInfo, retrieveTypesZipPath, rpc_exports as rpc, validateOptions };

View File

@@ -0,0 +1,73 @@
import { c as WEB_SOCKET_CONNECT_MAGIC_ID, i as DEFAULT_WEB_SOCKET_PORT, n as ActionKind, t as Action } from "./Action-DNNg2YDh.mjs";
import { t as getIpFromEntry } from "./utils-CkPvDGOy.mjs";
import WebSocket from "isomorphic-ws";
//#region src/server/message/Action/FetchTypes.ts
var FetchTypesAction = class extends Action {
constructor(payload) {
super({ payload }, ActionKind.FETCH_TYPES);
}
};
//#endregion
//#region src/server/message/Action/AddDynamicRemote.ts
var AddDynamicRemoteAction = class extends Action {
constructor(payload) {
super({ payload }, ActionKind.ADD_DYNAMIC_REMOTE);
}
};
//#endregion
//#region src/server/createWebsocket.ts
function createWebsocket() {
return new WebSocket(`ws://127.0.0.1:${DEFAULT_WEB_SOCKET_PORT}?WEB_SOCKET_CONNECT_MAGIC_ID=${WEB_SOCKET_CONNECT_MAGIC_ID}`);
}
//#endregion
//#region src/runtime-plugins/dynamic-remote-type-hints-plugin.ts
const PLUGIN_NAME = "dynamic-remote-type-hints-plugin";
function dynamicRemoteTypeHintsPlugin() {
let ws = createWebsocket();
let isConnected = false;
ws.onopen = () => {
isConnected = true;
};
ws.onerror = (err) => {
console.error(`[ ${PLUGIN_NAME} ] err: ${err}`);
};
return {
name: "dynamic-remote-type-hints-plugin",
registerRemote(args) {
const { remote, origin } = args;
try {
if (!isConnected) return args;
if (!("entry" in remote)) return args;
const defaultIpV4 = typeof FEDERATION_IPV4 === "string" ? FEDERATION_IPV4 : "127.0.0.1";
const remoteIp = getIpFromEntry(remote.entry, defaultIpV4);
const remoteInfo = {
name: remote.name,
url: remote.entry,
alias: remote.alias || remote.name
};
if (remoteIp) ws.send(JSON.stringify(new AddDynamicRemoteAction({
remoteIp,
remoteInfo,
name: origin.name,
ip: defaultIpV4
})));
ws.send(JSON.stringify(new FetchTypesAction({
name: origin.name,
ip: defaultIpV4,
remoteInfo
})));
return args;
} catch (err) {
console.error(new Error(err));
return args;
}
}
};
}
//#endregion
export { dynamicRemoteTypeHintsPlugin as default };

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,113 @@
import { _ as retrieveMfTypesPath, b as ModuleFederationDevServer, g as retrieveTypesZipPath, h as retrieveHostConfig, i as retrieveRemoteConfig, n as RpcGMCallTypes, o as getDTSManagerConstructor, t as exposeRpc, y as createHttpServer } from "./expose-rpc-BgxOTFXQ.mjs";
import { o as UpdateMode, r as DEFAULT_TAR_NAME } from "./Action-DNNg2YDh.mjs";
import { n as UpdateKind, o as getIPV4, s as fileLog } from "./Broker-z82OgzMe.mjs";
import "./consumeTypes-DCGF9bG3.mjs";
import "./core.mjs";
import { t as getIpFromEntry } from "./utils-CkPvDGOy.mjs";
import { decodeName } from "@module-federation/sdk";
//#region src/dev-worker/handleWorkerMessage.ts
function handleDevWorkerMessage(message, options = {}) {
const { moduleServer, processExit = process.exit, pid = process.pid, log = () => void 0 } = options;
log(`ChildProcess(${pid}), message: ${JSON.stringify(message)} `, "forkDevWorker", "info");
if (message.type === RpcGMCallTypes.EXIT) {
log(`ChildProcess(${pid}) SIGTERM, Federation DevServer will exit...`, "forkDevWorker", "error");
moduleServer?.exit();
processExit(0);
}
}
//#endregion
//#region src/dev-worker/forkDevWorker.ts
let typesManager, serverAddress, moduleServer, cacheOptions;
function getLocalRemoteNames(options, encodeNameIdentifier) {
if (!options) return [];
let hostConfig;
try {
hostConfig = retrieveHostConfig(options);
} catch (e) {
fileLog(`getLocalRemoteNames: retrieveHostConfig failed: ${e.message}`, "forkDevWorker", "warn");
return [];
}
const { mapRemotesToDownload } = hostConfig;
return Object.keys(mapRemotesToDownload).reduce((sum, remoteModuleName) => {
const remoteInfo = mapRemotesToDownload[remoteModuleName];
const name = encodeNameIdentifier ? decodeName(remoteInfo.name, encodeNameIdentifier) : remoteInfo.name;
const ip = getIpFromEntry(remoteInfo.url, getIPV4());
if (!ip) return sum;
sum.push({
name,
entry: remoteInfo.url,
ip
});
return sum;
}, []);
}
async function updateCallback({ updateMode, name, remoteTypeTarPath, remoteInfo, once }) {
const { disableHotTypesReload, disableLiveReload } = cacheOptions || {};
fileLog(`sync remote module ${name}, types to ${cacheOptions?.name},typesManager.updateTypes run`, "forkDevWorker", "info");
if (!disableLiveReload && moduleServer) moduleServer.update({
updateKind: UpdateKind.RELOAD_PAGE,
updateMode: UpdateMode.PASSIVE
});
if (!disableHotTypesReload && typesManager) await typesManager.updateTypes({
updateMode,
remoteName: name,
remoteTarPath: remoteTypeTarPath,
remoteInfo,
once
});
}
async function forkDevWorker(options, action) {
if (!typesManager) {
const { name, remote, host, extraOptions } = options;
typesManager = new (getDTSManagerConstructor(remote?.implementation))({
remote,
host,
extraOptions
});
if (!options.disableHotTypesReload && remote) {
const { remoteOptions, tsConfig } = retrieveRemoteConfig(remote);
const mfTypesZipPath = retrieveTypesZipPath(retrieveMfTypesPath(tsConfig, remoteOptions), remoteOptions);
await Promise.all([createHttpServer({ typeTarPath: mfTypesZipPath }).then((res) => {
serverAddress = res.serverAddress;
}), typesManager.generateTypes()]).catch((err) => {
fileLog(`${name} module generateTypes done, localServerAddress: ${JSON.stringify(err)}`, "forkDevWorker", "error");
});
fileLog(`${name} module generateTypes done, localServerAddress: ${serverAddress}`, "forkDevWorker", "info");
}
moduleServer = new ModuleFederationDevServer({
name,
remotes: getLocalRemoteNames(host, extraOptions?.["encodeNameIdentifier"]),
updateCallback,
remoteTypeTarPath: `${serverAddress}/${DEFAULT_TAR_NAME}`
});
cacheOptions = options;
}
if (action === "update" && cacheOptions) {
fileLog(`remoteModule ${cacheOptions.name} receive devWorker update, start typesManager.updateTypes `, "forkDevWorker", "info");
if (!cacheOptions.disableLiveReload) moduleServer?.update({
updateKind: UpdateKind.RELOAD_PAGE,
updateMode: UpdateMode.POSITIVE
});
if (!cacheOptions.disableHotTypesReload) typesManager?.updateTypes({
updateMode: UpdateMode.POSITIVE,
remoteName: cacheOptions.name
}).then(() => {
moduleServer?.update({
updateKind: UpdateKind.UPDATE_TYPE,
updateMode: UpdateMode.POSITIVE
});
});
}
}
process.on("message", (message) => {
handleDevWorkerMessage(message, {
moduleServer,
log: fileLog
});
});
exposeRpc(forkDevWorker);
//#endregion
export { forkDevWorker };

View File

@@ -0,0 +1,14 @@
import { n as RpcGMCallTypes, r as generateTypes, t as exposeRpc } from "./expose-rpc-BgxOTFXQ.mjs";
import "./Broker-z82OgzMe.mjs";
//#region src/core/lib/forkGenerateDts.ts
async function forkGenerateDts(options) {
return generateTypes(options);
}
process.on("message", (message) => {
if (message.type === RpcGMCallTypes.EXIT) process.exit(0);
});
exposeRpc(forkGenerateDts);
//#endregion
export { forkGenerateDts };

View File

@@ -0,0 +1,504 @@
import { a as cloneDeepOptions, c as isTSProject, l as retrieveTypesAssetsInfo, n as RpcGMCallTypes, r as generateTypes, u as validateOptions } from "./expose-rpc-BgxOTFXQ.mjs";
import { s as WEB_CLIENT_OPTIONS_IDENTIFIER } from "./Action-DNNg2YDh.mjs";
import { c as logger$1, o as getIPV4 } from "./Broker-z82OgzMe.mjs";
import { a as createRpcWorker, n as generateTypesInChildProcess, t as consumeTypes } from "./consumeTypes-DCGF9bG3.mjs";
import "./core.mjs";
import fs from "fs";
import * as path$1 from "path";
import path from "path";
import { rm } from "fs/promises";
import { TEMP_DIR, infrastructureLogger, logger, normalizeOptions } from "@module-federation/sdk";
//#region src/dev-worker/DevWorker.ts
var DevWorker = class {
constructor(options) {
this._options = cloneDeepOptions(options);
this.removeUnSerializationOptions();
this._rpcWorker = createRpcWorker(path.resolve(__dirname, "./fork-dev-worker.js"), {}, void 0, false);
this._res = this._rpcWorker.connect(this._options);
}
removeUnSerializationOptions() {
delete this._options.host?.moduleFederationConfig?.manifest;
delete this._options.remote?.moduleFederationConfig?.manifest;
}
get controlledPromise() {
return this._res;
}
update() {
this._rpcWorker.process?.send?.({
type: RpcGMCallTypes.CALL,
id: this._rpcWorker.id,
args: [void 0, "update"]
});
}
exit() {
this._rpcWorker?.terminate();
}
};
//#endregion
//#region src/dev-worker/createDevWorker.ts
async function removeLogFile() {
try {
await rm(path$1.resolve(process.cwd(), ".mf/typesGenerate.log"), {
force: true,
recursive: true
});
} catch (err) {
console.error("removeLogFile error", "forkDevWorker", err);
}
}
function createDevWorker(options) {
removeLogFile();
return new DevWorker({ ...options });
}
//#endregion
//#region src/plugins/utils.ts
function isDev() {
return process.env["NODE_ENV"] === "development";
}
function isPrd() {
return process.env["NODE_ENV"] === "production";
}
function getCompilerOutputDir(compiler) {
try {
return path.relative(compiler.context, compiler.outputPath || compiler.options.output.path);
} catch (err) {
return "";
}
}
//#endregion
//#region src/plugins/DevPlugin.ts
var PROCESS_EXIT_CODE = /* @__PURE__ */ function(PROCESS_EXIT_CODE) {
PROCESS_EXIT_CODE[PROCESS_EXIT_CODE["SUCCESS"] = 0] = "SUCCESS";
PROCESS_EXIT_CODE[PROCESS_EXIT_CODE["FAILURE"] = 1] = "FAILURE";
return PROCESS_EXIT_CODE;
}(PROCESS_EXIT_CODE || {});
function ensureTempDir(filePath) {
try {
const dir = path.dirname(filePath);
fs.mkdirSync(dir, { recursive: true });
} catch (_err) {}
}
var DevPlugin = class DevPlugin {
constructor(options, dtsOptions, generateTypesPromise, fetchRemoteTypeUrlsPromise) {
this.name = "MFDevPlugin";
this._options = options;
this.generateTypesPromise = generateTypesPromise;
this.dtsOptions = dtsOptions;
this.fetchRemoteTypeUrlsPromise = fetchRemoteTypeUrlsPromise;
}
static ensureLiveReloadEntry(options, filePath) {
ensureTempDir(filePath);
const liveReloadEntryWithOptions = fs.readFileSync(path.join(__dirname, "./iife/launch-web-client.iife.js")).toString("utf-8").replace(WEB_CLIENT_OPTIONS_IDENTIFIER, JSON.stringify(options));
fs.writeFileSync(filePath, liveReloadEntryWithOptions);
}
_stopWhenSIGTERMOrSIGINT() {
process.on("SIGTERM", () => {
logger$1.info(`${this._options.name} Process(${process.pid}) SIGTERM, mf server will exit...`);
this._exit(PROCESS_EXIT_CODE.SUCCESS);
});
process.on("SIGINT", () => {
logger$1.info(`${this._options.name} Process(${process.pid}) SIGINT, mf server will exit...`);
this._exit(PROCESS_EXIT_CODE.SUCCESS);
});
}
_handleUnexpectedExit() {
process.on("unhandledRejection", (error) => {
logger$1.error(error);
logger$1.error(`Process(${process.pid}) unhandledRejection, mf server will exit...`);
this._exit(PROCESS_EXIT_CODE.FAILURE);
});
process.on("uncaughtException", (error) => {
logger$1.error(error);
logger$1.error(`Process(${process.pid}) uncaughtException, mf server will exit...`);
this._exit(PROCESS_EXIT_CODE.FAILURE);
});
}
_exit(exitCode = 0) {
this._devWorker?.exit();
process.exit(exitCode);
}
_afterEmit() {
this._devWorker?.update();
}
apply(compiler) {
const { _options: { name, dev, dts } } = this;
const normalizedDev = normalizeOptions(true, {
disableLiveReload: true,
disableHotTypesReload: false,
disableDynamicRemoteTypeHints: false
}, "mfOptions.dev")(dev);
if (!isDev() || normalizedDev === false) return;
new compiler.webpack.DefinePlugin({ FEDERATION_IPV4: JSON.stringify(getIPV4()) }).apply(compiler);
if (normalizedDev.disableHotTypesReload && normalizedDev.disableLiveReload && normalizedDev.disableDynamicRemoteTypeHints) return;
if (!name) throw new Error("name is required if you want to enable dev server!");
if (!normalizedDev.disableDynamicRemoteTypeHints) {
if (!this._options.runtimePlugins) this._options.runtimePlugins = [];
this._options.runtimePlugins.push(path.resolve(__dirname, "dynamic-remote-type-hints-plugin.js"));
}
if (!normalizedDev.disableLiveReload) {
const TEMP_DIR$1 = path.join(`${process.cwd()}/node_modules`, TEMP_DIR);
const filepath = path.join(TEMP_DIR$1, `live-reload.js`);
if (typeof compiler.options.entry === "object") {
DevPlugin.ensureLiveReloadEntry({ name }, filepath);
Object.keys(compiler.options.entry).forEach((entry) => {
const normalizedEntry = compiler.options.entry[entry];
if (typeof normalizedEntry === "object" && Array.isArray(normalizedEntry.import)) normalizedEntry.import.unshift(filepath);
});
}
}
const defaultGenerateTypes = { compileInChildProcess: true };
const defaultConsumeTypes = { consumeAPITypes: true };
const normalizedDtsOptions = normalizeOptions(isTSProject(dts, compiler.context), {
generateTypes: defaultGenerateTypes,
consumeTypes: defaultConsumeTypes,
extraOptions: {},
displayErrorInTerminal: this.dtsOptions?.displayErrorInTerminal
}, "mfOptions.dts")(dts);
const normalizedGenerateTypes = normalizeOptions(Boolean(normalizedDtsOptions), defaultGenerateTypes, "mfOptions.dts.generateTypes")(normalizedDtsOptions === false ? void 0 : normalizedDtsOptions.generateTypes);
const remote = normalizedGenerateTypes === false ? void 0 : {
implementation: normalizedDtsOptions === false ? void 0 : normalizedDtsOptions.implementation,
context: compiler.context,
outputDir: getCompilerOutputDir(compiler),
moduleFederationConfig: { ...this._options },
hostRemoteTypesFolder: normalizedGenerateTypes.typesFolder || "@mf-types",
...normalizedGenerateTypes,
typesFolder: `.dev-server`
};
const normalizedConsumeTypes = normalizeOptions(Boolean(normalizedDtsOptions), defaultConsumeTypes, "mfOptions.dts.consumeTypes")(normalizedDtsOptions === false ? void 0 : normalizedDtsOptions.consumeTypes);
const host = normalizedConsumeTypes === false ? void 0 : {
implementation: normalizedDtsOptions === false ? void 0 : normalizedDtsOptions.implementation,
context: compiler.context,
moduleFederationConfig: this._options,
typesFolder: normalizedConsumeTypes.typesFolder || "@mf-types",
abortOnError: false,
...normalizedConsumeTypes
};
const extraOptions = normalizedDtsOptions ? normalizedDtsOptions.extraOptions || {} : {};
if (!remote && !host && normalizedDev.disableLiveReload) return;
if (remote && !remote?.tsConfigPath && typeof normalizedDtsOptions === "object" && normalizedDtsOptions.tsConfigPath) remote.tsConfigPath = normalizedDtsOptions.tsConfigPath;
Promise.all([this.generateTypesPromise, this.fetchRemoteTypeUrlsPromise]).then(([_, remoteTypeUrls]) => {
this._devWorker = createDevWorker({
name,
remote,
host: {
moduleFederationConfig: {},
...host,
remoteTypeUrls
},
extraOptions,
disableLiveReload: normalizedDev.disableHotTypesReload,
disableHotTypesReload: normalizedDev.disableHotTypesReload
});
});
this._stopWhenSIGTERMOrSIGINT();
this._handleUnexpectedExit();
compiler.hooks.afterEmit.tap(this.name, this._afterEmit.bind(this));
}
};
//#endregion
//#region src/plugins/ConsumeTypesPlugin.ts
const DEFAULT_CONSUME_TYPES = {
abortOnError: false,
consumeAPITypes: true,
typesOnBuild: false
};
const normalizeConsumeTypesOptions = ({ context, dtsOptions, pluginOptions }) => {
const normalizedConsumeTypes = normalizeOptions(true, DEFAULT_CONSUME_TYPES, "mfOptions.dts.consumeTypes")(dtsOptions.consumeTypes);
if (!normalizedConsumeTypes) return;
const dtsManagerOptions = {
host: {
implementation: dtsOptions.implementation,
context,
moduleFederationConfig: pluginOptions,
...normalizedConsumeTypes
},
extraOptions: dtsOptions.extraOptions || {},
displayErrorInTerminal: dtsOptions.displayErrorInTerminal
};
validateOptions(dtsManagerOptions.host);
return dtsManagerOptions;
};
const consumeTypesAPI = async (dtsManagerOptions, cb) => {
return (typeof dtsManagerOptions.host.remoteTypeUrls === "function" ? dtsManagerOptions.host.remoteTypeUrls() : Promise.resolve(dtsManagerOptions.host.remoteTypeUrls)).then((remoteTypeUrls) => {
consumeTypes({
...dtsManagerOptions,
host: {
...dtsManagerOptions.host,
remoteTypeUrls
}
}).then(() => {
typeof cb === "function" && cb(remoteTypeUrls);
}).catch(() => {
typeof cb === "function" && cb(remoteTypeUrls);
});
});
};
var ConsumeTypesPlugin = class {
constructor(pluginOptions, dtsOptions, fetchRemoteTypeUrlsResolve) {
this.pluginOptions = pluginOptions;
this.dtsOptions = dtsOptions;
this.fetchRemoteTypeUrlsResolve = fetchRemoteTypeUrlsResolve;
}
apply(compiler) {
const { dtsOptions, pluginOptions, fetchRemoteTypeUrlsResolve } = this;
const dtsManagerOptions = normalizeConsumeTypesOptions({
context: compiler.context,
dtsOptions,
pluginOptions
});
if (!dtsManagerOptions) {
fetchRemoteTypeUrlsResolve(void 0);
return;
}
if (isPrd() && !dtsManagerOptions.host.typesOnBuild) {
fetchRemoteTypeUrlsResolve(void 0);
return;
}
infrastructureLogger.debug("start fetching remote types...");
const promise = consumeTypesAPI(dtsManagerOptions, fetchRemoteTypeUrlsResolve);
compiler.hooks.thisCompilation.tap("mf:generateTypes", (compilation) => {
compilation.hooks.processAssets.tapPromise({
name: "mf:generateTypes",
stage: compilation.constructor.PROCESS_ASSETS_STAGE_OPTIMIZE_TRANSFER - 1
}, async () => {
await promise;
infrastructureLogger.debug("fetch remote types success!");
});
});
}
};
//#endregion
//#region src/plugins/GenerateTypesPlugin.ts
const DEFAULT_GENERATE_TYPES = {
generateAPITypes: true,
compileInChildProcess: true,
abortOnError: false,
extractThirdParty: false,
extractRemoteTypes: false
};
const normalizeGenerateTypesOptions = ({ context, outputDir, dtsOptions, pluginOptions }) => {
const normalizedGenerateTypes = normalizeOptions(true, DEFAULT_GENERATE_TYPES, "mfOptions.dts.generateTypes")(dtsOptions.generateTypes);
if (!normalizedGenerateTypes) return;
const normalizedConsumeTypes = normalizeOptions(true, {}, "mfOptions.dts.consumeTypes")(dtsOptions.consumeTypes);
const finalOptions = {
remote: {
implementation: dtsOptions.implementation,
context,
outputDir,
moduleFederationConfig: pluginOptions,
...normalizedGenerateTypes
},
host: normalizedConsumeTypes === false ? void 0 : {
context,
moduleFederationConfig: pluginOptions,
...normalizedConsumeTypes,
remoteTypeUrls: typeof normalizedConsumeTypes?.remoteTypeUrls === "object" ? normalizedConsumeTypes?.remoteTypeUrls : void 0
},
extraOptions: dtsOptions.extraOptions || {},
displayErrorInTerminal: dtsOptions.displayErrorInTerminal
};
if (dtsOptions.tsConfigPath && !finalOptions.remote.tsConfigPath) finalOptions.remote.tsConfigPath = dtsOptions.tsConfigPath;
validateOptions(finalOptions.remote);
return finalOptions;
};
const getGenerateTypesFn = (dtsManagerOptions) => {
let fn = generateTypes;
if (dtsManagerOptions.remote.compileInChildProcess) fn = generateTypesInChildProcess;
return fn;
};
const generateTypesAPI = ({ dtsManagerOptions }) => {
return getGenerateTypesFn(dtsManagerOptions)(dtsManagerOptions).then(async () => {
await callAfterGenerateHook({
dtsManagerOptions,
generatedTypes: retrieveTypesAssetsInfo(dtsManagerOptions.remote)
});
});
};
const callAfterGenerateHook = async ({ dtsManagerOptions, generatedTypes }) => {
const afterGenerate = dtsManagerOptions.remote.afterGenerate;
if (!afterGenerate) return;
try {
await afterGenerate(generatedTypes);
} catch (error) {
if (dtsManagerOptions.remote.abortOnError === false) {
if (dtsManagerOptions.displayErrorInTerminal) logger.error(error);
return;
}
throw error;
}
};
const WINDOWS_ABSOLUTE_PATH_REGEXP = /^[a-zA-Z]:[\\/]/;
const isSafeRelativePath = (relativePath) => {
return Boolean(relativePath) && !relativePath.startsWith("..") && !path.isAbsolute(relativePath) && !WINDOWS_ABSOLUTE_PATH_REGEXP.test(relativePath);
};
const resolveEmitAssetName = ({ compilerOutputPath, assetPath, fallbackName }) => {
if (!assetPath) return fallbackName;
const relativePath = path.relative(compilerOutputPath, assetPath);
return isSafeRelativePath(relativePath) ? relativePath : fallbackName;
};
var GenerateTypesPlugin = class {
constructor(pluginOptions, dtsOptions, fetchRemoteTypeUrlsPromise, callback) {
this.pluginOptions = pluginOptions;
this.dtsOptions = dtsOptions;
this.fetchRemoteTypeUrlsPromise = fetchRemoteTypeUrlsPromise;
this.callback = callback;
}
apply(compiler) {
const { dtsOptions, pluginOptions, fetchRemoteTypeUrlsPromise, callback } = this;
const outputDir = getCompilerOutputDir(compiler);
const context = compiler.context;
const dtsManagerOptions = normalizeGenerateTypesOptions({
context,
outputDir,
dtsOptions,
pluginOptions
});
if (!dtsManagerOptions) {
callback();
return;
}
const isProd = !isDev();
const compilerOutputPath = path.resolve(context, outputDir);
const emitTypesFiles = async (compilation) => {
try {
const { zipTypesPath, apiTypesPath, zipName, apiFileName } = retrieveTypesAssetsInfo(dtsManagerOptions.remote);
const emitZipName = resolveEmitAssetName({
compilerOutputPath,
assetPath: zipTypesPath,
fallbackName: zipName
});
const emitApiFileName = resolveEmitAssetName({
compilerOutputPath,
assetPath: apiTypesPath,
fallbackName: apiFileName
});
if (isProd && emitZipName && compilation.getAsset(emitZipName)) {
callback();
return;
}
logger.debug("start generating types...");
await generateTypesAPI({ dtsManagerOptions });
logger.debug("generate types success!");
if (isProd) {
if (zipTypesPath && !compilation.getAsset(emitZipName) && fs.existsSync(zipTypesPath)) compilation.emitAsset(emitZipName, new compiler.webpack.sources.RawSource(fs.readFileSync(zipTypesPath)));
if (apiTypesPath && !compilation.getAsset(emitApiFileName) && fs.existsSync(apiTypesPath)) compilation.emitAsset(emitApiFileName, new compiler.webpack.sources.RawSource(fs.readFileSync(apiTypesPath)));
callback();
} else {
const isEEXIST = (err) => {
return err.code == "EEXIST";
};
if (zipTypesPath && fs.existsSync(zipTypesPath)) {
const zipContent = fs.readFileSync(zipTypesPath);
const zipOutputPath = path.join(compiler.outputPath, emitZipName);
await new Promise((resolve, reject) => {
compiler.outputFileSystem.mkdir(path.dirname(zipOutputPath), { recursive: true }, (err) => {
if (err && !isEEXIST(err)) reject(err);
else compiler.outputFileSystem.writeFile(zipOutputPath, zipContent, (writeErr) => {
if (writeErr && !isEEXIST(writeErr)) reject(writeErr);
else resolve();
});
});
});
}
if (apiTypesPath && fs.existsSync(apiTypesPath)) {
const apiContent = fs.readFileSync(apiTypesPath);
const apiOutputPath = path.join(compiler.outputPath, emitApiFileName);
await new Promise((resolve, reject) => {
compiler.outputFileSystem.mkdir(path.dirname(apiOutputPath), { recursive: true }, (err) => {
if (err && !isEEXIST(err)) reject(err);
else compiler.outputFileSystem.writeFile(apiOutputPath, apiContent, (writeErr) => {
if (writeErr && !isEEXIST(writeErr)) reject(writeErr);
else resolve();
});
});
});
}
callback();
}
} catch (err) {
callback();
if (dtsManagerOptions.displayErrorInTerminal) console.error(err);
logger.debug("generate types fail!");
}
};
compiler.hooks.thisCompilation.tap("mf:generateTypes", (compilation) => {
compilation.hooks.processAssets.tapPromise({
name: "mf:generateTypes",
stage: compilation.constructor.PROCESS_ASSETS_STAGE_OPTIMIZE_TRANSFER
}, async () => {
await fetchRemoteTypeUrlsPromise;
const emitTypesFilesPromise = emitTypesFiles(compilation);
if (isProd) await emitTypesFilesPromise;
});
});
}
};
//#endregion
//#region src/plugins/DtsPlugin.ts
const normalizeDtsOptions = (options, context, defaultOptions) => {
return normalizeOptions(isTSProject(options.dts, context), {
generateTypes: defaultOptions?.defaultGenerateOptions || DEFAULT_GENERATE_TYPES,
consumeTypes: defaultOptions?.defaultConsumeOptions || DEFAULT_CONSUME_TYPES,
extraOptions: {},
displayErrorInTerminal: true
}, "mfOptions.dts")(options.dts);
};
const excludeDts = (filepath) => {
if (typeof filepath !== "string") return false;
const [_p, query] = filepath.split("?");
if (query && query.startsWith("exclude-mf-dts")) return true;
return false;
};
var DtsPlugin = class {
constructor(options) {
this.options = options;
this.clonedOptions = { ...options };
}
apply(compiler) {
const { options, clonedOptions } = this;
if (options.exposes && typeof options.exposes === "object") {
const cleanedExposes = {};
Object.entries(options.exposes).forEach(([key, value]) => {
if (typeof value === "string") {
const [filepath, _query] = value.split("?");
if (excludeDts(value)) return;
cleanedExposes[key] = filepath;
} else {
if (typeof value === "object" && Array.isArray(value.import) && value.import.some((v) => excludeDts(v))) return;
cleanedExposes[key] = value;
}
});
clonedOptions.exposes = cleanedExposes;
}
const normalizedDtsOptions = normalizeDtsOptions(clonedOptions, compiler.context);
if (typeof normalizedDtsOptions !== "object") return;
let fetchRemoteTypeUrlsResolve;
const fetchRemoteTypeUrlsPromise = new Promise((resolve) => {
fetchRemoteTypeUrlsResolve = resolve;
});
let generateTypesPromiseResolve;
new DevPlugin(clonedOptions, normalizedDtsOptions, new Promise((resolve) => {
generateTypesPromiseResolve = resolve;
}), fetchRemoteTypeUrlsPromise).apply(compiler);
new GenerateTypesPlugin(clonedOptions, normalizedDtsOptions, fetchRemoteTypeUrlsPromise, generateTypesPromiseResolve).apply(compiler);
new ConsumeTypesPlugin(clonedOptions, normalizedDtsOptions, fetchRemoteTypeUrlsResolve).apply(compiler);
}
addRuntimePlugins() {
const { options, clonedOptions } = this;
if (!clonedOptions.runtimePlugins) return;
if (!options.runtimePlugins) options.runtimePlugins = [];
clonedOptions.runtimePlugins.forEach((plugin) => {
options.runtimePlugins.includes(plugin) || options.runtimePlugins.push(plugin);
});
}
};
//#endregion
export { DtsPlugin, consumeTypesAPI, generateTypesAPI, isTSProject, normalizeConsumeTypesOptions, normalizeDtsOptions, normalizeGenerateTypesOptions };

View File

@@ -0,0 +1,22 @@
import { s as fileLog, t as Broker } from "./Broker-z82OgzMe.mjs";
//#region src/server/broker/startBroker.ts
let broker;
function getBroker() {
return broker;
}
async function startBroker() {
if (getBroker()) return;
broker = new Broker();
await broker.start();
process.send?.("ready");
}
process.on("message", (message) => {
if (message === "start") {
fileLog(`startBroker... ${process.pid}`, "StartBroker", "info");
startBroker();
}
});
//#endregion
export { getBroker };

View File

@@ -0,0 +1,13 @@
//#region src/dev-worker/utils.ts
const DEFAULT_LOCAL_IPS = ["localhost", "127.0.0.1"];
function getIpFromEntry(entry, ipv4) {
let ip;
entry.replace(/https?:\/\/([0-9|.]+|localhost):/, (str, matched) => {
ip = matched;
return str;
});
if (ip) return DEFAULT_LOCAL_IPS.includes(ip) ? ipv4 : ip;
}
//#endregion
export { getIpFromEntry as t };